diff --git a/Android.mk b/Android.mk index ca76fb4b74de60c2c2c79795acc2b6ddc10e064f..f32129e40fa55b9a23d3f2181831b97b03db51c5 100644 --- a/Android.mk +++ b/Android.mk @@ -64,15 +64,17 @@ endif ## ## READ ME: ######################################################## LOCAL_SRC_FILES += \ + core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl \ + core/java/android/accessibilityservice/IEventListener.aidl \ core/java/android/accounts/IAccountsService.aidl \ core/java/android/app/IActivityPendingResult.aidl \ core/java/android/app/IActivityWatcher.aidl \ core/java/android/app/IAlarmManager.aidl \ + core/java/android/app/IBackupAgent.aidl \ core/java/android/app/IInstrumentationWatcher.aidl \ - core/java/android/app/IIntentReceiver.aidl \ - core/java/android/app/IIntentSender.aidl \ core/java/android/app/INotificationManager.aidl \ core/java/android/app/ISearchManager.aidl \ + core/java/android/app/ISearchManagerCallback.aidl \ core/java/android/app/IServiceConnection.aidl \ core/java/android/app/IStatusBar.aidl \ core/java/android/app/IThumbnailReceiver.aidl \ @@ -80,15 +82,18 @@ LOCAL_SRC_FILES += \ core/java/android/app/IWallpaperService.aidl \ core/java/android/app/IWallpaperServiceCallback.aidl \ core/java/android/backup/IBackupManager.aidl \ - core/java/android/backup/IBackupService.aidl \ + core/java/android/backup/IRestoreObserver.aidl \ + core/java/android/backup/IRestoreSession.aidl \ core/java/android/bluetooth/IBluetoothA2dp.aidl \ core/java/android/bluetooth/IBluetoothDevice.aidl \ core/java/android/bluetooth/IBluetoothDeviceCallback.aidl \ core/java/android/bluetooth/IBluetoothHeadset.aidl \ - core/java/android/content/IContentService.aidl \ + core/java/android/content/IContentService.aidl \ + core/java/android/content/IIntentReceiver.aidl \ + core/java/android/content/IIntentSender.aidl \ core/java/android/content/ISyncAdapter.aidl \ core/java/android/content/ISyncContext.aidl \ - core/java/android/content/ISyncStatusObserver.aidl \ + core/java/android/content/ISyncStatusObserver.aidl \ core/java/android/content/pm/IPackageDataObserver.aidl \ core/java/android/content/pm/IPackageDeleteObserver.aidl \ core/java/android/content/pm/IPackageInstallObserver.aidl \ @@ -106,6 +111,8 @@ LOCAL_SRC_FILES += \ core/java/android/os/IPermissionController.aidl \ core/java/android/os/IPowerManager.aidl \ core/java/android/text/IClipboard.aidl \ + core/java/android/view/accessibility/IAccessibilityManager.aidl \ + core/java/android/view/accessibility/IAccessibilityManagerClient.aidl \ core/java/android/view/IApplicationToken.aidl \ core/java/android/view/IOnKeyguardExitResult.aidl \ core/java/android/view/IRotationWatcher.aidl \ @@ -114,6 +121,8 @@ LOCAL_SRC_FILES += \ core/java/android/view/IWindowSession.aidl \ core/java/android/speech/IRecognitionListener.aidl \ core/java/android/speech/IRecognitionService.aidl \ + core/java/android/speech/tts/ITts.aidl \ + core/java/android/speech/tts/ITtsCallback.aidl \ core/java/com/android/internal/app/IBatteryStats.aidl \ core/java/com/android/internal/app/IUsageStats.aidl \ core/java/com/android/internal/appwidget/IAppWidgetService.aidl \ @@ -131,7 +140,6 @@ LOCAL_SRC_FILES += \ location/java/android/location/IGeocodeProvider.aidl \ location/java/android/location/IGpsStatusListener.aidl \ location/java/android/location/IGpsStatusProvider.aidl \ - location/java/android/location/ILocationCollector.aidl \ location/java/android/location/ILocationListener.aidl \ location/java/android/location/ILocationManager.aidl \ location/java/android/location/ILocationProvider.aidl \ @@ -145,7 +153,8 @@ LOCAL_SRC_FILES += \ telephony/java/com/android/internal/telephony/IIccPhoneBook.aidl \ telephony/java/com/android/internal/telephony/ISms.aidl \ wifi/java/android/net/wifi/IWifiManager.aidl \ - telephony/java/com/android/internal/telephony/IExtendedNetworkService.aidl + telephony/java/com/android/internal/telephony/IExtendedNetworkService.aidl \ + vpn/java/android/net/vpn/IVpnService.aidl \ # FRAMEWORKS_BASE_JAVA_SRC_DIRS comes from build/core/pathmap.mk LOCAL_AIDL_INCLUDES += $(FRAMEWORKS_BASE_JAVA_SRC_DIRS) @@ -190,6 +199,7 @@ aidl_files := \ frameworks/base/core/java/android/app/PendingIntent.aidl \ frameworks/base/core/java/android/content/ComponentName.aidl \ frameworks/base/core/java/android/content/Intent.aidl \ + frameworks/base/core/java/android/content/IntentSender.aidl \ frameworks/base/core/java/android/content/SyncStats.aidl \ frameworks/base/core/java/android/content/res/Configuration.aidl \ frameworks/base/core/java/android/appwidget/AppWidgetProviderInfo.aidl \ @@ -215,7 +225,8 @@ aidl_files := \ frameworks/base/location/java/android/location/Location.aidl \ frameworks/base/telephony/java/android/telephony/ServiceState.aidl \ frameworks/base/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl \ - frameworks/base/telephony/java/com/android/internal/telephony/ITelephony.aidl + frameworks/base/telephony/java/com/android/internal/telephony/ITelephony.aidl \ + frameworks/base/vpn/java/android/net/vpn/IVpnService.aidl \ gen := $(TARGET_OUT_COMMON_INTERMEDIATES)/framework.aidl $(gen): PRIVATE_SRC_FILES := $(aidl_files) @@ -337,7 +348,7 @@ web_docs_sample_code_flags := \ # most current Android platform version included in the SDK package. framework_docs_SDK_VERSION := 1.5 # release version for SDK (ie "Release x") -framework_docs_SDK_REL_ID := 1 +framework_docs_SDK_REL_ID := 2 framework_docs_SDK_CURRENT_DIR := $(framework_docs_SDK_VERSION)_r$(framework_docs_SDK_REL_ID) framework_docs_LOCAL_DROIDDOC_OPTIONS += \ diff --git a/NOTICE b/NOTICE index 267a6aafd2e997ad920e714d5fff6b6db70536b4..bb9c5f269f89ed295bdd7cae09493b5f8cbc026f 100644 --- a/NOTICE +++ b/NOTICE @@ -220,3 +220,54 @@ the Apache2 License. END OF TERMS AND CONDITIONS + + +UNICODE, INC. LICENSE AGREEMENT - DATA FILES AND SOFTWARE + +Unicode Data Files include all data files under the directories +http://www.unicode.org/Public/, http://www.unicode.org/reports/, +and http://www.unicode.org/cldr/data/ . Unicode Software includes any +source code published in the Unicode Standard or under the directories +http://www.unicode.org/Public/, http://www.unicode.org/reports/, and +http://www.unicode.org/cldr/data/. + +NOTICE TO USER: Carefully read the following legal agreement. BY +DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING UNICODE INC.'S DATA +FILES ("DATA FILES"), AND/OR SOFTWARE ("SOFTWARE"), YOU UNEQUIVOCALLY +ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE TERMS AND CONDITIONS OF +THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT DOWNLOAD, INSTALL, COPY, +DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE. + +COPYRIGHT AND PERMISSION NOTICE + +Copyright © 1991-2008 Unicode, Inc. All rights reserved. Distributed +under the Terms of Use in http://www.unicode.org/copyright.html. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Unicode data files and any associated documentation (the +"Data Files") or Unicode software and any associated documentation (the +"Software") to deal in the Data Files or Software without restriction, +including without limitation the rights to use, copy, modify, merge, +publish, distribute, and/or sell copies of the Data Files or Software, +and to permit persons to whom the Data Files or Software are furnished to +do so, provided that (a) the above copyright notice(s) and this permission +notice appear with all copies of the Data Files or Software, (b) both the +above copyright notice(s) and this permission notice appear in associated +documentation, and (c) there is clear notice in each modified Data File +or in the Software as well as in the documentation associated with the +Data File(s) or Software that the data or software has been modified. + +THE DATA FILES AND SOFTWARE 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 +OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS +INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT +OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE +OR PERFORMANCE OF THE DATA FILES OR SOFTWARE. + +Except as contained in this notice, the name of a copyright holder +shall not be used in advertising or otherwise to promote the sale, use +or other dealings in these Data Files or Software without prior written +authorization of the copyright holder. diff --git a/api/4.xml b/api/4.xml index 893301e10009968e8f8c55a7be3405869ffbc5df..c8a2e83fd1368c3d8822c65bbc6d38a854e47d53 100644 --- a/api/4.xml +++ b/api/4.xml @@ -122,17 +122,6 @@ visibility="public" > - - - - - - - - + + - - - - + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -26988,6 +30337,17 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + @@ -134630,6 +139865,23 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -167532,6 +173661,17 @@ deprecated="not deprecated" visibility="public" > + + + + + + + + + + + + + + + + + + + + #include "CameraService.h" +#include #include namespace android { @@ -42,6 +43,7 @@ extern "C" { #include #include #include +#include } // When you enable this, as well as DEBUG_REFS=1 and @@ -63,6 +65,10 @@ extern "C" { static int debug_frame_cnt; #endif +static int getCallingPid() { + return IPCThreadState::self()->getCallingPid(); +} + // ---------------------------------------------------------------------------- void CameraService::instantiate() { @@ -76,6 +82,7 @@ CameraService::CameraService() : BnCameraService() { LOGI("CameraService started: pid=%d", getpid()); + mUsers = 0; } CameraService::~CameraService() @@ -87,72 +94,105 @@ CameraService::~CameraService() sp CameraService::connect(const sp& cameraClient) { - LOGD("Connect E from ICameraClient %p", cameraClient->asBinder().get()); + int callingPid = getCallingPid(); + LOGD("CameraService::connect E (pid %d, client %p)", callingPid, + cameraClient->asBinder().get()); - Mutex::Autolock lock(mLock); + Mutex::Autolock lock(mServiceLock); 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 (%p) is reconnecting...", cameraClient->asBinder().get()); + // This is the same client reconnecting... + LOGD("CameraService::connect X (pid %d, same client %p) is reconnecting...", + callingPid, cameraClient->asBinder().get()); return currentClient; } else { - // it's another client... reject it - LOGD("new client (%p) attempting to connect - rejected", cameraClient->asBinder().get()); + // It's another client... reject it + LOGD("CameraService::connect X (pid %d, new client %p) rejected. " + "(old pid %d, old client %p)", + callingPid, cameraClient->asBinder().get(), + currentClient->mClientPid, currentCameraClient->asBinder().get()); + if (kill(currentClient->mClientPid, 0) == -1 && errno == ESRCH) { + LOGD("The old client is dead!"); + } return client; } } else { // can't promote, the previous client has died... - LOGD("new client connecting, old reference was dangling..."); + LOGD("New client (pid %d) connecting, old reference was dangling...", + callingPid); mClient.clear(); } } + if (mUsers > 0) { + LOGD("Still have client, rejected"); + return client; + } + // create a new Client object - client = new Client(this, cameraClient, IPCThreadState::self()->getCallingPid()); + client = new Client(this, cameraClient, callingPid); mClient = client; #if DEBUG_CLIENT_REFERENCES // Enable tracking for this object, and track increments and decrements of // the refcount. client->trackMe(true, true); #endif - LOGD("Connect X"); + LOGD("CameraService::connect X"); return client; } void CameraService::removeClient(const sp& cameraClient) { - // declar this outside the lock to make absolutely sure the + int callingPid = getCallingPid(); + + // Declare this outside the lock to make absolutely sure the // destructor won't be called with the lock held. sp client; - Mutex::Autolock lock(mLock); + Mutex::Autolock lock(mServiceLock); if (mClient == 0) { // This happens when we have already disconnected. - LOGV("mClient is null."); + LOGD("removeClient (pid %d): already disconnected", callingPid); return; } - // Promote mClient. It should never fail because we're called from - // a binder call, so someone has to have a strong reference. + // Promote mClient. It can fail if we are called from this path: + // Client::~Client() -> disconnect() -> removeClient(). client = mClient.promote(); if (client == 0) { - LOGW("can't get a strong reference on mClient!"); + LOGD("removeClient (pid %d): no more strong reference", callingPid); mClient.clear(); return; } if (cameraClient->asBinder() != client->getCameraClient()->asBinder()) { // ugh! that's not our client!! - LOGW("removeClient() called, but mClient doesn't match!"); + LOGW("removeClient (pid %d): mClient doesn't match!", callingPid); } else { // okay, good, forget about mClient mClient.clear(); } + + LOGD("removeClient (pid %d) done", callingPid); +} + +// The reason we need this count is a new CameraService::connect() request may +// come in while the previous Client's destructor has not been run or is still +// running. If the last strong reference of the previous Client is gone but +// destructor has not been run, we should not allow the new Client to be created +// because we need to wait for the previous Client to tear down the hardware +// first. +void CameraService::incUsers() { + android_atomic_inc(&mUsers); +} + +void CameraService::decUsers() { + android_atomic_dec(&mUsers); } static sp newMediaPlayer(const char *file) @@ -177,7 +217,8 @@ static sp newMediaPlayer(const char *file) CameraService::Client::Client(const sp& cameraService, const sp& cameraClient, pid_t clientPid) { - LOGD("Client E constructor"); + int callingPid = getCallingPid(); + LOGD("Client::Client E (pid %d)", callingPid); mCameraService = cameraService; mCameraClient = cameraClient; mClientPid = clientPid; @@ -189,22 +230,28 @@ CameraService::Client::Client(const sp& cameraService, // Callback is disabled by default mPreviewCallbackFlag = FRAME_CALLBACK_FLAG_NOOP; - LOGD("Client X constructor"); + cameraService->incUsers(); + LOGD("Client::Client X (pid %d)", callingPid); } status_t CameraService::Client::checkPid() { - if (mClientPid == IPCThreadState::self()->getCallingPid()) return NO_ERROR; - LOGW("Attempt to use locked camera (%p) from different process", getCameraClient()->asBinder().get()); + int callingPid = getCallingPid(); + if (mClientPid == callingPid) return NO_ERROR; + LOGW("Attempt to use locked camera (client %p) from different process " + " (old pid %d, new pid %d)", + getCameraClient()->asBinder().get(), mClientPid, callingPid); return -EBUSY; } status_t CameraService::Client::lock() { + int callingPid = getCallingPid(); + LOGD("lock from pid %d (mClientPid %d)", callingPid, mClientPid); Mutex::Autolock _l(mLock); // lock camera to this client if the the camera is unlocked if (mClientPid == 0) { - mClientPid = IPCThreadState::self()->getCallingPid(); + mClientPid = callingPid; return NO_ERROR; } // returns NO_ERROR if the client already owns the camera, -EBUSY otherwise @@ -213,13 +260,14 @@ status_t CameraService::Client::lock() status_t CameraService::Client::unlock() { + int callingPid = getCallingPid(); + LOGD("unlock from pid %d (mClientPid %d)", callingPid, mClientPid); Mutex::Autolock _l(mLock); // allow anyone to use camera - LOGV("unlock (%p)", getCameraClient()->asBinder().get()); status_t result = checkPid(); if (result == NO_ERROR) { mClientPid = 0; - + LOGD("clear mCameraClient (pid %d)", callingPid); // we need to remove the reference so that when app goes // away, the reference count goes to 0. mCameraClient.clear(); @@ -229,15 +277,17 @@ status_t CameraService::Client::unlock() status_t CameraService::Client::connect(const sp& client) { + int callingPid = getCallingPid(); + // connect a new process to the camera - LOGV("connect (%p)", client->asBinder().get()); + LOGD("Client::connect E (pid %d, client %p)", callingPid, client->asBinder().get()); // 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. + // being torn down. { // hold a reference to the old client or we will deadlock if the client is @@ -245,25 +295,30 @@ status_t CameraService::Client::connect(const sp& client) sp oldClient; { Mutex::Autolock _l(mLock); - if (mClientPid != 0) { - LOGW("Tried to connect to locked camera"); + if (mClientPid != 0 && checkPid() != NO_ERROR) { + LOGW("Tried to connect to locked camera (old pid %d, new pid %d)", + mClientPid, callingPid); return -EBUSY; } oldClient = mCameraClient; // did the client actually change? - if (client->asBinder() == mCameraClient->asBinder()) return NO_ERROR; + if (client->asBinder() == mCameraClient->asBinder()) { + LOGD("Connect to the same client"); + return NO_ERROR; + } mCameraClient = client; mClientPid = -1; mPreviewCallbackFlag = FRAME_CALLBACK_FLAG_NOOP; - LOGV("connect new process (%d) to existing camera client", mClientPid); + LOGD("Connect to the new client (pid %d, client %p)", + callingPid, mCameraClient->asBinder().get()); } } // 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(); + mClientPid = callingPid; return NO_ERROR; } @@ -280,8 +335,11 @@ static void *unregister_surface(void *arg) CameraService::Client::~Client() { + int callingPid = getCallingPid(); + // tear down client - LOGD("Client (%p) E destructor", getCameraClient()->asBinder().get()); + LOGD("Client::~Client E (pid %d, client %p)", + callingPid, getCameraClient()->asBinder().get()); if (mSurface != 0 && !mUseOverlay) { #if HAVE_ANDROID_OS pthread_t thr; @@ -307,49 +365,59 @@ CameraService::Client::~Client() } // make sure we tear down the hardware - mClientPid = IPCThreadState::self()->getCallingPid(); + mClientPid = callingPid; disconnect(); - LOGD("Client X destructor"); + LOGD("Client::~Client X (pid %d)", mClientPid); } void CameraService::Client::disconnect() { - LOGD("Client (%p) E disconnect from (%d)", - getCameraClient()->asBinder().get(), - IPCThreadState::self()->getCallingPid()); + int callingPid = getCallingPid(); + + LOGD("Client::disconnect() E (pid %d client %p)", + callingPid, getCameraClient()->asBinder().get()); + Mutex::Autolock lock(mLock); if (mClientPid <= 0) { - LOGV("camera is unlocked, don't tear down hardware"); + LOGD("camera is unlocked (mClientPid = %d), don't tear down hardware", mClientPid); return; } if (checkPid() != NO_ERROR) { - LOGV("Different client - don't disconnect"); + LOGD("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(); - // Cancel all picture callbacks. - mHardware->cancelPicture(true, true, true); - // Release the hardware resources. - mHardware->release(); - } + // Make sure disconnect() is done once and once only, whether it is called + // from the user directly, or called by the destructor. + if (mHardware == 0) return; + + LOGD("hardware teardown"); + // Before destroying mHardware, we must make sure it's in the + // idle state. + mHardware->stopPreview(); + // Cancel all picture callbacks. + mHardware->cancelPicture(true, true, true); + // Release the hardware resources. + mHardware->release(); mHardware.clear(); - LOGD("Client X disconnect"); + + mCameraService->removeClient(mCameraClient); + mCameraService->decUsers(); + + LOGD("Client::disconnect() X (pid %d)", callingPid); } // pass the buffered ISurface to the camera service status_t CameraService::Client::setPreviewDisplay(const sp& surface) { - LOGD("setPreviewDisplay(%p)", surface.get()); + LOGD("setPreviewDisplay(%p) (pid %d)", + ((surface == NULL) ? NULL : surface.get()), getCallingPid()); Mutex::Autolock lock(mLock); status_t result = checkPid(); if (result != NO_ERROR) return result; + Mutex::Autolock surfaceLock(mSurfaceLock); + result = NO_ERROR; // asBinder() is safe on NULL (returns NULL) if (surface->asBinder() != mSurface->asBinder()) { if (mSurface != 0 && !mUseOverlay) { @@ -357,24 +425,35 @@ status_t CameraService::Client::setPreviewDisplay(const sp& surface) mSurface->unregisterBuffers(); } mSurface = surface; + // If preview has been already started, set overlay or register preview + // buffers now. + if (mHardware->previewEnabled()) { + if (mUseOverlay) { + result = setOverlay(); + } else if (mSurface != 0) { + result = registerPreviewBuffers(); + } + } } - return NO_ERROR; + return result; } // set the preview callback flag to affect how the received frames from // preview are handled. void CameraService::Client::setPreviewCallbackFlag(int callback_flag) { - LOGV("setPreviewCallbackFlag"); + LOGV("setPreviewCallbackFlag (pid %d)", getCallingPid()); Mutex::Autolock lock(mLock); if (checkPid() != NO_ERROR) return; mPreviewCallbackFlag = callback_flag; } -// start preview mode, must call setPreviewDisplay first +// start preview mode status_t CameraService::Client::startCameraMode(camera_mode mode) { - LOGD("startCameraMode(%d)", mode); + int callingPid = getCallingPid(); + + LOGD("startCameraMode(%d) (pid %d)", mode, callingPid); /* we cannot call into mHardware with mLock held because * mHardware has callbacks onto us which acquire this lock @@ -389,23 +468,25 @@ status_t CameraService::Client::startCameraMode(camera_mode mode) return INVALID_OPERATION; } - if (mSurface == 0) { - LOGE("setPreviewDisplay must be called before startCameraMode!"); - return INVALID_OPERATION; - } - switch(mode) { case CAMERA_RECORDING_MODE: + if (mSurface == 0) { + LOGE("setPreviewDisplay must be called before startRecordingMode."); + return INVALID_OPERATION; + } return startRecordingMode(); default: // CAMERA_PREVIEW_MODE + if (mSurface == 0) { + LOGD("mSurface is not set yet."); + } return startPreviewMode(); } } status_t CameraService::Client::startRecordingMode() { - LOGV("startRecordingMode"); + LOGD("startRecordingMode (pid %d)", getCallingPid()); status_t ret = UNKNOWN_ERROR; @@ -431,9 +512,65 @@ status_t CameraService::Client::startRecordingMode() return ret; } +status_t CameraService::Client::setOverlay() +{ + LOGD("setOverlay"); + int w, h; + CameraParameters params(mHardware->getParameters()); + params.getPreviewSize(&w, &h); + + const char *format = params.getPreviewFormat(); + int fmt; + 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; + } + + status_t ret = NO_ERROR; + if (mSurface != 0) { + sp ref = mSurface->createOverlay(w, h, fmt); + ret = mHardware->setOverlay(new Overlay(ref)); + } else { + ret = mHardware->setOverlay(NULL); + } + if (ret != NO_ERROR) { + LOGE("mHardware->setOverlay() failed with status %d\n", ret); + } + return ret; +} + +status_t CameraService::Client::registerPreviewBuffers() +{ + int w, h; + CameraParameters params(mHardware->getParameters()); + params.getPreviewSize(&w, &h); + + 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()); + + status_t ret = mSurface->registerBuffers(buffers); + if (ret != NO_ERROR) { + LOGE("registerBuffers failed with status %d", ret); + } + return ret; +} + status_t CameraService::Client::startPreviewMode() { - LOGV("startPreviewMode"); + LOGD("startPreviewMode (pid %d)", getCallingPid()); // if preview has been enabled, nothing needs to be done if (mHardware->previewEnabled()) { @@ -444,55 +581,24 @@ status_t CameraService::Client::startPreviewMode() #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); + status_t ret = NO_ERROR; 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; + // If preview display has been set, set overlay now. + if (mSurface != 0) { + ret = setOverlay(); } + if (ret != NO_ERROR) 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) { - - 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); + if (ret != NO_ERROR) return ret; + // If preview display has been set, register preview buffers now. + if (mSurface != 0) { + // Unregister here because the surface registered with raw heap. + mSurface->unregisterBuffers(); + ret = registerPreviewBuffers(); } } return ret; @@ -500,11 +606,15 @@ status_t CameraService::Client::startPreviewMode() status_t CameraService::Client::startPreview() { + LOGD("startPreview (pid %d)", getCallingPid()); + return startCameraMode(CAMERA_PREVIEW_MODE); } status_t CameraService::Client::startRecording() { + LOGD("startRecording (pid %d)", getCallingPid()); + if (mMediaPlayerBeep.get() != NULL) { mMediaPlayerBeep->seekTo(0); mMediaPlayerBeep->start(); @@ -515,7 +625,7 @@ status_t CameraService::Client::startRecording() // stop preview mode void CameraService::Client::stopPreview() { - LOGD("stopPreview()"); + LOGD("stopPreview (pid %d)", getCallingPid()); Mutex::Autolock lock(mLock); if (checkPid() != NO_ERROR) return; @@ -537,7 +647,7 @@ void CameraService::Client::stopPreview() // stop recording mode void CameraService::Client::stopRecording() { - LOGV("stopRecording()"); + LOGD("stopRecording (pid %d)", getCallingPid()); Mutex::Autolock lock(mLock); if (checkPid() != NO_ERROR) return; @@ -552,15 +662,13 @@ void CameraService::Client::stopRecording() mMediaPlayerBeep->start(); } mHardware->stopRecording(); - LOGV("stopRecording(), hardware stopped OK"); + LOGD("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; @@ -592,7 +700,7 @@ sp CameraService::Client::getClientFromCookie(void* user) sp client = 0; CameraService *service = static_cast(user); if (service != NULL) { - Mutex::Autolock ourLock(service->mLock); + Mutex::Autolock ourLock(service->mServiceLock); if (service->mClient != 0) { client = service->mClient.promote(); if (client == 0) { @@ -704,7 +812,7 @@ void CameraService::Client::recordingCallback(const sp& mem, void* user // take a picture - image is returned in callback status_t CameraService::Client::autoFocus() { - LOGV("autoFocus"); + LOGD("autoFocus (pid %d)", getCallingPid()); Mutex::Autolock lock(mLock); status_t result = checkPid(); @@ -722,7 +830,7 @@ status_t CameraService::Client::autoFocus() // take a picture - image is returned in callback status_t CameraService::Client::takePicture() { - LOGD("takePicture"); + LOGD("takePicture (pid %d)", getCallingPid()); Mutex::Autolock lock(mLock); status_t result = checkPid(); @@ -920,6 +1028,7 @@ void CameraService::Client::postAutoFocus(bool focused) void CameraService::Client::postShutter() { + LOGD("postShutter"); mCameraClient->notifyCallback(CAMERA_MSG_SHUTTER, 0, 0); } @@ -1029,12 +1138,12 @@ status_t CameraService::dump(int fd, const Vector& args) if (checkCallingPermission(String16("android.permission.DUMP")) == false) { snprintf(buffer, SIZE, "Permission Denial: " "can't dump CameraService from pid=%d, uid=%d\n", - IPCThreadState::self()->getCallingPid(), + getCallingPid(), IPCThreadState::self()->getCallingUid()); result.append(buffer); write(fd, result.string(), result.size()); } else { - AutoMutex lock(&mLock); + AutoMutex lock(&mServiceLock); if (mClient != 0) { sp currentClient = mClient.promote(); sprintf(buffer, "Client (%p) PID: %d\n", @@ -1052,8 +1161,6 @@ status_t CameraService::dump(int fd, const Vector& args) } -#if DEBUG_HEAP_LEAKS - #define CHECK_INTERFACE(interface, data, reply) \ do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ LOGW("Call incorrectly routed to " #interface); \ @@ -1085,6 +1192,7 @@ status_t CameraService::onTransact( status_t err = BnCameraService::onTransact(code, data, reply, flags); +#if DEBUG_HEAP_LEAKS LOGD("+++ onTransact err %d code %d", err, code); if (err == UNKNOWN_TRANSACTION || err == PERMISSION_DENIED) { @@ -1120,9 +1228,9 @@ status_t CameraService::onTransact( break; } } +#endif // DEBUG_HEAP_LEAKS + return err; } -#endif // DEBUG_HEAP_LEAKS - }; // namespace android diff --git a/camera/libcameraservice/CameraService.h b/camera/libcameraservice/CameraService.h index 6752f265dabbfaab658d994a1e7180221b7daeb8..0f07673940215bb634a904a47b55bcc4c0c85375 100644 --- a/camera/libcameraservice/CameraService.h +++ b/camera/libcameraservice/CameraService.h @@ -58,10 +58,8 @@ public: void removeClient(const sp& cameraClient); -#if DEBUG_HEAP_LEAKS virtual status_t onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags); -#endif private: @@ -159,6 +157,8 @@ private: status_t startCameraMode(camera_mode mode); status_t startPreviewMode(); status_t startRecordingMode(); + status_t setOverlay(); + status_t registerPreviewBuffers(); // Ensures atomicity among the public methods mutable Mutex mLock; @@ -196,7 +196,12 @@ private: CameraService(); virtual ~CameraService(); - mutable Mutex mLock; + // We use a count for number of clients (shoule only be 0 or 1). + volatile int32_t mUsers; + virtual void incUsers(); + virtual void decUsers(); + + mutable Mutex mServiceLock; wp mClient; #if DEBUG_HEAP_LEAKS diff --git a/cmds/am/am b/cmds/am/am index a5b13f9eb5ae0335ee8b60bcf9f63cdd89d5e5a3..c823634982420d4f3dba997657bbb899d3c85de0 100755 --- a/cmds/am/am +++ b/cmds/am/am @@ -3,5 +3,5 @@ # base=/system export CLASSPATH=$base/framework/am.jar -exec app_process $base/bin com.android.commands.am.Am $* +exec app_process $base/bin com.android.commands.am.Am "$@" diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java index 6d4b4552bbc06dbf2d48349e7825ea0e81989d78..3782136f5c73d8a3beea8834c18e19ba6ed6f039 100644 --- a/cmds/am/src/com/android/commands/am/Am.java +++ b/cmds/am/src/com/android/commands/am/Am.java @@ -26,10 +26,13 @@ import android.content.ComponentName; import android.content.Intent; import android.net.Uri; import android.os.Bundle; +import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.os.ServiceManager; import android.view.IWindowManager; +import java.io.File; +import java.io.FileNotFoundException; import java.util.Iterator; import java.util.Set; @@ -194,18 +197,17 @@ public class Am { if (intent != null) { System.out.println("Starting: " + intent); try { - intent.addFlags(intent.FLAG_ACTIVITY_NEW_TASK); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // XXX should do something to determine the MIME type. int res = mAm.startActivity(null, intent, intent.getType(), null, 0, null, null, 0, false, mDebugOption); switch (res) { case IActivityManager.START_SUCCESS: break; - case IActivityManager.START_CLASS_NOT_FOUND: - System.err.println("Error type 3"); - System.err.println("Error: Activity class " + - intent.getComponent().toShortString() - + " does not exist."); + case IActivityManager.START_SWITCHES_CANCELED: + System.err.println( + "Warning: Activity not started because the " + + " current activity is being kept for the user."); break; case IActivityManager.START_DELIVERED_TO_TOP: System.err.println( @@ -213,25 +215,36 @@ public class Am { + "been delivered to currently running " + "top-most instance."); break; - case IActivityManager.START_FORWARD_AND_REQUEST_CONFLICT: + case IActivityManager.START_RETURN_INTENT_TO_CALLER: System.err.println( - "Error: Activity not started, you requested to " - + "both forward and receive its result"); + "Warning: Activity not started because intent " + + "should be handled by the caller"); + break; + case IActivityManager.START_TASK_TO_FRONT: + System.err.println( + "Warning: Activity not started, its current " + + "task has been brought to the front"); break; case IActivityManager.START_INTENT_NOT_RESOLVED: System.err.println( "Error: Activity not started, unable to " + "resolve " + intent.toString()); break; - case IActivityManager.START_RETURN_INTENT_TO_CALLER: + case IActivityManager.START_CLASS_NOT_FOUND: + System.err.println("Error type 3"); + System.err.println("Error: Activity class " + + intent.getComponent().toShortString() + + " does not exist."); + break; + case IActivityManager.START_FORWARD_AND_REQUEST_CONFLICT: System.err.println( - "Warning: Activity not started because intent " - + "should be handled by the caller"); + "Error: Activity not started, you requested to " + + "both forward and receive its result"); break; - case IActivityManager.START_TASK_TO_FRONT: + case IActivityManager.START_PERMISSION_DENIED: System.err.println( - "Warning: Activity not started, its current " - + "task has been brought to the front"); + "Error: Activity not started, you do not " + + "have permission to access it."); break; default: System.err.println( @@ -436,6 +449,8 @@ public class Am { return; } + ParcelFileDescriptor fd = null; + String cmd = nextArg(); if ("start".equals(cmd)) { start = true; @@ -445,6 +460,16 @@ public class Am { showUsage(); return; } + try { + fd = ParcelFileDescriptor.open( + new File(profileFile), + ParcelFileDescriptor.MODE_CREATE | + ParcelFileDescriptor.MODE_TRUNCATE | + ParcelFileDescriptor.MODE_READ_WRITE); + } catch (FileNotFoundException e) { + System.err.println("Error: Unable to open file: " + profileFile); + return; + } } else if (!"stop".equals(cmd)) { System.err.println("Error: Profile command " + cmd + " not valid"); showUsage(); @@ -452,8 +477,8 @@ public class Am { } try { - if (!mAm.profileControl(process, start, profileFile)) { - System.out.println("PROFILE FAILED on process " + process); + if (!mAm.profileControl(process, start, profileFile, fd)) { + System.err.println("PROFILE FAILED on process " + process); return; } } catch (IllegalArgumentException e) { @@ -516,7 +541,7 @@ public class Am { private void showUsage() { System.err.println("usage: am [start|broadcast|instrument|profile]"); - System.err.println(" am start -D INTENT"); + System.err.println(" am start [-D] INTENT"); System.err.println(" am broadcast INTENT"); System.err.println(" am instrument [-r] [-e ] [-p ]"); System.err.println(" [-w] "); diff --git a/cmds/backup/Android.mk b/cmds/backup/Android.mk new file mode 100644 index 0000000000000000000000000000000000000000..508aec073570c7de9d35d952590c25a780ac8bde --- /dev/null +++ b/cmds/backup/Android.mk @@ -0,0 +1,15 @@ +# Copyright 2009 The Android Open Source Project + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= backup.cpp + +LOCAL_SHARED_LIBRARIES := libcutils libutils + +LOCAL_MODULE:= btool + +LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) +LOCAL_MODULE_TAGS := debug + +include $(BUILD_EXECUTABLE) diff --git a/cmds/backup/NOTICE b/cmds/backup/NOTICE new file mode 100644 index 0000000000000000000000000000000000000000..c5b1efa7aac764ae6d8da63476a2d5cec02a6a5d --- /dev/null +++ b/cmds/backup/NOTICE @@ -0,0 +1,190 @@ + + Copyright (c) 2005-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. + + 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. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + diff --git a/cmds/backup/backup.cpp b/cmds/backup/backup.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d4e669b5d9e8b5c6074886dbbf79a7c8fd803d60 --- /dev/null +++ b/cmds/backup/backup.cpp @@ -0,0 +1,118 @@ +/* + * 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. + */ + +#include +#include + +#include +#include +#include + +using namespace android; + +#include + +int +usage(int argc, const char** argv) +{ + const char* p = argv[0]; + + fprintf(stderr, "%s: Backs up your data.\n" + "\n" + "usage: %s\n" + " Prints all of the data that can be backed up to stdout.\n" + "\n" + "usage: %s list FILE\n" + " Lists the backup entities in the file.\n" + "\n" + "usage: %s print NAME FILE\n" + " Prints the entity named NAME in FILE.\n", + p, p, p, p); + return 1; +} + +int +perform_full_backup() +{ + printf("this would have written all of your data to stdout\n"); + return 0; +} + +int +perform_list(const char* filename) +{ + int err; + int fd; + + fd = open(filename, O_RDONLY); + if (fd == -1) { + fprintf(stderr, "Error opening: %s\n", filename); + return 1; + } + + BackupDataReader reader(fd); + bool done; + int type; + + while (reader.ReadNextHeader(&done, &type) == 0) { + if (done) { + break; + } + switch (type) { + case BACKUP_HEADER_ENTITY_V1: + { + String8 key; + size_t dataSize; + err = reader.ReadEntityHeader(&key, &dataSize); + if (err == 0) { + printf(" entity: %s (%d bytes)\n", key.string(), dataSize); + } else { + printf(" Error reading entity header\n"); + } + break; + } + default: + { + printf("Unknown chunk type: 0x%08x\n", type); + break; + } + } + } + + return 0; +} + +int perform_print(const char* entityname, const char* filename) +{ + printf("perform_print(%s, %s);", entityname, filename); + return 0; +} + +int +main(int argc, const char** argv) +{ + if (argc <= 1) { + return perform_full_backup(); + } + if (argc == 3 && 0 == strcmp(argv[1], "list")) { + return perform_list(argv[2]); + } + if (argc == 4 && 0 == strcmp(argv[1], "print")) { + return perform_print(argv[2], argv[3]); + } + return usage(argc, argv); +} + diff --git a/cmds/bmgr/Android.mk b/cmds/bmgr/Android.mk new file mode 100644 index 0000000000000000000000000000000000000000..8a1670b2ccff0d09e4d34518041016e99fd584c0 --- /dev/null +++ b/cmds/bmgr/Android.mk @@ -0,0 +1,15 @@ +# Copyright 2007 The Android Open Source Project +# +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := $(call all-subdir-java-files) +LOCAL_MODULE := bmgr +include $(BUILD_JAVA_LIBRARY) + + +include $(CLEAR_VARS) +ALL_PREBUILT += $(TARGET_OUT)/bin/bmgr +$(TARGET_OUT)/bin/bmgr : $(LOCAL_PATH)/bmgr | $(ACP) + $(transform-prebuilt-to-target) + diff --git a/cmds/bmgr/MODULE_LICENSE_APACHE2 b/cmds/bmgr/MODULE_LICENSE_APACHE2 new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/cmds/bmgr/NOTICE b/cmds/bmgr/NOTICE new file mode 100644 index 0000000000000000000000000000000000000000..64aaa8dbd68e6917b35c02655ba2f8d763165368 --- /dev/null +++ b/cmds/bmgr/NOTICE @@ -0,0 +1,190 @@ + + 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. + + 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. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + diff --git a/cmds/bmgr/bmgr b/cmds/bmgr/bmgr new file mode 100755 index 0000000000000000000000000000000000000000..6b4bbe2d90326f402b60754420c8abdc3e8897bf --- /dev/null +++ b/cmds/bmgr/bmgr @@ -0,0 +1,7 @@ +# Script to start "bmgr" on the device, which has a very rudimentary +# shell. +# +base=/system +export CLASSPATH=$base/framework/bmgr.jar +exec app_process $base/bin com.android.commands.bmgr.Bmgr "$@" + diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java new file mode 100644 index 0000000000000000000000000000000000000000..ee3ec1aa18fbb16be26da4100b4a4bddfacfb577 --- /dev/null +++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java @@ -0,0 +1,403 @@ +/* + * 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.commands.bmgr; + +import android.backup.IBackupManager; +import android.backup.IRestoreObserver; +import android.backup.IRestoreSession; +import android.backup.RestoreSet; +import android.os.RemoteException; +import android.os.ServiceManager; + +public final class Bmgr { + IBackupManager mBmgr; + IRestoreSession mRestore; + + static final String BMGR_NOT_RUNNING_ERR = + "Error: Could not access the Backup Manager. Is the system running?"; + static final String TRANSPORT_NOT_RUNNING_ERR = + "Error: Could not access the backup transport. Is the system running?"; + + private String[] mArgs; + private int mNextArg; + private String mCurArgData; + + public static void main(String[] args) { + try { + new Bmgr().run(args); + } catch (Exception e) { + System.err.println("Exception caught:"); + e.printStackTrace(); + } + } + + public void run(String[] args) { + boolean validCommand = false; + if (args.length < 1) { + showUsage(); + return; + } + + mBmgr = IBackupManager.Stub.asInterface(ServiceManager.getService("backup")); + if (mBmgr == null) { + System.err.println(BMGR_NOT_RUNNING_ERR); + return; + } + + mArgs = args; + String op = args[0]; + mNextArg = 1; + + if ("enabled".equals(op)) { + doEnabled(); + return; + } + + if ("enable".equals(op)) { + doEnable(); + return; + } + + if ("run".equals(op)) { + doRun(); + return; + } + + if ("backup".equals(op)) { + doBackup(); + return; + } + + if ("list".equals(op)) { + doList(); + return; + } + + if ("restore".equals(op)) { + doRestore(); + return; + } + + if ("transport".equals(op)) { + doTransport(); + return; + } + + if ("wipe".equals(op)) { + doWipe(); + return; + } + + System.err.println("Unknown command"); + showUsage(); + } + + private String enableToString(boolean enabled) { + return enabled ? "enabled" : "disabled"; + } + + private void doEnabled() { + try { + boolean isEnabled = mBmgr.isBackupEnabled(); + System.out.println("Backup Manager currently " + + enableToString(isEnabled)); + } catch (RemoteException e) { + System.err.println(e.toString()); + System.err.println(BMGR_NOT_RUNNING_ERR); + } + } + + private void doEnable() { + String arg = nextArg(); + if (arg == null) { + showUsage(); + return; + } + + try { + boolean enable = Boolean.parseBoolean(arg); + mBmgr.setBackupEnabled(enable); + System.out.println("Backup Manager now " + enableToString(enable)); + } catch (NumberFormatException e) { + showUsage(); + return; + } catch (RemoteException e) { + System.err.println(e.toString()); + System.err.println(BMGR_NOT_RUNNING_ERR); + } + } + + private void doRun() { + try { + mBmgr.backupNow(); + } catch (RemoteException e) { + System.err.println(e.toString()); + System.err.println(BMGR_NOT_RUNNING_ERR); + } + } + + private void doBackup() { + boolean isFull = false; + String pkg = nextArg(); + if ("-f".equals(pkg)) { + isFull = true; + pkg = nextArg(); + } + + if (pkg == null || pkg.startsWith("-")) { + showUsage(); + return; + } + + try { + // !!! TODO: handle full backup + mBmgr.dataChanged(pkg); + } catch (RemoteException e) { + System.err.println(e.toString()); + System.err.println(BMGR_NOT_RUNNING_ERR); + } + } + + private void doTransport() { + try { + String which = nextArg(); + String old = mBmgr.selectBackupTransport(which); + if (old == null) { + System.out.println("Unknown transport '" + which + + "' specified; no changes made."); + } else { + System.out.println("Selected transport " + which + " (formerly " + old + ")"); + } + } catch (RemoteException e) { + System.err.println(e.toString()); + System.err.println(BMGR_NOT_RUNNING_ERR); + } + } + + private void doWipe() { + String pkg = nextArg(); + if (pkg == null) { + showUsage(); + return; + } + + try { + mBmgr.clearBackupData(pkg); + System.out.println("Wiped backup data for " + pkg); + } catch (RemoteException e) { + System.err.println(e.toString()); + System.err.println(BMGR_NOT_RUNNING_ERR); + } + } + + private void doList() { + String arg = nextArg(); // sets, transports, packages set# + if ("transports".equals(arg)) { + doListTransports(); + return; + } + + // The rest of the 'list' options work with a restore session on the current transport + try { + String curTransport = mBmgr.getCurrentTransport(); + mRestore = mBmgr.beginRestoreSession(curTransport); + if (mRestore == null) { + System.err.println(BMGR_NOT_RUNNING_ERR); + return; + } + + if ("sets".equals(arg)) { + doListRestoreSets(); + } else if ("transports".equals(arg)) { + doListTransports(); + } + + mRestore.endRestoreSession(); + } catch (RemoteException e) { + System.err.println(e.toString()); + System.err.println(BMGR_NOT_RUNNING_ERR); + } + } + + private void doListTransports() { + try { + String current = mBmgr.getCurrentTransport(); + String[] transports = mBmgr.listAllTransports(); + if (transports == null || transports.length == 0) { + System.out.println("No transports available."); + return; + } + + for (String t : transports) { + String pad = (t.equals(current)) ? " * " : " "; + System.out.println(pad + t); + } + } catch (RemoteException e) { + System.err.println(e.toString()); + System.err.println(BMGR_NOT_RUNNING_ERR); + } + } + + private void doListRestoreSets() { + try { + RestoreSet[] sets = mRestore.getAvailableRestoreSets(); + if (sets == null || sets.length == 0) { + System.out.println("No restore sets available"); + } else { + printRestoreSets(sets); + } + } catch (RemoteException e) { + System.err.println(e.toString()); + System.err.println(TRANSPORT_NOT_RUNNING_ERR); + } + } + + private void printRestoreSets(RestoreSet[] sets) { + for (RestoreSet s : sets) { + System.out.println(" " + s.token + " : " + s.name); + } + } + + class RestoreObserver extends IRestoreObserver.Stub { + boolean done; + public void restoreStarting(int numPackages) { + System.out.println("restoreStarting: " + numPackages + " packages"); + } + + public void onUpdate(int nowBeingRestored) { + System.out.println("onUpdate: " + nowBeingRestored); + } + + public void restoreFinished(int error) { + System.out.println("restoreFinished: " + error); + synchronized (this) { + done = true; + this.notify(); + } + } + } + + private void doRestore() { + long token; + try { + token = Long.parseLong(nextArg()); + } catch (NumberFormatException e) { + showUsage(); + return; + } + + RestoreObserver observer = new RestoreObserver(); + + try { + boolean didRestore = false; + String curTransport = mBmgr.getCurrentTransport(); + mRestore = mBmgr.beginRestoreSession(curTransport); + if (mRestore == null) { + System.err.println(BMGR_NOT_RUNNING_ERR); + return; + } + RestoreSet[] sets = mRestore.getAvailableRestoreSets(); + for (RestoreSet s : sets) { + if (s.token == token) { + System.out.println("Scheduling restore: " + s.name); + mRestore.performRestore(token, observer); + didRestore = true; + break; + } + } + if (!didRestore) { + if (sets == null || sets.length == 0) { + System.out.println("No available restore sets; no restore performed"); + } else { + System.out.println("No matching restore set token. Available sets:"); + printRestoreSets(sets); + } + } + mRestore.endRestoreSession(); + } catch (RemoteException e) { + System.err.println(e.toString()); + System.err.println(BMGR_NOT_RUNNING_ERR); + } + + // now wait for it to be done + synchronized (observer) { + while (!observer.done) { + try { + observer.wait(); + } catch (InterruptedException ex) { + } + } + } + System.out.println("done"); + } + + private String nextArg() { + if (mNextArg >= mArgs.length) { + return null; + } + String arg = mArgs[mNextArg]; + mNextArg++; + return arg; + } + + private static void showUsage() { + System.err.println("usage: bmgr [backup|restore|list|transport|run]"); + System.err.println(" bmgr backup PACKAGE"); + System.err.println(" bmgr enable BOOL"); + System.err.println(" bmgr enabled"); + System.err.println(" bmgr list transports"); + System.err.println(" bmgr list sets"); + System.err.println(" bmgr transport WHICH"); + System.err.println(" bmgr restore TOKEN"); + System.err.println(" bmgr run"); + System.err.println(" bmgr wipe PACKAGE"); + System.err.println(""); + System.err.println("The 'backup' command schedules a backup pass for the named package."); + System.err.println("Note that the backup pass will effectively be a no-op if the package"); + System.err.println("does not actually have changed data to store."); + System.err.println(""); + System.err.println("The 'enable' command enables or disables the entire backup mechanism."); + System.err.println("If the argument is 'true' it will be enabled, otherwise it will be"); + System.err.println("disabled. When disabled, neither backup or restore operations will"); + System.err.println("be performed."); + System.err.println(""); + System.err.println("The 'enabled' command reports the current enabled/disabled state of"); + System.err.println("the backup mechanism."); + System.err.println(""); + System.err.println("The 'list transports' command reports the names of the backup transports"); + System.err.println("currently available on the device. These names can be passed as arguments"); + System.err.println("to the 'transport' command. The currently selected transport is indicated"); + System.err.println("with a '*' character."); + System.err.println(""); + System.err.println("The 'list sets' command reports the token and name of each restore set"); + System.err.println("available to the device via the current transport."); + System.err.println(""); + System.err.println("The 'transport' command designates the named transport as the currently"); + System.err.println("active one. This setting is persistent across reboots."); + System.err.println(""); + System.err.println("The 'restore' command initiates a restore operation, using the restore set"); + System.err.println("from the current transport whose token matches the argument."); + System.err.println(""); + System.err.println("The 'run' command causes any scheduled backup operation to be initiated"); + System.err.println("immediately, without the usual waiting period for batching together"); + System.err.println("data changes."); + System.err.println(""); + System.err.println("The 'wipe' command causes all backed-up data for the given package to be"); + System.err.println("erased from the current transport's storage. The next backup operation"); + System.err.println("that the given application performs will rewrite its entire data set."); + } +} diff --git a/cmds/bootanimation/Android.mk b/cmds/bootanimation/Android.mk new file mode 100644 index 0000000000000000000000000000000000000000..9c94c2ef3cf95ad29f4de12ad89a97dfdd178f1c --- /dev/null +++ b/cmds/bootanimation/Android.mk @@ -0,0 +1,30 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + bootanimation_main.cpp \ + BootAnimation.cpp + +# need "-lrt" on Linux simulator to pick up clock_gettime +ifeq ($(TARGET_SIMULATOR),true) + ifeq ($(HOST_OS),linux) + LOCAL_LDLIBS += -lrt + endif +endif + +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + libutils \ + libui \ + libcorecg \ + libsgl \ + libEGL \ + libGLESv1_CM + +LOCAL_C_INCLUDES := \ + $(call include-path-for, corecg graphics) + +LOCAL_MODULE:= bootanimation + + +include $(BUILD_EXECUTABLE) diff --git a/libs/surfaceflinger/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp similarity index 94% rename from libs/surfaceflinger/BootAnimation.cpp rename to cmds/bootanimation/BootAnimation.cpp index db403857323a41fa648c5c13e6864e3f90a83d8f..3b9db8db496fd6cc3ad2003a48114582b3175542 100644 --- a/libs/surfaceflinger/BootAnimation.cpp +++ b/cmds/bootanimation/BootAnimation.cpp @@ -22,6 +22,7 @@ #include #include +#include #include #include #include @@ -49,10 +50,9 @@ namespace android { // --------------------------------------------------------------------------- -BootAnimation::BootAnimation(const sp& composer) : - Thread(false) { - mSession = SurfaceComposerClient::clientForConnection( - composer->createConnection()->asBinder()); +BootAnimation::BootAnimation() : Thread(false) +{ + mSession = new SurfaceComposerClient(); } BootAnimation::~BootAnimation() { @@ -131,7 +131,7 @@ status_t BootAnimation::readyToRun() { // create the native surface sp s = session()->createSurface(getpid(), 0, dinfo.w, dinfo.h, - PIXEL_FORMAT_RGB_565); + PIXEL_FORMAT_RGB_565, ISurfaceComposer::eGPU); session()->openTransaction(); s->setLayer(0x40000000); session()->closeTransaction(); @@ -144,7 +144,10 @@ status_t BootAnimation::readyToRun() { EGLConfig config; EGLSurface surface; EGLContext context; + EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + + eglInitialize(display, 0, 0); eglChooseConfig(display, attribs, &config, 1, &numConfigs); mNativeWindowSurface = new EGLNativeWindowSurface(s); @@ -170,17 +173,15 @@ status_t BootAnimation::readyToRun() { return NO_ERROR; } -void BootAnimation::requestExit() { - mBarrier.open(); - Thread::requestExit(); -} - bool BootAnimation::threadLoop() { bool r = android(); eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); eglDestroyContext(mDisplay, mContext); eglDestroySurface(mDisplay, mSurface); mNativeWindowSurface.clear(); + mFlingerSurface.clear(); + eglTerminate(mDisplay); + IPCThreadState::self()->stopProcess(); return r; } @@ -227,8 +228,10 @@ bool BootAnimation::android() { glBindTexture(GL_TEXTURE_2D, mAndroid[0].name); glDrawTexiOES(xc, yc, 0, mAndroid[0].w, mAndroid[0].h); - eglSwapBuffers(mDisplay, mSurface); - + EGLBoolean res = eglSwapBuffers(mDisplay, mSurface); + if (res == EGL_FALSE) + break; + // 12fps: don't animate too fast to preserve CPU const nsecs_t sleepTime = 83333 - ns2us(systemTime() - now); if (sleepTime > 0) diff --git a/libs/surfaceflinger/BootAnimation.h b/cmds/bootanimation/BootAnimation.h similarity index 92% rename from libs/surfaceflinger/BootAnimation.h rename to cmds/bootanimation/BootAnimation.h index 3fb6670b3ef4514111db5ff85529df6aff3373b7..42e9eed7d3eb5079e2bed14387cf0170d23a16d6 100644 --- a/libs/surfaceflinger/BootAnimation.h +++ b/cmds/bootanimation/BootAnimation.h @@ -29,8 +29,6 @@ #include #include -#include "Barrier.h" - class SkBitmap; namespace android { @@ -43,11 +41,10 @@ class EGLNativeWindowSurface; class BootAnimation : public Thread { public: - BootAnimation(const sp& composer); + BootAnimation(); virtual ~BootAnimation(); const sp& session() const; - virtual void requestExit(); private: virtual bool threadLoop(); @@ -73,7 +70,6 @@ private: EGLDisplay mSurface; sp mFlingerSurface; sp mNativeWindowSurface; - Barrier mBarrier; }; // --------------------------------------------------------------------------- diff --git a/cmds/bootanimation/bootanimation_main.cpp b/cmds/bootanimation/bootanimation_main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..675ea8167a0ed238b13dd258d155eba8480e43ec --- /dev/null +++ b/cmds/bootanimation/bootanimation_main.cpp @@ -0,0 +1,52 @@ +/* + * 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 "BootAnimation" + +#include +#include +#include +#include +#include + +#include + +#if defined(HAVE_PTHREADS) +# include +# include +#endif + +#include "BootAnimation.h" + +using namespace android; + +// --------------------------------------------------------------------------- + +int main(int argc, char** argv) +{ +#if defined(HAVE_PTHREADS) + setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_DISPLAY); +#endif + + sp proc(ProcessState::self()); + ProcessState::self()->startThreadPool(); + + // create the boot animation object + sp boot = new BootAnimation(); + + IPCThreadState::self()->joinThreadPool(); + return 0; +} diff --git a/cmds/dumpstate/dumpstate.c b/cmds/dumpstate/dumpstate.c index eabf98e8d0e0feaae8144aaaf89bfa86692a17dc..cc951c1bcbd14bb11c07db173187f339a654ae1a 100644 --- a/cmds/dumpstate/dumpstate.c +++ b/cmds/dumpstate/dumpstate.c @@ -165,6 +165,7 @@ int main(int argc, char *argv[]) { int c, fd, vibrate_fd, fds[2]; char path[PATH_MAX]; pid_t pid; + gid_t groups[] = { AID_LOG, AID_SDCARD_RW }; /* set as high priority, and protect from OOM killer */ setpriority(PRIO_PROCESS, 0, -20); @@ -207,7 +208,7 @@ int main(int argc, char *argv[]) { vibrate_fd = -1; /* switch to non-root user and group */ - setgid(AID_LOG); + setgroups(sizeof(groups)/sizeof(groups[0]), groups); setuid(AID_SHELL); /* make it safe to use both printf and STDOUT_FILENO */ diff --git a/cmds/keystore/Android.mk b/cmds/keystore/Android.mk new file mode 100644 index 0000000000000000000000000000000000000000..3daf44e40a00218d9e124ed39da4e0fbe0b5df11 --- /dev/null +++ b/cmds/keystore/Android.mk @@ -0,0 +1,22 @@ +ifneq ($(TARGET_SIMULATOR),true) + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + netkeystore.c keymgmt.c + +LOCAL_C_INCLUDES := \ + $(call include-path-for, system-core)/cutils \ + external/openssl/include + +LOCAL_SHARED_LIBRARIES := \ + libcutils libssl + +LOCAL_STATIC_LIBRARIES := + +LOCAL_MODULE:= keystore + +include $(BUILD_EXECUTABLE) + +endif # !simulator)) diff --git a/cmds/keystore/certtool.h b/cmds/keystore/certtool.h new file mode 100644 index 0000000000000000000000000000000000000000..aefad668d160144a52b5f3cd5a76bd8395bb830c --- /dev/null +++ b/cmds/keystore/certtool.h @@ -0,0 +1,91 @@ +/* +** +** Copyright 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 __CERTTOOL_H__ +#define __CERTTOOL_H__ + +#include +#include +#include +#include + +#include "common.h" +#include "netkeystore.h" + +#define CERT_NAME_LEN (2 * MAX_KEY_NAME_LENGTH + 2) + +/* + * The specific function 'get_cert' is used in daemons to get the key value + * from keystore. Caller should allocate the buffer and the length of the buffer + * should be MAX_KEY_VALUE_LENGTH. + */ +static inline int get_cert(const char *certname, unsigned char *value, int *size) +{ + int count, fd, ret = -1; + LPC_MARSHAL cmd; + char delimiter[] = "_"; + char *namespace, *keyname; + char *context = NULL; + char cname[CERT_NAME_LEN]; + + if ((certname == NULL) || (value == NULL)) { + LOGE("get_cert: certname or value is null\n"); + return -1; + } + + if (strlcpy(cname, certname, CERT_NAME_LEN) >= CERT_NAME_LEN) { + LOGE("get_cert: keyname is too long\n"); + return -1; + } + + fd = socket_local_client(SOCKET_PATH, + ANDROID_SOCKET_NAMESPACE_RESERVED, + SOCK_STREAM); + if (fd == -1) { + LOGE("Keystore service is not up and running.\n"); + return -1; + } + + cmd.opcode = GET; + if (((namespace = strtok_r(cname, delimiter, &context)) == NULL) || + ((keyname = strtok_r(NULL, delimiter, &context)) == NULL)) { + goto err; + } + if ((cmd.len = snprintf((char*)cmd.data, BUFFER_MAX, "%s %s", namespace, keyname)) + > (2 * MAX_KEY_NAME_LENGTH + 1)) goto err; + + if (write_marshal(fd, &cmd)) { + LOGE("Incorrect command or command line is too long.\n"); + goto err; + } + if (read_marshal(fd, &cmd)) { + LOGE("Failed to read the result.\n"); + goto err; + } + + // copy the result if succeeded. + if (!cmd.retcode && cmd.len <= BUFFER_MAX) { + memcpy(value, cmd.data, cmd.len); + ret = 0; + *size = cmd.len; + } +err: + close(fd); + return ret; +} + +#endif diff --git a/cmds/keystore/common.h b/cmds/keystore/common.h new file mode 100644 index 0000000000000000000000000000000000000000..a18114e91abc7df68931d47e17462fd8824b092f --- /dev/null +++ b/cmds/keystore/common.h @@ -0,0 +1,60 @@ +/* +** +** Copyright 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 __COMMON_H__ +#define __COMMON_H__ + +#define SOCKET_PATH "keystore" +#define KEYSTORE_DIR "/data/misc/keystore/" + +#define READ_TIMEOUT 3 +#define MAX_KEY_NAME_LENGTH 64 +#define MAX_NAMESPACE_LENGTH MAX_KEY_NAME_LENGTH +#define MAX_KEY_VALUE_LENGTH 4096 + +#define BUFFER_MAX MAX_KEY_VALUE_LENGTH + +typedef enum { + BOOTUP, + UNINITIALIZED, + LOCKED, + UNLOCKED, +} KEYSTORE_STATE; + +typedef enum { + LOCK, + UNLOCK, + PASSWD, + GETSTATE, + LISTKEYS, + GET, + PUT, + REMOVE, + RESET, + MAX_OPCODE +} KEYSTORE_OPCODE; + +typedef struct { + uint32_t len; + union { + uint32_t opcode; + uint32_t retcode; + }; + unsigned char data[BUFFER_MAX + 1]; +} LPC_MARSHAL; + +#endif diff --git a/cmds/keystore/keymgmt.c b/cmds/keystore/keymgmt.c new file mode 100644 index 0000000000000000000000000000000000000000..66edd566166a1c78cd01343c870b953c5b34bf6b --- /dev/null +++ b/cmds/keystore/keymgmt.c @@ -0,0 +1,372 @@ +/* +** Copyright 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. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "keymgmt.h" + +static int retry_count = 0; +static unsigned char iv[IV_LEN]; +static KEYSTORE_STATE state = BOOTUP; +static AES_KEY encryptKey, decryptKey; + +inline void unlock_keystore(unsigned char *master_key) +{ + AES_set_encrypt_key(master_key, AES_KEY_LEN, &encryptKey); + AES_set_decrypt_key(master_key, AES_KEY_LEN, &decryptKey); + memset(master_key, 0, sizeof(master_key)); + state = UNLOCKED; +} + +inline void lock_keystore() +{ + memset(&encryptKey, 0 , sizeof(AES_KEY)); + memset(&decryptKey, 0 , sizeof(AES_KEY)); + state = LOCKED; +} + +inline void get_encrypt_key(char *passwd, AES_KEY *key) +{ + unsigned char user_key[USER_KEY_LEN]; + gen_key(passwd, user_key, USER_KEY_LEN); + AES_set_encrypt_key(user_key, AES_KEY_LEN, key); +} + +inline void get_decrypt_key(char *passwd, AES_KEY *key) +{ + unsigned char user_key[USER_KEY_LEN]; + gen_key(passwd, user_key, USER_KEY_LEN); + AES_set_decrypt_key(user_key, AES_KEY_LEN, key); +} + +static int gen_random_blob(unsigned char *key, int size) +{ + int ret = 0; + int fd = open("/dev/urandom", O_RDONLY); + if (fd == -1) return -1; + if (read(fd, key, size) != size) ret = -1; + close(fd); + return ret; +} + +static int encrypt_n_save(AES_KEY *enc_key, DATA_BLOB *blob, + const char *keyfile) +{ + int size, fd, ret = -1; + unsigned char enc_blob[MAX_BLOB_LEN]; + + char tmpfile[KEYFILE_LEN]; + strcpy(tmpfile, keyfile); + strcat(tmpfile, ".tmp"); + + // prepare the blob + memcpy(blob->iv, iv, IV_LEN); + blob->blob_size = get_blob_size(blob); + memcpy(enc_blob, blob->blob, blob->blob_size); + AES_cbc_encrypt((unsigned char *)enc_blob, (unsigned char *)blob->blob, + blob->blob_size, enc_key, iv, AES_ENCRYPT); + // write to keyfile + size = data_blob_size(blob); + if ((fd = open(tmpfile, O_CREAT|O_RDWR)) == -1) return -1; + if (write(fd, blob, size) == size) ret = 0; + close(fd); + if (!ret) { + unlink(keyfile); + rename(tmpfile, keyfile); + chmod(keyfile, 0440); + } + return ret; +} + +static int load_n_decrypt(const char *keyname, const char *keyfile, + AES_KEY *key, DATA_BLOB *blob) +{ + int fd, ret = -1; + if ((fd = open(keyfile, O_RDONLY)) == -1) return -1; + // get the encrypted blob and iv + if ((read(fd, blob->iv, sizeof(blob->iv)) != sizeof(blob->iv)) || + (read(fd, &blob->blob_size, sizeof(uint32_t)) != sizeof(uint32_t)) || + (blob->blob_size > MAX_BLOB_LEN)) { + goto err; + } else { + unsigned char enc_blob[MAX_BLOB_LEN]; + if (read(fd, enc_blob, blob->blob_size) != + (int) blob->blob_size) goto err; + // decrypt the blob + AES_cbc_encrypt((unsigned char *)enc_blob, (unsigned char*)blob->blob, + blob->blob_size, key, blob->iv, AES_DECRYPT); + if (strcmp(keyname, (char*)blob->keyname) == 0) ret = 0; + } +err: + close(fd); + return ret; +} + +static int store_master_key(char *upasswd, unsigned char *master_key) +{ + AES_KEY key; + DATA_BLOB blob; + + // prepare the blob + strlcpy(blob.keyname, MASTER_KEY_TAG, USER_KEY_LEN); + blob.value_size = USER_KEY_LEN; + memcpy((void*)blob.value, (const void*)master_key, USER_KEY_LEN); + + // generate the encryption key + get_encrypt_key(upasswd, &key); + return encrypt_n_save(&key, &blob, MASTER_KEY); +} + +static int get_master_key(char *upasswd, unsigned char *master_key) +{ + AES_KEY key; + int size, ret = 0; + DATA_BLOB blob; + + get_decrypt_key(upasswd, &key); + ret = load_n_decrypt(MASTER_KEY_TAG, MASTER_KEY, &key, &blob); + if (!ret) memcpy(master_key, blob.value, blob.value_size); + return ret; +} + +static int create_master_key(char *upasswd) +{ + int ret; + unsigned char mpasswd[AES_KEY_LEN]; + unsigned char master_key[USER_KEY_LEN]; + + gen_random_blob(mpasswd, AES_KEY_LEN); + gen_key((char*)mpasswd, master_key, USER_KEY_LEN); + if ((ret = store_master_key(upasswd, master_key)) == 0) { + unlock_keystore(master_key); + } + memset(master_key, 0, USER_KEY_LEN); + memset(mpasswd, 0, AES_KEY_LEN); + + return ret; +} + +static int change_passwd(char *data) +{ + unsigned char master_key[USER_KEY_LEN]; + char *old_pass, *new_pass = NULL, *p, *delimiter=" "; + int ret, count = 0; + char *context = NULL; + + old_pass = p = strtok_r(data, delimiter, &context); + while (p != NULL) { + count++; + new_pass = p; + p = strtok_r(NULL, delimiter, &context); + } + if (count != 2) return -1; + if (strlen(new_pass) < MIN_PASSWD_LENGTH) return -1; + if ((ret = get_master_key(old_pass, master_key)) == 0) { + ret = store_master_key(new_pass, master_key); + retry_count = 0; + } else { + ret = MAX_RETRY_COUNT - ++retry_count; + if (ret == 0) { + retry_count = 0; + LOGE("passwd:reach max retry count, reset the keystore now."); + reset_keystore(); + return -1; + } + + } + return ret; +} + +int remove_key(const char *namespace, const char *keyname) +{ + char keyfile[KEYFILE_LEN]; + + if (state != UNLOCKED) return -state; + sprintf(keyfile, KEYFILE_NAME, namespace, keyname); + return unlink(keyfile); +} + +int put_key(const char *namespace, const char *keyname, + unsigned char *data, int size) +{ + DATA_BLOB blob; + uint32_t real_size; + char keyfile[KEYFILE_LEN]; + + if (state != UNLOCKED) { + LOGE("Can not store key with current state %d\n", state); + return -state; + } + sprintf(keyfile, KEYFILE_NAME, namespace, keyname); + // flatten the args + strcpy(blob.keyname, keyname); + blob.value_size = size; + memcpy(blob.value, data, size); + return encrypt_n_save(&encryptKey, &blob, keyfile); +} + +int get_key(const char *namespace, const char *keyname, + unsigned char *data, int *size) +{ + int ret; + DATA_BLOB blob; + uint32_t blob_size; + char keyfile[KEYFILE_LEN]; + + if (state != UNLOCKED) { + LOGE("Can not retrieve key value with current state %d\n", state); + return -state; + } + sprintf(keyfile, KEYFILE_NAME, namespace, keyname); + ret = load_n_decrypt(keyname, keyfile, &decryptKey, &blob); + if (!ret) { + if ((blob.value_size > MAX_KEY_VALUE_LENGTH)) { + ret = -1; + } else { + *size = blob.value_size; + memcpy(data, blob.value, *size); + } + } + return ret; +} + +int list_keys(const char *namespace, char reply[BUFFER_MAX]) +{ + DIR *d; + struct dirent *de; + + if (state != UNLOCKED) { + LOGE("Can not list key with current state %d\n", state); + return -1; + } + + if (!namespace || ((d = opendir("."))) == NULL) { + LOGE("cannot open keystore dir or namespace is null\n"); + return -1; + } + while ((de = readdir(d))) { + char *prefix, *name, *keyfile = de->d_name; + char *context = NULL; + + if (de->d_type != DT_REG) continue; + if ((prefix = strtok_r(keyfile, NAME_DELIMITER, &context)) + == NULL) continue; + if (strcmp(prefix, namespace)) continue; + if ((name = strtok_r(NULL, NAME_DELIMITER, &context)) == NULL) continue; + // append the key name into reply + if (reply[0] != 0) strlcat(reply, " ", BUFFER_MAX); + if (strlcat(reply, name, BUFFER_MAX) >= BUFFER_MAX) { + LOGE("too many files under keystore directory\n"); + return -1; + } + } + closedir(d); + return 0; +} + +int passwd(char *data) +{ + if (state == UNINITIALIZED) { + if (strchr(data, ' ')) return -1; + if (strlen(data) < MIN_PASSWD_LENGTH) return -1; + return create_master_key(data); + } + return change_passwd(data); +} + +int lock() +{ + switch(state) { + case UNLOCKED: + lock_keystore(); + case LOCKED: + return 0; + default: + return -1; + } +} + +int unlock(char *passwd) +{ + unsigned char master_key[USER_KEY_LEN]; + int ret = get_master_key(passwd, master_key); + if (!ret) { + unlock_keystore(master_key); + retry_count = 0; + } else { + ret = MAX_RETRY_COUNT - ++retry_count; + if (ret == 0) { + retry_count = 0; + LOGE("unlock:reach max retry count, reset the keystore now."); + reset_keystore(); + return -1; + } + } + return ret; +} + +KEYSTORE_STATE get_state() +{ + return state; +} + +int reset_keystore() +{ + DIR *d; + struct dirent *de; + + if ((d = opendir(".")) == NULL) { + LOGE("cannot open keystore dir\n"); + return -1; + } + while ((de = readdir(d))) unlink(de->d_name); + closedir(d); + state = UNINITIALIZED; + LOGI("keystore is reset."); + return 0; +} + +int init_keystore(const char *dir) +{ + int fd; + + if (!dir) mkdir(dir, 0770); + if (!dir || chdir(dir)) { + LOGE("Can not open/create the keystore directory %s\n", + dir ? dir : "(null)"); + return -1; + } + gen_random_blob(iv, IV_LEN); + if ((fd = open(MASTER_KEY, O_RDONLY)) == -1) { + state = UNINITIALIZED; + return 0; + } + close(fd); + state = LOCKED; + return 0; +} diff --git a/cmds/keystore/keymgmt.h b/cmds/keystore/keymgmt.h new file mode 100644 index 0000000000000000000000000000000000000000..0e928db494fdb99e0bd03aff0271613bc186a508 --- /dev/null +++ b/cmds/keystore/keymgmt.h @@ -0,0 +1,82 @@ +/* +** Copyright 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 __KEYMGMT_H__ +#define __KEYMGMT_H__ + +#define MASTER_KEY_TAG "master_key" +#define MASTER_KEY ".keymaster" +#define MAX_PATH_LEN 128 +#define SALT "Android Keystore 0.1" +#define NAME_DELIMITER "_" +#define KEYFILE_NAME "%s"NAME_DELIMITER"%s" +#define KEYGEN_ITER 1024 +#define AES_KEY_LEN 128 +#define USER_KEY_LEN (AES_KEY_LEN/8) +#define IV_LEN USER_KEY_LEN +#define MAX_RETRY_COUNT 6 +#define MIN_PASSWD_LENGTH 8 + +#define gen_key(passwd, key, len) \ + PKCS5_PBKDF2_HMAC_SHA1(passwd, strlen(passwd), \ + (unsigned char*)SALT, \ + strlen(SALT), KEYGEN_ITER, \ + len, key) + +#define KEYFILE_LEN MAX_NAMESPACE_LENGTH + MAX_KEY_NAME_LENGTH + 6 + +#define get_blob_size(blob) \ + (((blob->value_size + sizeof(uint32_t) + MAX_KEY_NAME_LENGTH \ + + USER_KEY_LEN - 1) / USER_KEY_LEN) * USER_KEY_LEN) + +#define MAX_BLOB_LEN ((MAX_KEY_VALUE_LENGTH + MAX_KEY_NAME_LENGTH + \ + sizeof(uint32_t) + USER_KEY_LEN - 1) / USER_KEY_LEN)\ + * USER_KEY_LEN + +#define data_blob_size(blob) USER_KEY_LEN + sizeof(uint32_t) + blob->blob_size + +typedef struct { + unsigned char iv[USER_KEY_LEN]; + uint32_t blob_size; + union { + unsigned char blob[1]; + struct { + uint32_t value_size; + char keyname[MAX_KEY_NAME_LENGTH]; + unsigned char value[MAX_KEY_VALUE_LENGTH]; + } __attribute__((packed)); + }; +} DATA_BLOB; + +typedef struct { + char tag[USER_KEY_LEN]; + unsigned char master_key[USER_KEY_LEN]; +} MASTER_BLOB; + +int put_key(const char *namespace, const char *keyname, + unsigned char *data, int size); +int get_key(const char *namespace, const char *keyname, + unsigned char *data, int *size); +int remove_key(const char *namespace, const char *keyname); +int list_keys(const char *namespace, char reply[BUFFER_MAX]); +int passwd(char *data); +int lock(); +int unlock(char *passwd); +KEYSTORE_STATE get_state(); +int reset_keystore(); +int init_keystore(const char *dir); + +#endif diff --git a/cmds/keystore/keystore_get.h b/cmds/keystore/keystore_get.h new file mode 100644 index 0000000000000000000000000000000000000000..a7fd9a556af8d5f8759464649b674acbfc73a52c --- /dev/null +++ b/cmds/keystore/keystore_get.h @@ -0,0 +1,53 @@ +/* +** +** Copyright 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 __KEYSTORE_GET_H__ +#define __KEYSTORE_GET_H__ + +#include +#include +#include + +#include "certtool.h" + +/* This function is provided to native components to get values from keystore. + * Users are required to link against libcutils. If something goes wrong, NULL + * is returned. Otherwise it returns the value in dynamically allocated memory + * and sets the size if the pointer is not NULL. One can release the memory by + * calling free(). */ +static char *keystore_get(char *key, int *size) +{ + char buffer[MAX_KEY_VALUE_LENGTH]; + char *value; + int length; + + if (get_cert(key, (unsigned char *)buffer, &length) != 0) { + return NULL; + } + value = malloc(length + 1); + if (!value) { + return NULL; + } + memcpy(value, buffer, length); + value[length] = 0; + if (size) { + *size = length; + } + return value; +} + +#endif diff --git a/cmds/keystore/netkeystore.c b/cmds/keystore/netkeystore.c new file mode 100644 index 0000000000000000000000000000000000000000..eac455e0c5447530a0ae33a95edae89315f61315 --- /dev/null +++ b/cmds/keystore/netkeystore.c @@ -0,0 +1,410 @@ +/* +** Copyright 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 "keystore" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "netkeystore.h" +#include "keymgmt.h" + +#define CMD_PUT_WITH_FILE "putfile" + +typedef void CMD_FUNC(LPC_MARSHAL *cmd, LPC_MARSHAL *reply); + +struct cmdinfo { + const char *name; + CMD_FUNC *func; +}; + +static CMD_FUNC do_lock; +static CMD_FUNC do_unlock; +static CMD_FUNC do_passwd; +static CMD_FUNC do_get_state;; +static CMD_FUNC do_listkeys; +static CMD_FUNC do_get_key; +static CMD_FUNC do_put_key; +static CMD_FUNC do_remove_key; +static CMD_FUNC do_reset_keystore; + +#define str(x) #x + +struct cmdinfo cmds[] = { + { str(LOCK), do_lock }, + { str(UNLOCK), do_unlock }, + { str(PASSWD), do_passwd }, + { str(GETSTATE), do_get_state }, + { str(LISTKEYS), do_listkeys }, + { str(GET), do_get_key }, + { str(PUT), do_put_key }, + { str(REMOVE), do_remove_key }, + { str(RESET), do_reset_keystore }, +}; + +static struct ucred cr; + +static int check_get_perm(int uid) +{ + if (uid == AID_WIFI || uid == AID_VPN) return 0; + return -1; +} + +static int check_reset_perm(int uid) +{ + if (uid == AID_SYSTEM) return 0; + return -1; +} + +static int parse_keyname(char *name, uint32_t len, + char *namespace, char *keyname) +{ + int count = 0; + char *c = namespace, *p = namespace, *t = name; + + if (!name || !namespace || !keyname) return -1; + while (t < name + len && (*t != 0)) { + if (*t == ' ') { + if (c == keyname) return -1; + *p = count = 0; + c = p = keyname; + t++; + } else { + if (!isalnum(*t)) return -1; + *p++ = *t++; + // also check if the keyname/namespace is too long. + if (count++ == MAX_KEY_NAME_LENGTH) return -1; + } + } + *p = 0; + return 0; +} + +// args of passwd(): +// firstPassword - for the first time +// oldPassword newPassword - for changing the password +static void do_passwd(LPC_MARSHAL *cmd, LPC_MARSHAL *reply) +{ + reply->retcode = passwd((char*)cmd->data); +} + +// args of lock(): +// no argument +static void do_lock(LPC_MARSHAL *cmd, LPC_MARSHAL *reply) +{ + reply->retcode = lock(); +} + +// args of unlock(): +// password +static void do_unlock(LPC_MARSHAL *cmd, LPC_MARSHAL *reply) +{ + reply->retcode = unlock((char*)cmd->data); +} + +// args of get_state(): +// no argument +static void do_get_state(LPC_MARSHAL *cmd, LPC_MARSHAL *reply) +{ + reply->retcode = get_state(); +} + +// args of listkeys(): +// namespace +static void do_listkeys(LPC_MARSHAL *cmd, LPC_MARSHAL *reply) +{ + reply->retcode = list_keys((const char*)cmd->data, (char*)reply->data); + if (!reply->retcode) reply->len = strlen((char*)reply->data); +} + +// args of get(): +// namespace keyname +static void do_get_key(LPC_MARSHAL *cmd, LPC_MARSHAL *reply) +{ + char namespace[MAX_KEY_NAME_LENGTH]; + char keyname[MAX_KEY_NAME_LENGTH]; + + if (check_get_perm(cr.uid)) { + LOGE("uid %d doesn't have the permission to get key value\n", cr.uid); + reply->retcode = -1; + return; + } + + if (parse_keyname((char*)cmd->data, cmd->len, namespace, keyname)) { + reply->retcode = -1; + } else { + reply->retcode = get_key(namespace, keyname, reply->data, + (int*)&reply->len); + } +} + +static int get_value_index(LPC_MARSHAL *cmd) +{ + uint32_t count = 0, i; + for (i = 0 ; i < cmd->len ; ++i) { + if (cmd->data[i] == ' ') { + if (++count == 2) return ++i; + } + } + return -1; +} + +// args of put(): +// namespace keyname keyvalue +static void do_put_key(LPC_MARSHAL *cmd, LPC_MARSHAL *reply) +{ + char namespace[MAX_KEY_NAME_LENGTH]; + char keyname[MAX_KEY_NAME_LENGTH]; + + int p = get_value_index(cmd); + if (p == -1) { + reply->retcode = -1; + } else { + unsigned char *value; + if (parse_keyname((char*)cmd->data, p - 1, namespace, keyname)) { + reply->retcode = -1; + return; + } + value = &cmd->data[p]; + int len = cmd->len - p; + reply->retcode = put_key(namespace, keyname, value, len); + } +} + +// args of remove_key(): +// namespace keyname +static void do_remove_key(LPC_MARSHAL *cmd, LPC_MARSHAL *reply) +{ + char namespace[MAX_KEY_NAME_LENGTH]; + char keyname[MAX_KEY_NAME_LENGTH]; + if (parse_keyname((char*)cmd->data, cmd->len, namespace, keyname)) { + reply->retcode = -1; + return; + } + reply->retcode = remove_key(namespace, keyname); +} + +// args of reset_keystore(): +// no argument +static void do_reset_keystore(LPC_MARSHAL *cmd, LPC_MARSHAL *reply) +{ + if (check_reset_perm(cr.uid)) { + LOGE("uid %d doesn't have the permission to reset the keystore\n", + cr.uid); + reply->retcode = -1; + return; + } + reply->retcode = reset_keystore(); +} + +static void execute(LPC_MARSHAL *cmd, LPC_MARSHAL *reply) +{ + uint32_t cmd_max = sizeof(cmds)/sizeof(struct cmdinfo); + + if (cmd->opcode >= cmd_max) { + LOGE("the opcode (%d) is not valid", cmd->opcode); + reply->retcode = -1; + return; + } + cmds[cmd->opcode].func(cmd, reply); +} + +static int set_read_timeout(int socket) +{ + struct timeval tv; + tv.tv_sec = READ_TIMEOUT; + if (setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof tv)) + { + LOGE("setsockopt failed"); + return -1; + } + return 0; +} + +static int append_input_from_file(const char *filename, LPC_MARSHAL *cmd) +{ + int fd, len, ret = 0; + + // get opcode of the function put() + if ((fd = open(filename, O_RDONLY)) == -1) { + fprintf(stderr, "Can not open file %s\n", filename); + return -1; + } + cmd->data[cmd->len] = ' '; + cmd->len++; + len = read(fd, cmd->data + cmd->len, BUFFER_MAX - cmd->len); + if (len < 0 || (len == (int)(BUFFER_MAX - cmd->len))) { + ret = -1; + } else { + cmd->len += len; + } + close(fd); + return ret; +} + +static int flatten_str_args(int argc, const char **argv, LPC_MARSHAL *cmd) +{ + int i, len = 0; + char *buf = (char*)cmd->data; + buf[0] = 0; + for (i = 0 ; i < argc ; ++i) { + if (i == 0) { + len = strlcpy(buf, argv[i], BUFFER_MAX); + } else { + len += snprintf(buf + len, BUFFER_MAX - len, " %s", argv[i]); + } + if (len >= BUFFER_MAX) return -1; + } + if (len) cmd->len = len; + return 0; +} + +static int parse_cmd(int argc, const char **argv, LPC_MARSHAL *cmd) +{ + uint32_t i, len = 0; + uint32_t cmd_max = sizeof(cmds)/sizeof(cmds[0]); + + for (i = 0 ; i < cmd_max ; ++i) { + if (!strcasecmp(argv[0], cmds[i].name)) break; + } + + if (i == cmd_max) { + // check if this is a command to put the key value with a file. + if (strcmp(argv[0], CMD_PUT_WITH_FILE) != 0) return -1; + cmd->opcode = PUT; + if (argc != 4) { + fprintf(stderr, "%s args\n\tnamespace keyname filename\n", + argv[0]); + return -1; + } + if (flatten_str_args(argc - 2, argv + 1, cmd)) return -1; + return append_input_from_file(argv[3], cmd); + } else { + cmd->opcode = i; + return flatten_str_args(argc - 1, argv + 1, cmd); + } +} + +static int shell_command(const int argc, const char **argv) +{ + int fd, i; + LPC_MARSHAL cmd; + + if (parse_cmd(argc, argv , &cmd)) { + fprintf(stderr, "Incorrect command or command line is too long.\n"); + exit(1); + } + fd = socket_local_client(SOCKET_PATH, + ANDROID_SOCKET_NAMESPACE_RESERVED, + SOCK_STREAM); + if (fd == -1) { + fprintf(stderr, "Keystore service is not up and running.\n"); + exit(1); + } + + if (write_marshal(fd, &cmd)) { + fprintf(stderr, "Incorrect command or command line is too long.\n"); + exit(1); + } + if (read_marshal(fd, &cmd)) { + fprintf(stderr, "Failed to read the result.\n"); + exit(1); + } + cmd.data[cmd.len] = 0; + fprintf(stdout, "%s\n", (cmd.retcode == 0) ? "Succeeded!" : "Failed!"); + if (cmd.len) fprintf(stdout, "\t%s\n", (char*)cmd.data); + close(fd); + return 0; +} + +int main(const int argc, const char *argv[]) +{ + struct sockaddr addr; + socklen_t alen; + int lsocket, s; + LPC_MARSHAL cmd, reply; + + if (argc > 1) { + return shell_command(argc - 1, argv + 1); + } + + if (init_keystore(KEYSTORE_DIR)) { + LOGE("Can not initialize the keystore, the directory exist?\n"); + exit(1); + } + + lsocket = android_get_control_socket(SOCKET_PATH); + if (lsocket < 0) { + LOGE("Failed to get socket from environment: %s\n", strerror(errno)); + exit(1); + } + if (listen(lsocket, 5)) { + LOGE("Listen on socket failed: %s\n", strerror(errno)); + exit(1); + } + fcntl(lsocket, F_SETFD, FD_CLOEXEC); + memset(&reply, 0, sizeof(LPC_MARSHAL)); + + for (;;) { + socklen_t cr_size = sizeof(cr); + alen = sizeof(addr); + s = accept(lsocket, &addr, &alen); + + /* retrieve the caller info here */ + if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &cr_size) < 0) { + close(s); + LOGE("Unable to recieve socket options\n"); + continue; + } + + if (s < 0) { + LOGE("Accept failed: %s\n", strerror(errno)); + continue; + } + fcntl(s, F_SETFD, FD_CLOEXEC); + if (set_read_timeout(s)) { + close(s); + continue; + } + + // read the command, execute and send the result back. + if(read_marshal(s, &cmd)) goto err; + LOGI("new connection\n"); + execute(&cmd, &reply); + write_marshal(s, &reply); +err: + memset(&reply, 0, sizeof(LPC_MARSHAL)); + LOGI("closing connection\n"); + close(s); + } + + return 0; +} diff --git a/cmds/keystore/netkeystore.h b/cmds/keystore/netkeystore.h new file mode 100644 index 0000000000000000000000000000000000000000..a87a667e91236a88a7737ab5948729fe670962cc --- /dev/null +++ b/cmds/keystore/netkeystore.h @@ -0,0 +1,96 @@ +/* +** +** Copyright 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 __NETKEYSTORE_H__ +#define __NETKEYSTORE_H__ + +#include +#include +#include + +#include "common.h" + +static inline int readx(int s, void *_buf, int count) +{ + char *buf = _buf; + int n = 0, r; + if (count < 0) return -1; + while (n < count) { + r = read(s, buf + n, count - n); + if (r < 0) { + if (errno == EINTR) continue; + LOGE("read error: %s\n", strerror(errno)); + return -1; + } + if (r == 0) { + LOGE("eof\n"); + return -1; /* EOF */ + } + n += r; + } + return 0; +} + +static inline int writex(int s, const void *_buf, int count) +{ + const char *buf = _buf; + int n = 0, r; + if (count < 0) return -1; + while (n < count) { + r = write(s, buf + n, count - n); + if (r < 0) { + if (errno == EINTR) continue; + LOGE("write error: %s\n", strerror(errno)); + return -1; + } + n += r; + } + return 0; +} + +static inline int read_marshal(int s, LPC_MARSHAL *cmd) +{ + if (readx(s, cmd, 2 * sizeof(uint32_t))) { + LOGE("failed to read header\n"); + return -1; + } + if (cmd->len > BUFFER_MAX) { + LOGE("invalid size %d\n", cmd->len); + return -1; + } + if (readx(s, cmd->data, cmd->len)) { + LOGE("failed to read data\n"); + return -1; + } + cmd->data[cmd->len] = 0; + return 0; +} + +static inline int write_marshal(int s, LPC_MARSHAL *cmd) +{ + if (writex(s, cmd, 2 * sizeof(uint32_t))) { + LOGE("failed to write marshal header\n"); + return -1; + } + if (writex(s, cmd->data, cmd->len)) { + LOGE("failed to write marshal data\n"); + return -1; + } + return 0; +} + +#endif diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java index 8212b9212af8d8ad5dc8600a0225acafa341f059..fd9e70884e68641bb117dcb8e9321207bcab693a 100644 --- a/cmds/pm/src/com/android/commands/pm/Pm.java +++ b/cmds/pm/src/com/android/commands/pm/Pm.java @@ -543,6 +543,9 @@ public final class Pm { case PackageManager.INSTALL_FAILED_TEST_ONLY: s = "INSTALL_FAILED_TEST_ONLY"; break; + case PackageManager.INSTALL_FAILED_CPU_ABI_INCOMPATIBLE: + s = "INSTALL_FAILED_CPU_ABI_INCOMPATIBLE"; + break; case PackageManager.INSTALL_PARSE_FAILED_NOT_APK: s = "INSTALL_PARSE_FAILED_NOT_APK"; break; diff --git a/cmds/runtime/main_runtime.cpp b/cmds/runtime/main_runtime.cpp index 1531a9efd154d228b1219c935669cce0fdf07a30..476f38a4d073eb5f943da84486b64bbe95613ac6 100644 --- a/cmds/runtime/main_runtime.cpp +++ b/cmds/runtime/main_runtime.cpp @@ -45,9 +45,9 @@ static const char* ZYGOTE_ARGV[] = { "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,3001,3002,3003", /* CAP_SYS_TTY_CONFIG & CAP_SYS_RESOURCE & CAP_NET_BROADCAST & * CAP_NET_ADMIN & CAP_NET_RAW & CAP_NET_BIND_SERVICE & CAP_KILL & - * CAP_SYS_BOOT + * CAP_SYS_BOOT CAP_SYS_NICE */ - "--capabilities=88161312,88161312", + "--capabilities=96549920,96549920", "--runtime-init", "--nice-name=system_server", "com.android.server.SystemServer" diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java new file mode 100644 index 0000000000000000000000000000000000000000..a3456c756404fb936b417ad787386bd981e82944 --- /dev/null +++ b/core/java/android/accessibilityservice/AccessibilityService.java @@ -0,0 +1,225 @@ +/* + * 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.accessibilityservice; + +import com.android.internal.os.HandlerCaller; + +import android.app.Service; +import android.content.Intent; +import android.os.IBinder; +import android.os.Message; +import android.os.RemoteException; +import android.util.Log; +import android.view.accessibility.AccessibilityEvent; + +/** + * An accessibility service runs in the background and receives callbacks by the system + * when {@link AccessibilityEvent}s are fired. Such events denote some state transition + * in the user interface, for example, the focus has changed, a button has been clicked, + * etc. + *

+ * An accessibility service extends this class and implements its abstract methods. Such + * a service is declared as any other service in an AndroidManifest.xml but it must also + * specify that it handles the "android.accessibilityservice.AccessibilityService" + * {@link android.content.Intent}. Following is an example of such a declaration: + *

+ * + * <service android:name=".MyAccessibilityService">
+ * <intent-filter>
+ * <action android:name="android.accessibilityservice.AccessibilityService" />
+ * </intent-filter>
+ * </service>
+ *
+ *

+ * The lifecycle of an accessibility service is managed exclusively by the system. Starting + * or stopping an accessibility service is triggered by an explicit user action through + * enabling or disabling it in the device settings. After the system binds to a service it + * calls {@link AccessibilityService#onServiceConnected()}. This method can be + * overriden by clients that want to perform post binding setup. An accessibility service + * is configured though setting an {@link AccessibilityServiceInfo} by calling + * {@link AccessibilityService#setServiceInfo(AccessibilityServiceInfo)}. You can call this + * method any time to change the service configuration but it is good practice to do that + * in the overriden {@link AccessibilityService#onServiceConnected()}. + *

+ * An accessibility service can be registered for events in specific packages to provide a + * specific type of feedback and is notified with a certain timeout after the last event + * of interest has been fired. + *

+ * Notification strategy + *

+ * For each feedback type only one accessibility service is notified. Services are notified + * in the order of registration. Hence, if two services are registered for the same + * feedback type in the same package the first one wins. It is possible however, to + * register a service as the default one for a given feedback type. In such a case this + * service is invoked if no other service was interested in the event. In other words, default + * services do not compete with other services and are notified last regardless of the + * registration order. This enables "generic" accessibility services that work reasonably + * well with most applications to coexist with "polished" ones that are targeted for + * specific applications. + *

+ * Event types + *

+ * {@link AccessibilityEvent#TYPE_VIEW_CLICKED} + * {@link AccessibilityEvent#TYPE_VIEW_LONG_CLICKED} + * {@link AccessibilityEvent#TYPE_VIEW_FOCUSED} + * {@link AccessibilityEvent#TYPE_VIEW_SELECTED} + * {@link AccessibilityEvent#TYPE_VIEW_TEXT_CHANGED} + * {@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED} + * {@link AccessibilityEvent#TYPE_NOTIFICATION_STATE_CHANGED} + *

+ * Feedback types + *

+ * {@link AccessibilityServiceInfo#FEEDBACK_AUDIBLE} + * {@link AccessibilityServiceInfo#FEEDBACK_HAPTIC} + * {@link AccessibilityServiceInfo#FEEDBACK_AUDIBLE} + * {@link AccessibilityServiceInfo#FEEDBACK_VISUAL} + * {@link AccessibilityServiceInfo#FEEDBACK_GENERIC} + * + * @see AccessibilityEvent + * @see AccessibilityServiceInfo + * @see android.view.accessibility.AccessibilityManager + * + * Note: The event notification timeout is useful to avoid propagating events to the client + * too frequently since this is accomplished via an expensive interprocess call. + * One can think of the timeout as a criteria to determine when event generation has + * settled down. + */ +public abstract class AccessibilityService extends Service { + /** + * The {@link Intent} that must be declared as handled by the service. + */ + public static final String SERVICE_INTERFACE = + "android.accessibilityservice.AccessibilityService"; + + private static final String LOG_TAG = "AccessibilityService"; + + private AccessibilityServiceInfo mInfo; + + IAccessibilityServiceConnection mConnection; + + /** + * Callback for {@link android.view.accessibility.AccessibilityEvent}s. + * + * @param event An event. + */ + public abstract void onAccessibilityEvent(AccessibilityEvent event); + + /** + * Callback for interrupting the accessibility feedback. + */ + public abstract void onInterrupt(); + + /** + * This method is a part of the {@link AccessibilityService} lifecycle and is + * called after the system has successfully bound to the service. If is + * convenient to use this method for setting the {@link AccessibilityServiceInfo}. + * + * @see AccessibilityServiceInfo + * @see #setServiceInfo(AccessibilityServiceInfo) + */ + protected void onServiceConnected() { + + } + + /** + * Sets the {@link AccessibilityServiceInfo} that describes this service. + *

+ * Note: You can call this method any time but the info will be picked up after + * the system has bound to this service and when this method is called thereafter. + * + * @param info The info. + */ + public final void setServiceInfo(AccessibilityServiceInfo info) { + mInfo = info; + sendServiceInfo(); + } + + /** + * Sets the {@link AccessibilityServiceInfo} for this service if the latter is + * properly set and there is an {@link IAccessibilityServiceConnection} to the + * AccessibilityManagerService. + */ + private void sendServiceInfo() { + if (mInfo != null && mConnection != null) { + try { + mConnection.setServiceInfo(mInfo); + } catch (RemoteException re) { + Log.w(LOG_TAG, "Error while setting AccessibilityServiceInfo", re); + } + } + } + + @Override + public final IBinder onBind(Intent intent) { + return new IEventListenerWrapper(this); + } + + /** + * Implements the internal {@link IEventListener} interface to convert + * incoming calls to it back to calls on an {@link AccessibilityService}. + */ + class IEventListenerWrapper extends IEventListener.Stub + implements HandlerCaller.Callback { + + private static final int DO_SET_SET_CONNECTION = 10; + private static final int DO_ON_INTERRUPT = 20; + private static final int DO_ON_ACCESSIBILITY_EVENT = 30; + + private final HandlerCaller mCaller; + + private AccessibilityService mTarget; + + public IEventListenerWrapper(AccessibilityService context) { + mTarget = context; + mCaller = new HandlerCaller(context, this); + } + + public void setConnection(IAccessibilityServiceConnection connection) { + Message message = mCaller.obtainMessageO(DO_SET_SET_CONNECTION, connection); + mCaller.sendMessage(message); + } + + public void onInterrupt() { + Message message = mCaller.obtainMessage(DO_ON_INTERRUPT); + mCaller.sendMessage(message); + } + + public void onAccessibilityEvent(AccessibilityEvent event) { + Message message = mCaller.obtainMessageO(DO_ON_ACCESSIBILITY_EVENT, event); + mCaller.sendMessage(message); + } + + public void executeMessage(Message message) { + switch (message.what) { + case DO_ON_ACCESSIBILITY_EVENT : + AccessibilityEvent event = (AccessibilityEvent) message.obj; + mTarget.onAccessibilityEvent(event); + event.recycle(); + return; + case DO_ON_INTERRUPT : + mTarget.onInterrupt(); + return; + case DO_SET_SET_CONNECTION : + mConnection = ((IAccessibilityServiceConnection) message.obj); + mTarget.onServiceConnected(); + return; + default : + Log.w(LOG_TAG, "Unknown message type " + message.what); + } + } + } +} diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.aidl b/core/java/android/accessibilityservice/AccessibilityServiceInfo.aidl new file mode 100644 index 0000000000000000000000000000000000000000..1f5d3850d46f18966edd8a3ebd1c0d35193a1a08 --- /dev/null +++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.aidl @@ -0,0 +1,19 @@ +/* + * 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.accessibilityservice; + +parcelable AccessibilityServiceInfo; diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java new file mode 100644 index 0000000000000000000000000000000000000000..4761f98ed7c2722ec90dfeccbff56fd8def06e24 --- /dev/null +++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java @@ -0,0 +1,145 @@ +/* + * 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.accessibilityservice; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * This class describes an {@link AccessibilityService}. The system + * notifies an {@link AccessibilityService} for + * {@link android.view.accessibility.AccessibilityEvent}s + * according to the information encapsulated in this class. + * + * @see AccessibilityService + * @see android.view.accessibility.AccessibilityEvent + */ +public class AccessibilityServiceInfo implements Parcelable { + + /** + * Denotes spoken feedback. + */ + public static final int FEEDBACK_SPOKEN = 0x0000001; + + /** + * Denotes haptic feedback. + */ + public static final int FEEDBACK_HAPTIC = 0x0000002; + + /** + * Denotes audible (not spoken) feedback. + */ + public static final int FEEDBACK_AUDIBLE = 0x0000004; + + /** + * Denotes visual feedback. + */ + public static final int FEEDBACK_VISUAL = 0x0000008; + + /** + * Denotes generic feedback. + */ + public static final int FEEDBACK_GENERIC = 0x0000010; + + /** + * If an {@link AccessibilityService} is the default for a given type. + * Default service is invoked only if no package specific one exists. In case of + * more than one package specific service only the earlier registered is notified. + */ + public static final int DEFAULT = 0x0000001; + + /** + * The event types an {@link AccessibilityService} is interested in. + * + * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_CLICKED + * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_FOCUSED + * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_SELECTED + * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_TEXT_CHANGED + * @see android.view.accessibility.AccessibilityEvent#TYPE_ACTIVITY_STARTED + * @see android.view.accessibility.AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED + * @see android.view.accessibility.AccessibilityEvent#TYPE_NOTIFICATION_STATE_CHANGED + */ + public int eventTypes; + + /** + * The package names an {@link AccessibilityService} is interested in. Setting + * to null is equivalent to all packages. + */ + public String[] packageNames; + + /** + * The feedback type an {@link AccessibilityService} provides. + * + * @see #FEEDBACK_AUDIBLE + * @see #FEEDBACK_GENERIC + * @see #FEEDBACK_HAPTIC + * @see #FEEDBACK_SPOKEN + * @see #FEEDBACK_VISUAL + */ + public int feedbackType; + + /** + * The timeout after the most recent event of a given type before an + * {@link AccessibilityService} is notified. + *

+ * Note: The event notification timeout is useful to avoid propagating events to the client + * too frequently since this is accomplished via an expensive interprocess call. + * One can think of the timeout as a criteria to determine when event generation has + * settled down + */ + public long notificationTimeout; + + /** + * This field represents a set of flags used for configuring an + * {@link AccessibilityService}. + * + * @see #DEFAULT + */ + public int flags; + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel parcel, int flags) { + parcel.writeInt(eventTypes); + parcel.writeStringArray(packageNames); + parcel.writeInt(feedbackType); + parcel.writeLong(notificationTimeout); + parcel.writeInt(flags); + } + + /** + * @see Parcelable.Creator + */ + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { + public AccessibilityServiceInfo createFromParcel(Parcel parcel) { + AccessibilityServiceInfo info = new AccessibilityServiceInfo(); + info.eventTypes = parcel.readInt(); + info.packageNames = parcel.readStringArray(); + info.feedbackType = parcel.readInt(); + info.notificationTimeout = parcel.readLong(); + info.flags = parcel.readInt(); + return info; + } + + public AccessibilityServiceInfo[] newArray(int size) { + return new AccessibilityServiceInfo[size]; + } + }; +} diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl new file mode 100644 index 0000000000000000000000000000000000000000..7157def606fa2f5b1ed361e3da7de388254a73ec --- /dev/null +++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl @@ -0,0 +1,30 @@ +/* + * 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.accessibilityservice; + +import android.accessibilityservice.AccessibilityServiceInfo; + +/** + * Interface AccessibilityManagerService#Service implements, and passes to an + * AccessibilityService so it can dynamically configure how the system handles it. + * + * @hide + */ +oneway interface IAccessibilityServiceConnection { + + void setServiceInfo(in AccessibilityServiceInfo info); +} diff --git a/core/java/android/accessibilityservice/IEventListener.aidl b/core/java/android/accessibilityservice/IEventListener.aidl new file mode 100644 index 0000000000000000000000000000000000000000..5b849f1e40fdadaec283a61d7bb8f943d8563400 --- /dev/null +++ b/core/java/android/accessibilityservice/IEventListener.aidl @@ -0,0 +1,34 @@ +/* +** Copyright 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.accessibilityservice; + +import android.accessibilityservice.IAccessibilityServiceConnection; +import android.view.accessibility.AccessibilityEvent; + +/** + * Top-level interface to accessibility service component (implemented in Service). + * + * @hide + */ + oneway interface IEventListener { + + void setConnection(in IAccessibilityServiceConnection connection); + + void onAccessibilityEvent(in AccessibilityEvent event); + + void onInterrupt(); +} diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 9b1f0f97171f0ea29695cdca49ab8693b9b38a19..ca9632a490b1ad1555f85c6ba4871cbaab7aeef8 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -16,11 +16,14 @@ package android.app; +import com.android.internal.policy.PolicyManager; + import android.content.ComponentCallbacks; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; +import android.content.IIntentSender; import android.content.SharedPreferences; import android.content.pm.ActivityInfo; import android.content.res.Configuration; @@ -32,11 +35,12 @@ import android.graphics.drawable.Drawable; import android.media.AudioManager; import android.net.Uri; import android.os.Bundle; -import android.os.RemoteException; import android.os.Handler; import android.os.IBinder; +import android.os.RemoteException; import android.text.Selection; import android.text.SpannableStringBuilder; +import android.text.TextUtils; import android.text.method.TextKeyListener; import android.util.AttributeSet; import android.util.Config; @@ -58,10 +62,10 @@ import android.view.Window; import android.view.WindowManager; import android.view.ContextMenu.ContextMenuInfo; import android.view.View.OnCreateContextMenuListener; +import android.view.ViewGroup.LayoutParams; +import android.view.accessibility.AccessibilityEvent; import android.widget.AdapterView; -import com.android.internal.policy.PolicyManager; - import java.util.ArrayList; import java.util.HashMap; @@ -625,6 +629,8 @@ public class Activity extends ContextThemeWrapper boolean mStartedActivity; /*package*/ int mConfigChangeFlags; /*package*/ Configuration mCurrentConfig; + private SearchManager mSearchManager; + private Bundle mSearchDialogState = null; private Window mWindow; @@ -785,6 +791,9 @@ public class Activity extends ContextThemeWrapper protected void onCreate(Bundle savedInstanceState) { mVisibleFromClient = mWindow.getWindowStyle().getBoolean( com.android.internal.R.styleable.Window_windowNoDisplay, true); + // uses super.getSystemService() since this.getSystemService() looks at the + // mSearchManager field. + mSearchManager = (SearchManager) super.getSystemService(Context.SEARCH_SERVICE); mCalled = true; } @@ -802,9 +811,10 @@ public class Activity extends ContextThemeWrapper // Also restore the state of a search dialog (if any) // TODO more generic than just this manager - SearchManager searchManager = - (SearchManager) getSystemService(Context.SEARCH_SERVICE); - searchManager.restoreSearchDialog(savedInstanceState, SAVED_SEARCH_DIALOG_KEY); + Bundle searchState = savedInstanceState.getBundle(SAVED_SEARCH_DIALOG_KEY); + if (searchState != null) { + mSearchManager.restoreSearchDialog(searchState); + } } /** @@ -854,13 +864,26 @@ public class Activity extends ContextThemeWrapper final Integer dialogId = ids[i]; Bundle dialogState = b.getBundle(savedDialogKeyFor(dialogId)); if (dialogState != null) { - final Dialog dialog = onCreateDialog(dialogId); - dialog.onRestoreInstanceState(dialogState); + // Calling onRestoreInstanceState() below will invoke dispatchOnCreate + // so tell createDialog() not to do it, otherwise we get an exception + final Dialog dialog = createDialog(dialogId, false); mManagedDialogs.put(dialogId, dialog); + onPrepareDialog(dialogId, dialog); + dialog.onRestoreInstanceState(dialogState); } } } + private Dialog createDialog(Integer dialogId, boolean dispatchOnCreate) { + final Dialog dialog = onCreateDialog(dialogId); + if (dialog == null) { + throw new IllegalArgumentException("Activity#onCreateDialog did " + + "not create a dialog for id " + dialogId); + } + if (dispatchOnCreate) dialog.dispatchOnCreate(null); + return dialog; + } + private String savedDialogKeyFor(int key) { return SAVED_DIALOG_KEY_PREFIX + key; } @@ -1010,9 +1033,11 @@ public class Activity extends ContextThemeWrapper // Also save the state of a search dialog (if any) // TODO more generic than just this manager - SearchManager searchManager = - (SearchManager) getSystemService(Context.SEARCH_SERVICE); - searchManager.saveSearchDialog(outState, SAVED_SEARCH_DIALOG_KEY); + // onPause() should always be called before this method, so mSearchManagerState + // should be up to date. + if (mSearchDialogState != null) { + outState.putBundle(SAVED_SEARCH_DIALOG_KEY, mSearchDialogState); + } } /** @@ -1283,12 +1308,6 @@ public class Activity extends ContextThemeWrapper } } } - - // also dismiss search dialog if showing - // TODO more generic than just this manager - SearchManager searchManager = - (SearchManager) getSystemService(Context.SEARCH_SERVICE); - searchManager.stopSearch(); // close any cursors we are managing. int numCursors = mManagedCursors.size(); @@ -1298,6 +1317,10 @@ public class Activity extends ContextThemeWrapper c.mCursor.close(); } } + + // Clear any search state saved in performPause(). If the state may be needed in the + // future, it will have been saved by performSaveInstanceState() + mSearchDialogState = null; } /** @@ -1321,9 +1344,7 @@ public class Activity extends ContextThemeWrapper // also update search dialog if showing // TODO more generic than just this manager - SearchManager searchManager = - (SearchManager) getSystemService(Context.SEARCH_SERVICE); - searchManager.onConfigurationChanged(newConfig); + mSearchManager.onConfigurationChanged(newConfig); if (mWindow != null) { // Pass the configuration changed event to the window @@ -2013,7 +2034,24 @@ public class Activity extends ContextThemeWrapper } return onTrackballEvent(ev); } - + + public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { + event.setClassName(getClass().getName()); + event.setPackageName(getPackageName()); + + LayoutParams params = getWindow().getAttributes(); + boolean isFullScreen = (params.width == LayoutParams.FILL_PARENT) && + (params.height == LayoutParams.FILL_PARENT); + event.setFullScreen(isFullScreen); + + CharSequence title = getTitle(); + if (!TextUtils.isEmpty(title)) { + event.getText().add(title); + } + + return true; + } + /** * Default implementation of * {@link android.view.Window.Callback#onCreatePanelView} @@ -2394,12 +2432,7 @@ public class Activity extends ContextThemeWrapper } Dialog dialog = mManagedDialogs.get(id); if (dialog == null) { - dialog = onCreateDialog(id); - if (dialog == null) { - throw new IllegalArgumentException("Activity#onCreateDialog did " - + "not create a dialog for id " + id); - } - dialog.dispatchOnCreate(null); + dialog = createDialog(id, true); mManagedDialogs.put(id, dialog); } @@ -2523,10 +2556,7 @@ public class Activity extends ContextThemeWrapper */ public void startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData, boolean globalSearch) { - // activate the search manager and start it up! - SearchManager searchManager = (SearchManager) - getSystemService(Context.SEARCH_SERVICE); - searchManager.startSearch(initialQuery, selectInitialQuery, getComponentName(), + mSearchManager.startSearch(initialQuery, selectInitialQuery, getComponentName(), appSearchData, globalSearch); } @@ -3245,6 +3275,8 @@ public class Activity extends ContextThemeWrapper if (WINDOW_SERVICE.equals(name)) { return mWindowManager; + } else if (SEARCH_SERVICE.equals(name)) { + return mSearchManager; } return super.getSystemService(name); } @@ -3543,10 +3575,21 @@ public class Activity extends ContextThemeWrapper "Activity " + mComponent.toShortString() + " did not call through to super.onPostResume()"); } + + // restore search dialog, if any + if (mSearchDialogState != null) { + mSearchManager.restoreSearchDialog(mSearchDialogState); + } + mSearchDialogState = null; } final void performPause() { onPause(); + + // save search dialog state if the search dialog is open, + // and then dismiss the search dialog + mSearchDialogState = mSearchManager.saveSearchDialog(); + mSearchManager.stopSearch(); } final void performUserLeaving() { diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index 541f413676c8530215344b521f4cff90b0465f5f..dfa8139eecf46b4206d016895d137b1bd459d6a9 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -17,9 +17,11 @@ package android.app; import android.content.ComponentName; -import android.content.ContentResolver; import android.content.Intent; import android.content.IntentFilter; +import android.content.IIntentSender; +import android.content.IIntentReceiver; +import android.content.pm.ApplicationInfo; import android.content.pm.ConfigurationInfo; import android.content.pm.IPackageDataObserver; import android.content.res.Configuration; @@ -984,7 +986,9 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM String process = data.readString(); boolean start = data.readInt() != 0; String path = data.readString(); - boolean res = profileControl(process, start, path); + ParcelFileDescriptor fd = data.readInt() != 0 + ? data.readFileDescriptor() : null; + boolean res = profileControl(process, start, path, fd); reply.writeNoException(); reply.writeInt(res ? 1 : 0); return true; @@ -998,6 +1002,20 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM return true; } + case STOP_APP_SWITCHES_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + stopAppSwitches(); + reply.writeNoException(); + return true; + } + + case RESUME_APP_SWITCHES_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + resumeAppSwitches(); + reply.writeNoException(); + return true; + } + case PEEK_SERVICE_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); Intent service = Intent.CREATOR.createFromParcel(data); @@ -1007,6 +1025,33 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM reply.writeStrongBinder(binder); return true; } + + case START_BACKUP_AGENT_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + ApplicationInfo info = ApplicationInfo.CREATOR.createFromParcel(data); + int backupRestoreMode = data.readInt(); + boolean success = bindBackupAgent(info, backupRestoreMode); + reply.writeNoException(); + reply.writeInt(success ? 1 : 0); + return true; + } + + case BACKUP_AGENT_CREATED_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + String packageName = data.readString(); + IBinder agent = data.readStrongBinder(); + backupAgentCreated(packageName, agent); + reply.writeNoException(); + return true; + } + + case UNBIND_BACKUP_AGENT_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + ApplicationInfo info = ApplicationInfo.CREATOR.createFromParcel(data); + unbindBackupAgent(info); + reply.writeNoException(); + return true; + } } return super.onTransact(code, data, reply, flags); @@ -1667,6 +1712,43 @@ class ActivityManagerProxy implements IActivityManager return binder; } + public boolean bindBackupAgent(ApplicationInfo app, int backupRestoreMode) + throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + app.writeToParcel(data, 0); + data.writeInt(backupRestoreMode); + mRemote.transact(START_BACKUP_AGENT_TRANSACTION, data, reply, 0); + reply.readException(); + boolean success = reply.readInt() != 0; + reply.recycle(); + data.recycle(); + return success; + } + + public void backupAgentCreated(String packageName, IBinder agent) throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + data.writeString(packageName); + data.writeStrongBinder(agent); + mRemote.transact(BACKUP_AGENT_CREATED_TRANSACTION, data, reply, 0); + reply.recycle(); + data.recycle(); + } + + public void unbindBackupAgent(ApplicationInfo app) throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + app.writeToParcel(data, 0); + mRemote.transact(UNBIND_BACKUP_AGENT_TRANSACTION, data, reply, 0); + reply.readException(); + reply.recycle(); + data.recycle(); + } + public boolean startInstrumentation(ComponentName className, String profileFile, int flags, Bundle arguments, IInstrumentationWatcher watcher) throws RemoteException { @@ -2152,7 +2234,7 @@ class ActivityManagerProxy implements IActivityManager } public boolean profileControl(String process, boolean start, - String path) throws RemoteException + String path, ParcelFileDescriptor fd) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); @@ -2160,6 +2242,12 @@ class ActivityManagerProxy implements IActivityManager data.writeString(process); data.writeInt(start ? 1 : 0); data.writeString(path); + if (fd != null) { + data.writeInt(1); + fd.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE); + } else { + data.writeInt(0); + } mRemote.transact(PROFILE_CONTROL_TRANSACTION, data, reply, 0); reply.readException(); boolean res = reply.readInt() != 0; @@ -2182,5 +2270,25 @@ class ActivityManagerProxy implements IActivityManager return res; } + public void stopAppSwitches() throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + mRemote.transact(STOP_APP_SWITCHES_TRANSACTION, data, reply, 0); + reply.readException(); + reply.recycle(); + data.recycle(); + } + + public void resumeAppSwitches() throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + mRemote.transact(RESUME_APP_SWITCHES_TRANSACTION, data, reply, 0); + reply.readException(); + reply.recycle(); + data.recycle(); + } + private IBinder mRemote; } diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 1e15d14271d3815816b92f1de0ce979a34bd64a4..5ee29ac4f473821581195407bbe69b7b5900fb15 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -23,6 +23,7 @@ import android.content.ContentProvider; import android.content.Context; import android.content.IContentProvider; import android.content.Intent; +import android.content.IIntentReceiver; import android.content.ServiceConnection; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; @@ -31,6 +32,7 @@ import android.content.pm.InstrumentationInfo; import android.content.pm.PackageManager; import android.content.pm.ProviderInfo; import android.content.pm.ServiceInfo; +import android.content.pm.PackageParser.Component; import android.content.res.AssetManager; import android.content.res.Configuration; import android.content.res.Resources; @@ -46,6 +48,7 @@ import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.MessageQueue; +import android.os.ParcelFileDescriptor; import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; @@ -72,6 +75,7 @@ import org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl; import java.io.File; import java.io.FileDescriptor; import java.io.FileOutputStream; +import java.io.IOException; import java.io.PrintWriter; import java.lang.ref.WeakReference; import java.util.ArrayList; @@ -115,6 +119,7 @@ public final class ActivityThread { private static final boolean localLOGV = DEBUG ? Config.LOGD : Config.LOGV; private static final boolean DEBUG_BROADCAST = false; private static final boolean DEBUG_RESULTS = false; + private static final boolean DEBUG_BACKUP = true; private static final long MIN_TIME_BETWEEN_GCS = 5*1000; private static final Pattern PATTERN_SEMICOLON = Pattern.compile(";"); private static final int SQLITE_MEM_RELEASED_EVENT_LOG_TAG = 75003; @@ -161,7 +166,7 @@ public final class ActivityThread { return metrics; } - Resources getTopLevelResources(String appDir, float applicationScale) { + Resources getTopLevelResources(String appDir, PackageInfo pkgInfo) { synchronized (mPackages) { //Log.w(TAG, "getTopLevelResources: " + appDir); WeakReference wr = mActiveResources.get(appDir); @@ -180,23 +185,17 @@ public final class ActivityThread { if (assets.addAssetPath(appDir) == 0) { return null; } - DisplayMetrics metrics = getDisplayMetricsLocked(false); - // density used to load resources - // scaledDensity is calculated in Resources constructor - // - boolean usePreloaded = true; - - // TODO: use explicit flag to indicate the compatibility mode. - if (applicationScale != 1.0f) { - usePreloaded = false; - DisplayMetrics newMetrics = new DisplayMetrics(); - newMetrics.setTo(metrics); - float newDensity = metrics.density / applicationScale; - newMetrics.updateDensity(newDensity); - metrics = newMetrics; + ApplicationInfo appInfo; + try { + appInfo = getPackageManager().getApplicationInfo( + pkgInfo.getPackageName(), + PackageManager.GET_SUPPORTS_DENSITIES); + } catch (RemoteException e) { + throw new AssertionError(e); } //Log.i(TAG, "Resource:" + appDir + ", display metrics=" + metrics); - r = new Resources(assets, metrics, getConfiguration(), usePreloaded); + DisplayMetrics metrics = getDisplayMetricsLocked(false); + r = new Resources(assets, metrics, getConfiguration(), appInfo); //Log.i(TAG, "Created app resources " + r + ": " + r.getConfiguration()); // XXX need to remove entries when weak references go away mActiveResources.put(appDir, new WeakReference(r)); @@ -224,7 +223,6 @@ public final class ActivityThread { private Resources mResources; private ClassLoader mClassLoader; private Application mApplication; - private float mApplicationScale; private final HashMap> mReceivers = new HashMap>(); @@ -267,8 +265,6 @@ public final class ActivityThread { mClassLoader = mSystemContext.getClassLoader(); mResources = mSystemContext.getResources(); } - - mApplicationScale = -1.0f; } public PackageInfo(ActivityThread activityThread, String name, @@ -287,56 +283,20 @@ public final class ActivityThread { mIncludeCode = true; mClassLoader = systemContext.getClassLoader(); mResources = systemContext.getResources(); - mApplicationScale = systemContext.getApplicationScale(); } public String getPackageName() { return mPackageName; } + public ApplicationInfo getApplicationInfo() { + return mApplicationInfo; + } + public boolean isSecurityViolation() { return mSecurityViolation; } - public float getApplicationScale() { - if (mApplicationScale > 0.0f) { - return mApplicationScale; - } - DisplayMetrics metrics = mActivityThread.getDisplayMetricsLocked(false); - // Find out the density scale (relative to 160) of the supported density that - // is closest to the system's density. - try { - ApplicationInfo ai = getPackageManager().getApplicationInfo( - mPackageName, PackageManager.GET_SUPPORTS_DENSITIES); - - float appScale = -1.0f; - if (ai.supportsDensities != null) { - int minDiff = Integer.MAX_VALUE; - for (int density : ai.supportsDensities) { - int tmpDiff = (int) Math.abs(DisplayMetrics.DEVICE_DENSITY - density); - if (tmpDiff == 0) { - appScale = 1.0f; - break; - } - // prefer higher density (appScale>1.0), unless that's only option. - if (tmpDiff < minDiff && appScale < 1.0f) { - appScale = DisplayMetrics.DEVICE_DENSITY / density; - minDiff = tmpDiff; - } - } - } - if (appScale < 0.0f) { - mApplicationScale = metrics.density; - } else { - mApplicationScale = appScale; - } - } catch (RemoteException e) { - throw new AssertionError(e); - } - if (localLOGV) Log.v(TAG, "appScale=" + mApplicationScale + ", pkg=" + mPackageName); - return mApplicationScale; - } - /** * Gets the array of shared libraries that are listed as * used by the given package. @@ -494,12 +454,12 @@ public final class ActivityThread { public Resources getResources(ActivityThread mainThread) { if (mResources == null) { - mResources = mainThread.getTopLevelResources(mResDir, getApplicationScale()); + mResources = mainThread.getTopLevelResources(mResDir, this); } return mResources; } - public Application makeApplication() { + public Application makeApplication(boolean forceDefaultAppClass) { if (mApplication != null) { return mApplication; } @@ -507,7 +467,7 @@ public final class ActivityThread { Application app = null; String appClass = mApplicationInfo.className; - if (appClass == null) { + if (forceDefaultAppClass || (appClass == null)) { appClass = "android.app.Application"; } @@ -1199,6 +1159,16 @@ public final class ActivityThread { } } + private static final class CreateBackupAgentData { + ApplicationInfo appInfo; + int backupMode; + public String toString() { + return "CreateBackupAgentData{appInfo=" + appInfo + + " backupAgent=" + appInfo.backupAgentName + + " mode=" + backupMode + "}"; + } + } + private static final class CreateServiceData { IBinder token; ServiceInfo info; @@ -1239,6 +1209,7 @@ public final class ActivityThread { Bundle instrumentationArgs; IInstrumentationWatcher instrumentationWatcher; int debugMode; + boolean restrictedBackupMode; Configuration config; boolean handlingProfiling; public String toString() { @@ -1267,6 +1238,11 @@ public final class ActivityThread { String who; } + private static final class ProfilerControlData { + String path; + ParcelFileDescriptor fd; + } + private final class ApplicationThread extends ApplicationThreadNative { private static final String HEAP_COLUMN = "%17s %8s %8s %8s %8s"; private static final String ONE_COUNT_COLUMN = "%17s %8d"; @@ -1374,6 +1350,21 @@ public final class ActivityThread { queueOrSendMessage(H.RECEIVER, r); } + public final void scheduleCreateBackupAgent(ApplicationInfo app, int backupMode) { + CreateBackupAgentData d = new CreateBackupAgentData(); + d.appInfo = app; + d.backupMode = backupMode; + + queueOrSendMessage(H.CREATE_BACKUP_AGENT, d); + } + + public final void scheduleDestroyBackupAgent(ApplicationInfo app) { + CreateBackupAgentData d = new CreateBackupAgentData(); + d.appInfo = app; + + queueOrSendMessage(H.DESTROY_BACKUP_AGENT, d); + } + public final void scheduleCreateService(IBinder token, ServiceInfo info) { CreateServiceData s = new CreateServiceData(); @@ -1419,7 +1410,7 @@ public final class ActivityThread { ApplicationInfo appInfo, List providers, ComponentName instrumentationName, String profileFile, Bundle instrumentationArgs, IInstrumentationWatcher instrumentationWatcher, - int debugMode, Configuration config, + int debugMode, boolean isRestrictedBackupMode, Configuration config, Map services) { Process.setArgV0(processName); @@ -1437,6 +1428,7 @@ public final class ActivityThread { data.instrumentationArgs = instrumentationArgs; data.instrumentationWatcher = instrumentationWatcher; data.debugMode = debugMode; + data.restrictedBackupMode = isRestrictedBackupMode; data.config = config; queueOrSendMessage(H.BIND_APPLICATION, data); } @@ -1509,10 +1501,25 @@ public final class ActivityThread { } } - public void profilerControl(boolean start, String path) { - queueOrSendMessage(H.PROFILER_CONTROL, path, start ? 1 : 0); + public void profilerControl(boolean start, String path, ParcelFileDescriptor fd) { + ProfilerControlData pcd = new ProfilerControlData(); + pcd.path = path; + pcd.fd = fd; + queueOrSendMessage(H.PROFILER_CONTROL, pcd, start ? 1 : 0); + } + + public void setSchedulingGroup(int group) { + // Note: do this immediately, since going into the foreground + // should happen regardless of what pending work we have to do + // and the activity manager will wait for us to report back that + // we are done before sending us to the background. + try { + Process.setProcessGroup(Process.myPid(), group); + } catch (Exception e) { + Log.w(TAG, "Failed setting process group to " + group, e); + } } - + @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { long nativeMax = Debug.getNativeHeapSize() / 1024; @@ -1706,6 +1713,8 @@ public final class ActivityThread { public static final int ACTIVITY_CONFIGURATION_CHANGED = 125; public static final int RELAUNCH_ACTIVITY = 126; public static final int PROFILER_CONTROL = 127; + public static final int CREATE_BACKUP_AGENT = 128; + public static final int DESTROY_BACKUP_AGENT = 129; String codeToString(int code) { if (localLOGV) { switch (code) { @@ -1737,6 +1746,8 @@ public final class ActivityThread { case ACTIVITY_CONFIGURATION_CHANGED: return "ACTIVITY_CONFIGURATION_CHANGED"; case RELAUNCH_ACTIVITY: return "RELAUNCH_ACTIVITY"; case PROFILER_CONTROL: return "PROFILER_CONTROL"; + case CREATE_BACKUP_AGENT: return "CREATE_BACKUP_AGENT"; + case DESTROY_BACKUP_AGENT: return "DESTROY_BACKUP_AGENT"; } } return "(unknown)"; @@ -1837,7 +1848,13 @@ public final class ActivityThread { handleActivityConfigurationChanged((IBinder)msg.obj); break; case PROFILER_CONTROL: - handleProfilerControl(msg.arg1 != 0, (String)msg.obj); + handleProfilerControl(msg.arg1 != 0, (ProfilerControlData)msg.obj); + break; + case CREATE_BACKUP_AGENT: + handleCreateBackupAgent((CreateBackupAgentData)msg.obj); + break; + case DESTROY_BACKUP_AGENT: + handleDestroyBackupAgent((CreateBackupAgentData)msg.obj); break; } } @@ -1896,6 +1913,8 @@ public final class ActivityThread { Application mInitialApplication; final ArrayList mAllApplications = new ArrayList(); + // set of instantiated backup agents, keyed by package name + final HashMap mBackupAgents = new HashMap(); static final ThreadLocal sThreadLocal = new ThreadLocal(); Instrumentation mInstrumentation; String mInstrumentationAppDir = null; @@ -2079,6 +2098,10 @@ public final class ActivityThread { return mInitialApplication; } + public String getProcessName() { + return mBoundApplication.processName; + } + public ApplicationContext getSystemContext() { synchronized (this) { if (mSystemContext == null) { @@ -2257,7 +2280,7 @@ public final class ActivityThread { } try { - Application app = r.packageInfo.makeApplication(); + Application app = r.packageInfo.makeApplication(false); if (localLOGV) Log.v(TAG, "Performing launch of " + r); if (localLOGV) Log.v( @@ -2452,7 +2475,7 @@ public final class ActivityThread { } try { - Application app = packageInfo.makeApplication(); + Application app = packageInfo.makeApplication(false); if (localLOGV) Log.v( TAG, "Performing receive of " + data.intent @@ -2495,6 +2518,85 @@ public final class ActivityThread { } } + // Instantiate a BackupAgent and tell it that it's alive + private final void handleCreateBackupAgent(CreateBackupAgentData data) { + if (DEBUG_BACKUP) Log.v(TAG, "handleCreateBackupAgent: " + data); + + // no longer idle; we have backup work to do + unscheduleGcIdler(); + + // instantiate the BackupAgent class named in the manifest + PackageInfo packageInfo = getPackageInfoNoCheck(data.appInfo); + String packageName = packageInfo.mPackageName; + if (mBackupAgents.get(packageName) != null) { + Log.d(TAG, "BackupAgent " + " for " + packageName + + " already exists"); + return; + } + + BackupAgent agent = null; + String classname = data.appInfo.backupAgentName; + if (classname == null) { + if (data.backupMode == IApplicationThread.BACKUP_MODE_INCREMENTAL) { + Log.e(TAG, "Attempted incremental backup but no defined agent for " + + packageName); + return; + } + classname = "android.app.FullBackupAgent"; + } + try { + java.lang.ClassLoader cl = packageInfo.getClassLoader(); + agent = (BackupAgent) cl.loadClass(data.appInfo.backupAgentName).newInstance(); + } catch (Exception e) { + throw new RuntimeException("Unable to instantiate backup agent " + + data.appInfo.backupAgentName + ": " + e.toString(), e); + } + + // set up the agent's context + try { + if (DEBUG_BACKUP) Log.v(TAG, "Initializing BackupAgent " + + data.appInfo.backupAgentName); + + ApplicationContext context = new ApplicationContext(); + context.init(packageInfo, null, this); + context.setOuterContext(agent); + agent.attach(context); + agent.onCreate(); + + // tell the OS that we're live now + IBinder binder = agent.onBind(); + try { + ActivityManagerNative.getDefault().backupAgentCreated(packageName, binder); + } catch (RemoteException e) { + // nothing to do. + } + mBackupAgents.put(packageName, agent); + } catch (Exception e) { + throw new RuntimeException("Unable to create BackupAgent " + + data.appInfo.backupAgentName + ": " + e.toString(), e); + } + } + + // Tear down a BackupAgent + private final void handleDestroyBackupAgent(CreateBackupAgentData data) { + if (DEBUG_BACKUP) Log.v(TAG, "handleDestroyBackupAgent: " + data); + + PackageInfo packageInfo = getPackageInfoNoCheck(data.appInfo); + String packageName = packageInfo.mPackageName; + BackupAgent agent = mBackupAgents.get(packageName); + if (agent != null) { + try { + agent.onDestroy(); + } catch (Exception e) { + Log.w(TAG, "Exception thrown in onDestroy by backup agent of " + data.appInfo); + e.printStackTrace(); + } + mBackupAgents.remove(packageName); + } else { + Log.w(TAG, "Attempt to destroy unknown backup agent " + data); + } + } + private final void handleCreateService(CreateServiceData data) { // If we are getting ready to gc after going to the background, well // we are back active so skip it. @@ -2520,7 +2622,7 @@ public final class ActivityThread { ApplicationContext context = new ApplicationContext(); context.init(packageInfo, null, this); - Application app = packageInfo.makeApplication(); + Application app = packageInfo.makeApplication(false); context.setOuterContext(service); service.attach(context, this, data.info.name, data.token, app, ActivityManagerNative.getDefault()); @@ -3134,7 +3236,7 @@ public final class ActivityThread { r.activity.getComponentName().getClassName()); if (!r.activity.mCalled) { throw new SuperNotCalledException( - "Activity " + r.intent.getComponent().toShortString() + "Activity " + safeToComponentShortString(r.intent) + " did not call through to super.onPause()"); } } catch (SuperNotCalledException e) { @@ -3143,7 +3245,7 @@ public final class ActivityThread { if (!mInstrumentation.onException(r.activity, e)) { throw new RuntimeException( "Unable to pause activity " - + r.intent.getComponent().toShortString() + + safeToComponentShortString(r.intent) + ": " + e.toString(), e); } } @@ -3158,7 +3260,7 @@ public final class ActivityThread { if (!mInstrumentation.onException(r.activity, e)) { throw new RuntimeException( "Unable to stop activity " - + r.intent.getComponent().toShortString() + + safeToComponentShortString(r.intent) + ": " + e.toString(), e); } } @@ -3183,7 +3285,7 @@ public final class ActivityThread { if (!mInstrumentation.onException(r.activity, e)) { throw new RuntimeException( "Unable to retain child activities " - + r.intent.getComponent().toShortString() + + safeToComponentShortString(r.intent) + ": " + e.toString(), e); } } @@ -3194,7 +3296,7 @@ public final class ActivityThread { r.activity.onDestroy(); if (!r.activity.mCalled) { throw new SuperNotCalledException( - "Activity " + r.intent.getComponent().toShortString() + + "Activity " + safeToComponentShortString(r.intent) + " did not call through to super.onDestroy()"); } if (r.window != null) { @@ -3205,8 +3307,7 @@ public final class ActivityThread { } catch (Exception e) { if (!mInstrumentation.onException(r.activity, e)) { throw new RuntimeException( - "Unable to destroy activity " - + r.intent.getComponent().toShortString() + "Unable to destroy activity " + safeToComponentShortString(r.intent) + ": " + e.toString(), e); } } @@ -3216,6 +3317,11 @@ public final class ActivityThread { return r; } + private static String safeToComponentShortString(Intent intent) { + ComponentName component = intent.getComponent(); + return component == null ? "[Unknown]" : component.toShortString(); + } + private final void handleDestroyActivity(IBinder token, boolean finishing, int configChanges, boolean getNonConfigInstance) { ActivityRecord r = performDestroyActivity(token, finishing, @@ -3475,8 +3581,6 @@ public final class ActivityThread { } mConfiguration.updateFrom(config); DisplayMetrics dm = getDisplayMetricsLocked(true); - DisplayMetrics appDm = new DisplayMetrics(); - appDm.setTo(dm); // set it for java, this also affects newly created Resources if (config.locale != null) { @@ -3496,11 +3600,7 @@ public final class ActivityThread { WeakReference v = it.next(); Resources r = v.get(); if (r != null) { - // keep the original density based on application cale. - appDm.updateDensity(r.getDisplayMetrics().density); - r.updateConfiguration(config, appDm); - // reset - appDm.setTo(dm); + r.updateConfiguration(config, dm); //Log.i(TAG, "Updated app resources " + v.getKey() // + " " + r + ": " + r.getConfiguration()); } else { @@ -3528,15 +3628,20 @@ public final class ActivityThread { performConfigurationChanged(r.activity, mConfiguration); } - final void handleProfilerControl(boolean start, String path) { + final void handleProfilerControl(boolean start, ProfilerControlData pcd) { if (start) { - File file = new File(path); - file.getParentFile().mkdirs(); try { - Debug.startMethodTracing(file.toString(), 8 * 1024 * 1024); + Debug.startMethodTracing(pcd.path, pcd.fd.getFileDescriptor(), + 8 * 1024 * 1024, 0); } catch (RuntimeException e) { - Log.w(TAG, "Profiling failed on path " + path + Log.w(TAG, "Profiling failed on path " + pcd.path + " -- can the process access this path?"); + } finally { + try { + pcd.fd.close(); + } catch (IOException e) { + Log.w(TAG, "Failure closing profile fd", e); + } } } else { Debug.stopMethodTracing(); @@ -3592,6 +3697,13 @@ public final class ActivityThread { */ Locale.setDefault(data.config.locale); + /* + * Update the system configuration since its preloaded and might not + * reflect configuration changes. The configuration object passed + * in AppBindData can be safely assumed to be up to date + */ + Resources.getSystem().updateConfiguration(mConfiguration, null); + data.info = getPackageInfoNoCheck(data.appInfo); if (data.debugMode != IApplicationThread.DEBUG_OFF) { @@ -3682,7 +3794,9 @@ public final class ActivityThread { mInstrumentation = new Instrumentation(); } - Application app = data.info.makeApplication(); + // If the app is being launched for full backup or restore, bring it up in + // a restricted environment with the base application class. + Application app = data.info.makeApplication(data.restrictedBackupMode); mInitialApplication = app; List providers = data.providers; @@ -3867,7 +3981,10 @@ public final class ActivityThread { ProviderRecord pr = mProviderMap.get(name); if (pr.mProvider.asBinder() == provider.asBinder()) { Log.i(TAG, "Removing dead content provider: " + name); - mProviderMap.remove(name); + ProviderRecord removed = mProviderMap.remove(name); + if (removed != null) { + removed.mProvider.asBinder().unlinkToDeath(removed, 0); + } } } } @@ -3876,7 +3993,10 @@ public final class ActivityThread { ProviderRecord pr = mProviderMap.get(name); if (pr.mProvider.asBinder() == provider.asBinder()) { Log.i(TAG, "Removing dead content provider: " + name); - mProviderMap.remove(name); + ProviderRecord removed = mProviderMap.remove(name); + if (removed != null) { + removed.mProvider.asBinder().unlinkToDeath(removed, 0); + } } } diff --git a/core/java/android/app/ApplicationContext.java b/core/java/android/app/ApplicationContext.java index bb17dc35b4e098709bf6a86fc079efa881113d0b..38ea686f7077a900d35b3316d1b738878bb44a97 100644 --- a/core/java/android/app/ApplicationContext.java +++ b/core/java/android/app/ApplicationContext.java @@ -16,8 +16,11 @@ package android.app; -import com.google.android.collect.Maps; +import com.android.internal.policy.PolicyManager; import com.android.internal.util.XmlUtils; +import com.google.android.collect.Maps; + +import org.xmlpull.v1.XmlPullParserException; import android.bluetooth.BluetoothDevice; import android.bluetooth.IBluetoothDevice; @@ -29,6 +32,8 @@ import android.content.ContextWrapper; import android.content.IContentProvider; import android.content.Intent; import android.content.IntentFilter; +import android.content.IIntentReceiver; +import android.content.IntentSender; import android.content.ReceiverCallNotAllowedException; import android.content.ServiceConnection; import android.content.SharedPreferences; @@ -37,9 +42,9 @@ import android.content.pm.ApplicationInfo; import android.content.pm.ComponentInfo; import android.content.pm.IPackageDataObserver; import android.content.pm.IPackageDeleteObserver; -import android.content.pm.IPackageStatsObserver; import android.content.pm.IPackageInstallObserver; import android.content.pm.IPackageManager; +import android.content.pm.IPackageStatsObserver; import android.content.pm.InstrumentationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; @@ -68,29 +73,30 @@ import android.net.wifi.IWifiManager; import android.net.wifi.WifiManager; import android.os.Binder; import android.os.Bundle; -import android.os.Looper; -import android.os.RemoteException; import android.os.FileUtils; import android.os.Handler; import android.os.IBinder; import android.os.IPowerManager; +import android.os.Looper; import android.os.ParcelFileDescriptor; import android.os.PowerManager; import android.os.Process; +import android.os.RemoteException; import android.os.ServiceManager; import android.os.Vibrator; import android.os.FileUtils.FileStatus; import android.telephony.TelephonyManager; import android.text.ClipboardManager; import android.util.AndroidRuntimeException; +import android.util.DisplayMetrics; import android.util.Log; import android.view.ContextThemeWrapper; +import android.view.Display; import android.view.LayoutInflater; import android.view.WindowManagerImpl; +import android.view.accessibility.AccessibilityManager; import android.view.inputmethod.InputMethodManager; -import com.android.internal.policy.PolicyManager; - import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; @@ -100,16 +106,14 @@ import java.io.InputStream; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.WeakHashMap; import java.util.Set; -import java.util.HashSet; +import java.util.WeakHashMap; import java.util.Map.Entry; -import org.xmlpull.v1.XmlPullParserException; - class ReceiverRestrictedContext extends ContextWrapper { ReceiverRestrictedContext(Context base) { super(base); @@ -147,6 +151,7 @@ class ReceiverRestrictedContext extends ContextWrapper { */ class ApplicationContext extends Context { private final static String TAG = "ApplicationContext"; + private final static boolean DEBUG = false; private final static boolean DEBUG_ICONS = false; private static final Object sSync = new Object(); @@ -172,6 +177,7 @@ class ApplicationContext extends Context { private Resources.Theme mTheme = null; private PackageManager mPackageManager; private NotificationManager mNotificationManager = null; + private AccessibilityManager mAccessibilityManager = null; private ActivityManager mActivityManager = null; private Context mReceiverRestrictedContext = null; private SearchManager mSearchManager = null; @@ -181,6 +187,7 @@ class ApplicationContext extends Context { private StatusBarManager mStatusBarManager = null; private TelephonyManager mTelephonyManager = null; private ClipboardManager mClipboardManager = null; + private boolean mRestricted; private final Object mSync = new Object(); @@ -279,6 +286,14 @@ class ApplicationContext extends Context { throw new RuntimeException("Not supported in system context"); } + @Override + public ApplicationInfo getApplicationInfo() { + if (mPackageInfo != null) { + return mPackageInfo.getApplicationInfo(); + } + throw new RuntimeException("Not supported in system context"); + } + @Override public String getPackageResourcePath() { if (mPackageInfo != null) { @@ -299,10 +314,14 @@ class ApplicationContext extends Context { return new File(prefsFile.getPath() + ".bak"); } + public File getSharedPrefsFile(String name) { + return makeFilename(getPreferencesDir(), name + ".xml"); + } + @Override public SharedPreferences getSharedPreferences(String name, int mode) { SharedPreferencesImpl sp; - File f = makeFilename(getPreferencesDir(), name + ".xml"); + File f = getSharedPrefsFile(name); synchronized (sSharedPrefs) { sp = sSharedPrefs.get(f); if (sp != null && !sp.hasFileChanged()) { @@ -550,19 +569,6 @@ class ApplicationContext extends Context { } } - /** - * @hide - */ - @Override - public float getApplicationScale() { - if (mPackageInfo != null) { - return mPackageInfo.getApplicationScale(); - } else { - // same as system density - return 1.0f; - } - } - @Override public void setWallpaper(Bitmap bitmap) throws IOException { try { @@ -904,6 +910,8 @@ class ApplicationContext extends Context { return getNotificationManager(); } else if (KEYGUARD_SERVICE.equals(name)) { return new KeyguardManager(); + } else if (ACCESSIBILITY_SERVICE.equals(name)) { + return AccessibilityManager.getInstance(this); } else if (LOCATION_SERVICE.equals(name)) { return getLocationManager(); } else if (SEARCH_SERVICE.equals(name)) { @@ -1033,11 +1041,6 @@ class ApplicationContext extends Context { } private SearchManager getSearchManager() { - // This is only useable in Activity Contexts - if (getActivityToken() == null) { - throw new AndroidRuntimeException( - "Acquiring SearchManager objects only valid in Activity Contexts."); - } synchronized (mSync) { if (mSearchManager == null) { mSearchManager = new SearchManager(getOuterContext(), mMainThread.getHandler()); @@ -1238,7 +1241,7 @@ class ApplicationContext extends Context { @Override public int checkUriPermission(Uri uri, String readPermission, String writePermission, int pid, int uid, int modeFlags) { - if (false) { + if (DEBUG) { Log.i("foo", "checkUriPermission: uri=" + uri + "readPermission=" + readPermission + " writePermission=" + writePermission + " pid=" + pid + " uid=" + uid + " mode" + modeFlags); @@ -1337,8 +1340,22 @@ class ApplicationContext extends Context { mMainThread.getPackageInfo(packageName, flags); if (pi != null) { ApplicationContext c = new ApplicationContext(); + c.mRestricted = (flags & CONTEXT_RESTRICTED) == CONTEXT_RESTRICTED; c.init(pi, null, mMainThread); if (c.mResources != null) { + Resources newRes = c.mResources; + if (mResources.getCompatibilityInfo().applicationScale != + newRes.getCompatibilityInfo().applicationScale) { + DisplayMetrics dm = mMainThread.getDisplayMetricsLocked(false); + c.mResources = new Resources(newRes.getAssets(), dm, + newRes.getConfiguration(), + mResources.getCompatibilityInfo().copy()); + if (DEBUG) { + Log.d(TAG, "loaded context has different scaling. Using container's" + + " compatiblity info:" + mResources.getDisplayMetrics()); + } + + } return c; } } @@ -1348,6 +1365,11 @@ class ApplicationContext extends Context { "Application package " + packageName + " not found"); } + @Override + public boolean isRestricted() { + return mRestricted; + } + private File getDataDirFile() { if (mPackageInfo != null) { return mPackageInfo.getDataDirFile(); @@ -1453,7 +1475,7 @@ class ApplicationContext extends Context { if ((mode&MODE_WORLD_WRITEABLE) != 0) { perms |= FileUtils.S_IWOTH; } - if (false) { + if (DEBUG) { Log.i(TAG, "File " + name + ": mode=0x" + Integer.toHexString(mode) + ", perms=0x" + Integer.toHexString(perms)); } @@ -1516,43 +1538,33 @@ class ApplicationContext extends Context { throw new NameNotFoundException(packageName); } - public Intent getLaunchIntentForPackage(String packageName) - throws NameNotFoundException { + @Override + public Intent getLaunchIntentForPackage(String packageName) { // First see if the package has an INFO activity; the existence of // such an activity is implied to be the desired front-door for the // overall package (such as if it has multiple launcher entries). - Intent intent = getLaunchIntentForPackageCategory(this, packageName, - Intent.CATEGORY_INFO); - if (intent != null) { - return intent; - } - + Intent intentToResolve = new Intent(Intent.ACTION_MAIN); + intentToResolve.addCategory(Intent.CATEGORY_INFO); + intentToResolve.setPackage(packageName); + ResolveInfo resolveInfo = resolveActivity(intentToResolve, 0); + // Otherwise, try to find a main launcher activity. - return getLaunchIntentForPackageCategory(this, packageName, - Intent.CATEGORY_LAUNCHER); - } - - // XXX This should be implemented as a call to the package manager, - // to reduce the work needed. - static Intent getLaunchIntentForPackageCategory(PackageManager pm, - String packageName, String category) { + if (resolveInfo == null) { + // reuse the intent instance + intentToResolve.removeCategory(Intent.CATEGORY_INFO); + intentToResolve.addCategory(Intent.CATEGORY_LAUNCHER); + intentToResolve.setPackage(packageName); + resolveInfo = resolveActivity(intentToResolve, 0); + } + if (resolveInfo == null) { + return null; + } Intent intent = new Intent(Intent.ACTION_MAIN); + intent.setClassName(packageName, resolveInfo.activityInfo.name); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - Intent intentToResolve = new Intent(Intent.ACTION_MAIN, null); - intentToResolve.addCategory(category); - final List apps = - pm.queryIntentActivities(intentToResolve, 0); - // I wish there were a way to directly get the "main" activity of a - // package but ... - for (ResolveInfo app : apps) { - if (app.activityInfo.packageName.equals(packageName)) { - intent.setClassName(packageName, app.activityInfo.name); - return intent; - } - } - return null; + return intent; } - + @Override public int[] getPackageGids(String packageName) throws NameNotFoundException { @@ -2024,8 +2036,7 @@ class ApplicationContext extends Context { ActivityThread.PackageInfo pi = mContext.mMainThread.getPackageInfoNoCheck(app); Resources r = mContext.mMainThread.getTopLevelResources( app.uid == Process.myUid() ? app.sourceDir - : app.publicSourceDir, - pi.getApplicationScale()); + : app.publicSourceDir, pi); if (r != null) { return r; } @@ -2363,11 +2374,11 @@ class ApplicationContext extends Context { // Should never happen! } } - + @Override - public void freeStorage(long idealStorageSize, PendingIntent opFinishedIntent) { + public void freeStorage(long freeStorageSize, IntentSender pi) { try { - mPM.freeStorage(idealStorageSize, opFinishedIntent); + mPM.freeStorage(freeStorageSize, pi); } catch (RemoteException e) { // Should never happen! } @@ -2420,6 +2431,16 @@ class ApplicationContext extends Context { } } + @Override + public void replacePreferredActivity(IntentFilter filter, + int match, ComponentName[] set, ComponentName activity) { + try { + mPM.replacePreferredActivity(filter, match, set, activity); + } catch (RemoteException e) { + // Should never happen! + } + } + @Override public void clearPackagePreferredActivities(String packageName) { try { diff --git a/core/java/android/app/ApplicationErrorReport.java b/core/java/android/app/ApplicationErrorReport.java new file mode 100644 index 0000000000000000000000000000000000000000..6b172363296e7a6a6516f72bdcda2d255ccddfe8 --- /dev/null +++ b/core/java/android/app/ApplicationErrorReport.java @@ -0,0 +1,308 @@ +/* + * 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.app; + +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Printer; + +/** + * Describes an application error. + * + * A report has a type, which is one of + *

    + *
  • {@link #TYPE_CRASH} application crash. Information about the crash + * is stored in {@link #crashInfo}. + *
  • {@link #TYPE_ANR} application not responding. Information about the + * ANR is stored in {@link #anrInfo}. + *
  • {@link #TYPE_NONE} uninitialized instance of {@link ApplicationErrorReport}. + *
+ * + * @hide + */ + +public class ApplicationErrorReport implements Parcelable { + /** + * Uninitialized error report. + */ + public static final int TYPE_NONE = 0; + + /** + * An error report about an application crash. + */ + public static final int TYPE_CRASH = 1; + + /** + * An error report about an application that's not responding. + */ + public static final int TYPE_ANR = 2; + + /** + * Type of this report. Can be one of {@link #TYPE_NONE}, + * {@link #TYPE_CRASH} or {@link #TYPE_ANR}. + */ + public int type; + + /** + * Package name of the application. + */ + public String packageName; + + /** + * Package name of the application which installed the application this + * report pertains to. + * This identifies which Market the application came from. + */ + public String installerPackageName; + + /** + * Process name of the application. + */ + public String processName; + + /** + * Time at which the error occurred. + */ + public long time; + + /** + * If this report is of type {@link #TYPE_CRASH}, contains an instance + * of CrashInfo describing the crash; otherwise null. + */ + public CrashInfo crashInfo; + + /** + * If this report is of type {@link #TYPE_ANR}, contains an instance + * of AnrInfo describing the ANR; otherwise null. + */ + public AnrInfo anrInfo; + + /** + * Create an uninitialized instance of {@link ApplicationErrorReport}. + */ + public ApplicationErrorReport() { + } + + /** + * Create an instance of {@link ApplicationErrorReport} initialized from + * a parcel. + */ + ApplicationErrorReport(Parcel in) { + readFromParcel(in); + } + + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(type); + dest.writeString(packageName); + dest.writeString(installerPackageName); + dest.writeString(processName); + dest.writeLong(time); + + switch (type) { + case TYPE_CRASH: + crashInfo.writeToParcel(dest, flags); + break; + case TYPE_ANR: + anrInfo.writeToParcel(dest, flags); + break; + } + } + + public void readFromParcel(Parcel in) { + type = in.readInt(); + packageName = in.readString(); + installerPackageName = in.readString(); + processName = in.readString(); + time = in.readLong(); + + switch (type) { + case TYPE_CRASH: + crashInfo = new CrashInfo(in); + anrInfo = null; + break; + case TYPE_ANR: + anrInfo = new AnrInfo(in); + crashInfo = null; + break; + } + } + + /** + * Describes an application crash. + */ + public static class CrashInfo { + /** + * Class name of the exception that caused the crash. + */ + public String exceptionClassName; + + /** + * Message stored in the exception. + */ + public String exceptionMessage; + + /** + * File which the exception was thrown from. + */ + public String throwFileName; + + /** + * Class which the exception was thrown from. + */ + public String throwClassName; + + /** + * Method which the exception was thrown from. + */ + public String throwMethodName; + + /** + * Stack trace. + */ + public String stackTrace; + + /** + * Create an uninitialized instance of CrashInfo. + */ + public CrashInfo() { + } + + /** + * Create an instance of CrashInfo initialized from a Parcel. + */ + public CrashInfo(Parcel in) { + exceptionClassName = in.readString(); + exceptionMessage = in.readString(); + throwFileName = in.readString(); + throwClassName = in.readString(); + throwMethodName = in.readString(); + stackTrace = in.readString(); + } + + /** + * Save a CrashInfo instance to a parcel. + */ + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(exceptionClassName); + dest.writeString(exceptionMessage); + dest.writeString(throwFileName); + dest.writeString(throwClassName); + dest.writeString(throwMethodName); + dest.writeString(stackTrace); + } + + /** + * Dump a CrashInfo instance to a Printer. + */ + public void dump(Printer pw, String prefix) { + pw.println(prefix + "exceptionClassName: " + exceptionClassName); + pw.println(prefix + "exceptionMessage: " + exceptionMessage); + pw.println(prefix + "throwFileName: " + throwFileName); + pw.println(prefix + "throwClassName: " + throwClassName); + pw.println(prefix + "throwMethodName: " + throwMethodName); + pw.println(prefix + "stackTrace: " + stackTrace); + } + } + + /** + * Describes an application not responding error. + */ + public static class AnrInfo { + /** + * Activity name. + */ + public String activity; + + /** + * Description of the operation that timed out. + */ + public String cause; + + /** + * Additional info, including CPU stats. + */ + public String info; + + /** + * Create an uninitialized instance of AnrInfo. + */ + public AnrInfo() { + } + + /** + * Create an instance of AnrInfo initialized from a Parcel. + */ + public AnrInfo(Parcel in) { + activity = in.readString(); + cause = in.readString(); + info = in.readString(); + } + + /** + * Save an AnrInfo instance to a parcel. + */ + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(activity); + dest.writeString(cause); + dest.writeString(info); + } + + /** + * Dump an AnrInfo instance to a Printer. + */ + public void dump(Printer pw, String prefix) { + pw.println(prefix + "activity: " + activity); + pw.println(prefix + "cause: " + cause); + pw.println(prefix + "info: " + info); + } + } + + public static final Parcelable.Creator CREATOR + = new Parcelable.Creator() { + public ApplicationErrorReport createFromParcel(Parcel source) { + return new ApplicationErrorReport(source); + } + + public ApplicationErrorReport[] newArray(int size) { + return new ApplicationErrorReport[size]; + } + }; + + public int describeContents() { + return 0; + } + + /** + * Dump the report to a Printer. + */ + public void dump(Printer pw, String prefix) { + pw.println(prefix + "type: " + type); + pw.println(prefix + "packageName: " + packageName); + pw.println(prefix + "installerPackageName: " + installerPackageName); + pw.println(prefix + "processName: " + processName); + pw.println(prefix + "time: " + time); + + switch (type) { + case TYPE_CRASH: + crashInfo.dump(pw, prefix); + break; + case TYPE_ANR: + anrInfo.dump(pw, prefix); + break; + } + } +} diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java index bcc9302082060c1ec0e8177d50cfe054432bfd41..b052c99e75fc2bc35b57aa152ab431ffeba4d216 100644 --- a/core/java/android/app/ApplicationThreadNative.java +++ b/core/java/android/app/ApplicationThreadNative.java @@ -18,6 +18,7 @@ package android.app; import android.content.ComponentName; import android.content.Intent; +import android.content.IIntentReceiver; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.ProviderInfo; @@ -25,6 +26,7 @@ import android.content.pm.ServiceInfo; import android.content.res.Configuration; import android.os.Binder; import android.os.Bundle; +import android.os.Parcelable; import android.os.RemoteException; import android.os.IBinder; import android.os.Parcel; @@ -230,11 +232,13 @@ public abstract class ApplicationThreadNative extends Binder IBinder binder = data.readStrongBinder(); IInstrumentationWatcher testWatcher = IInstrumentationWatcher.Stub.asInterface(binder); int testMode = data.readInt(); + boolean restrictedBackupMode = (data.readInt() != 0); Configuration config = Configuration.CREATOR.createFromParcel(data); HashMap services = data.readHashMap(null); bindApplication(packageName, info, providers, testName, profileName, - testArgs, testWatcher, testMode, config, services); + testArgs, testWatcher, testMode, restrictedBackupMode, + config, services); return true; } @@ -328,7 +332,34 @@ public abstract class ApplicationThreadNative extends Binder data.enforceInterface(IApplicationThread.descriptor); boolean start = data.readInt() != 0; String path = data.readString(); - profilerControl(start, path); + ParcelFileDescriptor fd = data.readInt() != 0 + ? data.readFileDescriptor() : null; + profilerControl(start, path, fd); + return true; + } + + case SET_SCHEDULING_GROUP_TRANSACTION: + { + data.enforceInterface(IApplicationThread.descriptor); + int group = data.readInt(); + setSchedulingGroup(group); + return true; + } + + case SCHEDULE_CREATE_BACKUP_AGENT_TRANSACTION: + { + data.enforceInterface(IApplicationThread.descriptor); + ApplicationInfo appInfo = ApplicationInfo.CREATOR.createFromParcel(data); + int backupMode = data.readInt(); + scheduleCreateBackupAgent(appInfo, backupMode); + return true; + } + + case SCHEDULE_DESTROY_BACKUP_AGENT_TRANSACTION: + { + data.enforceInterface(IApplicationThread.descriptor); + ApplicationInfo appInfo = ApplicationInfo.CREATOR.createFromParcel(data); + scheduleDestroyBackupAgent(appInfo); return true; } } @@ -484,6 +515,24 @@ class ApplicationThreadProxy implements IApplicationThread { data.recycle(); } + public final void scheduleCreateBackupAgent(ApplicationInfo app, int backupMode) + throws RemoteException { + Parcel data = Parcel.obtain(); + data.writeInterfaceToken(IApplicationThread.descriptor); + app.writeToParcel(data, 0); + data.writeInt(backupMode); + mRemote.transact(SCHEDULE_CREATE_BACKUP_AGENT_TRANSACTION, data, null, 0); + data.recycle(); + } + + public final void scheduleDestroyBackupAgent(ApplicationInfo app) throws RemoteException { + Parcel data = Parcel.obtain(); + data.writeInterfaceToken(IApplicationThread.descriptor); + app.writeToParcel(data, 0); + mRemote.transact(SCHEDULE_DESTROY_BACKUP_AGENT_TRANSACTION, data, null, 0); + data.recycle(); + } + public final void scheduleCreateService(IBinder token, ServiceInfo info) throws RemoteException { Parcel data = Parcel.obtain(); @@ -543,7 +592,8 @@ class ApplicationThreadProxy implements IApplicationThread { public final void bindApplication(String packageName, ApplicationInfo info, List providers, ComponentName testName, String profileName, Bundle testArgs, IInstrumentationWatcher testWatcher, int debugMode, - Configuration config, Map services) throws RemoteException { + boolean restrictedBackupMode, Configuration config, + Map services) throws RemoteException { Parcel data = Parcel.obtain(); data.writeInterfaceToken(IApplicationThread.descriptor); data.writeString(packageName); @@ -559,6 +609,7 @@ class ApplicationThreadProxy implements IApplicationThread { data.writeBundle(testArgs); data.writeStrongInterface(testWatcher); data.writeInt(debugMode); + data.writeInt(restrictedBackupMode ? 1 : 0); config.writeToParcel(data, 0); data.writeMap(services); mRemote.transact(BIND_APPLICATION_TRANSACTION, data, null, @@ -663,14 +714,30 @@ class ApplicationThreadProxy implements IApplicationThread { data.recycle(); } - public void profilerControl(boolean start, String path) throws RemoteException { + public void profilerControl(boolean start, String path, + ParcelFileDescriptor fd) throws RemoteException { Parcel data = Parcel.obtain(); data.writeInterfaceToken(IApplicationThread.descriptor); data.writeInt(start ? 1 : 0); data.writeString(path); + if (fd != null) { + data.writeInt(1); + fd.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE); + } else { + data.writeInt(0); + } mRemote.transact(PROFILER_CONTROL_TRANSACTION, data, null, IBinder.FLAG_ONEWAY); data.recycle(); } + + public void setSchedulingGroup(int group) throws RemoteException { + Parcel data = Parcel.obtain(); + data.writeInterfaceToken(IApplicationThread.descriptor); + data.writeInt(group); + mRemote.transact(SET_SCHEDULING_GROUP_TRANSACTION, data, null, + IBinder.FLAG_ONEWAY); + data.recycle(); + } } diff --git a/core/java/android/backup/BackupService.java b/core/java/android/app/BackupAgent.java similarity index 61% rename from core/java/android/backup/BackupService.java rename to core/java/android/app/BackupAgent.java index 50a5921c0421e4449a12cd8468ecfd5e5765a5de..0ac8a1e4cfdc390ab45a90917bdb66bdbf108fbf 100644 --- a/core/java/android/backup/BackupService.java +++ b/core/java/android/app/BackupAgent.java @@ -14,47 +14,38 @@ * limitations under the License. */ -package android.backup; +package android.app; -import android.annotation.SdkConstant; -import android.annotation.SdkConstant.SdkConstantType; -import android.app.Service; -import android.backup.IBackupService; -import android.content.Intent; +import android.app.IBackupAgent; +import android.backup.BackupDataInput; +import android.backup.BackupDataOutput; +import android.content.Context; +import android.content.ContextWrapper; import android.os.IBinder; import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.util.Log; +import java.io.IOException; + /** * This is the central interface between an application and Android's * settings backup mechanism. * - * In order to use the backup service, your application must implement a - * subclass of BackupService, and declare an intent filter - * in the application manifest specifying that your BackupService subclass - * handles the {@link BackupService#SERVICE_ACTION} intent action. For example: - * - *
- *      <!-- Use the class "MyBackupService" to perform backups for my app -->
- *      <service android:name=".MyBackupService">
- *          <intent-filter>
- *              <action android:name="android.backup.BackupService.SERVICE" />
- *          </intent-filter>
- *      </service>
- * * @hide pending API solidification */ +public abstract class BackupAgent extends ContextWrapper { + private static final String TAG = "BackupAgent"; -public abstract class BackupService extends Service { - /** - * Service Action: Participate in the backup infrastructure. Applications - * that wish to use the Android backup mechanism must provide an exported - * subclass of BackupService and give it an {@link android.content.IntentFilter - * IntentFilter} that accepts this action. - */ - @SdkConstant(SdkConstantType.SERVICE_ACTION) - public static final String SERVICE_ACTION = "android.backup.BackupService.SERVICE"; + public BackupAgent() { + super(null); + } + + public void onCreate() { + } + + public void onDestroy() { + } /** * The application is being asked to write any data changed since the @@ -76,7 +67,7 @@ public abstract class BackupService extends Service { * here after writing the requested data to dataFd. */ public abstract void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data, - ParcelFileDescriptor newState); + ParcelFileDescriptor newState) throws IOException; /** * The application is being restored from backup, and should replace any @@ -87,11 +78,17 @@ public abstract class BackupService extends Service { * * @param data An open, read-only ParcelFileDescriptor pointing to a full snapshot * of the application's data. + * @param appVersionCode The android:versionCode value of the application that backed + * up this particular data set. This makes it easier for an application's + * agent to distinguish among several possible older data versions when + * asked to perform the restore operation. * @param newState An open, read/write ParcelFileDescriptor pointing to an empty * file. The application should record the final backup state * here after restoring its data from dataFd. */ - public abstract void onRestore(ParcelFileDescriptor /* TODO: BackupDataInput */ data, ParcelFileDescriptor newState); + public abstract void onRestore(BackupDataInput data, int appVersionCode, + ParcelFileDescriptor newState) + throws IOException; // ----- Core implementation ----- @@ -100,38 +97,52 @@ public abstract class BackupService extends Service { * Returns the private interface called by the backup system. Applications will * not typically override this. */ - public IBinder onBind(Intent intent) { - if (intent.getAction().equals(SERVICE_ACTION)) { - return mBinder; - } - return null; + public IBinder onBind() { + return mBinder; } private final IBinder mBinder = new BackupServiceBinder().asBinder(); + /** @hide */ + public void attach(Context context) { + attachBaseContext(context); + } + // ----- IBackupService binder interface ----- - private class BackupServiceBinder extends IBackupService.Stub { + private class BackupServiceBinder extends IBackupAgent.Stub { + private static final String TAG = "BackupServiceBinder"; + public void doBackup(ParcelFileDescriptor oldState, ParcelFileDescriptor data, ParcelFileDescriptor newState) throws RemoteException { // !!! TODO - real implementation; for now just invoke the callbacks directly - Log.v("BackupServiceBinder", "doBackup() invoked"); - BackupDataOutput output = new BackupDataOutput(BackupService.this, - data.getFileDescriptor()); + Log.v(TAG, "doBackup() invoked"); + BackupDataOutput output = new BackupDataOutput(data.getFileDescriptor()); try { - BackupService.this.onBackup(oldState, output, newState); + BackupAgent.this.onBackup(oldState, output, newState); + } catch (IOException ex) { + Log.d(TAG, "onBackup (" + BackupAgent.this.getClass().getName() + ") threw", ex); + throw new RuntimeException(ex); } catch (RuntimeException ex) { - Log.d("BackupService", "onBackup (" - + BackupService.this.getClass().getName() + ") threw", ex); + Log.d(TAG, "onBackup (" + BackupAgent.this.getClass().getName() + ") threw", ex); throw ex; } } - public void doRestore(ParcelFileDescriptor data, + public void doRestore(ParcelFileDescriptor data, int appVersionCode, ParcelFileDescriptor newState) throws RemoteException { // !!! TODO - real implementation; for now just invoke the callbacks directly - Log.v("BackupServiceBinder", "doRestore() invoked"); - BackupService.this.onRestore(data, newState); + Log.v(TAG, "doRestore() invoked"); + BackupDataInput input = new BackupDataInput(data.getFileDescriptor()); + try { + BackupAgent.this.onRestore(input, appVersionCode, newState); + } catch (IOException ex) { + Log.d(TAG, "onRestore (" + BackupAgent.this.getClass().getName() + ") threw", ex); + throw new RuntimeException(ex); + } catch (RuntimeException ex) { + Log.d(TAG, "onRestore (" + BackupAgent.this.getClass().getName() + ") threw", ex); + throw ex; + } } } } diff --git a/core/java/android/app/DatePickerDialog.java b/core/java/android/app/DatePickerDialog.java index 863cbcc4d11302a6ddcee5514d597dcd689eaa18..78bbb4f42c19ad24cbaa656b9fd8f645a6a570cd 100644 --- a/core/java/android/app/DatePickerDialog.java +++ b/core/java/android/app/DatePickerDialog.java @@ -46,7 +46,6 @@ public class DatePickerDialog extends AlertDialog implements OnClickListener, private final DatePicker mDatePicker; private final OnDateSetListener mCallBack; private final Calendar mCalendar; - private final java.text.DateFormat mDateFormat; private final java.text.DateFormat mTitleDateFormat; private final String[] mWeekDays; @@ -108,7 +107,6 @@ public class DatePickerDialog extends AlertDialog implements OnClickListener, DateFormatSymbols symbols = new DateFormatSymbols(); mWeekDays = symbols.getShortWeekdays(); - mDateFormat = DateFormat.getMediumDateFormat(context); mTitleDateFormat = java.text.DateFormat. getDateInstance(java.text.DateFormat.FULL); mCalendar = Calendar.getInstance(); diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java index b09a57fb27a524b8e6ed5ae885d979e826d4cccc..222fe75fb3a27a24df466533b807227f2d89c4ad 100644 --- a/core/java/android/app/Dialog.java +++ b/core/java/android/app/Dialog.java @@ -16,32 +16,34 @@ package android.app; +import com.android.internal.policy.PolicyManager; + import android.content.Context; import android.content.DialogInterface; import android.graphics.drawable.Drawable; import android.net.Uri; +import android.os.Bundle; import android.os.Handler; import android.os.Message; -import android.os.Bundle; import android.util.Config; import android.util.Log; import android.view.ContextMenu; import android.view.ContextThemeWrapper; import android.view.Gravity; import android.view.KeyEvent; +import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; import android.view.ViewGroup; -import android.view.LayoutInflater; import android.view.Window; import android.view.WindowManager; import android.view.ContextMenu.ContextMenuInfo; import android.view.View.OnCreateContextMenuListener; - -import com.android.internal.policy.PolicyManager; +import android.view.ViewGroup.LayoutParams; +import android.view.accessibility.AccessibilityEvent; import java.lang.ref.WeakReference; @@ -81,6 +83,7 @@ public class Dialog implements DialogInterface, Window.Callback, * {@hide} */ protected boolean mCancelable = true; + private Message mCancelMessage; private Message mDismissMessage; @@ -209,7 +212,9 @@ public class Dialog implements DialogInterface, Window.Callback, if (mShowing) { if (Config.LOGV) Log.v(LOG_TAG, "[Dialog] start: already showing, ignore"); - if (mDecor != null) mDecor.setVisibility(View.VISIBLE); + if (mDecor != null) { + mDecor.setVisibility(View.VISIBLE); + } return; } @@ -236,7 +241,9 @@ public class Dialog implements DialogInterface, Window.Callback, * Hide the dialog, but do not dismiss it. */ public void hide() { - if (mDecor != null) mDecor.setVisibility(View.GONE); + if (mDecor != null) { + mDecor.setVisibility(View.GONE); + } } /** @@ -266,6 +273,7 @@ public class Dialog implements DialogInterface, Window.Callback, } mWindowManager.removeView(mDecor); + mDecor = null; mWindow.closeAllPanels(); onStop(); @@ -280,7 +288,7 @@ public class Dialog implements DialogInterface, Window.Callback, Message.obtain(mDismissMessage).sendToTarget(); } } - + // internal method to make sure mcreated is set properly without requiring // users to call through to super in onCreate void dispatchOnCreate(Bundle savedInstanceState) { @@ -608,6 +616,18 @@ public class Dialog implements DialogInterface, Window.Callback, return onTrackballEvent(ev); } + public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { + event.setClassName(getClass().getName()); + event.setPackageName(mContext.getPackageName()); + + LayoutParams params = getWindow().getAttributes(); + boolean isFullScreen = (params.width == LayoutParams.FILL_PARENT) && + (params.height == LayoutParams.FILL_PARENT); + event.setFullScreen(isFullScreen); + + return false; + } + /** * @see Activity#onCreatePanelView(int) */ diff --git a/core/java/android/app/FullBackupAgent.java b/core/java/android/app/FullBackupAgent.java new file mode 100644 index 0000000000000000000000000000000000000000..d89db96674737c21f005ab59bd14cacc95d649ea --- /dev/null +++ b/core/java/android/app/FullBackupAgent.java @@ -0,0 +1,58 @@ +package android.app; + +import android.backup.BackupDataInput; +import android.backup.BackupDataOutput; +import android.backup.FileBackupHelper; +import android.os.ParcelFileDescriptor; +import android.util.Log; + +import java.io.File; +import java.util.ArrayList; +import java.util.LinkedList; + +/** + * Backs up an application's entire /data/data/<package>/... file system. This + * class is used by the desktop full backup mechanism and is not intended for direct + * use by applications. + * + * {@hide} + */ + +public class FullBackupAgent extends BackupAgent { + // !!! TODO: turn off debugging + private static final String TAG = "FullBackupAgent"; + private static final boolean DEBUG = true; + + @Override + public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data, + ParcelFileDescriptor newState) { + LinkedList dirsToScan = new LinkedList(); + ArrayList allFiles = new ArrayList(); + + // build the list of files in the app's /data/data tree + dirsToScan.add(getFilesDir()); + if (DEBUG) Log.v(TAG, "Backing up dir tree @ " + getFilesDir().getAbsolutePath() + " :"); + while (dirsToScan.size() > 0) { + File dir = dirsToScan.removeFirst(); + File[] contents = dir.listFiles(); + if (contents != null) { + for (File f : contents) { + if (f.isDirectory()) { + dirsToScan.add(f); + } else if (f.isFile()) { + if (DEBUG) Log.v(TAG, " " + f.getAbsolutePath()); + allFiles.add(f.getAbsolutePath()); + } + } + } + } + + // That's the file set; now back it all up + FileBackupHelper helper = new FileBackupHelper(this, (String[])allFiles.toArray()); + helper.performBackup(oldState, data, newState); + } + + @Override + public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState) { + } +} diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index 56b29c1c941fd2a056638de220b68974faed0053..3ec7938b5a10a276978760cb9a7a70c2f31c39d3 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -21,6 +21,9 @@ import android.content.ContentProviderNative; import android.content.IContentProvider; import android.content.Intent; import android.content.IntentFilter; +import android.content.IIntentSender; +import android.content.IIntentReceiver; +import android.content.pm.ApplicationInfo; import android.content.pm.ConfigurationInfo; import android.content.pm.IPackageDataObserver; import android.content.pm.ProviderInfo; @@ -44,9 +47,30 @@ import java.util.List; * {@hide} */ public interface IActivityManager extends IInterface { + /** + * Returned by startActivity() if the start request was canceled because + * app switches are temporarily canceled to ensure the user's last request + * (such as pressing home) is performed. + */ + public static final int START_SWITCHES_CANCELED = 4; + /** + * Returned by startActivity() if an activity wasn't really started, but + * the given Intent was given to the existing top activity. + */ public static final int START_DELIVERED_TO_TOP = 3; + /** + * Returned by startActivity() if an activity wasn't really started, but + * a task was simply brought to the foreground. + */ public static final int START_TASK_TO_FRONT = 2; + /** + * Returned by startActivity() if the caller asked that the Intent not + * be executed if it is the recipient, and that is indeed the case. + */ public static final int START_RETURN_INTENT_TO_CALLER = 1; + /** + * Activity was started successfully as normal. + */ public static final int START_SUCCESS = 0; public static final int START_INTENT_NOT_RESOLVED = -1; public static final int START_CLASS_NOT_FOUND = -2; @@ -128,6 +152,11 @@ public interface IActivityManager extends IInterface { public void serviceDoneExecuting(IBinder token) throws RemoteException; public IBinder peekService(Intent service, String resolvedType) throws RemoteException; + public boolean bindBackupAgent(ApplicationInfo appInfo, int backupRestoreMode) + throws RemoteException; + public void backupAgentCreated(String packageName, IBinder agent) throws RemoteException; + public void unbindBackupAgent(ApplicationInfo appInfo) throws RemoteException; + public boolean startInstrumentation(ComponentName className, String profileFile, int flags, Bundle arguments, IInstrumentationWatcher watcher) throws RemoteException; @@ -221,10 +250,13 @@ public interface IActivityManager extends IInterface { // Turn on/off profiling in a particular process. public boolean profileControl(String process, boolean start, - String path) throws RemoteException; + String path, ParcelFileDescriptor fd) throws RemoteException; public boolean shutdown(int timeout) throws RemoteException; + public void stopAppSwitches() throws RemoteException; + public void resumeAppSwitches() throws RemoteException; + /* * Private non-Binder interfaces */ @@ -371,4 +403,9 @@ public interface IActivityManager extends IInterface { int PEEK_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+84; int PROFILE_CONTROL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+85; int SHUTDOWN_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+86; + int STOP_APP_SWITCHES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+87; + int RESUME_APP_SWITCHES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+88; + int START_BACKUP_AGENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+89; + int BACKUP_AGENT_CREATED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+90; + int UNBIND_BACKUP_AGENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+91; } diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java index 9f3534b0924acf594a16ae20e806d7932ee45ae9..c0bc2a03ac91d9ad55b3634e0b7f22b67a7de971 100644 --- a/core/java/android/app/IApplicationThread.java +++ b/core/java/android/app/IApplicationThread.java @@ -18,12 +18,14 @@ package android.app; import android.content.ComponentName; import android.content.Intent; +import android.content.IIntentReceiver; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.ProviderInfo; import android.content.pm.ServiceInfo; import android.content.res.Configuration; import android.os.Bundle; +import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.os.IBinder; import android.os.IInterface; @@ -59,6 +61,11 @@ public interface IApplicationThread extends IInterface { int configChanges) throws RemoteException; void scheduleReceiver(Intent intent, ActivityInfo info, int resultCode, String data, Bundle extras, boolean sync) throws RemoteException; + static final int BACKUP_MODE_INCREMENTAL = 0; + static final int BACKUP_MODE_FULL = 1; + static final int BACKUP_MODE_RESTORE = 2; + void scheduleCreateBackupAgent(ApplicationInfo app, int backupMode) throws RemoteException; + void scheduleDestroyBackupAgent(ApplicationInfo app) throws RemoteException; void scheduleCreateService(IBinder token, ServiceInfo info) throws RemoteException; void scheduleBindService(IBinder token, Intent intent, boolean rebind) throws RemoteException; @@ -71,8 +78,8 @@ public interface IApplicationThread extends IInterface { static final int DEBUG_WAIT = 2; void bindApplication(String packageName, ApplicationInfo info, List providers, ComponentName testName, String profileName, Bundle testArguments, - IInstrumentationWatcher testWatcher, int debugMode, Configuration config, Map services) throws RemoteException; + IInstrumentationWatcher testWatcher, int debugMode, boolean restrictedBackupMode, + Configuration config, Map services) throws RemoteException; void scheduleExit() throws RemoteException; void requestThumbnail(IBinder token) throws RemoteException; void scheduleConfigurationChanged(Configuration config) throws RemoteException; @@ -86,8 +93,10 @@ public interface IApplicationThread extends IInterface { void scheduleLowMemory() throws RemoteException; void scheduleActivityConfigurationChanged(IBinder token) throws RemoteException; void requestPss() throws RemoteException; - void profilerControl(boolean start, String path) throws RemoteException; - + void profilerControl(boolean start, String path, ParcelFileDescriptor fd) + throws RemoteException; + void setSchedulingGroup(int group) throws RemoteException; + String descriptor = "android.app.IApplicationThread"; int SCHEDULE_PAUSE_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION; @@ -117,4 +126,7 @@ public interface IApplicationThread extends IInterface { int SCHEDULE_RELAUNCH_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+25; int REQUEST_PSS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+26; int PROFILER_CONTROL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+27; + int SET_SCHEDULING_GROUP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+28; + int SCHEDULE_CREATE_BACKUP_AGENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+29; + int SCHEDULE_DESTROY_BACKUP_AGENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+30; } diff --git a/core/java/android/backup/IBackupService.aidl b/core/java/android/app/IBackupAgent.aidl similarity index 82% rename from core/java/android/backup/IBackupService.aidl rename to core/java/android/app/IBackupAgent.aidl index 1bde8eac9bdb175c884ecd323cff23de6b1c1b82..9b0550fce64b1f5a6aec16c9f64902e40e3522f7 100644 --- a/core/java/android/backup/IBackupService.aidl +++ b/core/java/android/app/IBackupAgent.aidl @@ -14,18 +14,18 @@ * limitations under the License. */ -package android.backup; +package android.app; import android.os.ParcelFileDescriptor; /** * Interface presented by applications being asked to participate in the * backup & restore mechanism. End user code does not typically implement - * this interface; they subclass BackupService instead. + * this interface; they subclass BackupAgent instead. * * {@hide} */ -interface IBackupService { +interface IBackupAgent { /** * Request that the app perform an incremental backup. * @@ -51,9 +51,14 @@ interface IBackupService { * app's backup. This is to be a replacement of the app's * current data, not to be merged into it. * + * @param appVersionCode The android:versionCode attribute of the application + * that created this data set. This can help the agent distinguish among + * various historical backup content possibilities. + * * @param newState Read-write file, empty when onRestore() is called, * that is to be written with the state description that holds after * the restore has been completed. */ - void doRestore(in ParcelFileDescriptor data, in ParcelFileDescriptor newState); + void doRestore(in ParcelFileDescriptor data, int appVersionCode, + in ParcelFileDescriptor newState); } diff --git a/core/java/android/app/ISearchManager.aidl b/core/java/android/app/ISearchManager.aidl index 39eb4f1ccbe863438dfceec9fca2d4ce7c3dfb8b..e8bd60a4d86fabc1f50800533a6eeaaa1078483d 100644 --- a/core/java/android/app/ISearchManager.aidl +++ b/core/java/android/app/ISearchManager.aidl @@ -16,11 +16,28 @@ package android.app; +import android.app.ISearchManagerCallback; import android.content.ComponentName; +import android.content.res.Configuration; +import android.os.Bundle; import android.server.search.SearchableInfo; /** @hide */ interface ISearchManager { SearchableInfo getSearchableInfo(in ComponentName launchActivity, boolean globalSearch); List getSearchablesInGlobalSearch(); + List getSearchablesForWebSearch(); + SearchableInfo getDefaultSearchableForWebSearch(); + void setDefaultWebSearch(in ComponentName component); + void startSearch(in String initialQuery, + boolean selectInitialQuery, + in ComponentName launchActivity, + in Bundle appSearchData, + boolean globalSearch, + ISearchManagerCallback searchManagerCallback); + void stopSearch(); + boolean isVisible(); + Bundle onSaveInstanceState(); + void onRestoreInstanceState(in Bundle savedInstanceState); + void onConfigurationChanged(in Configuration newConfig); } diff --git a/core/java/android/app/ISearchManagerCallback.aidl b/core/java/android/app/ISearchManagerCallback.aidl new file mode 100644 index 0000000000000000000000000000000000000000..bdfb2bad7ab4d2b563e1dde70c6217341053e86c --- /dev/null +++ b/core/java/android/app/ISearchManagerCallback.aidl @@ -0,0 +1,23 @@ +/** + * 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.app; + +/** @hide */ +oneway interface ISearchManagerCallback { + void onDismiss(); + void onCancel(); +} diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java index f6a28b23a3a89b362864751d8ebbea08dadfc20e..e31f4f834404d9562e6cab0e3757cda156b844e7 100644 --- a/core/java/android/app/Instrumentation.java +++ b/core/java/android/app/Instrumentation.java @@ -446,13 +446,13 @@ public class Instrumentation { if (ai == null) { throw new RuntimeException("Unable to resolve activity for: " + intent); } - if (!ai.applicationInfo.processName.equals( - getTargetContext().getPackageName())) { + String myProc = mThread.getProcessName(); + if (!ai.processName.equals(myProc)) { // todo: if this intent is ambiguous, look here to see if // there is a single match that is in our package. - throw new RuntimeException("Intent resolved to different package " - + ai.applicationInfo.packageName + ": " - + intent); + throw new RuntimeException("Intent in process " + + myProc + " resolved to different process " + + ai.processName + ": " + intent); } intent.setComponent(new ComponentName( diff --git a/core/java/android/app/LauncherActivity.java b/core/java/android/app/LauncherActivity.java index 8d249da960bdbbe1fac1f5c437958e65ac3d0dfb..accdda9ba599b1b73ef54ba59816c8631c5504d0 100644 --- a/core/java/android/app/LauncherActivity.java +++ b/core/java/android/app/LauncherActivity.java @@ -60,26 +60,20 @@ public abstract class LauncherActivity extends ListActivity { * An item in the list */ public static class ListItem { + public ResolveInfo resolveInfo; public CharSequence label; - //public CharSequence description; public Drawable icon; public String packageName; public String className; public Bundle extras; ListItem(PackageManager pm, ResolveInfo resolveInfo, IconResizer resizer) { + this.resolveInfo = resolveInfo; 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; @@ -122,6 +116,14 @@ public abstract class LauncherActivity extends ListActivity { return intent; } + public ListItem itemForPosition(int position) { + if (mActivitiesList == null) { + return null; + } + + return mActivitiesList.get(position); + } + public int getCount() { return mActivitiesList != null ? mActivitiesList.size() : 0; } @@ -353,6 +355,16 @@ public abstract class LauncherActivity extends ListActivity { return adapter.intentForPosition(position); } + /** + * Return the {@link ListItem} for a specific position in our + * {@link android.widget.ListView}. + * @param position The item to return + */ + protected ListItem itemForPosition(int position) { + ActivityAdapter adapter = (ActivityAdapter) mAdapter; + return adapter.itemForPosition(position); + } + /** * Get the base intent to use when running * {@link PackageManager#queryIntentActivities(Intent, int)}. diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java index cb660c7fde6f55540ceec1f2a1424d0f2001ec87..f9c38f998c35ad0cccd1ffb4d49bbbc2add81709 100644 --- a/core/java/android/app/PendingIntent.java +++ b/core/java/android/app/PendingIntent.java @@ -18,6 +18,9 @@ package android.app; import android.content.Context; import android.content.Intent; +import android.content.IIntentReceiver; +import android.content.IIntentSender; +import android.content.IntentSender; import android.os.Bundle; import android.os.RemoteException; import android.os.Handler; @@ -105,7 +108,7 @@ public final class PendingIntent implements Parcelable { public CanceledException(Exception cause) { super(cause); } - }; + } /** * Callback interface for discovering when a send operation has @@ -270,6 +273,21 @@ public final class PendingIntent implements Parcelable { return null; } + private class IntentSenderWrapper extends IntentSender { + protected IntentSenderWrapper(IIntentSender target) { + super(target); + } + } + /** + * Retrieve a IntentSender object that wraps the existing sender of the PendingIntent + * + * @return Returns a IntentSender object that wraps the sender of PendingIntent + * + */ + public IntentSender getIntentSender() { + return new IntentSenderWrapper(mTarget); + } + /** * Cancel a currently active PendingIntent. Only the original application * owning an PendingIntent can cancel it. diff --git a/core/java/android/app/SearchDialog.java b/core/java/android/app/SearchDialog.java index 343380cc766e63651b85112700f51baa68dfc49a..fdb619ae2da6ed5634149e649ec8ff925cb5c817 100644 --- a/core/java/android/app/SearchDialog.java +++ b/core/java/android/app/SearchDialog.java @@ -24,6 +24,8 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.ContentResolver; +import android.content.ContentValues; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; @@ -32,6 +34,7 @@ import android.content.res.Configuration; import android.content.res.Resources; import android.database.Cursor; import android.graphics.drawable.Drawable; +import android.graphics.drawable.Animatable; import android.net.Uri; import android.os.Bundle; import android.os.SystemClock; @@ -41,8 +44,10 @@ import android.text.Editable; import android.text.InputType; import android.text.TextUtils; import android.text.TextWatcher; +import android.text.util.Regex; import android.util.AttributeSet; import android.util.Log; +import android.view.ContextThemeWrapper; import android.view.Gravity; import android.view.KeyEvent; import android.view.MotionEvent; @@ -51,6 +56,7 @@ import android.view.ViewConfiguration; import android.view.ViewGroup; import android.view.Window; import android.view.WindowManager; +import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodManager; import android.widget.AdapterView; import android.widget.AutoCompleteTextView; @@ -67,8 +73,8 @@ import java.util.WeakHashMap; import java.util.concurrent.atomic.AtomicLong; /** - * In-application-process implementation of Search Bar. This is still controlled by the - * SearchManager, but it runs in the current activity's process to keep things lighter weight. + * System search dialog. This is controlled by the + * SearchManagerService and runs in the system process. * * @hide */ @@ -82,13 +88,11 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS private static final String INSTANCE_KEY_COMPONENT = "comp"; private static final String INSTANCE_KEY_APPDATA = "data"; private static final String INSTANCE_KEY_GLOBALSEARCH = "glob"; - private static final String INSTANCE_KEY_DISPLAY_QUERY = "dQry"; - private static final String INSTANCE_KEY_DISPLAY_SEL_START = "sel1"; - private static final String INSTANCE_KEY_DISPLAY_SEL_END = "sel2"; - private static final String INSTANCE_KEY_SELECTED_ELEMENT = "slEl"; - private static final int INSTANCE_SELECTED_BUTTON = -2; - private static final int INSTANCE_SELECTED_QUERY = -1; - + private static final String INSTANCE_KEY_STORED_COMPONENT = "sComp"; + private static final String INSTANCE_KEY_STORED_APPDATA = "sData"; + private static final String INSTANCE_KEY_PREVIOUS_COMPONENTS = "sPrev"; + private static final String INSTANCE_KEY_USER_QUERY = "uQry"; + private static final int SEARCH_PLATE_LEFT_PADDING_GLOBAL = 12; private static final int SEARCH_PLATE_LEFT_PADDING_NON_GLOBAL = 7; @@ -103,6 +107,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS private Button mGoButton; private ImageButton mVoiceButton; private View mSearchPlate; + private Drawable mWorkingSpinner; // interaction with searchable application private SearchableInfo mSearchable; @@ -129,9 +134,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS private SuggestionsAdapter mSuggestionsAdapter; // Whether to rewrite queries when selecting suggestions - // TODO: This is disabled because of problems with persistent selections - // causing non-user-initiated rewrites. - private static final boolean REWRITE_QUERIES = false; + private static final boolean REWRITE_QUERIES = true; // The query entered by the user. This is not changed when selecting a suggestion // that modifies the contents of the text field. But if the user then edits @@ -142,14 +145,17 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS // more than once. private final WeakHashMap mOutsideDrawablesCache = new WeakHashMap(); - + + // Last known IME options value for the search edit text. + private int mSearchAutoCompleteImeOptions; + /** * Constructor - fires it up and makes it look like the search UI. * * @param context Application Context we can use for system acess */ public SearchDialog(Context context) { - super(context, com.android.internal.R.style.Theme_SearchBar); + super(context, com.android.internal.R.style.Theme_GlobalSearchBar); } /** @@ -160,17 +166,17 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - Window theWindow = getWindow(); - theWindow.setGravity(Gravity.TOP|Gravity.FILL_HORIZONTAL); - setContentView(com.android.internal.R.layout.search_bar); - theWindow.setLayout(ViewGroup.LayoutParams.FILL_PARENT, - // taking up the whole window (even when transparent) is less than ideal, - // but necessary to show the popup window until the window manager supports - // having windows anchored by their parent but not clipped by them. - ViewGroup.LayoutParams.FILL_PARENT); + Window theWindow = getWindow(); WindowManager.LayoutParams lp = theWindow.getAttributes(); + lp.type = WindowManager.LayoutParams.TYPE_SEARCH_BAR; + lp.width = ViewGroup.LayoutParams.FILL_PARENT; + // taking up the whole window (even when transparent) is less than ideal, + // but necessary to show the popup window until the window manager supports + // having windows anchored by their parent but not clipped by them. + lp.height = ViewGroup.LayoutParams.FILL_PARENT; + lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL; lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; theWindow.setAttributes(lp); @@ -182,6 +188,8 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS mGoButton = (Button) findViewById(com.android.internal.R.id.search_go_btn); mVoiceButton = (ImageButton) findViewById(com.android.internal.R.id.search_voice_btn); mSearchPlate = findViewById(com.android.internal.R.id.search_plate); + mWorkingSpinner = getContext().getResources(). + getDrawable(com.android.internal.R.drawable.search_spinner); // attach listeners mSearchAutoComplete.addTextChangedListener(mTextWatcher); @@ -213,10 +221,14 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS // Save voice intent for later queries/launching mVoiceWebSearchIntent = new Intent(RecognizerIntent.ACTION_WEB_SEARCH); + mVoiceWebSearchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); mVoiceWebSearchIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_WEB_SEARCH); mVoiceAppSearchIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH); + mVoiceAppSearchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + + mSearchAutoCompleteImeOptions = mSearchAutoComplete.getImeOptions(); } /** @@ -226,20 +238,21 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS */ public boolean show(String initialQuery, boolean selectInitialQuery, ComponentName componentName, Bundle appSearchData, boolean globalSearch) { - if (isShowing()) { - // race condition - already showing but not handling events yet. - // in this case, just discard the "show" request - return true; - } - + // Reset any stored values from last time dialog was shown. mStoredComponentName = null; mStoredAppSearchData = null; - - return doShow(initialQuery, selectInitialQuery, componentName, appSearchData, globalSearch); + + boolean success = doShow(initialQuery, selectInitialQuery, componentName, appSearchData, + globalSearch); + if (success) { + // Display the drop down as soon as possible instead of waiting for the rest of the + // pending UI stuff to get done, so that things appear faster to the user. + mSearchAutoComplete.showDropDownAfterLayout(); + } + return success; } - /** * Called in response to a press of the hard search button in * {@link #onKeyDown(int, KeyEvent)}, this method toggles between in-app @@ -309,15 +322,17 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS + appSearchData + ", " + globalSearch + ")"); } + SearchManager searchManager = (SearchManager) + mContext.getSystemService(Context.SEARCH_SERVICE); // Try to get the searchable info for the provided component (or for global search, // if globalSearch == true). - mSearchable = SearchManager.getSearchableInfo(componentName, globalSearch); + mSearchable = searchManager.getSearchableInfo(componentName, globalSearch); // If we got back nothing, and it wasn't a request for global search, then try again // for global search, as we'll try to launch that in lieu of any component-specific search. if (!globalSearch && mSearchable == null) { globalSearch = true; - mSearchable = SearchManager.getSearchableInfo(componentName, globalSearch); + mSearchable = searchManager.getSearchableInfo(componentName, globalSearch); // If we still get back null (i.e., there's not even a searchable info available // for global search), then really give up. @@ -332,7 +347,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS mAppSearchData = appSearchData; // Using globalSearch here is just an optimization, just calling // isDefaultSearchable() should always give the same result. - mGlobalSearchMode = globalSearch || SearchManager.isDefaultSearchable(mSearchable); + mGlobalSearchMode = globalSearch || searchManager.isDefaultSearchable(mSearchable); mActivityContext = mSearchable.getActivityContext(getContext()); // show the dialog. this will call onStart(). @@ -345,6 +360,21 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS getContext().getSystemService(Context.INPUT_METHOD_SERVICE); inputManager.showSoftInputUnchecked(0, null); } + + // The Dialog uses a ContextThemeWrapper for the context; use this to change the + // theme out from underneath us, between the global search theme and the in-app + // search theme. They are identical except that the global search theme does not + // dim the background of the window (because global search is full screen so it's + // not needed and this should save a little bit of time on global search invocation). + Object context = getContext(); + if (context instanceof ContextThemeWrapper) { + ContextThemeWrapper wrapper = (ContextThemeWrapper) context; + if (globalSearch) { + wrapper.setTheme(com.android.internal.R.style.Theme_GlobalSearchBar); + } else { + wrapper.setTheme(com.android.internal.R.style.Theme_SearchBar); + } + } show(); } @@ -372,11 +402,6 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS public void onStop() { super.onStop(); - // TODO: Removing the listeners means that they never get called, since - // Dialog.dismissDialog() calls onStop() before sendDismissMessage(). - setOnCancelListener(null); - setOnDismissListener(null); - // stop receiving broadcasts (throws exception if none registered) try { getContext().unregisterReceiver(mBroadcastReceiver); @@ -394,6 +419,24 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS mUserQuery = null; mPreviousComponents = null; } + + /** + * Sets the search dialog to the 'working' state, which shows a working spinner in the + * right hand size of the text field. + * + * @param working true to show spinner, false to hide spinner + */ + public void setWorking(boolean working) { + if (working) { + mSearchAutoComplete.setCompoundDrawablesWithIntrinsicBounds( + null, null, mWorkingSpinner, null); + ((Animatable) mWorkingSpinner).start(); + } else { + mSearchAutoComplete.setCompoundDrawablesWithIntrinsicBounds( + null, null, null, null); + ((Animatable) mWorkingSpinner).stop(); + } + } /** * Closes and gets rid of the suggestions adapter. @@ -412,8 +455,6 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS /** * Save the minimal set of data necessary to recreate the search * - * TODO: go through this and make sure that it saves everything that is needed - * * @return A bundle with the state of the dialog. */ @Override @@ -424,20 +465,11 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS bundle.putParcelable(INSTANCE_KEY_COMPONENT, mLaunchComponent); bundle.putBundle(INSTANCE_KEY_APPDATA, mAppSearchData); bundle.putBoolean(INSTANCE_KEY_GLOBALSEARCH, mGlobalSearchMode); - - // UI state - bundle.putString(INSTANCE_KEY_DISPLAY_QUERY, mSearchAutoComplete.getText().toString()); - bundle.putInt(INSTANCE_KEY_DISPLAY_SEL_START, mSearchAutoComplete.getSelectionStart()); - bundle.putInt(INSTANCE_KEY_DISPLAY_SEL_END, mSearchAutoComplete.getSelectionEnd()); - - int selectedElement = INSTANCE_SELECTED_QUERY; - if (mGoButton.isFocused()) { - selectedElement = INSTANCE_SELECTED_BUTTON; - } else if (mSearchAutoComplete.isPopupShowing()) { - selectedElement = 0; // TODO mSearchTextField.getListSelection() // 0..n - } - bundle.putInt(INSTANCE_KEY_SELECTED_ELEMENT, selectedElement); - + bundle.putParcelable(INSTANCE_KEY_STORED_COMPONENT, mStoredComponentName); + bundle.putBundle(INSTANCE_KEY_STORED_APPDATA, mStoredAppSearchData); + bundle.putParcelableArrayList(INSTANCE_KEY_PREVIOUS_COMPONENTS, mPreviousComponents); + bundle.putString(INSTANCE_KEY_USER_QUERY, mUserQuery); + return bundle; } @@ -451,45 +483,27 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS */ @Override public void onRestoreInstanceState(Bundle savedInstanceState) { - // Get the launch info ComponentName launchComponent = savedInstanceState.getParcelable(INSTANCE_KEY_COMPONENT); Bundle appSearchData = savedInstanceState.getBundle(INSTANCE_KEY_APPDATA); boolean globalSearch = savedInstanceState.getBoolean(INSTANCE_KEY_GLOBALSEARCH); - - // get the UI state - String displayQuery = savedInstanceState.getString(INSTANCE_KEY_DISPLAY_QUERY); - int querySelStart = savedInstanceState.getInt(INSTANCE_KEY_DISPLAY_SEL_START, -1); - int querySelEnd = savedInstanceState.getInt(INSTANCE_KEY_DISPLAY_SEL_END, -1); - int selectedElement = savedInstanceState.getInt(INSTANCE_KEY_SELECTED_ELEMENT); - - // show the dialog. skip any show/hide animation, we want to go fast. - // send the text that actually generates the suggestions here; we'll replace the display - // text as necessary in a moment. - if (!show(displayQuery, false, launchComponent, appSearchData, globalSearch)) { + ComponentName storedComponentName = + savedInstanceState.getParcelable(INSTANCE_KEY_STORED_COMPONENT); + Bundle storedAppSearchData = + savedInstanceState.getBundle(INSTANCE_KEY_STORED_APPDATA); + ArrayList previousComponents = + savedInstanceState.getParcelableArrayList(INSTANCE_KEY_PREVIOUS_COMPONENTS); + String userQuery = savedInstanceState.getString(INSTANCE_KEY_USER_QUERY); + + // Set stored state + mStoredComponentName = storedComponentName; + mStoredAppSearchData = storedAppSearchData; + mPreviousComponents = previousComponents; + + // show the dialog. + if (!doShow(userQuery, false, launchComponent, appSearchData, globalSearch)) { // for some reason, we couldn't re-instantiate return; } - - mSearchAutoComplete.setText(displayQuery); - - // clean up the selection state - switch (selectedElement) { - case INSTANCE_SELECTED_BUTTON: - mGoButton.setEnabled(true); - mGoButton.setFocusable(true); - mGoButton.requestFocus(); - break; - case INSTANCE_SELECTED_QUERY: - if (querySelStart >= 0 && querySelEnd >= 0) { - mSearchAutoComplete.requestFocus(); - mSearchAutoComplete.setSelection(querySelStart, querySelEnd); - } - break; - default: - // TODO: defer selecting a list element until suggestion list appears -// mSearchAutoComplete.setListSelection(selectedElement) - break; - } } /** @@ -534,7 +548,8 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS } } mSearchAutoComplete.setInputType(inputType); - mSearchAutoComplete.setImeOptions(mSearchable.getImeOptions()); + mSearchAutoCompleteImeOptions = mSearchable.getImeOptions(); + mSearchAutoComplete.setImeOptions(mSearchAutoCompleteImeOptions); } } @@ -547,24 +562,20 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS mSearchAutoComplete.setDropDownAnimationStyle(0); // no animation mSearchAutoComplete.setThreshold(mSearchable.getSuggestThreshold()); + // we dismiss the entire dialog instead + mSearchAutoComplete.setDropDownDismissedOnCompletion(false); if (mGlobalSearchMode) { mSearchAutoComplete.setDropDownAlwaysVisible(true); // fill space until results come in - mSearchAutoComplete.setDropDownDismissedOnCompletion(false); - mSearchAutoComplete.setDropDownBackgroundResource( - com.android.internal.R.drawable.search_dropdown_background); } else { mSearchAutoComplete.setDropDownAlwaysVisible(false); - mSearchAutoComplete.setDropDownDismissedOnCompletion(true); - mSearchAutoComplete.setDropDownBackgroundResource( - com.android.internal.R.drawable.search_dropdown_background_apps); } // attach the suggestions adapter, if suggestions are available // The existence of a suggestions authority is the proxy for "suggestions available here" if (mSearchable.getSuggestAuthority() != null) { - mSuggestionsAdapter = new SuggestionsAdapter(getContext(), mSearchable, - mOutsideDrawablesCache); + mSuggestionsAdapter = new SuggestionsAdapter(getContext(), this, mSearchable, + mOutsideDrawablesCache, mGlobalSearchMode); mSearchAutoComplete.setAdapter(mSuggestionsAdapter); } } @@ -597,7 +608,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS mSearchPlate.getPaddingBottom()); } else { PackageManager pm = getContext().getPackageManager(); - Drawable icon = null; + Drawable icon; try { ActivityInfo info = pm.getActivityInfo(mLaunchComponent, 0); icon = pm.getApplicationIcon(info.applicationInfo); @@ -765,7 +776,24 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS } } - public void afterTextChanged(Editable s) { } + public void afterTextChanged(Editable s) { + if (!mSearchAutoComplete.isPerformingCompletion()) { + // The user changed the query, check if it is a URL and if so change the search + // button in the soft keyboard to the 'Go' button. + int options = (mSearchAutoComplete.getImeOptions() & (~EditorInfo.IME_MASK_ACTION)); + if (Regex.WEB_URL_PATTERN.matcher(mUserQuery).matches()) { + options = options | EditorInfo.IME_ACTION_GO; + } else { + options = options | EditorInfo.IME_ACTION_SEARCH; + } + if (options != mSearchAutoCompleteImeOptions) { + mSearchAutoCompleteImeOptions = options; + mSearchAutoComplete.setImeOptions(options); + // This call is required to update the soft keyboard UI with latest IME flags. + mSearchAutoComplete.setInputType(mSearchAutoComplete.getInputType()); + } + } + } }; /** @@ -902,6 +930,32 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS return voiceIntent; } + /** + * Corrects http/https typo errors in the given url string, and if the protocol specifier was + * not present defaults to http. + * + * @param inUrl URL to check and fix + * @return fixed URL string. + */ + private String fixUrl(String inUrl) { + if (inUrl.startsWith("http://") || inUrl.startsWith("https://")) + return inUrl; + + if (inUrl.startsWith("http:") || inUrl.startsWith("https:")) { + if (inUrl.startsWith("http:/") || inUrl.startsWith("https:/")) { + inUrl = inUrl.replaceFirst("/", "//"); + } else { + inUrl = inUrl.replaceFirst(":", "://"); + } + } + + if (inUrl.indexOf("://") == -1) { + inUrl = "http://" + inUrl; + } + + return inUrl; + } + /** * 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. @@ -932,7 +986,19 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS if (keyCode == KeyEvent.KEYCODE_ENTER && event.getAction() == KeyEvent.ACTION_UP) { v.cancelLongPress(); - launchQuerySearch(); + + // If this is a url entered by the user and we displayed the 'Go' button which + // the user clicked, launch the url instead of using it as a search query. + if ((mSearchAutoCompleteImeOptions & EditorInfo.IME_MASK_ACTION) + == EditorInfo.IME_ACTION_GO) { + Uri uri = Uri.parse(fixUrl(mSearchAutoComplete.getText().toString())); + Intent intent = new Intent(Intent.ACTION_VIEW, uri); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + launchIntent(intent); + } else { + // Launch as a regular search. + launchQuerySearch(); + } return true; } if (event.getAction() == KeyEvent.ACTION_DOWN) { @@ -1069,7 +1135,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS */ protected void launchQuerySearch(int actionKey, String actionMsg) { String query = mSearchAutoComplete.getText().toString(); - Intent intent = createIntent(Intent.ACTION_SEARCH, null, query, null, + Intent intent = createIntent(Intent.ACTION_SEARCH, null, null, query, null, actionKey, actionMsg); launchIntent(intent); } @@ -1097,15 +1163,121 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS protected boolean launchSuggestion(int position, int actionKey, String actionMsg) { Cursor c = mSuggestionsAdapter.getCursor(); if ((c != null) && c.moveToPosition(position)) { + Intent intent = createIntentFromSuggestion(c, actionKey, actionMsg); + + // report back about the click + if (mGlobalSearchMode) { + // in global search mode, do it via cursor + mSuggestionsAdapter.callCursorOnClick(c, position); + } else if (intent != null + && mPreviousComponents != null + && !mPreviousComponents.isEmpty()) { + // in-app search (and we have pivoted in as told by mPreviousComponents, + // which is used for keeping track of what we pop back to when we are pivoting into + // in app search.) + reportInAppClickToGlobalSearch(c, intent); + } + + // launch the intent launchIntent(intent); + return true; } return false; } - + + /** + * Report a click from an in app search result back to global search for shortcutting porpoises. + * + * @param c The cursor that is pointing to the clicked position. + * @param intent The intent that will be launched for the click. + */ + private void reportInAppClickToGlobalSearch(Cursor c, Intent intent) { + // for in app search, still tell global search via content provider + Uri uri = getClickReportingUri(); + final ContentValues cv = new ContentValues(); + cv.put(SearchManager.SEARCH_CLICK_REPORT_COLUMN_QUERY, mUserQuery); + final ComponentName source = mSearchable.getSearchActivity(); + cv.put(SearchManager.SEARCH_CLICK_REPORT_COLUMN_COMPONENT, source.flattenToShortString()); + + // grab the intent columns from the intent we created since it has additional + // logic for falling back on the searchable default + cv.put(SearchManager.SUGGEST_COLUMN_INTENT_ACTION, intent.getAction()); + cv.put(SearchManager.SUGGEST_COLUMN_INTENT_DATA, intent.getDataString()); + cv.put(SearchManager.SUGGEST_COLUMN_INTENT_COMPONENT_NAME, + intent.getStringExtra(SearchManager.COMPONENT_NAME_KEY)); + + // ensure the icons will work for global search + cv.put(SearchManager.SUGGEST_COLUMN_ICON_1, + wrapIconForPackage( + source, + getColumnString(c, SearchManager.SUGGEST_COLUMN_ICON_1))); + cv.put(SearchManager.SUGGEST_COLUMN_ICON_2, + wrapIconForPackage( + source, + getColumnString(c, SearchManager.SUGGEST_COLUMN_ICON_2))); + + // the rest can be passed through directly + cv.put(SearchManager.SUGGEST_COLUMN_FORMAT, + getColumnString(c, SearchManager.SUGGEST_COLUMN_FORMAT)); + cv.put(SearchManager.SUGGEST_COLUMN_TEXT_1, + getColumnString(c, SearchManager.SUGGEST_COLUMN_TEXT_1)); + cv.put(SearchManager.SUGGEST_COLUMN_TEXT_2, + getColumnString(c, SearchManager.SUGGEST_COLUMN_TEXT_2)); + cv.put(SearchManager.SUGGEST_COLUMN_QUERY, + getColumnString(c, SearchManager.SUGGEST_COLUMN_QUERY)); + cv.put(SearchManager.SUGGEST_COLUMN_SHORTCUT_ID, + getColumnString(c, SearchManager.SUGGEST_COLUMN_SHORTCUT_ID)); + // note: deliberately omitting background color since it is only for global search + // "more results" entries + mContext.getContentResolver().insert(uri, cv); + } + /** - * Launches an intent. Also dismisses the search dialog if not in global search mode. + * @return A URI appropriate for reporting a click. + */ + private Uri getClickReportingUri() { + Uri.Builder uriBuilder = new Uri.Builder() + .scheme(ContentResolver.SCHEME_CONTENT) + .authority(SearchManager.SEARCH_CLICK_REPORT_AUTHORITY); + + uriBuilder.appendPath(SearchManager.SEARCH_CLICK_REPORT_URI_PATH); + + return uriBuilder + .query("") // TODO: Remove, workaround for a bug in Uri.writeToParcel() + .fragment("") // TODO: Remove, workaround for a bug in Uri.writeToParcel() + .build(); + } + + /** + * Wraps an icon for a particular package. If the icon is a resource id, it is converted into + * an android.resource:// URI. + * + * @param source The source of the icon + * @param icon The icon retrieved from a suggestion column + * @return An icon string appropriate for the package. + */ + private String wrapIconForPackage(ComponentName source, String icon) { + if (icon == null || icon.length() == 0 || "0".equals(icon)) { + // SearchManager specifies that null or zero can be returned to indicate + // no icon. We also allow empty string. + return null; + } else if (!Character.isDigit(icon.charAt(0))){ + return icon; + } else { + String packageName = source.getPackageName(); + return new Uri.Builder() + .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE) + .authority(packageName) + .encodedPath(icon) + .toString(); + } + } + + /** + * Launches an intent and dismisses the search dialog (unless the intent + * is one of the special intents that modifies the state of the search dialog). */ private void launchIntent(Intent intent) { if (intent == null) { @@ -1114,9 +1286,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS if (handleSpecialIntent(intent)){ return; } - if (!mGlobalSearchMode) { - dismiss(); - } + dismiss(); getContext().startActivity(intent); } @@ -1130,15 +1300,12 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS if (SearchManager.INTENT_ACTION_CHANGE_SEARCH_SOURCE.equals(action)) { handleChangeSourceIntent(intent); return true; - } else if (SearchManager.INTENT_ACTION_CURSOR_RESPOND.equals(action)) { - handleCursorRespondIntent(intent); - return true; } return false; } /** - * Handles SearchManager#INTENT_ACTION_CHANGE_SOURCE. + * Handles {@link SearchManager#INTENT_ACTION_CHANGE_SEARCH_SOURCE}. */ private void handleChangeSourceIntent(Intent intent) { Uri dataUri = intent.getData(); @@ -1162,18 +1329,16 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS String query = intent.getStringExtra(SearchManager.QUERY); setUserQuery(query); + mSearchAutoComplete.showDropDown(); } - + /** - * Handles {@link SearchManager#INTENT_ACTION_CURSOR_RESPOND}. + * Sets the list item selection in the AutoCompleteTextView's ListView. */ - private void handleCursorRespondIntent(Intent intent) { - Cursor c = mSuggestionsAdapter.getCursor(); - if (c != null) { - c.respond(intent.getExtras()); - } + public void setListSelection(int index) { + mSearchAutoComplete.setListSelection(index); } - + /** * Saves the previous component that was searched, so that we can go * back to it. @@ -1243,6 +1408,12 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS try { // use specific action if supplied, or default action if supplied, or fixed default String action = getColumnString(c, SearchManager.SUGGEST_COLUMN_INTENT_ACTION); + + // some items are display only, or have effect via the cursor respond click reporting. + if (SearchManager.INTENT_ACTION_NONE.equals(action)) { + return null; + } + if (action == null) { action = mSearchable.getSuggestIntentAction(); } @@ -1264,11 +1435,14 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS } Uri dataUri = (data == null) ? null : Uri.parse(data); - String extraData = getColumnString(c, SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA); - + String componentName = getColumnString( + c, SearchManager.SUGGEST_COLUMN_INTENT_COMPONENT_NAME); + String query = getColumnString(c, SearchManager.SUGGEST_COLUMN_QUERY); + String extraData = getColumnString(c, SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA); - return createIntent(action, dataUri, query, extraData, actionKey, actionMsg); + return createIntent(action, dataUri, extraData, query, componentName, actionKey, + actionMsg); } catch (RuntimeException e ) { int rowNum; try { // be really paranoid now @@ -1287,27 +1461,33 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS * * @param action Intent action. * @param data Intent data, or null. - * @param query Intent query, or null. * @param extraData Data for {@link SearchManager#EXTRA_DATA_KEY} or null. + * @param query Intent query, or null. + * @param componentName Data for {@link SearchManager#COMPONENT_NAME_KEY} or null. * @param actionKey The key code of the action key that was pressed, * or {@link KeyEvent#KEYCODE_UNKNOWN} if none. * @param actionMsg The message for the action key that was pressed, * or null if none. * @return The intent. */ - private Intent createIntent(String action, Uri data, String query, String extraData, - int actionKey, String actionMsg) { + private Intent createIntent(String action, Uri data, String extraData, String query, + String componentName, int actionKey, String actionMsg) { // Now build the Intent Intent intent = new Intent(action); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); if (data != null) { intent.setData(data); } + intent.putExtra(SearchManager.USER_QUERY, mUserQuery); if (query != null) { intent.putExtra(SearchManager.QUERY, query); } if (extraData != null) { intent.putExtra(SearchManager.EXTRA_DATA_KEY, extraData); } + if (componentName != null) { + intent.putExtra(SearchManager.COMPONENT_NAME_KEY, componentName); + } if (mAppSearchData != null) { intent.putExtra(SearchManager.APP_DATA, mAppSearchData); } @@ -1383,20 +1563,22 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS private boolean isEmpty() { return TextUtils.getTrimmedLength(getText()) == 0; } - + /** - * Clears the entered text. + * We override this method to avoid replacing the query box text + * when a suggestion is clicked. */ - private void clear() { - setText(""); + @Override + protected void replaceText(CharSequence text) { } /** - * We override this method to avoid replacing the query box text - * when a suggestion is clicked. + * We override this method to avoid an extra onItemClick being called on the + * drop-down's OnItemClickListener by {@link AutoCompleteTextView#onKeyUp(int, KeyEvent)} + * when an item is clicked with the trackball. */ @Override - protected void replaceText(CharSequence text) { + public void performCompletion() { } /** diff --git a/core/java/android/app/SearchManager.java b/core/java/android/app/SearchManager.java index 3bf37c3b2b0169e7bc5db227d0fd3a07fbd1772c..e5ba6a4a03c7e409e6ffa563ddd40b721c2b528a 100644 --- a/core/java/android/app/SearchManager.java +++ b/core/java/android/app/SearchManager.java @@ -28,6 +28,7 @@ import android.os.Handler; import android.os.RemoteException; import android.os.ServiceManager; import android.server.search.SearchableInfo; +import android.util.Log; import android.view.KeyEvent; import java.util.List; @@ -1108,6 +1109,10 @@ import java.util.List; public class SearchManager implements DialogInterface.OnDismissListener, DialogInterface.OnCancelListener { + + private static final boolean DBG = false; + private static final String TAG = "SearchManager"; + /** * This is a shortcut definition for the default menu key to use for invoking search. * @@ -1130,6 +1135,20 @@ public class SearchManager */ public final static String QUERY = "query"; + /** + * Intent extra data key: Use this key with + * {@link android.content.Intent#getStringExtra + * content.Intent.getStringExtra()} + * to obtain the query string typed in by the user. + * This may be different from the value of {@link #QUERY} + * if the intent is the result of selecting a suggestion. + * In that case, {@link #QUERY} will contain the value of + * {@link #SUGGEST_COLUMN_QUERY} for the suggestion, and + * {@link #USER_QUERY} will contain the string typed by the + * user. + */ + public final static String USER_QUERY = "user_query"; + /** * Intent extra data key: Use this key with Intent.ACTION_SEARCH and * {@link android.content.Intent#getBundleExtra @@ -1148,7 +1167,7 @@ public class SearchManager * @hide */ public final static String SOURCE = "source"; - + /** * Intent extra data key: Use this key with Intent.ACTION_SEARCH and * {@link android.content.Intent#getIntExtra content.Intent.getIntExtra()} @@ -1159,13 +1178,67 @@ public class SearchManager */ public final static String ACTION_KEY = "action_key"; + /** + * Intent component name key: This key will be used for the extra populated by the + * {@link #SUGGEST_COLUMN_INTENT_COMPONENT_NAME} column. + * + * {@hide} + */ + public final static String COMPONENT_NAME_KEY = "intent_component_name_key"; + /** * Intent extra data key: This key will be used for the extra populated by the * {@link #SUGGEST_COLUMN_INTENT_EXTRA_DATA} column. + * * {@hide} */ public final static String EXTRA_DATA_KEY = "intent_extra_data_key"; - + + /** + * Defines the constants used in the communication between {@link android.app.SearchDialog} and + * the global search provider via {@link Cursor#respond(android.os.Bundle)}. + * + * @hide + */ + public static class DialogCursorProtocol { + + /** + * The sent bundle will contain this integer key, with a value set to one of the events + * below. + */ + public final static String METHOD = "DialogCursorProtocol.method"; + + /** + * After data has been refreshed. + */ + public final static int POST_REFRESH = 0; + public final static String POST_REFRESH_RECEIVE_ISPENDING + = "DialogCursorProtocol.POST_REFRESH.isPending"; + public final static String POST_REFRESH_RECEIVE_DISPLAY_NOTIFY + = "DialogCursorProtocol.POST_REFRESH.displayNotify"; + + /** + * Just before closing the cursor. + */ + public final static int PRE_CLOSE = 1; + public final static String PRE_CLOSE_SEND_MAX_DISPLAY_POS + = "DialogCursorProtocol.PRE_CLOSE.sendDisplayPosition"; + + /** + * When a position has been clicked. + */ + public final static int CLICK = 2; + public final static String CLICK_SEND_POSITION + = "DialogCursorProtocol.CLICK.sendPosition"; + public final static String CLICK_RECEIVE_SELECTED_POS + = "DialogCursorProtocol.CLICK.receiveSelectedPosition"; + + /** + * When the threshold received in {@link #POST_REFRESH_RECEIVE_DISPLAY_NOTIFY} is displayed. + */ + public final static int THRESH_HIT = 3; + } + /** * Intent extra data key: Use this key with Intent.ACTION_SEARCH and * {@link android.content.Intent#getStringExtra content.Intent.getStringExtra()} @@ -1210,6 +1283,41 @@ public class SearchManager */ public final static String SHORTCUT_MIME_TYPE = "vnd.android.cursor.item/vnd.android.search.suggest"; + + + /** + * The authority of the provider to report clicks to when a click is detected after pivoting + * into a specific app's search from global search. + * + * In addition to the columns below, the suggestion columns are used to pass along the full + * suggestion so it can be shortcutted. + * + * @hide + */ + public final static String SEARCH_CLICK_REPORT_AUTHORITY = + "com.android.globalsearch.stats"; + + /** + * The path the write goes to. + * + * @hide + */ + public final static String SEARCH_CLICK_REPORT_URI_PATH = "click"; + + /** + * The column storing the query for the click. + * + * @hide + */ + public final static String SEARCH_CLICK_REPORT_COLUMN_QUERY = "query"; + + /** + * The column storing the component name of the application that was pivoted into. + * + * @hide + */ + public final static String SEARCH_CLICK_REPORT_COLUMN_COMPONENT = "component"; + /** * Column name for suggestions cursor. Unused - can be null or column can be omitted. */ @@ -1257,28 +1365,6 @@ public class SearchManager * for more information on these schemes. */ public final static String SUGGEST_COLUMN_ICON_2 = "suggest_icon_2"; - /** - * Column name for suggestions cursor. Optional. If your cursor includes this column, - * then all suggestions will be provided in a format that includes space for two small icons, - * one at the left and one at the right of each suggestion. The data in the column must - * be a blob that contains a bitmap. - * - * This column overrides any icon provided in the {@link #SUGGEST_COLUMN_ICON_1} column. - * - * @hide - */ - public final static String SUGGEST_COLUMN_ICON_1_BITMAP = "suggest_icon_1_bitmap"; - /** - * Column name for suggestions cursor. Optional. If your cursor includes this column, - * then all suggestions will be provided in a format that includes space for two small icons, - * one at the left and one at the right of each suggestion. The data in the column must - * be a blob that contains a bitmap. - * - * This column overrides any icon provided in the {@link #SUGGEST_COLUMN_ICON_2} column. - * - * @hide - */ - public final static String SUGGEST_COLUMN_ICON_2_BITMAP = "suggest_icon_2_bitmap"; /** * Column name for suggestions cursor. Optional. If this column exists and * this element exists at the given row, this is the action that will be used when @@ -1299,14 +1385,25 @@ public class SearchManager * it is more efficient to specify it using XML metadata and omit it from the cursor. */ public final static String SUGGEST_COLUMN_INTENT_DATA = "suggest_intent_data"; + /** + * Column name for suggestions cursor. Optional. If this column exists and + * this element exists at the given row, this is the data that will be used when + * forming the suggestion's intent. If not provided, the Intent's extra data field will be null. + * This column allows suggestions to provide additional arbitrary data which will be included as + * an extra under the key EXTRA_DATA_KEY. + * + * @hide Pending API council approval. + */ + public final static String SUGGEST_COLUMN_INTENT_EXTRA_DATA = "suggest_intent_extra_data"; /** * Column name for suggestions cursor. Optional. This column allows suggestions * to provide additional arbitrary data which will be included as an extra under the key - * {@link #EXTRA_DATA_KEY}. - * - * @hide pending API council approval + * {@link #COMPONENT_NAME_KEY}. For use by the global search system only - if other providers + * attempt to use this column, the value will be overwritten by global search. + * + * @hide */ - public final static String SUGGEST_COLUMN_INTENT_EXTRA_DATA = "suggest_intent_extra_data"; + public final static String SUGGEST_COLUMN_INTENT_COMPONENT_NAME = "suggest_intent_component"; /** * Column name for suggestions cursor. Optional. If this column exists and * this element exists at the given row, then "/" and this value will be appended to the data @@ -1334,6 +1431,25 @@ public class SearchManager */ public final static String SUGGEST_COLUMN_SHORTCUT_ID = "suggest_shortcut_id"; + /** + * Column name for suggestions cursor. Optional. This column is used to specify the + * cursor item's background color if it needs a non-default background color. A non-zero value + * indicates a valid background color to override the default. + * + * @hide For internal use, not part of the public API. + */ + public final static String SUGGEST_COLUMN_BACKGROUND_COLOR = "suggest_background_color"; + + /** + * Column name for suggestions cursor. Optional. This column is used to specify + * that a spinner should be shown in lieu of an icon2 while the shortcut of this suggestion + * is being refreshed. + * + * @hide Pending API council approval. + */ + public final static String SUGGEST_COLUMN_SPINNER_WHILE_REFRESHING = + "suggest_spinner_while_refreshing"; + /** * Column value for suggestion column {@link #SUGGEST_COLUMN_SHORTCUT_ID} when a suggestion * should not be stored as a shortcut in global search. @@ -1362,21 +1478,7 @@ public class SearchManager */ public final static String INTENT_ACTION_CHANGE_SEARCH_SOURCE = "android.search.action.CHANGE_SEARCH_SOURCE"; - - /** - * If a suggestion has this value in {@link #SUGGEST_COLUMN_INTENT_ACTION}, - * the search dialog will call {@link Cursor#respond(Bundle)} when the - * suggestion is clicked. - * - * The {@link Bundle} argument will be constructed - * in the same way as the "extra" bundle included in an Intent constructed - * from the suggestion. - * - * @hide Pending API council approval. - */ - public final static String INTENT_ACTION_CURSOR_RESPOND - = "android.search.action.CURSOR_RESPOND"; - + /** * Intent action for finding the global search activity. * The global search provider should handle this intent. @@ -1395,22 +1497,54 @@ public class SearchManager public final static String INTENT_ACTION_SEARCH_SETTINGS = "android.search.action.SEARCH_SETTINGS"; + /** + * Intent action for starting a web search provider's settings activity. + * Web search providers should handle this intent if they have provider-specific + * settings to implement. + * + * @hide Pending API council approval. + */ + public final static String INTENT_ACTION_WEB_SEARCH_SETTINGS + = "android.search.action.WEB_SEARCH_SETTINGS"; + + /** + * Intent action broadcasted to inform that the searchables list or default have changed. + * Components should handle this intent if they cache any searchable data and wish to stay + * up to date on changes. + * + * @hide Pending API council approval. + */ + public final static String INTENT_ACTION_SEARCHABLES_CHANGED + = "android.search.action.SEARCHABLES_CHANGED"; + + /** + * If a suggestion has this value in {@link #SUGGEST_COLUMN_INTENT_ACTION}, + * the search dialog will take no action. + * + * @hide + */ + public final static String INTENT_ACTION_NONE = "android.search.action.ZILCH"; + /** * Reference to the shared system search service. */ - private static ISearchManager sService = getSearchManagerService(); + private static ISearchManager mService; private final Context mContext; - private final Handler mHandler; - - private SearchDialog mSearchDialog; - - private OnDismissListener mDismissListener = null; - private OnCancelListener mCancelListener = null; + + // package private since they are used by the inner class SearchManagerCallback + /* package */ boolean mIsShowing = false; + /* package */ final Handler mHandler; + /* package */ OnDismissListener mDismissListener = null; + /* package */ OnCancelListener mCancelListener = null; + + private final SearchManagerCallback mSearchManagerCallback = new SearchManagerCallback(); /*package*/ SearchManager(Context context, Handler handler) { mContext = context; mHandler = handler; + mService = ISearchManager.Stub.asInterface( + ServiceManager.getService(Context.SEARCH_SERVICE)); } /** @@ -1458,17 +1592,16 @@ public class SearchManager ComponentName launchActivity, Bundle appSearchData, boolean globalSearch) { - - if (mSearchDialog == null) { - mSearchDialog = new SearchDialog(mContext); + if (DBG) debug("startSearch(), mIsShowing=" + mIsShowing); + if (mIsShowing) return; + try { + mIsShowing = true; + // activate the search manager and start it up! + mService.startSearch(initialQuery, selectInitialQuery, launchActivity, appSearchData, + globalSearch, mSearchManagerCallback); + } catch (RemoteException ex) { + Log.e(TAG, "startSearch() failed: " + ex); } - - // activate the search manager and start it up! - mSearchDialog.show(initialQuery, selectInitialQuery, launchActivity, appSearchData, - globalSearch); - - mSearchDialog.setOnCancelListener(this); - mSearchDialog.setOnDismissListener(this); } /** @@ -1482,9 +1615,16 @@ public class SearchManager * * @see #startSearch */ - public void stopSearch() { - if (mSearchDialog != null) { - mSearchDialog.cancel(); + public void stopSearch() { + if (DBG) debug("stopSearch(), mIsShowing=" + mIsShowing); + if (!mIsShowing) return; + try { + mService.stopSearch(); + // onDismiss will also clear this, but we do it here too since onDismiss() is + // called asynchronously. + mIsShowing = false; + } catch (RemoteException ex) { + Log.e(TAG, "stopSearch() failed: " + ex); } } @@ -1497,33 +1637,33 @@ public class SearchManager * * @hide */ - public boolean isVisible() { - if (mSearchDialog != null) { - return mSearchDialog.isShowing(); - } - return false; + public boolean isVisible() { + if (DBG) debug("isVisible(), mIsShowing=" + mIsShowing); + return mIsShowing; } - + /** - * See {@link #setOnDismissListener} for configuring your activity to monitor search UI state. + * See {@link SearchManager#setOnDismissListener} for configuring your activity to monitor + * search UI state. */ public interface OnDismissListener { /** - * This method will be called when the search UI is dismissed. To make use if it, you must - * implement this method in your activity, and call {@link #setOnDismissListener} to - * register it. + * This method will be called when the search UI is dismissed. To make use of it, you must + * implement this method in your activity, and call + * {@link SearchManager#setOnDismissListener} to register it. */ public void onDismiss(); } /** - * See {@link #setOnCancelListener} for configuring your activity to monitor search UI state. + * See {@link SearchManager#setOnCancelListener} for configuring your activity to monitor + * search UI state. */ public interface OnCancelListener { /** * This method will be called when the search UI is canceled. To make use if it, you must - * implement this method in your activity, and call {@link #setOnCancelListener} to - * register it. + * implement this method in your activity, and call + * {@link SearchManager#setOnCancelListener} to register it. */ public void onCancel(); } @@ -1536,84 +1676,112 @@ public class SearchManager public void setOnDismissListener(final OnDismissListener listener) { mDismissListener = listener; } - - /** - * The callback from the search dialog when dismissed - * @hide - */ - public void onDismiss(DialogInterface dialog) { - if (dialog == mSearchDialog) { - if (mDismissListener != null) { - mDismissListener.onDismiss(); - } - } - } /** * Set or clear the callback that will be invoked whenever the search UI is canceled. * * @param listener The {@link OnCancelListener} to use, or null. */ - public void setOnCancelListener(final OnCancelListener listener) { + public void setOnCancelListener(OnCancelListener listener) { mCancelListener = listener; } - - - /** - * The callback from the search dialog when canceled - * @hide - */ - public void onCancel(DialogInterface dialog) { - if (dialog == mSearchDialog) { - if (mCancelListener != null) { - mCancelListener.onCancel(); + + private class SearchManagerCallback extends ISearchManagerCallback.Stub { + + private final Runnable mFireOnDismiss = new Runnable() { + public void run() { + if (DBG) debug("mFireOnDismiss"); + mIsShowing = false; + if (mDismissListener != null) { + mDismissListener.onDismiss(); + } + } + }; + + private final Runnable mFireOnCancel = new Runnable() { + public void run() { + if (DBG) debug("mFireOnCancel"); + // doesn't need to clear mIsShowing since onDismiss() always gets called too + if (mCancelListener != null) { + mCancelListener.onCancel(); + } } + }; + + public void onDismiss() { + if (DBG) debug("onDismiss()"); + mHandler.post(mFireOnDismiss); + } + + public void onCancel() { + if (DBG) debug("onCancel()"); + mHandler.post(mFireOnCancel); } + + } + + // TODO: remove the DialogInterface interfaces from SearchManager. + // This changes the public API, so I'll do it in a separate change. + public void onCancel(DialogInterface dialog) { + throw new UnsupportedOperationException(); + } + public void onDismiss(DialogInterface dialog) { + throw new UnsupportedOperationException(); } /** - * Save instance state so we can recreate after a rotation. - * + * Saves the state of the search UI. + * + * @return A Bundle containing the state of the search dialog, or {@code null} + * if the search UI is not visible. + * * @hide */ - void saveSearchDialog(Bundle outState, String key) { - if (mSearchDialog != null && mSearchDialog.isShowing()) { - Bundle searchDialogState = mSearchDialog.onSaveInstanceState(); - outState.putBundle(key, searchDialogState); + public Bundle saveSearchDialog() { + if (DBG) debug("saveSearchDialog(), mIsShowing=" + mIsShowing); + if (!mIsShowing) return null; + try { + return mService.onSaveInstanceState(); + } catch (RemoteException ex) { + Log.e(TAG, "onSaveInstanceState() failed: " + ex); + return null; } } /** - * Restore instance state after a rotation. - * + * Restores the state of the search dialog. + * + * @param searchDialogState Bundle to read the state from. + * * @hide */ - void restoreSearchDialog(Bundle inState, String key) { - Bundle searchDialogState = inState.getBundle(key); - if (searchDialogState != null) { - if (mSearchDialog == null) { - mSearchDialog = new SearchDialog(mContext); - } - mSearchDialog.onRestoreInstanceState(searchDialogState); + public void restoreSearchDialog(Bundle searchDialogState) { + if (DBG) debug("restoreSearchDialog(" + searchDialogState + ")"); + if (searchDialogState == null) return; + try { + mService.onRestoreInstanceState(searchDialogState); + } catch (RemoteException ex) { + Log.e(TAG, "onRestoreInstanceState() failed: " + ex); } } - + /** - * Hook for updating layout on a rotation - * + * Update the search dialog after a configuration change. + * + * @param newConfig The new configuration. + * * @hide */ - void onConfigurationChanged(Configuration newConfig) { - if (mSearchDialog != null && mSearchDialog.isShowing()) { - mSearchDialog.onConfigurationChanged(newConfig); + public void onConfigurationChanged(Configuration newConfig) { + if (DBG) debug("onConfigurationChanged(" + newConfig + "), mIsShowing=" + mIsShowing); + if (!mIsShowing) return; + try { + mService.onConfigurationChanged(newConfig); + } catch (RemoteException ex) { + Log.e(TAG, "onConfigurationChanged() failed:" + ex); } } - - private static ISearchManager getSearchManagerService() { - return ISearchManager.Stub.asInterface( - ServiceManager.getService(Context.SEARCH_SERVICE)); - } - + /** * Gets information about a searchable activity. This method is static so that it can * be used from non-Activity contexts. @@ -1625,11 +1793,12 @@ public class SearchManager * * @hide because SearchableInfo is not part of the API. */ - public static SearchableInfo getSearchableInfo(ComponentName componentName, + public SearchableInfo getSearchableInfo(ComponentName componentName, boolean globalSearch) { try { - return sService.getSearchableInfo(componentName, globalSearch); - } catch (RemoteException e) { + return mService.getSearchableInfo(componentName, globalSearch); + } catch (RemoteException ex) { + Log.e(TAG, "getSearchableInfo() failed: " + ex); return null; } } @@ -1639,23 +1808,22 @@ public class SearchManager * * @hide because SearchableInfo is not part of the API. */ - public static boolean isDefaultSearchable(SearchableInfo searchable) { - SearchableInfo defaultSearchable = SearchManager.getSearchableInfo(null, true); + public boolean isDefaultSearchable(SearchableInfo searchable) { + SearchableInfo defaultSearchable = getSearchableInfo(null, true); return defaultSearchable != null && defaultSearchable.getSearchActivity().equals(searchable.getSearchActivity()); } - + /** - * Gets a cursor with search suggestions. This method is static so that it can - * be used from non-Activity context. + * Gets a cursor with search suggestions. * * @param searchable Information about how to get the suggestions. * @param query The search text entered (so far). - * @return a cursor with suggestions, or null the suggestion query failed. - * + * @return a cursor with suggestions, or null the suggestion query failed. + * * @hide because SearchableInfo is not part of the API. */ - public static Cursor getSuggestions(Context context, SearchableInfo searchable, String query) { + public Cursor getSuggestions(SearchableInfo searchable, String query) { if (searchable == null) { return null; } @@ -1694,7 +1862,7 @@ public class SearchManager .build(); // finally, make the query - return context.getContentResolver().query(uri, null, selection, selArgs, null); + return mContext.getContentResolver().query(uri, null, selection, selArgs, null); } /** @@ -1706,11 +1874,65 @@ public class SearchManager * * @hide because SearchableInfo is not part of the API. */ - public static List getSearchablesInGlobalSearch() { + public List getSearchablesInGlobalSearch() { try { - return sService.getSearchablesInGlobalSearch(); + return mService.getSearchablesInGlobalSearch(); } catch (RemoteException e) { + Log.e(TAG, "getSearchablesInGlobalSearch() failed: " + e); return null; } } + + /** + * Returns a list of the searchable activities that handle web searches. + * + * @return a list of all searchable activities that handle + * {@link android.content.Intent#ACTION_WEB_SEARCH}. + * + * @hide because SearchableInfo is not part of the API. + */ + public List getSearchablesForWebSearch() { + try { + return mService.getSearchablesForWebSearch(); + } catch (RemoteException e) { + Log.e(TAG, "getSearchablesForWebSearch() failed: " + e); + return null; + } + } + + /** + * Returns the default searchable activity for web searches. + * + * @return searchable information for the activity handling web searches by default. + * + * @hide because SearchableInfo is not part of the API. + */ + public SearchableInfo getDefaultSearchableForWebSearch() { + try { + return mService.getDefaultSearchableForWebSearch(); + } catch (RemoteException e) { + Log.e(TAG, "getDefaultSearchableForWebSearch() failed: " + e); + return null; + } + } + + /** + * Sets the default searchable activity for web searches. + * + * @param component Name of the component to set as default activity for web searches. + * + * @hide + */ + public void setDefaultWebSearch(ComponentName component) { + try { + mService.setDefaultWebSearch(component); + } catch (RemoteException e) { + Log.e(TAG, "setDefaultWebSearch() failed: " + e); + } + } + + private static void debug(String msg) { + Thread thread = Thread.currentThread(); + Log.d(TAG, msg + " (" + thread.getName() + "-" + thread.getId() + ")"); + } } diff --git a/core/java/android/app/SuggestionsAdapter.java b/core/java/android/app/SuggestionsAdapter.java index 6a02fc930a1c3bdf1a45e3fe72398459dbd4dbc1..49c94d12f8d03ce41ee739fc9a7712d82451f268 100644 --- a/core/java/android/app/SuggestionsAdapter.java +++ b/core/java/android/app/SuggestionsAdapter.java @@ -20,62 +20,103 @@ import android.content.ContentResolver; import android.content.Context; import android.content.res.Resources.NotFoundException; import android.database.Cursor; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.drawable.BitmapDrawable; +import android.graphics.Canvas; import android.graphics.drawable.Drawable; import android.net.Uri; +import android.os.Bundle; import android.server.search.SearchableInfo; import android.text.Html; import android.text.TextUtils; +import android.util.DisplayMetrics; import android.util.Log; +import android.util.TypedValue; import android.view.View; import android.view.ViewGroup; -import android.widget.CursorAdapter; +import android.widget.AbsListView; import android.widget.ImageView; import android.widget.ResourceCursorAdapter; import android.widget.TextView; +import static android.app.SearchManager.DialogCursorProtocol; + import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; import java.util.WeakHashMap; /** * Provides the contents for the suggestion drop-down list.in {@link SearchDialog}. - * + * * @hide */ class SuggestionsAdapter extends ResourceCursorAdapter { + private static final boolean DBG = false; private static final String LOG_TAG = "SuggestionsAdapter"; - + + private SearchManager mSearchManager; + private SearchDialog mSearchDialog; private SearchableInfo mSearchable; private Context mProviderContext; private WeakHashMap mOutsideDrawablesCache; + private boolean mGlobalSearchMode; - // Cached column indexes, updated when the cursor changes. + // Cached column indexes, updated when the cursor changes. private int mFormatCol; private int mText1Col; private int mText2Col; private int mIconName1Col; private int mIconName2Col; - private int mIconBitmap1Col; - private int mIconBitmap2Col; - - public SuggestionsAdapter(Context context, SearchableInfo searchable, - WeakHashMap outsideDrawablesCache) { + private int mBackgroundColorCol; + + // This value is stored in SuggestionsAdapter by the SearchDialog to indicate whether + // a particular list item should be selected upon the next call to notifyDataSetChanged. + // This is used to indicate the index of the "More results..." list item so that when + // the data set changes after a click of "More results...", we can correctly tell the + // ListView to scroll to the right line item. It gets reset to NONE every time it + // is consumed. + private int mListItemToSelect = NONE; + static final int NONE = -1; + + // holds the maximum position that has been displayed to the user + int mMaxDisplayed = NONE; + + // holds the position that, when displayed, should result in notifying the cursor + int mDisplayNotifyPos = NONE; + + private final Runnable mStartSpinnerRunnable; + private final Runnable mStopSpinnerRunnable; + + public SuggestionsAdapter(Context context, SearchDialog searchDialog, SearchableInfo searchable, + WeakHashMap outsideDrawablesCache, boolean globalSearchMode) { super(context, com.android.internal.R.layout.search_dropdown_item_icons_2line, null, // no initial cursor true); // auto-requery + mSearchManager = (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE); + mSearchDialog = searchDialog; mSearchable = searchable; - + // set up provider resources (gives us icons, etc.) Context activityContext = mSearchable.getActivityContext(mContext); mProviderContext = mSearchable.getProviderContext(mContext, activityContext); - + mOutsideDrawablesCache = outsideDrawablesCache; + mGlobalSearchMode = globalSearchMode; + + mStartSpinnerRunnable = new Runnable() { + public void run() { + mSearchDialog.setWorking(true); + } + }; + + mStopSpinnerRunnable = new Runnable() { + public void run() { + mSearchDialog.setWorking(false); + } + }; } - + /** * Overridden to always return false, since we cannot be sure that * suggestion sources return stable IDs. @@ -94,20 +135,41 @@ class SuggestionsAdapter extends ResourceCursorAdapter { public Cursor runQueryOnBackgroundThread(CharSequence constraint) { if (DBG) Log.d(LOG_TAG, "runQueryOnBackgroundThread(" + constraint + ")"); String query = (constraint == null) ? "" : constraint.toString(); + if (!mGlobalSearchMode) { + /** + * for in app search we show the progress spinner until the cursor is returned with + * the results. for global search we manage the progress bar using + * {@link DialogCursorProtocol#POST_REFRESH_RECEIVE_ISPENDING}. + */ + mSearchDialog.getWindow().getDecorView().post(mStartSpinnerRunnable); + } try { - return SearchManager.getSuggestions(mContext, mSearchable, query); + final Cursor cursor = mSearchManager.getSuggestions(mSearchable, query); + // trigger fill window so the spinner stays up until the results are copied over and + // closer to being ready + if (!mGlobalSearchMode && cursor != null) cursor.getCount(); + return cursor; } catch (RuntimeException e) { Log.w(LOG_TAG, "Search suggestions query threw an exception.", e); return null; + } finally { + if (!mGlobalSearchMode) { + mSearchDialog.getWindow().getDecorView().post(mStopSpinnerRunnable); + } } } - + /** * Cache columns. */ @Override public void changeCursor(Cursor c) { if (DBG) Log.d(LOG_TAG, "changeCursor(" + c + ")"); + + if (mCursor != null) { + callCursorPreClose(mCursor); + } + super.changeCursor(c); if (c != null) { mFormatCol = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_FORMAT); @@ -115,21 +177,86 @@ class SuggestionsAdapter extends ResourceCursorAdapter { mText2Col = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_TEXT_2); mIconName1Col = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_ICON_1); mIconName2Col = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_ICON_2); - mIconBitmap1Col = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_ICON_1_BITMAP); - mIconBitmap2Col = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_ICON_2_BITMAP); + mBackgroundColorCol = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_BACKGROUND_COLOR); + } + } + + /** + * Handle sending and receiving information associated with + * {@link DialogCursorProtocol#PRE_CLOSE}. + * + * @param cursor The cursor to call. + */ + private void callCursorPreClose(Cursor cursor) { + if (!mGlobalSearchMode) return; + final Bundle request = new Bundle(); + request.putInt(DialogCursorProtocol.METHOD, DialogCursorProtocol.PRE_CLOSE); + request.putInt(DialogCursorProtocol.PRE_CLOSE_SEND_MAX_DISPLAY_POS, mMaxDisplayed); + final Bundle response = cursor.respond(request); + + mMaxDisplayed = -1; + } + + @Override + public void notifyDataSetChanged() { + if (DBG) Log.d(LOG_TAG, "notifyDataSetChanged"); + super.notifyDataSetChanged(); + + callCursorPostRefresh(mCursor); + + // look out for the pending item we are supposed to scroll to + if (mListItemToSelect != NONE) { + mSearchDialog.setListSelection(mListItemToSelect); + mListItemToSelect = NONE; } } - + + /** + * Handle sending and receiving information associated with + * {@link DialogCursorProtocol#POST_REFRESH}. + * + * @param cursor The cursor to call. + */ + private void callCursorPostRefresh(Cursor cursor) { + if (!mGlobalSearchMode) return; + final Bundle request = new Bundle(); + request.putInt(DialogCursorProtocol.METHOD, DialogCursorProtocol.POST_REFRESH); + final Bundle response = cursor.respond(request); + + mSearchDialog.setWorking( + response.getBoolean(DialogCursorProtocol.POST_REFRESH_RECEIVE_ISPENDING, false)); + + mDisplayNotifyPos = + response.getInt(DialogCursorProtocol.POST_REFRESH_RECEIVE_DISPLAY_NOTIFY, -1); + } + + /** + * Tell the cursor which position was clicked, handling sending and receiving information + * associated with {@link DialogCursorProtocol#CLICK}. + * + * @param cursor The cursor + * @param position The position that was clicked. + */ + void callCursorOnClick(Cursor cursor, int position) { + if (!mGlobalSearchMode) return; + final Bundle request = new Bundle(1); + request.putInt(DialogCursorProtocol.METHOD, DialogCursorProtocol.CLICK); + request.putInt(DialogCursorProtocol.CLICK_SEND_POSITION, position); + final Bundle response = cursor.respond(request); + mListItemToSelect = response.getInt( + DialogCursorProtocol.CLICK_RECEIVE_SELECTED_POS, SuggestionsAdapter.NONE); + } + /** * Tags the view with cached child view look-ups. */ @Override public View newView(Context context, Cursor cursor, ViewGroup parent) { - View v = super.newView(context, cursor, parent); + View v = new SuggestionItemView(context, cursor); v.setTag(new ChildViewCache(v)); return v; } - + /** * Cache of the child views of drop-drown list items, to avoid looking up the children * each time the contents of a list item are changed. @@ -139,7 +266,7 @@ class SuggestionsAdapter extends ResourceCursorAdapter { public final TextView mText2; public final ImageView mIcon1; public final ImageView mIcon2; - + public ChildViewCache(View v) { mText1 = (TextView) v.findViewById(com.android.internal.R.id.text1); mText2 = (TextView) v.findViewById(com.android.internal.R.id.text2); @@ -147,21 +274,38 @@ class SuggestionsAdapter extends ResourceCursorAdapter { mIcon2 = (ImageView) v.findViewById(com.android.internal.R.id.icon2); } } - + @Override public void bindView(View view, Context context, Cursor cursor) { ChildViewCache views = (ChildViewCache) view.getTag(); - boolean isHtml = false; - if (mFormatCol >= 0) { - String format = cursor.getString(mFormatCol); - isHtml = "html".equals(format); + final int pos = cursor.getPosition(); + + // update the maximum position displayed since last refresh + if (pos > mMaxDisplayed) { + mMaxDisplayed = pos; } + + // if the cursor wishes to be notified about this position, send it + if (mGlobalSearchMode && mDisplayNotifyPos != NONE && pos == mDisplayNotifyPos) { + final Bundle request = new Bundle(); + request.putInt(DialogCursorProtocol.METHOD, DialogCursorProtocol.THRESH_HIT); + mCursor.respond(request); + mDisplayNotifyPos = NONE; // only notify the first time + } + + int backgroundColor = 0; + if (mBackgroundColorCol != -1) { + backgroundColor = cursor.getInt(mBackgroundColorCol); + } + ((SuggestionItemView)view).setColor(backgroundColor); + + final boolean isHtml = mFormatCol > 0 && "html".equals(cursor.getString(mFormatCol)); setViewText(cursor, views.mText1, mText1Col, isHtml); setViewText(cursor, views.mText2, mText2Col, isHtml); - setViewIcon(cursor, views.mIcon1, mIconBitmap1Col, mIconName1Col); - setViewIcon(cursor, views.mIcon2, mIconBitmap2Col, mIconName2Col); + setViewIcon(cursor, views.mIcon1, mIconName1Col); + setViewIcon(cursor, views.mIcon2, mIconName2Col); } - + private void setViewText(Cursor cursor, TextView v, int textCol, boolean isHtml) { if (v == null) { return; @@ -173,49 +317,46 @@ class SuggestionsAdapter extends ResourceCursorAdapter { } // Set the text even if it's null, since we need to clear any previous text. v.setText(text); - + if (TextUtils.isEmpty(text)) { v.setVisibility(View.GONE); } else { v.setVisibility(View.VISIBLE); } } - - private void setViewIcon(Cursor cursor, ImageView v, int iconBitmapCol, int iconNameCol) { + + private void setViewIcon(Cursor cursor, ImageView v, int iconNameCol) { if (v == null) { return; } - Drawable drawable = null; - // First try the bitmap column - if (iconBitmapCol >= 0) { - byte[] data = cursor.getBlob(iconBitmapCol); - if (data != null) { - Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length); - if (bitmap != null) { - drawable = new BitmapDrawable(bitmap); - } - } - } - // If there was no bitmap, try the icon resource column. - if (drawable == null && iconNameCol >= 0) { - String value = cursor.getString(iconNameCol); - drawable = getDrawableFromResourceValue(value); + if (iconNameCol < 0) { + return; } + String value = cursor.getString(iconNameCol); + Drawable drawable = getDrawableFromResourceValue(value); // Set the icon even if the drawable is null, since we need to clear any // previous icon. v.setImageDrawable(drawable); - + if (drawable == null) { v.setVisibility(View.GONE); } else { v.setVisibility(View.VISIBLE); + + // This is a hack to get any animated drawables (like a 'working' spinner) + // to animate. You have to setVisible true on an AnimationDrawable to get + // it to start animating, but it must first have been false or else the + // call to setVisible will be ineffective. We need to clear up the story + // about animated drawables in the future, see http://b/1878430. + drawable.setVisible(false, false); + drawable.setVisible(true, false); } } - + /** * Gets the text to show in the query field when a suggestion is selected. - * - * @param cursor The Cursor to read the suggestion data from. The Cursor should already + * + * @param cursor The Cursor to read the suggestion data from. The Cursor should already * be moved to the suggestion that is to be read from. * @return The text to show, or null if the query should not be * changed when selecting this suggestion. @@ -225,36 +366,36 @@ class SuggestionsAdapter extends ResourceCursorAdapter { if (cursor == null) { return null; } - + String query = getColumnString(cursor, SearchManager.SUGGEST_COLUMN_QUERY); if (query != null) { return query; } - + if (mSearchable.shouldRewriteQueryFromData()) { String data = getColumnString(cursor, SearchManager.SUGGEST_COLUMN_INTENT_DATA); if (data != null) { return data; } } - + if (mSearchable.shouldRewriteQueryFromText()) { String text1 = getColumnString(cursor, SearchManager.SUGGEST_COLUMN_TEXT_1); if (text1 != null) { return text1; } } - + return null; } - + /** * This method is overridden purely to provide a bit of protection against * flaky content providers. - * + * * @see android.widget.ListAdapter#getView(int, View, ViewGroup) */ - @Override + @Override public View getView(int position, View convertView, ViewGroup parent) { try { return super.getView(position, convertView, parent); @@ -263,28 +404,28 @@ class SuggestionsAdapter extends ResourceCursorAdapter { // Put exception string in item title View v = newView(mContext, mCursor, parent); if (v != null) { - ChildViewCache views = (ChildViewCache) v.getTag(); + ChildViewCache views = (ChildViewCache) v.getTag(); TextView tv = views.mText1; tv.setText(e.toString()); } return v; } } - + /** * Gets a drawable given a value provided by a suggestion provider. - * + * * This value could be just the string value of a resource id * (e.g., "2130837524"), in which case we will try to retrieve a drawable from * the provider's resources. If the value is not an integer, it is - * treated as a Uri and opened with + * treated as a Uri and opened with * {@link ContentResolver#openOutputStream(android.net.Uri, String)}. * * All resources and URIs are read using the suggestion provider's context. * * If the string is not formatted as expected, or no drawable can be found for * the provided value, this method returns null. - * + * * @param drawableId a string like "2130837524", * "android.resource://com.android.alarmclock/2130837524", * or "content://contacts/photos/253". @@ -294,43 +435,58 @@ class SuggestionsAdapter extends ResourceCursorAdapter { if (drawableId == null || drawableId.length() == 0 || "0".equals(drawableId)) { return null; } - + // First, check the cache. Drawable drawable = mOutsideDrawablesCache.get(drawableId); - if (drawable != null) return drawable; + if (drawable != null) { + if (DBG) Log.d(LOG_TAG, "Found icon in cache: " + drawableId); + return drawable; + } try { // Not cached, try using it as a plain resource ID in the provider's context. int resourceId = Integer.parseInt(drawableId); drawable = mProviderContext.getResources().getDrawable(resourceId); + if (DBG) Log.d(LOG_TAG, "Found icon by resource ID: " + drawableId); } catch (NumberFormatException nfe) { // The id was not an integer resource id. // Let the ContentResolver handle content, android.resource and file URIs. try { Uri uri = Uri.parse(drawableId); - drawable = Drawable.createFromStream( - mProviderContext.getContentResolver().openInputStream(uri), - null); + InputStream stream = mProviderContext.getContentResolver().openInputStream(uri); + if (stream != null) { + try { + drawable = Drawable.createFromStream(stream, null); + } finally { + try { + stream.close(); + } catch (IOException ex) { + Log.e(LOG_TAG, "Error closing icon stream for " + uri, ex); + } + } + } + if (DBG) Log.d(LOG_TAG, "Opened icon input stream: " + drawableId); } catch (FileNotFoundException fnfe) { + if (DBG) Log.d(LOG_TAG, "Icon stream not found: " + drawableId); // drawable = null; } - + // If we got a drawable for this resource id, then stick it in the // map so we don't do this lookup again. if (drawable != null) { mOutsideDrawablesCache.put(drawableId, drawable); } } catch (NotFoundException nfe) { - // Resource could not be found + if (DBG) Log.d(LOG_TAG, "Icon resource not found: " + drawableId); // drawable = null; } - + return drawable; } - + /** * Gets the value of a string column by name. - * + * * @param cursor Cursor to read the value from. * @param columnName The name of the column to read. * @return The value of the given column, or null @@ -338,10 +494,75 @@ class SuggestionsAdapter extends ResourceCursorAdapter { */ public static String getColumnString(Cursor cursor, String columnName) { int col = cursor.getColumnIndex(columnName); - if (col == -1) { + if (col == NONE) { return null; } return cursor.getString(col); } + /** + * A parent viewgroup class which holds the actual suggestion item as a child. + * + * The sole purpose of this class is to draw the given background color when the item is in + * normal state and not draw the background color when it is pressed, so that when pressed the + * list view's selection highlight will be displayed properly (if we draw our background it + * draws on top of the list view selection highlight). + */ + private class SuggestionItemView extends ViewGroup { + private int mBackgroundColor; // the background color to draw in normal state. + private View mView; // the suggestion item's view. + + protected SuggestionItemView(Context context, Cursor cursor) { + // Initialize ourselves + super(context); + mBackgroundColor = 0; // transparent by default. + + // For our layout use the default list item height from the current theme. + TypedValue lineHeight = new TypedValue(); + context.getTheme().resolveAttribute( + com.android.internal.R.attr.searchResultListItemHeight, lineHeight, true); + DisplayMetrics metrics = new DisplayMetrics(); + metrics.setToDefaults(); + AbsListView.LayoutParams layout = new AbsListView.LayoutParams( + AbsListView.LayoutParams.FILL_PARENT, + (int)lineHeight.getDimension(metrics)); + + setLayoutParams(layout); + + // Initialize the child view + mView = SuggestionsAdapter.super.newView(context, cursor, this); + if (mView != null) { + addView(mView, layout.width, layout.height); + mView.setVisibility(View.VISIBLE); + } + } + + public void setColor(int backgroundColor) { + mBackgroundColor = backgroundColor; + } + + @Override + public void dispatchDraw(Canvas canvas) { + if (mBackgroundColor != 0 && !isPressed() && !isSelected()) { + canvas.drawColor(mBackgroundColor); + } + super.dispatchDraw(canvas); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + if (mView != null) { + mView.measure(widthMeasureSpec, heightMeasureSpec); + } + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + if (mView != null) { + mView.layout(0, 0, mView.getMeasuredWidth(), mView.getMeasuredHeight()); + } + } + } + } diff --git a/core/java/android/appwidget/AppWidgetHost.java b/core/java/android/appwidget/AppWidgetHost.java index 10c2b02d4c3529004a6b147564cfb0f822f185b3..03e8623a63de43223003e107e6d9600a9d2f55b2 100644 --- a/core/java/android/appwidget/AppWidgetHost.java +++ b/core/java/android/appwidget/AppWidgetHost.java @@ -26,7 +26,6 @@ import android.widget.RemoteViews; import java.util.ArrayList; import java.util.HashMap; -import java.util.List; import com.android.internal.appwidget.IAppWidgetHost; import com.android.internal.appwidget.IAppWidgetService; @@ -40,7 +39,7 @@ public class AppWidgetHost { static final int HANDLE_UPDATE = 1; static final int HANDLE_PROVIDER_CHANGED = 2; - static Object sServiceLock = new Object(); + final static Object sServiceLock = new Object(); static IAppWidgetService sService; Context mContext; @@ -85,7 +84,7 @@ public class AppWidgetHost { int mHostId; Callbacks mCallbacks = new Callbacks(); - HashMap mViews = new HashMap(); + final HashMap mViews = new HashMap(); public AppWidgetHost(Context context, int hostId) { mContext = context; @@ -104,8 +103,8 @@ public class AppWidgetHost { * becomes visible, i.e. from onStart() in your Activity. */ public void startListening() { - int[] updatedIds = null; - ArrayList updatedViews = new ArrayList(); + int[] updatedIds; + ArrayList updatedViews = new ArrayList(); try { if (mPackageName == null) { @@ -209,7 +208,7 @@ public class AppWidgetHost { synchronized (mViews) { mViews.put(appWidgetId, view); } - RemoteViews views = null; + RemoteViews views; try { views = sService.getAppWidgetViews(appWidgetId); } catch (RemoteException e) { @@ -231,6 +230,7 @@ public class AppWidgetHost { /** * Called when the AppWidget provider for a AppWidget has been upgraded to a new apk. */ + @SuppressWarnings({"UnusedDeclaration"}) protected void onProviderChanged(int appWidgetId, AppWidgetProviderInfo appWidget) { } diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java index be0f96e8ab6a6d6cef4dcdf4ba305c9604589fa5..62d92674d86485b2b19521f00407b50d267ab99e 100644 --- a/core/java/android/appwidget/AppWidgetHostView.java +++ b/core/java/android/appwidget/AppWidgetHostView.java @@ -22,16 +22,12 @@ import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; -import android.os.Handler; -import android.os.Message; import android.os.SystemClock; -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.view.animation.Animation; import android.widget.FrameLayout; import android.widget.RemoteViews; import android.widget.TextView; @@ -86,6 +82,7 @@ public class AppWidgetHostView extends FrameLayout { * @param animationIn Resource ID of in animation to use * @param animationOut Resource ID of out animation to use */ + @SuppressWarnings({"UnusedDeclaration"}) public AppWidgetHostView(Context context, int animationIn, int animationOut) { super(context); mContext = context; @@ -272,7 +269,7 @@ public class AppWidgetHostView extends FrameLayout { try { if (mInfo != null) { Context theirContext = mContext.createPackageContext( - mInfo.provider.getPackageName(), 0 /* no flags */); + mInfo.provider.getPackageName(), Context.CONTEXT_RESTRICTED); LayoutInflater inflater = (LayoutInflater) theirContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); inflater = inflater.cloneInContext(theirContext); diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java index eca04b311330837d84efa1b5f6a1e703c637ac87..3660001b1968901a3d17e60616c89d36e5186f96 100644 --- a/core/java/android/appwidget/AppWidgetManager.java +++ b/core/java/android/appwidget/AppWidgetManager.java @@ -21,7 +21,9 @@ import android.content.Context; import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceManager; +import android.util.DisplayMetrics; import android.util.Log; +import android.util.TypedValue; import android.widget.RemoteViews; import com.android.internal.appwidget.IAppWidgetService; @@ -187,6 +189,8 @@ public class AppWidgetManager { Context mContext; + private DisplayMetrics mDisplayMetrics; + /** * Get the AppWidgetManager instance to use for the supplied {@link android.content.Context * Context} object. @@ -213,6 +217,7 @@ public class AppWidgetManager { private AppWidgetManager(Context context) { mContext = context; + mDisplayMetrics = context.getResources().getDisplayMetrics(); } /** @@ -292,7 +297,15 @@ public class AppWidgetManager { */ public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) { try { - return sService.getAppWidgetInfo(appWidgetId); + AppWidgetProviderInfo info = sService.getAppWidgetInfo(appWidgetId); + if (info != null) { + // Converting complex to dp. + info.minWidth = + TypedValue.complexToDimensionPixelSize(info.minWidth, mDisplayMetrics); + info.minHeight = + TypedValue.complexToDimensionPixelSize(info.minHeight, mDisplayMetrics); + } + return info; } catch (RemoteException e) { throw new RuntimeException("system server dead?", e); diff --git a/core/java/android/backup/AbsoluteFileBackupHelper.java b/core/java/android/backup/AbsoluteFileBackupHelper.java new file mode 100644 index 0000000000000000000000000000000000000000..ab246754b5afa8fd350b4fc745a3feb28a73ec70 --- /dev/null +++ b/core/java/android/backup/AbsoluteFileBackupHelper.java @@ -0,0 +1,66 @@ +/* + * 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.backup; + +import android.content.Context; +import android.os.ParcelFileDescriptor; +import android.util.Log; + +import java.io.File; +import java.io.FileDescriptor; + +/** + * Like FileBackupHelper, but takes absolute paths for the files instead of + * subpaths of getFilesDir() + * + * @hide + */ +public class AbsoluteFileBackupHelper extends FileBackupHelperBase implements BackupHelper { + private static final String TAG = "AbsoluteFileBackupHelper"; + + Context mContext; + String[] mFiles; + + public AbsoluteFileBackupHelper(Context context, String... files) { + super(context); + + mContext = context; + mFiles = files; + } + + /** + * Based on oldState, determine which of the files from the application's data directory + * need to be backed up, write them to the data stream, and fill in newState with the + * state as it exists now. + */ + public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput data, + ParcelFileDescriptor newState) { + // use the file paths as the keys, too + performBackup_checked(oldState, data, newState, mFiles, mFiles); + } + + public void restoreEntity(BackupDataInputStream data) { + // TODO: turn this off before ship + Log.d(TAG, "got entity '" + data.getKey() + "' size=" + data.size()); + String key = data.getKey(); + if (isKeyInList(key, mFiles)) { + File f = new File(key); + writeFile(f, data); + } + } +} + diff --git a/core/java/android/backup/BackupDataInput.java b/core/java/android/backup/BackupDataInput.java new file mode 100644 index 0000000000000000000000000000000000000000..69c206ce1c8155092d080133af95996260c9afdc --- /dev/null +++ b/core/java/android/backup/BackupDataInput.java @@ -0,0 +1,117 @@ +/* + * 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.backup; + +import android.content.Context; + +import java.io.FileDescriptor; +import java.io.IOException; + +/** @hide */ +public class BackupDataInput { + int mBackupReader; + + private EntityHeader mHeader = new EntityHeader(); + private boolean mHeaderReady; + + private static class EntityHeader { + String key; + int dataSize; + } + + public BackupDataInput(FileDescriptor fd) { + if (fd == null) throw new NullPointerException(); + mBackupReader = ctor(fd); + if (mBackupReader == 0) { + throw new RuntimeException("Native initialization failed with fd=" + fd); + } + } + + protected void finalize() throws Throwable { + try { + dtor(mBackupReader); + } finally { + super.finalize(); + } + } + + public boolean readNextHeader() throws IOException { + int result = readNextHeader_native(mBackupReader, mHeader); + if (result == 0) { + // read successfully + mHeaderReady = true; + return true; + } else if (result > 0) { + // done + mHeaderReady = false; + return false; + } else { + // error + mHeaderReady = false; + throw new IOException("result=0x" + Integer.toHexString(result)); + } + } + + public String getKey() { + if (mHeaderReady) { + return mHeader.key; + } else { + throw new IllegalStateException("mHeaderReady=false"); + } + } + + public int getDataSize() { + if (mHeaderReady) { + return mHeader.dataSize; + } else { + throw new IllegalStateException("mHeaderReady=false"); + } + } + + public int readEntityData(byte[] data, int offset, int size) throws IOException { + if (mHeaderReady) { + int result = readEntityData_native(mBackupReader, data, offset, size); + if (result >= 0) { + return result; + } else { + throw new IOException("result=0x" + Integer.toHexString(result)); + } + } else { + throw new IllegalStateException("mHeaderReady=false"); + } + } + + public void skipEntityData() throws IOException { + if (mHeaderReady) { + int result = skipEntityData_native(mBackupReader); + if (result >= 0) { + return; + } else { + throw new IOException("result=0x" + Integer.toHexString(result)); + } + } else { + throw new IllegalStateException("mHeaderReady=false"); + } + } + + private native static int ctor(FileDescriptor fd); + private native static void dtor(int mBackupReader); + + private native int readNextHeader_native(int mBackupReader, EntityHeader entity); + private native int readEntityData_native(int mBackupReader, byte[] data, int offset, int size); + private native int skipEntityData_native(int mBackupReader); +} diff --git a/core/java/android/backup/BackupDataInputStream.java b/core/java/android/backup/BackupDataInputStream.java new file mode 100644 index 0000000000000000000000000000000000000000..b705c4c3602237a4dbba13cd2dac298e9aab58f6 --- /dev/null +++ b/core/java/android/backup/BackupDataInputStream.java @@ -0,0 +1,63 @@ +/* + * 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.backup; + +import android.util.Log; + +import java.io.InputStream; +import java.io.IOException; + +/** @hide */ +public class BackupDataInputStream extends InputStream { + + String key; + int dataSize; + + BackupDataInput mData; + byte[] mOneByte; + + BackupDataInputStream(BackupDataInput data) { + mData = data; + } + + public int read() throws IOException { + byte[] one = mOneByte; + if (mOneByte == null) { + one = mOneByte = new byte[1]; + } + mData.readEntityData(one, 0, 1); + return one[0]; + } + + public int read(byte[] b, int offset, int size) throws IOException { + return mData.readEntityData(b, offset, size); + } + + public int read(byte[] b) throws IOException { + return mData.readEntityData(b, 0, b.length); + } + + public String getKey() { + return this.key; + } + + public int size() { + return this.dataSize; + } +} + + diff --git a/core/java/android/backup/BackupDataOutput.java b/core/java/android/backup/BackupDataOutput.java index 555494e595240b6ba99379b94492d48d82bd40a1..d29c5ba02f1403cfdf8cfa533751cb9f5321e3ff 100644 --- a/core/java/android/backup/BackupDataOutput.java +++ b/core/java/android/backup/BackupDataOutput.java @@ -19,27 +19,59 @@ package android.backup; import android.content.Context; import java.io.FileDescriptor; +import java.io.IOException; /** @hide */ public class BackupDataOutput { - /* package */ FileDescriptor fd; + int mBackupWriter; public static final int OP_UPDATE = 1; public static final int OP_DELETE = 2; - public BackupDataOutput(Context context, FileDescriptor fd) { - this.fd = fd; + public BackupDataOutput(FileDescriptor fd) { + if (fd == null) throw new NullPointerException(); + mBackupWriter = ctor(fd); + if (mBackupWriter == 0) { + throw new RuntimeException("Native initialization failed with fd=" + fd); + } } - public void close() { - // do we close the fd? + // A dataSize of -1 indicates that the record under this key should be deleted + public int writeEntityHeader(String key, int dataSize) throws IOException { + int result = writeEntityHeader_native(mBackupWriter, key, dataSize); + if (result >= 0) { + return result; + } else { + throw new IOException("result=0x" + Integer.toHexString(result)); + } } - public native void flush(); - public native void write(byte[] buffer); - public native void write(int oneByte); - public native void write(byte[] buffer, int offset, int count); - public native void writeOperation(int op); - public native void writeKey(String key); + public int writeEntityData(byte[] data, int size) throws IOException { + int result = writeEntityData_native(mBackupWriter, data, size); + if (result >= 0) { + return result; + } else { + throw new IOException("result=0x" + Integer.toHexString(result)); + } + } + + public void setKeyPrefix(String keyPrefix) { + setKeyPrefix_native(mBackupWriter, keyPrefix); + } + + protected void finalize() throws Throwable { + try { + dtor(mBackupWriter); + } finally { + super.finalize(); + } + } + + private native static int ctor(FileDescriptor fd); + private native static void dtor(int mBackupWriter); + + private native static int writeEntityHeader_native(int mBackupWriter, String key, int dataSize); + private native static int writeEntityData_native(int mBackupWriter, byte[] data, int size); + private native static void setKeyPrefix_native(int mBackupWriter, String keyPrefix); } diff --git a/core/java/android/backup/BackupHelper.java b/core/java/android/backup/BackupHelper.java new file mode 100644 index 0000000000000000000000000000000000000000..3983e28cce39927331cbece04685952bfadf8b11 --- /dev/null +++ b/core/java/android/backup/BackupHelper.java @@ -0,0 +1,46 @@ +/* + * 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.backup; + +import android.os.ParcelFileDescriptor; + +import java.io.InputStream; + +/** @hide */ +public interface BackupHelper { + /** + * Based on oldState, determine which of the files from the application's data directory + * need to be backed up, write them to the data stream, and fill in newState with the + * state as it exists now. + */ + public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput data, + ParcelFileDescriptor newState); + + /** + * Called by BackupHelperDispatcher to dispatch one entity of data. + *

+ * Do not close the data stream. Do not read more than + * dataSize bytes from data. + */ + public void restoreEntity(BackupDataInputStream data); + + /** + * + */ + public void writeRestoreSnapshot(ParcelFileDescriptor fd); +} + diff --git a/core/java/android/backup/BackupHelperAgent.java b/core/java/android/backup/BackupHelperAgent.java new file mode 100644 index 0000000000000000000000000000000000000000..5d0c4a2514db31725f4cb486d2ff0cd685edac80 --- /dev/null +++ b/core/java/android/backup/BackupHelperAgent.java @@ -0,0 +1,56 @@ +/* + * 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 android.backup; + +import android.app.BackupAgent; +import android.backup.BackupHelper; +import android.backup.BackupHelperDispatcher; +import android.backup.BackupDataInput; +import android.backup.BackupDataOutput; +import android.os.ParcelFileDescriptor; +import android.util.Log; + +import java.io.IOException; + +/** @hide */ +public class BackupHelperAgent extends BackupAgent { + static final String TAG = "BackupHelperAgent"; + + BackupHelperDispatcher mDispatcher = new BackupHelperDispatcher(); + + @Override + public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data, + ParcelFileDescriptor newState) throws IOException { + mDispatcher.performBackup(oldState, data, newState); + } + + @Override + public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState) + throws IOException { + mDispatcher.performRestore(data, appVersionCode, newState); + } + + public BackupHelperDispatcher getDispatcher() { + return mDispatcher; + } + + public void addHelper(String keyPrefix, BackupHelper helper) { + mDispatcher.addHelper(keyPrefix, helper); + } +} + + diff --git a/core/java/android/backup/BackupHelperDispatcher.java b/core/java/android/backup/BackupHelperDispatcher.java new file mode 100644 index 0000000000000000000000000000000000000000..6ccb83effd45400c81cf69d3c531005a96b48fab --- /dev/null +++ b/core/java/android/backup/BackupHelperDispatcher.java @@ -0,0 +1,151 @@ +/* + * 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.backup; + +import android.os.ParcelFileDescriptor; +import android.util.Log; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.FileDescriptor; +import java.util.TreeMap; +import java.util.Map; + +/** @hide */ +public class BackupHelperDispatcher { + private static final String TAG = "BackupHelperDispatcher"; + + private static class Header { + int chunkSize; // not including the header + String keyPrefix; + } + + TreeMap mHelpers = new TreeMap(); + + public BackupHelperDispatcher() { + } + + public void addHelper(String keyPrefix, BackupHelper helper) { + mHelpers.put(keyPrefix, helper); + } + + public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput data, + ParcelFileDescriptor newState) throws IOException { + // First, do the helpers that we've already done, since they're already in the state + // file. + int err; + Header header = new Header(); + TreeMap helpers = (TreeMap)mHelpers.clone(); + FileDescriptor oldStateFD = null; + FileDescriptor newStateFD = newState.getFileDescriptor(); + + if (oldState != null) { + oldStateFD = oldState.getFileDescriptor(); + while ((err = readHeader_native(header, oldStateFD)) >= 0) { + if (err == 0) { + BackupHelper helper = helpers.get(header.keyPrefix); + Log.d(TAG, "handling existing helper '" + header.keyPrefix + "' " + helper); + if (helper != null) { + doOneBackup(oldState, data, newState, header, helper); + helpers.remove(header.keyPrefix); + } else { + skipChunk_native(oldStateFD, header.chunkSize); + } + } + } + } + + // Then go through and do the rest that we haven't done. + for (Map.Entry entry: helpers.entrySet()) { + header.keyPrefix = entry.getKey(); + Log.d(TAG, "handling new helper '" + header.keyPrefix + "'"); + BackupHelper helper = entry.getValue(); + doOneBackup(oldState, data, newState, header, helper); + } + } + + private void doOneBackup(ParcelFileDescriptor oldState, BackupDataOutput data, + ParcelFileDescriptor newState, Header header, BackupHelper helper) + throws IOException { + int err; + FileDescriptor newStateFD = newState.getFileDescriptor(); + + // allocate space for the header in the file + int pos = allocateHeader_native(header, newStateFD); + if (pos < 0) { + throw new IOException("allocateHeader_native failed (error " + pos + ")"); + } + + data.setKeyPrefix(header.keyPrefix); + + // do the backup + helper.performBackup(oldState, data, newState); + + // fill in the header (seeking back to pos). The file pointer will be returned to + // where it was at the end of performBackup. Header.chunkSize will not be filled in. + err = writeHeader_native(header, newStateFD, pos); + if (err != 0) { + throw new IOException("writeHeader_native failed (error " + err + ")"); + } + } + + public void performRestore(BackupDataInput input, int appVersionCode, + ParcelFileDescriptor newState) + throws IOException { + boolean alreadyComplained = false; + + BackupDataInputStream stream = new BackupDataInputStream(input); + while (input.readNextHeader()) { + + String rawKey = input.getKey(); + int pos = rawKey.indexOf(':'); + if (pos > 0) { + String prefix = rawKey.substring(0, pos); + BackupHelper helper = mHelpers.get(prefix); + if (helper != null) { + stream.dataSize = input.getDataSize(); + stream.key = rawKey.substring(pos+1); + helper.restoreEntity(stream); + } else { + if (!alreadyComplained) { + Log.w(TAG, "Couldn't find helper for: '" + rawKey + "'"); + alreadyComplained = true; + } + } + } else { + if (!alreadyComplained) { + Log.w(TAG, "Entity with no prefix: '" + rawKey + "'"); + alreadyComplained = true; + } + } + input.skipEntityData(); // In case they didn't consume the data. + } + + // Write out the state files -- mHelpers is a TreeMap, so the order is well defined. + for (BackupHelper helper: mHelpers.values()) { + helper.writeRestoreSnapshot(newState); + } + } + + private static native int readHeader_native(Header h, FileDescriptor fd); + private static native int skipChunk_native(FileDescriptor fd, int bytesToSkip); + + private static native int allocateHeader_native(Header h, FileDescriptor fd); + private static native int writeHeader_native(Header h, FileDescriptor fd, int pos); +} + diff --git a/core/java/android/backup/BackupManager.java b/core/java/android/backup/BackupManager.java index 6f0b2eef603f7f720072dedaadbd02734fcd4973..34a1a0c8f2f47d85157b2a7ddb1df8f1f0205288 100644 --- a/core/java/android/backup/BackupManager.java +++ b/core/java/android/backup/BackupManager.java @@ -19,6 +19,7 @@ package android.backup; import android.content.Context; import android.os.RemoteException; import android.os.ServiceManager; +import android.util.Log; /** * BackupManager is the interface to the system's backup service. @@ -32,14 +33,24 @@ import android.os.ServiceManager; * until the backup operation actually occurs. * *

The backup operation itself begins with the system launching the - * {@link BackupService} subclass declared in your manifest. See the documentation - * for {@link BackupService} for a detailed description of how the backup then proceeds. + * {@link android.app.BackupAgent} subclass declared in your manifest. See the + * documentation for {@link android.app.BackupAgent} for a detailed description + * of how the backup then proceeds. * * @hide pending API solidification */ public class BackupManager { + private static final String TAG = "BackupManager"; + private Context mContext; - private IBackupManager mService; + private static IBackupManager sService; + + private static void checkServiceBinder() { + if (sService == null) { + sService = IBackupManager.Stub.asInterface( + ServiceManager.getService(Context.BACKUP_SERVICE)); + } + } /** * Constructs a BackupManager object through which the application can @@ -51,19 +62,60 @@ public class BackupManager { */ public BackupManager(Context context) { mContext = context; - mService = IBackupManager.Stub.asInterface( - ServiceManager.getService(Context.BACKUP_SERVICE)); } /** * Notifies the Android backup system that your application wishes to back up * new changes to its data. A backup operation using your application's - * {@link BackupService} subclass will be scheduled when you call this method. + * {@link android.app.BackupAgent} subclass will be scheduled when you call this method. */ public void dataChanged() { - try { - mService.dataChanged(mContext.getPackageName()); - } catch (RemoteException e) { + checkServiceBinder(); + if (sService != null) { + try { + sService.dataChanged(mContext.getPackageName()); + } catch (RemoteException e) { + Log.d(TAG, "dataChanged() couldn't connect"); + } + } + } + + /** + * Convenience method for callers who need to indicate that some other package + * needs a backup pass. This can be relevant in the case of groups of packages + * that share a uid, for example. + * + * This method requires that the application hold the "android.permission.BACKUP" + * permission if the package named in the argument is not the caller's own. + */ + public static void dataChanged(String packageName) { + checkServiceBinder(); + if (sService != null) { + try { + sService.dataChanged(packageName); + } catch (RemoteException e) { + Log.d(TAG, "dataChanged(pkg) couldn't connect"); + } + } + } + + /** + * Begin the process of restoring system data from backup. This method requires + * that the application hold the "android.permission.BACKUP" permission, and is + * not public. + * + * {@hide} + */ + public IRestoreSession beginRestoreSession(String transport) { + IRestoreSession binder = null; + checkServiceBinder(); + if (sService != null) { + try { + binder = sService.beginRestoreSession(transport); + } catch (RemoteException e) { + Log.d(TAG, "beginRestoreSession() couldn't connect"); + } } + return binder; } } diff --git a/core/java/android/backup/FileBackupHelper.java b/core/java/android/backup/FileBackupHelper.java index 05159dc3bdf21bb34a7029cea18aa01deff98317..405849705ff769b14619786213df945bde9395cf 100644 --- a/core/java/android/backup/FileBackupHelper.java +++ b/core/java/android/backup/FileBackupHelper.java @@ -20,54 +20,53 @@ import android.content.Context; import android.os.ParcelFileDescriptor; import android.util.Log; +import java.io.File; import java.io.FileDescriptor; /** @hide */ -public class FileBackupHelper { +public class FileBackupHelper extends FileBackupHelperBase implements BackupHelper { private static final String TAG = "FileBackupHelper"; + Context mContext; + File mFilesDir; + String[] mFiles; + + public FileBackupHelper(Context context, String... files) { + super(context); + + mContext = context; + mFilesDir = context.getFilesDir(); + mFiles = files; + } + /** * Based on oldState, determine which of the files from the application's data directory * need to be backed up, write them to the data stream, and fill in newState with the * state as it exists now. */ - public static void performBackup(Context context, - ParcelFileDescriptor oldState, BackupDataOutput data, - ParcelFileDescriptor newState, String[] files) { - String basePath = context.getFilesDir().getAbsolutePath(); - performBackup_checked(basePath, oldState, data, newState, files); - } - - /** - * Check the parameters so the native code doens't have to throw all the exceptions - * since it's easier to do that from java. - */ - static void performBackup_checked(String basePath, - ParcelFileDescriptor oldState, BackupDataOutput data, - ParcelFileDescriptor newState, String[] files) { - if (files.length == 0) { - return; - } - if (basePath == null) { - throw new NullPointerException(); - } - // oldStateFd can be null - FileDescriptor oldStateFd = oldState != null ? oldState.getFileDescriptor() : null; - if (data.fd == null) { - throw new NullPointerException(); - } - FileDescriptor newStateFd = newState.getFileDescriptor(); - if (newStateFd == null) { - throw new NullPointerException(); + public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput data, + ParcelFileDescriptor newState) { + // file names + String[] files = mFiles; + File base = mContext.getFilesDir(); + final int N = files.length; + String[] fullPaths = new String[N]; + for (int i=0; iCallers must hold the android.permission.BACKUP permission to use this method. + */ + void setBackupEnabled(boolean isEnabled); + + /** + * Indicate that any necessary one-time provisioning has occurred. + * + *

Callers must hold the android.permission.BACKUP permission to use this method. + */ + void setBackupProvisioned(boolean isProvisioned); + + /** + * Report whether the backup mechanism is currently enabled. + * + *

Callers must hold the android.permission.BACKUP permission to use this method. + */ + boolean isBackupEnabled(); + + /** + * Schedule an immediate backup attempt for all pending updates. This is + * primarily intended for transports to use when they detect a suitable + * opportunity for doing a backup pass. If there are no pending updates to + * be sent, no action will be taken. Even if some updates are pending, the + * transport will still be asked to confirm via the usual requestBackupTime() + * method. + * + *

Callers must hold the android.permission.BACKUP permission to use this method. + */ + void backupNow(); + + /** + * Identify the currently selected transport. Callers must hold the + * android.permission.BACKUP permission to use this method. + */ + String getCurrentTransport(); + + /** + * Request a list of all available backup transports' names. Callers must + * hold the android.permission.BACKUP permission to use this method. + */ + String[] listAllTransports(); + + /** + * Specify the current backup transport. Callers must hold the + * android.permission.BACKUP permission to use this method. + * + * @param transport The name of the transport to select. This should be one + * of {@link BackupManager.TRANSPORT_GOOGLE} or {@link BackupManager.TRANSPORT_ADB}. + * @return The name of the previously selected transport. If the given transport + * name is not one of the currently available transports, no change is made to + * the current transport setting and the method returns null. */ - oneway void dataChanged(String packageName); + String selectBackupTransport(String transport); /** - * Schedule a full backup of the given package. - * !!! TODO: protect with a signature-or-system permission? + * Begin a restore session with the given transport (which may differ from the + * currently-active backup transport). + * + * @param transport The name of the transport to use for the restore operation. + * @return An interface to the restore session, or null on error. */ - oneway void scheduleFullBackup(String packageName); + IRestoreSession beginRestoreSession(String transportID); } diff --git a/core/java/android/backup/IRestoreObserver.aidl b/core/java/android/backup/IRestoreObserver.aidl new file mode 100644 index 0000000000000000000000000000000000000000..59e59fc1f70aa62c0352e78c9a456adf73a439df --- /dev/null +++ b/core/java/android/backup/IRestoreObserver.aidl @@ -0,0 +1,50 @@ +/* + * 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.backup; + +/** + * Callback class for receiving progress reports during a restore operation. + * + * @hide + */ +interface IRestoreObserver { + /** + * The restore operation has begun. + * + * @param numPackages The total number of packages being processed in + * this restore operation. + */ + void restoreStarting(int numPackages); + + /** + * An indication of which package is being restored currently, out of the + * total number provided in the restoreStarting() callback. This method + * is not guaranteed to be called. + * + * @param nowBeingRestored The index, between 1 and the numPackages parameter + * to the restoreStarting() callback, of the package now being restored. + */ + void onUpdate(int nowBeingRestored); + + /** + * The restore operation has completed. + * + * @param error Zero on success; a nonzero error code if the restore operation + * as a whole failed. + */ + void restoreFinished(int error); +} diff --git a/core/java/android/backup/IRestoreSession.aidl b/core/java/android/backup/IRestoreSession.aidl new file mode 100644 index 0000000000000000000000000000000000000000..2a1fbc179933c3114dfb22cd50489a23964a58b7 --- /dev/null +++ b/core/java/android/backup/IRestoreSession.aidl @@ -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. + */ + +package android.backup; + +import android.backup.RestoreSet; +import android.backup.IRestoreObserver; + +/** + * Binder interface used by clients who wish to manage a restore operation. Every + * method in this interface requires the android.permission.BACKUP permission. + * + * {@hide} + */ +interface IRestoreSession { + /** + * Ask the current transport what the available restore sets are. + * + * @return A bundle containing two elements: an int array under the key + * "tokens" whose entries are a transport-private identifier for each backup set; + * and a String array under the key "names" whose entries are the user-meaningful + * text corresponding to the backup sets at each index in the tokens array. + */ + RestoreSet[] getAvailableRestoreSets(); + + /** + * Restore the given set onto the device, replacing the current data of any app + * contained in the restore set with the data previously backed up. + * + * @param token The token from {@link getAvailableRestoreSets()} corresponding to + * the restore set that should be used. + * @param observer If non-null, this binder points to an object that will receive + * progress callbacks during the restore operation. + */ + int performRestore(long token, IRestoreObserver observer); + + /** + * End this restore session. After this method is called, the IRestoreSession binder + * is no longer valid. + */ + void endRestoreSession(); +} diff --git a/core/java/android/backup/RestoreSet.aidl b/core/java/android/backup/RestoreSet.aidl new file mode 100644 index 0000000000000000000000000000000000000000..42e77bfe59901b933632815c0323e2eee95eb7b7 --- /dev/null +++ b/core/java/android/backup/RestoreSet.aidl @@ -0,0 +1,19 @@ +/* + * 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.backup; + +parcelable RestoreSet; \ No newline at end of file diff --git a/core/java/android/backup/RestoreSet.java b/core/java/android/backup/RestoreSet.java new file mode 100644 index 0000000000000000000000000000000000000000..eeca148667f3a9dd018496e43cca933e684255e9 --- /dev/null +++ b/core/java/android/backup/RestoreSet.java @@ -0,0 +1,87 @@ +/* + * 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.backup; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Descriptive information about a set of backed-up app data available for restore. + * Used by IRestoreSession clients. + * + * @hide + */ +public class RestoreSet implements Parcelable { + /** + * Name of this restore set. May be user generated, may simply be the name + * of the handset model, e.g. "T-Mobile G1". + */ + public String name; + + /** + * Identifier of the device whose data this is. This will be as unique as + * is practically possible; for example, it might be an IMEI. + */ + public String device; + + /** + * Token that identifies this backup set unambiguously to the backup/restore + * transport. This is guaranteed to be valid for the duration of a restore + * session, but is meaningless once the session has ended. + */ + public long token; + + + public RestoreSet() { + // Leave everything zero / null + } + + public RestoreSet(String _name, String _dev, long _token) { + name = _name; + device = _dev; + token = _token; + } + + + // Parcelable implementation + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel out, int flags) { + out.writeString(name); + out.writeString(device); + out.writeLong(token); + } + + public static final Parcelable.Creator CREATOR + = new Parcelable.Creator() { + public RestoreSet createFromParcel(Parcel in) { + return new RestoreSet(in); + } + + public RestoreSet[] newArray(int size) { + return new RestoreSet[size]; + } + }; + + private RestoreSet(Parcel in) { + name = in.readString(); + device = in.readString(); + token = in.readLong(); + } +} diff --git a/core/java/android/backup/SharedPreferencesBackupHelper.java b/core/java/android/backup/SharedPreferencesBackupHelper.java index 8627f08cf57b16884ace6386e9d93e73895ac22e..4a7b399a5bde0aeeedffac378472f0c343e8013a 100644 --- a/core/java/android/backup/SharedPreferencesBackupHelper.java +++ b/core/java/android/backup/SharedPreferencesBackupHelper.java @@ -18,24 +18,51 @@ package android.backup; import android.content.Context; import android.os.ParcelFileDescriptor; +import android.util.Log; +import java.io.File; import java.io.FileDescriptor; /** @hide */ -public class SharedPreferencesBackupHelper { - public static void performBackup(Context context, - ParcelFileDescriptor oldSnapshot, ParcelFileDescriptor newSnapshot, - BackupDataOutput data, String[] prefGroups) { - String basePath = "/xxx"; //context.getPreferencesDir(); +public class SharedPreferencesBackupHelper extends FileBackupHelperBase implements BackupHelper { + private static final String TAG = "SharedPreferencesBackupHelper"; + private Context mContext; + private String[] mPrefGroups; + + public SharedPreferencesBackupHelper(Context context, String... prefGroups) { + super(context); + + mContext = context; + mPrefGroups = prefGroups; + } + + public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput data, + ParcelFileDescriptor newState) { + Context context = mContext; + // make filenames for the prefGroups + String[] prefGroups = mPrefGroups; final int N = prefGroups.length; String[] files = new String[N]; for (int i=0; i + * Call mOpenHelper.getWritableDatabase() and mDb.beginTransaction(). + * {@link #endTransaction} MUST be called after calling this method. + * Those methods should be used like this: + *

+ * + *
+     * boolean successful = false;
+     * beginTransaction();
+     * try {
+     *     // Do something related to mDb
+     *     successful = true;
+     *     return ret;
+     * } finally {
+     *     endTransaction(successful);
+     * }
+     * 
+ * + * @hide This method is dangerous from the view of database manipulation, though using + * this makes batch insertion/update/delete much faster. + */ + public final void beginTransaction() { mDb = mOpenHelper.getWritableDatabase(); mDb.beginTransaction(); + } + + /** + *

+ * Call mDb.endTransaction(). If successful is true, try to call + * mDb.setTransactionSuccessful() before calling mDb.endTransaction(). + * This method MUST be used with {@link #beginTransaction()}. + *

+ * + * @hide This method is dangerous from the view of database manipulation, though using + * this makes batch insertion/update/delete much faster. + */ + public final void endTransaction(boolean successful) { try { - if (isTemporary() && mSyncState.matches(url)) { - int numRows = mSyncState.asContentProvider().update( - url, values, selection, selectionArgs); + if (successful) { + // setTransactionSuccessful() must be called just once during opening the + // transaction. mDb.setTransactionSuccessful(); - return numRows; } + } finally { + mDb.endTransaction(); + } + } - int result = updateInternal(url, values, selection, selectionArgs); - mDb.setTransactionSuccessful(); + @Override + public final int update(final Uri uri, final ContentValues values, + final String selection, final String[] selectionArgs) { + boolean successful = false; + beginTransaction(); + try { + int ret = nonTransactionalUpdate(uri, values, selection, selectionArgs); + successful = true; + return ret; + } finally { + endTransaction(successful); + } + } - if (!isTemporary() && result > 0) { - getContext().getContentResolver().notifyChange(url, null /* observer */, - changeRequiresLocalSync(url)); - } + /** + * @hide + */ + public final int nonTransactionalUpdate(final Uri uri, final ContentValues values, + final String selection, final String[] selectionArgs) { + if (isTemporary() && mSyncState.matches(uri)) { + int numRows = mSyncState.asContentProvider().update( + uri, values, selection, selectionArgs); + return numRows; + } - return result; - } finally { - mDb.endTransaction(); + int result = updateInternal(uri, values, selection, selectionArgs); + if (!isTemporary() && result > 0) { + getContext().getContentResolver().notifyChange(uri, null /* observer */, + changeRequiresLocalSync(uri)); } + + return result; } @Override - public final int delete(final Uri url, final String selection, + public final int delete(final Uri uri, final String selection, final String[] selectionArgs) { - mDb = mOpenHelper.getWritableDatabase(); - mDb.beginTransaction(); + boolean successful = false; + beginTransaction(); try { - if (isTemporary() && mSyncState.matches(url)) { - int numRows = mSyncState.asContentProvider().delete(url, selection, selectionArgs); - mDb.setTransactionSuccessful(); - return numRows; - } - int result = deleteInternal(url, selection, selectionArgs); - mDb.setTransactionSuccessful(); - if (!isTemporary() && result > 0) { - getContext().getContentResolver().notifyChange(url, null /* observer */, - changeRequiresLocalSync(url)); - } - return result; + int ret = nonTransactionalDelete(uri, selection, selectionArgs); + successful = true; + return ret; } finally { - mDb.endTransaction(); + endTransaction(successful); } } + /** + * @hide + */ + public final int nonTransactionalDelete(final Uri uri, final String selection, + final String[] selectionArgs) { + if (isTemporary() && mSyncState.matches(uri)) { + int numRows = mSyncState.asContentProvider().delete(uri, selection, selectionArgs); + return numRows; + } + int result = deleteInternal(uri, selection, selectionArgs); + if (!isTemporary() && result > 0) { + getContext().getContentResolver().notifyChange(uri, null /* observer */, + changeRequiresLocalSync(uri)); + } + return result; + } + @Override - public final Uri insert(final Uri url, final ContentValues values) { - mDb = mOpenHelper.getWritableDatabase(); - mDb.beginTransaction(); + public final Uri insert(final Uri uri, final ContentValues values) { + boolean successful = false; + beginTransaction(); try { - if (isTemporary() && mSyncState.matches(url)) { - Uri result = mSyncState.asContentProvider().insert(url, values); - mDb.setTransactionSuccessful(); - return result; - } - Uri result = insertInternal(url, values); - mDb.setTransactionSuccessful(); - if (!isTemporary() && result != null) { - getContext().getContentResolver().notifyChange(url, null /* observer */, - changeRequiresLocalSync(url)); - } - return result; + Uri ret = nonTransactionalInsert(uri, values); + successful = true; + return ret; } finally { - mDb.endTransaction(); + endTransaction(successful); + } + } + + /** + * @hide + */ + public final Uri nonTransactionalInsert(final Uri uri, final ContentValues values) { + if (isTemporary() && mSyncState.matches(uri)) { + Uri result = mSyncState.asContentProvider().insert(uri, values); + return result; + } + Uri result = insertInternal(uri, values); + if (!isTemporary() && result != null) { + getContext().getContentResolver().notifyChange(uri, null /* observer */, + changeRequiresLocalSync(uri)); } + return result; } @Override diff --git a/core/java/android/content/AbstractTableMerger.java b/core/java/android/content/AbstractTableMerger.java index 700f1d88241c18333642c74549a89468c88ddb91..9c760d94ad63197df938464e3891724723bc4768 100644 --- a/core/java/android/content/AbstractTableMerger.java +++ b/core/java/android/content/AbstractTableMerger.java @@ -61,8 +61,10 @@ public abstract class AbstractTableMerger _SYNC_ID +"=? and " + _SYNC_ACCOUNT + "=?"; private static final String SELECT_BY_ID = BaseColumns._ID +"=?"; - private static final String SELECT_UNSYNCED = "" - + _SYNC_DIRTY + " > 0 and (" + _SYNC_ACCOUNT + "=? or " + _SYNC_ACCOUNT + " is null)"; + private static final String SELECT_UNSYNCED = + "(" + _SYNC_ACCOUNT + " IS NULL OR " + _SYNC_ACCOUNT + "=?) AND " + + "(" + _SYNC_ID + " IS NULL OR (" + _SYNC_DIRTY + " > 0 AND " + + _SYNC_VERSION + " IS NOT NULL))"; public AbstractTableMerger(SQLiteDatabase database, String table, Uri tableURL, String deletedTable, @@ -365,26 +367,32 @@ public abstract class AbstractTableMerger if (!TextUtils.isEmpty(localSyncID)) { // An existing server item has changed - boolean recordChanged = (localSyncVersion == null) || - !serverSyncVersion.equals(localSyncVersion); - if (recordChanged) { - if (localSyncDirty) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "remote record " + serverSyncId - + " conflicts with local _sync_id " + localSyncID - + ", local _id " + localRowId); + // If serverSyncVersion is null, there is no edit URL; + // server won't let this change be written. + // Just hold onto it, I guess, in case the server permissions + // change later. + if (serverSyncVersion != null) { + boolean recordChanged = (localSyncVersion == null) || + !serverSyncVersion.equals(localSyncVersion); + if (recordChanged) { + if (localSyncDirty) { + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.v(TAG, "remote record " + serverSyncId + + " conflicts with local _sync_id " + localSyncID + + ", local _id " + localRowId); + } + conflict = true; + } else { + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.v(TAG, + "remote record " + + serverSyncId + + " updates local _sync_id " + + localSyncID + ", local _id " + + localRowId); + } + update = true; } - conflict = true; - } else { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, - "remote record " + - serverSyncId + - " updates local _sync_id " + - localSyncID + ", local _id " + - localRowId); - } - update = true; } } } else { diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index f2ad2485d0e1ceb7df4f282c8b03bc42ce1c1b47..9e37ae448731cc82897c57eb5e26591f14ba6498 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -16,6 +16,7 @@ package android.content; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.res.AssetManager; import android.content.res.Resources; @@ -233,6 +234,9 @@ public abstract class Context { /** Return the name of this application's package. */ public abstract String getPackageName(); + /** Return the full application info for this context's package. */ + public abstract ApplicationInfo getApplicationInfo(); + /** * {@hide} * Return the full path to this context's resource files. This is the ZIP files @@ -254,11 +258,19 @@ public abstract class Context { *

Note: this is not generally useful for applications, since they should * not be directly accessing the file system. * - * * @return String Path to the code and assets. */ public abstract String getPackageCodePath(); + /** + * {@hide} + * Return the full path to the shared prefs file for the given prefs group name. + * + *

Note: this is not generally useful for applications, since they should + * not be directly accessing the file system. + */ + public abstract File getSharedPrefsFile(String name); + /** * Retrieve and hold the contents of the preferences file 'name', returning * a SharedPreferences through which you can retrieve and modify its @@ -526,16 +538,6 @@ public abstract class Context { */ public abstract int getWallpaperDesiredMinimumHeight(); - /** - * Returns the scale in which the application will be drawn on the - * screen. This is usually 1.0f if the application supports the device's - * resolution/density. This will be 1.5f, for example, if the application - * that supports only 160 density runs on 240 density screen. - * - * @hide - */ - public abstract float getApplicationScale(); - /** * Change the current system wallpaper to a bitmap. The given bitmap is * converted to a PNG and stored as the wallpaper. On success, the intent @@ -1133,6 +1135,15 @@ public abstract class Context { * @see android.app.NotificationManager */ public static final String NOTIFICATION_SERVICE = "notification"; + /** + * Use with {@link #getSystemService} to retrieve a + * {@link android.view.accessibility.AccessibilityManager} for giving the user + * feedback for UI events through the registered event listeners. + * + * @see #getSystemService + * @see android.view.accessibility.AccessibilityManager + */ + public static final String ACCESSIBILITY_SERVICE = "accessibility"; /** * Use with {@link #getSystemService} to retrieve a * {@link android.app.NotificationManager} for controlling keyguard. @@ -1643,6 +1654,13 @@ public abstract class Context { * with extreme care! */ public static final int CONTEXT_IGNORE_SECURITY = 0x00000002; + + /** + * Flag for use with {@link #createPackageContext}: a restricted context may + * disable specific features. For instance, a View associated with a restricted + * context would ignore particular XML attributes. + */ + public static final int CONTEXT_RESTRICTED = 0x00000004; /** * Return a new Context object for the given application name. This @@ -1671,4 +1689,15 @@ public abstract class Context { */ public abstract Context createPackageContext(String packageName, int flags) throws PackageManager.NameNotFoundException; + + /** + * Indicates whether this Context is restricted. + * + * @return True if this Context is restricted, false otherwise. + * + * @see #CONTEXT_RESTRICTED + */ + public boolean isRestricted() { + return false; + } } diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java index 25b2caeb72790f3b490d52a21e466fc2930769e6..45a082a9520ee5c35c8804d9986b0357f1afd4ae 100644 --- a/core/java/android/content/ContextWrapper.java +++ b/core/java/android/content/ContextWrapper.java @@ -16,6 +16,7 @@ package android.content; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.res.AssetManager; import android.content.res.Resources; @@ -119,6 +120,11 @@ public class ContextWrapper extends Context { return mBase.getPackageName(); } + @Override + public ApplicationInfo getApplicationInfo() { + return mBase.getApplicationInfo(); + } + @Override public String getPackageResourcePath() { return mBase.getPackageResourcePath(); @@ -129,6 +135,11 @@ public class ContextWrapper extends Context { return mBase.getPackageCodePath(); } + @Override + public File getSharedPrefsFile(String name) { + return mBase.getSharedPrefsFile(name); + } + @Override public SharedPreferences getSharedPreferences(String name, int mode) { return mBase.getSharedPreferences(name, mode); @@ -420,11 +431,8 @@ public class ContextWrapper extends Context { return mBase.createPackageContext(packageName, flags); } - /** - * @hide - */ @Override - public float getApplicationScale() { - return mBase.getApplicationScale(); + public boolean isRestricted() { + return mBase.isRestricted(); } } diff --git a/core/java/android/content/IIntentReceiver.aidl b/core/java/android/content/IIntentReceiver.aidl new file mode 100755 index 0000000000000000000000000000000000000000..443db2d06d0f603e5a5a9490bd9187d439262a89 --- /dev/null +++ b/core/java/android/content/IIntentReceiver.aidl @@ -0,0 +1,33 @@ +/* + * 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.content; + +import android.content.Intent; +import android.os.Bundle; + +/** + * System private API for dispatching intent broadcasts. This is given to the + * activity manager as part of registering for an intent broadcasts, and is + * called when it receives intents. + * + * {@hide} + */ +oneway interface IIntentReceiver { + void performReceive(in Intent intent, int resultCode, + String data, in Bundle extras, boolean ordered); +} + diff --git a/core/java/android/content/IIntentSender.aidl b/core/java/android/content/IIntentSender.aidl new file mode 100644 index 0000000000000000000000000000000000000000..b7da47219ce4725473eda23a51d1b57ecf91a960 --- /dev/null +++ b/core/java/android/content/IIntentSender.aidl @@ -0,0 +1,26 @@ +/* + * 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.content; + +import android.content.IIntentReceiver; +import android.content.Intent; + +/** @hide */ +interface IIntentSender { + int send(int code, in Intent intent, String resolvedType, + IIntentReceiver finishedReceiver); +} diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 24262f51197d5f44b1b6c2797d64fcc5176a24c8..263f9279e69adc8abb77a5ed3903d69269901bbf 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -240,35 +240,35 @@ import java.util.Set; * * <activity class=".NotesList" android:label="@string/title_notes_list"> * <intent-filter> - * <action android:value="android.intent.action.MAIN" /> - * <category android:value="android.intent.category.LAUNCHER" /> + * <action android:name="android.intent.action.MAIN" /> + * <category android:name="android.intent.category.LAUNCHER" /> * </intent-filter> * <intent-filter> - * <action android:value="android.intent.action.VIEW" /> - * <action android:value="android.intent.action.EDIT" /> - * <action android:value="android.intent.action.PICK" /> - * <category android:value="android.intent.category.DEFAULT" /> - * <type android:value="vnd.android.cursor.dir/vnd.google.note" /> + * <action android:name="android.intent.action.VIEW" /> + * <action android:name="android.intent.action.EDIT" /> + * <action android:name="android.intent.action.PICK" /> + * <category android:name="android.intent.category.DEFAULT" /> + * <data android:mimeType="vnd.android.cursor.dir/vnd.google.note" /> * </intent-filter> * <intent-filter> - * <action android:value="android.intent.action.GET_CONTENT" /> - * <category android:value="android.intent.category.DEFAULT" /> - * <type android:value="vnd.android.cursor.item/vnd.google.note" /> + * <action android:name="android.intent.action.GET_CONTENT" /> + * <category android:name="android.intent.category.DEFAULT" /> + * <data android:mimeType="vnd.android.cursor.item/vnd.google.note" /> * </intent-filter> * </activity> * * <activity class=".NoteEditor" android:label="@string/title_note"> * <intent-filter android:label="@string/resolve_edit"> - * <action android:value="android.intent.action.VIEW" /> - * <action android:value="android.intent.action.EDIT" /> - * <category android:value="android.intent.category.DEFAULT" /> - * <type android:value="vnd.android.cursor.item/vnd.google.note" /> + * <action android:name="android.intent.action.VIEW" /> + * <action android:name="android.intent.action.EDIT" /> + * <category android:name="android.intent.category.DEFAULT" /> + * <data android:mimeType="vnd.android.cursor.item/vnd.google.note" /> * </intent-filter> * * <intent-filter> - * <action android:value="android.intent.action.INSERT" /> - * <category android:value="android.intent.category.DEFAULT" /> - * <type android:value="vnd.android.cursor.dir/vnd.google.note" /> + * <action android:name="android.intent.action.INSERT" /> + * <category android:name="android.intent.category.DEFAULT" /> + * <data android:mimeType="vnd.android.cursor.dir/vnd.google.note" /> * </intent-filter> * * </activity> @@ -276,11 +276,11 @@ import java.util.Set; * <activity class=".TitleEditor" android:label="@string/title_edit_title" * android:theme="@android:style/Theme.Dialog"> * <intent-filter android:label="@string/resolve_title"> - * <action android:value="com.android.notepad.action.EDIT_TITLE" /> - * <category android:value="android.intent.category.DEFAULT" /> - * <category android:value="android.intent.category.ALTERNATIVE" /> - * <category android:value="android.intent.category.SELECTED_ALTERNATIVE" /> - * <type android:value="vnd.android.cursor.item/vnd.google.note" /> + * <action android:name="com.android.notepad.action.EDIT_TITLE" /> + * <category android:name="android.intent.category.DEFAULT" /> + * <category android:name="android.intent.category.ALTERNATIVE" /> + * <category android:name="android.intent.category.SELECTED_ALTERNATIVE" /> + * <data android:mimeType="vnd.android.cursor.item/vnd.google.note" /> * </intent-filter> * </activity> * @@ -294,8 +294,8 @@ import java.util.Set; *

    *
  1.   * <intent-filter>
    - *     <action android:value="{@link #ACTION_MAIN android.intent.action.MAIN}" />
    - *     <category android:value="{@link #CATEGORY_LAUNCHER android.intent.category.LAUNCHER}" />
    + *     <action android:name="{@link #ACTION_MAIN android.intent.action.MAIN}" />
    + *     <category android:name="{@link #CATEGORY_LAUNCHER android.intent.category.LAUNCHER}" />
      * </intent-filter>
    *

    This provides a top-level entry into the NotePad application: the standard * MAIN action is a main entry point (not requiring any other information in @@ -303,11 +303,11 @@ import java.util.Set; * listed in the application launcher.

    *
  2.   * <intent-filter>
    - *     <action android:value="{@link #ACTION_VIEW android.intent.action.VIEW}" />
    - *     <action android:value="{@link #ACTION_EDIT android.intent.action.EDIT}" />
    - *     <action android:value="{@link #ACTION_PICK android.intent.action.PICK}" />
    - *     <category android:value="{@link #CATEGORY_DEFAULT android.intent.category.DEFAULT}" />
    - *     <type android:value="vnd.android.cursor.dir/vnd.google.note" />
    + *     <action android:name="{@link #ACTION_VIEW android.intent.action.VIEW}" />
    + *     <action android:name="{@link #ACTION_EDIT android.intent.action.EDIT}" />
    + *     <action android:name="{@link #ACTION_PICK android.intent.action.PICK}" />
    + *     <category android:name="{@link #CATEGORY_DEFAULT android.intent.category.DEFAULT}" />
    + *     <data mimeType:name="vnd.android.cursor.dir/vnd.google.note" />
      * </intent-filter>
    *

    This declares the things that the activity can do on a directory of * notes. The type being supported is given with the <type> tag, where @@ -322,9 +322,9 @@ import java.util.Set; * activity when its component name is not explicitly specified.

    *
  3.   * <intent-filter>
    - *     <action android:value="{@link #ACTION_GET_CONTENT android.intent.action.GET_CONTENT}" />
    - *     <category android:value="{@link #CATEGORY_DEFAULT android.intent.category.DEFAULT}" />
    - *     <type android:value="vnd.android.cursor.item/vnd.google.note" />
    + *     <action android:name="{@link #ACTION_GET_CONTENT android.intent.action.GET_CONTENT}" />
    + *     <category android:name="{@link #CATEGORY_DEFAULT android.intent.category.DEFAULT}" />
    + *     <data android:mimeType="vnd.android.cursor.item/vnd.google.note" />
      * </intent-filter>
    *

    This filter describes the ability return to the caller a note selected by * the user without needing to know where it came from. The data type @@ -371,10 +371,10 @@ import java.util.Set; *

      *
    1.   * <intent-filter android:label="@string/resolve_edit">
      - *     <action android:value="{@link #ACTION_VIEW android.intent.action.VIEW}" />
      - *     <action android:value="{@link #ACTION_EDIT android.intent.action.EDIT}" />
      - *     <category android:value="{@link #CATEGORY_DEFAULT android.intent.category.DEFAULT}" />
      - *     <type android:value="vnd.android.cursor.item/vnd.google.note" />
      + *     <action android:name="{@link #ACTION_VIEW android.intent.action.VIEW}" />
      + *     <action android:name="{@link #ACTION_EDIT android.intent.action.EDIT}" />
      + *     <category android:name="{@link #CATEGORY_DEFAULT android.intent.category.DEFAULT}" />
      + *     <data android:mimeType="vnd.android.cursor.item/vnd.google.note" />
        * </intent-filter>
      *

      The first, primary, purpose of this activity is to let the user interact * with a single note, as decribed by the MIME type @@ -384,9 +384,9 @@ import java.util.Set; * specifying its component.

      *
    2.   * <intent-filter>
      - *     <action android:value="{@link #ACTION_INSERT android.intent.action.INSERT}" />
      - *     <category android:value="{@link #CATEGORY_DEFAULT android.intent.category.DEFAULT}" />
      - *     <type android:value="vnd.android.cursor.dir/vnd.google.note" />
      + *     <action android:name="{@link #ACTION_INSERT android.intent.action.INSERT}" />
      + *     <category android:name="{@link #CATEGORY_DEFAULT android.intent.category.DEFAULT}" />
      + *     <data android:mimeType="vnd.android.cursor.dir/vnd.google.note" />
        * </intent-filter>
      *

      The secondary use of this activity is to insert a new note entry into * an existing directory of notes. This is used when the user creates a new @@ -422,11 +422,11 @@ import java.util.Set; * *

        * <intent-filter android:label="@string/resolve_title">
      - *     <action android:value="com.android.notepad.action.EDIT_TITLE" />
      - *     <category android:value="{@link #CATEGORY_DEFAULT android.intent.category.DEFAULT}" />
      - *     <category android:value="{@link #CATEGORY_ALTERNATIVE android.intent.category.ALTERNATIVE}" />
      - *     <category android:value="{@link #CATEGORY_SELECTED_ALTERNATIVE android.intent.category.SELECTED_ALTERNATIVE}" />
      - *     <type android:value="vnd.android.cursor.item/vnd.google.note" />
      + *     <action android:name="com.android.notepad.action.EDIT_TITLE" />
      + *     <category android:name="{@link #CATEGORY_DEFAULT android.intent.category.DEFAULT}" />
      + *     <category android:name="{@link #CATEGORY_ALTERNATIVE android.intent.category.ALTERNATIVE}" />
      + *     <category android:name="{@link #CATEGORY_SELECTED_ALTERNATIVE android.intent.category.SELECTED_ALTERNATIVE}" />
      + *     <data android:mimeType="vnd.android.cursor.item/vnd.google.note" />
        * </intent-filter>
      * *

      In the single intent template here, we @@ -509,8 +509,8 @@ import java.util.Set; *

    3. {@link #ACTION_UID_REMOVED} *
    4. {@link #ACTION_BATTERY_CHANGED} *
    5. {@link #ACTION_POWER_CONNECTED} - *
    6. {@link #ACTION_POWER_DISCONNECTED} - *
    7. {@link #ACTION_SHUTDOWN} + *
    8. {@link #ACTION_POWER_DISCONNECTED} + *
    9. {@link #ACTION_SHUTDOWN} * * *

      Standard Categories

      @@ -914,6 +914,23 @@ public class Intent implements Parcelable { */ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_SEND = "android.intent.action.SEND"; + /** + * Activity Action: Deliver multiple data to someone else. + *

      + * Like ACTION_SEND, except the data is multiple. + *

      + * Input: {@link #getType} is the MIME type of the data being sent. + * get*ArrayListExtra can have either a {@link #EXTRA_TEXT} or {@link + * #EXTRA_STREAM} field, containing the data to be sent. + *

      + * Optional standard extras, which may be interpreted by some recipients as + * appropriate, are: {@link #EXTRA_EMAIL}, {@link #EXTRA_CC}, + * {@link #EXTRA_BCC}, {@link #EXTRA_SUBJECT}. + *

      + * Output: nothing. + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_SEND_MULTIPLE = "android.intent.action.SEND_MULTIPLE"; /** * Activity Action: Handle an incoming phone call. *

      Input: nothing. @@ -1059,6 +1076,36 @@ public class Intent implements Parcelable { */ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_APP_ERROR = "android.intent.action.APP_ERROR"; + + /** + * Activity Action: Show power usage information to the user. + *

      Input: Nothing. + *

      Output: Nothing. + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_POWER_USAGE_SUMMARY = "android.intent.action.POWER_USAGE_SUMMARY"; + + /** + * Activity Action: Setup wizard to launch after a platform update. This + * activity should have a string meta-data field associated with it, + * {@link #METADATA_SETUP_VERSION}, which defines the current version of + * the platform for setup. The activity will be launched only if + * {@link android.provider.Settings.Secure#LAST_SETUP_SHOWN} is not the + * same value. + *

      Input: Nothing. + *

      Output: Nothing. + * @hide + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_UPGRADE_SETUP = "android.intent.action.UPGRADE_SETUP"; + + /** + * A string associated with a {@link #ACTION_UPGRADE_SETUP} activity + * describing the last run version of the platform that was setup. + * @hide + */ + public static final String METADATA_SETUP_VERSION = "android.SETUP_VERSION"; + // --------------------------------------------------------------------- // --------------------------------------------------------------------- // Standard intent broadcast actions (see action variable). @@ -1263,6 +1310,13 @@ public class Intent implements Parcelable { */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_BATTERY_LOW = "android.intent.action.BATTERY_LOW"; + /** + * Broadcast Action: Indicates the battery is now okay after being low. + * This will be sent after {@link #ACTION_BATTERY_LOW} once the battery has + * gone back up to an okay state. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_BATTERY_OKAY = "android.intent.action.BATTERY_OKAY"; /** * Broadcast Action: External power has been connected to the device. * This is intended for applications that wish to register specifically to this notification. @@ -1277,10 +1331,10 @@ public class Intent implements Parcelable { * This is intended for applications that wish to register specifically to this notification. * Unlike ACTION_BATTERY_CHANGED, applications will be woken for this and so do not have to * stay active to receive this notification. This action can be used to implement actions - * that wait until power is available to trigger. + * that wait until power is available to trigger. */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_POWER_DISCONNECTED = "android.intent.action.ACTION_POWER_DISCONNECTED"; + public static final String ACTION_POWER_DISCONNECTED = "android.intent.action.ACTION_POWER_DISCONNECTED"; /** * Broadcast Action: Device is shutting down. * This is broadcast when the device is being shut down (completely turned @@ -1289,7 +1343,7 @@ public class Intent implements Parcelable { * to handle this, since the forground activity will be paused as well. */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_SHUTDOWN = "android.intent.action.ACTION_SHUTDOWN"; + public static final String ACTION_SHUTDOWN = "android.intent.action.ACTION_SHUTDOWN"; /** * Broadcast Action: Indicates low memory condition on the device */ @@ -1552,6 +1606,16 @@ public class Intent implements Parcelable { public static final String ACTION_REBOOT = "android.intent.action.REBOOT"; + /** + * @hide + * TODO: This will be unhidden in a later CL. + * Broadcast Action: The TextToSpeech synthesizer has completed processing + * all of the text in the speech queue. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_TTS_QUEUE_PROCESSING_COMPLETED = + "android.intent.action.TTS_QUEUE_PROCESSING_COMPLETED"; + // --------------------------------------------------------------------- // --------------------------------------------------------------------- // Standard intent categories (see addCategory()). @@ -1791,23 +1855,23 @@ public class Intent implements Parcelable { * delivered. */ public static final String EXTRA_ALARM_COUNT = "android.intent.extra.ALARM_COUNT"; - + /** * Used as a parcelable extra field in {@link #ACTION_APP_ERROR}, containing * the bug report. - * + * * @hide */ public static final String EXTRA_BUG_REPORT = "android.intent.extra.BUG_REPORT"; /** - * Used as a string extra field when sending an intent to PackageInstaller to install a + * Used as a string extra field when sending an intent to PackageInstaller to install a * package. Specifies the installer package name; this package will receive the * {@link #ACTION_APP_ERROR} intent. - * + * * @hide */ - public static final String EXTRA_INSTALLER_PACKAGE_NAME + public static final String EXTRA_INSTALLER_PACKAGE_NAME = "android.intent.extra.INSTALLER_PACKAGE_NAME"; // --------------------------------------------------------------------- @@ -2039,11 +2103,26 @@ public class Intent implements Parcelable { */ public static final int FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT = 0x20000000; + // --------------------------------------------------------------------- + // --------------------------------------------------------------------- + // toUri() and parseUri() options. + + /** + * Flag for use with {@link #toUri} and {@link #parseUri}: the URI string + * always has the "intent:" scheme. This syntax can be used when you want + * to later disambiguate between URIs that are intended to describe an + * Intent vs. all others that should be treated as raw URIs. When used + * with {@link #parseUri}, any other scheme will result in a generic + * VIEW action for that raw URI. + */ + public static final int URI_INTENT_SCHEME = 1<<0; + // --------------------------------------------------------------------- private String mAction; private Uri mData; private String mType; + private String mPackage; private ComponentName mComponent; private int mFlags; private HashSet mCategories; @@ -2064,6 +2143,7 @@ public class Intent implements Parcelable { this.mAction = o.mAction; this.mData = o.mData; this.mType = o.mType; + this.mPackage = o.mPackage; this.mComponent = o.mComponent; this.mFlags = o.mFlags; if (o.mCategories != null) { @@ -2083,6 +2163,7 @@ public class Intent implements Parcelable { this.mAction = o.mAction; this.mData = o.mData; this.mType = o.mType; + this.mPackage = o.mPackage; this.mComponent = o.mComponent; if (o.mCategories != null) { this.mCategories = new HashSet(o.mCategories); @@ -2182,24 +2263,51 @@ public class Intent implements Parcelable { mComponent = new ComponentName(packageContext, cls); } + /** + * Call {@link #parseUri} with 0 flags. + * @deprecated Use {@link #parseUri} instead. + */ + @Deprecated + public static Intent getIntent(String uri) throws URISyntaxException { + return parseUri(uri, 0); + } + /** * Create an intent from a URI. This URI may encode the action, - * category, and other intent fields, if it was returned by toURI(). If - * the Intent was not generate by toURI(), its data will be the entire URI - * and its action will be ACTION_VIEW. + * category, and other intent fields, if it was returned by + * {@link #toUri}.. If the Intent was not generate by toUri(), its data + * will be the entire URI and its action will be ACTION_VIEW. * *

      The URI given here must not be relative -- that is, it must include * the scheme and full path. * * @param uri The URI to turn into an Intent. + * @param flags Additional processing flags. Either 0 or * * @return Intent The newly created Intent object. * - * @see #toURI + * @throws URISyntaxException Throws URISyntaxError if the basic URI syntax + * it bad (as parsed by the Uri class) or the Intent data within the + * URI is invalid. + * + * @see #toUri */ - public static Intent getIntent(String uri) throws URISyntaxException { + public static Intent parseUri(String uri, int flags) throws URISyntaxException { int i = 0; try { + // Validate intent scheme for if requested. + if ((flags&URI_INTENT_SCHEME) != 0) { + if (!uri.startsWith("intent:")) { + Intent intent = new Intent(ACTION_VIEW); + try { + intent.setData(Uri.parse(uri)); + } catch (IllegalArgumentException e) { + throw new URISyntaxException(uri, e.getMessage()); + } + return intent; + } + } + // simple case i = uri.lastIndexOf("#"); if (i == -1) return new Intent(ACTION_VIEW, Uri.parse(uri)); @@ -2211,16 +2319,15 @@ public class Intent implements Parcelable { Intent intent = new Intent(ACTION_VIEW); // fetch data part, if present - if (i > 0) { - intent.mData = Uri.parse(uri.substring(0, i)); - } + String data = i >= 0 ? uri.substring(0, i) : null; + String scheme = null; i += "#Intent;".length(); // loop over contents of Intent, all name=value; while (!uri.startsWith("end", i)) { int eq = uri.indexOf('=', i); int semi = uri.indexOf(';', eq); - String value = uri.substring(eq + 1, semi); + String value = Uri.decode(uri.substring(eq + 1, semi)); // action if (uri.startsWith("action=", i)) { @@ -2242,15 +2349,24 @@ public class Intent implements Parcelable { intent.mFlags = Integer.decode(value).intValue(); } + // package + else if (uri.startsWith("package=", i)) { + intent.mPackage = value; + } + // component else if (uri.startsWith("component=", i)) { intent.mComponent = ComponentName.unflattenFromString(value); } + // scheme + else if (uri.startsWith("scheme=", i)) { + scheme = value; + } + // extra else { String key = Uri.decode(uri.substring(i + 2, eq)); - value = Uri.decode(value); // create Bundle if it doesn't already exist if (intent.mExtras == null) intent.mExtras = new Bundle(); Bundle b = intent.mExtras; @@ -2271,6 +2387,23 @@ public class Intent implements Parcelable { i = semi + 1; } + if (data != null) { + if (data.startsWith("intent:")) { + data = data.substring(7); + if (scheme != null) { + data = scheme + ':' + data; + } + } + + if (data.length() > 0) { + try { + intent.mData = Uri.parse(data); + } catch (IllegalArgumentException e) { + throw new URISyntaxException(uri, e.getMessage()); + } + } + } + return intent; } catch (IndexOutOfBoundsException e) { @@ -3083,6 +3216,20 @@ public class Intent implements Parcelable { return mFlags; } + /** + * Retrieve the application package name this Intent is limited to. When + * resolving an Intent, if non-null this limits the resolution to only + * components in the given application package. + * + * @return The name of the application package for the Intent. + * + * @see #resolveActivity + * @see #setPackage + */ + public String getPackage() { + return mPackage; + } + /** * Retrieve the concrete component associated with the intent. When receiving * an intent, this is the component that was found to best handle it (that is, @@ -3118,6 +3265,9 @@ public class Intent implements Parcelable { *

      If {@link #addCategory} has added any categories, the activity must * handle ALL of the categories specified. * + *

      If {@link #getPackage} is non-NULL, only activity components in + * that application package will be considered. + * *

      If there are no activities that satisfy all of these conditions, a * null string is returned. * @@ -3239,7 +3389,7 @@ public class Intent implements Parcelable { * only specify a type and not data, for example to indicate the type of * data to return. This method automatically clears any data that was * previously set by {@link #setData}. - * + * *

      Note: MIME type matching in the Android framework is * case-sensitive, unlike formal RFC MIME types. As a result, * you should always write your MIME types with lower case letters, @@ -4088,6 +4238,27 @@ public class Intent implements Parcelable { return this; } + /** + * (Usually optional) Set an explicit application package name that limits + * the components this Intent will resolve to. If left to the default + * value of null, all components in all applications will considered. + * If non-null, the Intent can only match the components in the given + * application package. + * + * @param packageName The name of the application package to handle the + * intent, or null to allow any application package. + * + * @return Returns the same Intent object, for chaining multiple calls + * into a single statement. + * + * @see #getPackage + * @see #resolveActivity + */ + public Intent setPackage(String packageName) { + mPackage = packageName; + return this; + } + /** * (Usually optional) Explicitly set the component to handle the intent. * If left with the default value of null, the system will determine the @@ -4199,6 +4370,12 @@ public class Intent implements Parcelable { */ public static final int FILL_IN_COMPONENT = 1<<3; + /** + * Use with {@link #fillIn} to allow the current package value to be + * overwritten, even if it is already set. + */ + public static final int FILL_IN_PACKAGE = 1<<4; + /** * Copy the contents of other in to this object, but only * where fields are not defined by this object. For purposes of a field @@ -4210,14 +4387,15 @@ public class Intent implements Parcelable { *

    10. data URI and MIME type, as set by {@link #setData(Uri)}, * {@link #setType(String)}, or {@link #setDataAndType(Uri, String)}. *
    11. categories, as set by {@link #addCategory}. + *
    12. package, as set by {@link #setPackage}. *
    13. component, as set by {@link #setComponent(ComponentName)} or * related methods. *
    14. each top-level name in the associated extras. * * *

      In addition, you can use the {@link #FILL_IN_ACTION}, - * {@link #FILL_IN_DATA}, {@link #FILL_IN_CATEGORIES}, and - * {@link #FILL_IN_COMPONENT} to override the restriction where the + * {@link #FILL_IN_DATA}, {@link #FILL_IN_CATEGORIES}, {@link #FILL_IN_PACKAGE}, + * and {@link #FILL_IN_COMPONENT} to override the restriction where the * corresponding field will not be replaced if it is already set. * *

      For example, consider Intent A with {data="foo", categories="bar"} @@ -4233,32 +4411,39 @@ public class Intent implements Parcelable { * @param flags Options to control which fields can be filled in. * * @return Returns a bit mask of {@link #FILL_IN_ACTION}, - * {@link #FILL_IN_DATA}, {@link #FILL_IN_CATEGORIES}, and - * {@link #FILL_IN_COMPONENT} indicating which fields were changed. + * {@link #FILL_IN_DATA}, {@link #FILL_IN_CATEGORIES}, {@link #FILL_IN_PACKAGE}, + * and {@link #FILL_IN_COMPONENT} indicating which fields were changed. */ public int fillIn(Intent other, int flags) { int changes = 0; - if ((mAction == null && other.mAction == null) - || (flags&FILL_IN_ACTION) != 0) { + if (other.mAction != null + && (mAction == null || (flags&FILL_IN_ACTION) != 0)) { mAction = other.mAction; changes |= FILL_IN_ACTION; } - if ((mData == null && mType == null && - (other.mData != null || other.mType != null)) - || (flags&FILL_IN_DATA) != 0) { + if ((other.mData != null || other.mType != null) + && ((mData == null && mType == null) + || (flags&FILL_IN_DATA) != 0)) { mData = other.mData; mType = other.mType; changes |= FILL_IN_DATA; } - if ((mCategories == null && other.mCategories == null) - || (flags&FILL_IN_CATEGORIES) != 0) { + if (other.mCategories != null + && (mCategories == null || (flags&FILL_IN_CATEGORIES) != 0)) { if (other.mCategories != null) { mCategories = new HashSet(other.mCategories); } changes |= FILL_IN_CATEGORIES; } - if ((mComponent == null && other.mComponent == null) - || (flags&FILL_IN_COMPONENT) != 0) { + if (other.mPackage != null + && (mPackage == null || (flags&FILL_IN_PACKAGE) != 0)) { + mPackage = other.mPackage; + changes |= FILL_IN_PACKAGE; + } + // Component is special: it can -only- be set if explicitly allowed, + // since otherwise the sender could force the intent somewhere the + // originator didn't intend. + if (other.mComponent != null && (flags&FILL_IN_COMPONENT) != 0) { mComponent = other.mComponent; changes |= FILL_IN_COMPONENT; } @@ -4373,6 +4558,17 @@ public class Intent implements Parcelable { } } } + if (mPackage != other.mPackage) { + if (mPackage != null) { + if (!mPackage.equals(other.mPackage)) { + return false; + } + } else { + if (!other.mPackage.equals(mPackage)) { + return false; + } + } + } if (mComponent != other.mComponent) { if (mComponent != null) { if (!mComponent.equals(other.mComponent)) { @@ -4418,6 +4614,9 @@ public class Intent implements Parcelable { if (mType != null) { code += mType.hashCode(); } + if (mPackage != null) { + code += mPackage.hashCode(); + } if (mComponent != null) { code += mComponent.hashCode(); } @@ -4444,7 +4643,7 @@ public class Intent implements Parcelable { toShortString(b, comp, extras); return b.toString(); } - + /** @hide */ public void toShortString(StringBuilder b, boolean comp, boolean extras) { boolean first = true; @@ -4488,6 +4687,13 @@ public class Intent implements Parcelable { first = false; b.append("flg=0x").append(Integer.toHexString(mFlags)); } + if (mPackage != null) { + if (!first) { + b.append(' '); + } + first = false; + b.append("pkg=").append(mPackage); + } if (comp && mComponent != null) { if (!first) { b.append(' '); @@ -4504,28 +4710,87 @@ public class Intent implements Parcelable { } } + /** + * Call {@link #toUri} with 0 flags. + * @deprecated Use {@link #toUri} instead. + */ + @Deprecated public String toURI() { + return toUri(0); + } + + /** + * Convert this Intent into a String holding a URI representation of it. + * The returned URI string has been properly URI encoded, so it can be + * used with {@link Uri#parse Uri.parse(String)}. The URI contains the + * Intent's data as the base URI, with an additional fragment describing + * the action, categories, type, flags, package, component, and extras. + * + *

      You can convert the returned string back to an Intent with + * {@link #getIntent}. + * + * @param flags Additional operating flags. Either 0 or + * {@link #URI_INTENT_SCHEME}. + * + * @return Returns a URI encoding URI string describing the entire contents + * of the Intent. + */ + public String toUri(int flags) { StringBuilder uri = new StringBuilder(128); - if (mData != null) uri.append(mData.toString()); + String scheme = null; + if (mData != null) { + String data = mData.toString(); + if ((flags&URI_INTENT_SCHEME) != 0) { + final int N = data.length(); + for (int i=0; i= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') + || c == '.' || c == '-') { + continue; + } + if (c == ':' && i > 0) { + // Valid scheme. + scheme = data.substring(0, i); + uri.append("intent:"); + data = data.substring(i+1); + break; + } + + // No scheme. + break; + } + } + uri.append(data); + + } else if ((flags&URI_INTENT_SCHEME) != 0) { + uri.append("intent:"); + } uri.append("#Intent;"); + if (scheme != null) { + uri.append("scheme=").append(scheme).append(';'); + } if (mAction != null) { - uri.append("action=").append(mAction).append(';'); + uri.append("action=").append(Uri.encode(mAction)).append(';'); } if (mCategories != null) { for (String category : mCategories) { - uri.append("category=").append(category).append(';'); + uri.append("category=").append(Uri.encode(category)).append(';'); } } if (mType != null) { - uri.append("type=").append(mType).append(';'); + uri.append("type=").append(Uri.encode(mType, "/")).append(';'); } if (mFlags != 0) { uri.append("launchFlags=0x").append(Integer.toHexString(mFlags)).append(';'); } + if (mPackage != null) { + uri.append("package=").append(Uri.encode(mPackage)).append(';'); + } if (mComponent != null) { - uri.append("component=").append(mComponent.flattenToShortString()).append(';'); + uri.append("component=").append(Uri.encode( + mComponent.flattenToShortString(), "/")).append(';'); } if (mExtras != null) { for (String key : mExtras.keySet()) { @@ -4567,6 +4832,7 @@ public class Intent implements Parcelable { Uri.writeToParcel(out, mData); out.writeString(mType); out.writeInt(mFlags); + out.writeString(mPackage); ComponentName.writeToParcel(mComponent, out); if (mCategories != null) { @@ -4600,6 +4866,7 @@ public class Intent implements Parcelable { mData = Uri.CREATOR.createFromParcel(in); mType = in.readString(); mFlags = in.readInt(); + mPackage = in.readString(); mComponent = ComponentName.readFromParcel(in); int N = in.readInt(); diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java index e5c5dc8a5e0ccf70d52c9a7bb25fa2f421983cea..365f26983a33c356ed159988a851cb470d777bc1 100644 --- a/core/java/android/content/IntentFilter.java +++ b/core/java/android/content/IntentFilter.java @@ -366,6 +366,7 @@ public class IntentFilter implements Parcelable { throws MalformedMimeTypeException { mPriority = 0; mActions = new ArrayList(); + addAction(action); addDataType(dataType); } diff --git a/core/java/android/content/IntentSender.aidl b/core/java/android/content/IntentSender.aidl new file mode 100644 index 0000000000000000000000000000000000000000..741bc8c953cb3b637aef7365a9f4de8568bcff90 --- /dev/null +++ b/core/java/android/content/IntentSender.aidl @@ -0,0 +1,19 @@ +/* + * 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.content; + +parcelable IntentSender; diff --git a/core/java/android/content/IntentSender.java b/core/java/android/content/IntentSender.java new file mode 100644 index 0000000000000000000000000000000000000000..4da49d974fd53f72e38508a3df6233c9e241db96 --- /dev/null +++ b/core/java/android/content/IntentSender.java @@ -0,0 +1,255 @@ +/* + * 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.content; + +import android.content.Context; +import android.content.Intent; +import android.content.IIntentSender; +import android.content.IIntentReceiver; +import android.os.Bundle; +import android.os.RemoteException; +import android.os.Handler; +import android.os.IBinder; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.AndroidException; + + +/** + * A description of an Intent and target action to perform with it. + * The returned object can be + * handed to other applications so that they can perform the action you + * described on your behalf at a later time. + * + *

      By giving a IntentSender to another application, + * you are granting it the right to perform the operation you have specified + * as if the other application was yourself (with the same permissions and + * identity). As such, you should be careful about how you build the IntentSender: + * often, for example, the base Intent you supply will have the component + * name explicitly set to one of your own components, to ensure it is ultimately + * sent there and nowhere else. + * + *

      A IntentSender itself is simply a reference to a token maintained by + * the system describing the original data used to retrieve it. This means + * that, even if its owning application's process is killed, the + * IntentSender itself will remain usable from other processes that + * have been given it. If the creating application later re-retrieves the + * same kind of IntentSender (same operation, same Intent action, data, + * categories, and components, and same flags), it will receive a IntentSender + * representing the same token if that is still valid. + * + */ +public class IntentSender implements Parcelable { + private final IIntentSender mTarget; + + /** + * Exception thrown when trying to send through a PendingIntent that + * has been canceled or is otherwise no longer able to execute the request. + */ + public static class SendIntentException extends AndroidException { + public SendIntentException() { + } + + public SendIntentException(String name) { + super(name); + } + + public SendIntentException(Exception cause) { + super(cause); + } + } + + /** + * Callback interface for discovering when a send operation has + * completed. Primarily for use with a IntentSender that is + * performing a broadcast, this provides the same information as + * calling {@link Context#sendOrderedBroadcast(Intent, String, + * android.content.BroadcastReceiver, Handler, int, String, Bundle) + * Context.sendBroadcast()} with a final BroadcastReceiver. + */ + public interface OnFinished { + /** + * Called when a send operation as completed. + * + * @param IntentSender The IntentSender this operation was sent through. + * @param intent The original Intent that was sent. + * @param resultCode The final result code determined by the send. + * @param resultData The final data collected by a broadcast. + * @param resultExtras The final extras collected by a broadcast. + */ + void onSendFinished(IntentSender IntentSender, Intent intent, + int resultCode, String resultData, Bundle resultExtras); + } + + private static class FinishedDispatcher extends IIntentReceiver.Stub + implements Runnable { + private final IntentSender mIntentSender; + private final OnFinished mWho; + private final Handler mHandler; + private Intent mIntent; + private int mResultCode; + private String mResultData; + private Bundle mResultExtras; + FinishedDispatcher(IntentSender pi, OnFinished who, Handler handler) { + mIntentSender = pi; + mWho = who; + mHandler = handler; + } + public void performReceive(Intent intent, int resultCode, + String data, Bundle extras, boolean serialized) { + mIntent = intent; + mResultCode = resultCode; + mResultData = data; + mResultExtras = extras; + if (mHandler == null) { + run(); + } else { + mHandler.post(this); + } + } + public void run() { + mWho.onSendFinished(mIntentSender, mIntent, mResultCode, + mResultData, mResultExtras); + } + } + + /** + * Perform the operation associated with this IntentSender, allowing the + * caller to specify information about the Intent to use and be notified + * when the send has completed. + * + * @param context The Context of the caller. This may be null if + * intent is also null. + * @param code Result code to supply back to the IntentSender's target. + * @param intent Additional Intent data. See {@link Intent#fillIn + * Intent.fillIn()} for information on how this is applied to the + * original Intent. Use null to not modify the original Intent. + * @param onFinished The object to call back on when the send has + * completed, or null for no callback. + * @param handler Handler identifying the thread on which the callback + * should happen. If null, the callback will happen from the thread + * pool of the process. + * + * + * @throws SendIntentException Throws CanceledIntentException if the IntentSender + * is no longer allowing more intents to be sent through it. + */ + public void sendIntent(Context context, int code, Intent intent, + OnFinished onFinished, Handler handler) throws SendIntentException { + try { + String resolvedType = intent != null ? + intent.resolveTypeIfNeeded(context.getContentResolver()) + : null; + int res = mTarget.send(code, intent, resolvedType, + onFinished != null + ? new FinishedDispatcher(this, onFinished, handler) + : null); + if (res < 0) { + throw new SendIntentException(); + } + } catch (RemoteException e) { + throw new SendIntentException(); + } + } + + /** + * Comparison operator on two IntentSender objects, such that true + * is returned then they both represent the same operation from the + * same package. + */ + @Override + public boolean equals(Object otherObj) { + if (otherObj instanceof IntentSender) { + return mTarget.asBinder().equals(((IntentSender)otherObj) + .mTarget.asBinder()); + } + return false; + } + + @Override + public int hashCode() { + return mTarget.asBinder().hashCode(); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(128); + sb.append("IntentSender{"); + sb.append(Integer.toHexString(System.identityHashCode(this))); + sb.append(": "); + sb.append(mTarget != null ? mTarget.asBinder() : null); + sb.append('}'); + return sb.toString(); + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel out, int flags) { + out.writeStrongBinder(mTarget.asBinder()); + } + + public static final Parcelable.Creator CREATOR + = new Parcelable.Creator() { + public IntentSender createFromParcel(Parcel in) { + IBinder target = in.readStrongBinder(); + return target != null ? new IntentSender(target) : null; + } + + public IntentSender[] newArray(int size) { + return new IntentSender[size]; + } + }; + + /** + * Convenience function for writing either a IntentSender or null pointer to + * a Parcel. You must use this with {@link #readIntentSenderOrNullFromParcel} + * for later reading it. + * + * @param sender The IntentSender to write, or null. + * @param out Where to write the IntentSender. + */ + public static void writeIntentSenderOrNullToParcel(IntentSender sender, + Parcel out) { + out.writeStrongBinder(sender != null ? sender.mTarget.asBinder() + : null); + } + + /** + * Convenience function for reading either a Messenger or null pointer from + * a Parcel. You must have previously written the Messenger with + * {@link #writeIntentSenderOrNullToParcel}. + * + * @param in The Parcel containing the written Messenger. + * + * @return Returns the Messenger read from the Parcel, or null if null had + * been written. + */ + public static IntentSender readIntentSenderOrNullFromParcel(Parcel in) { + IBinder b = in.readStrongBinder(); + return b != null ? new IntentSender(b) : null; + } + + protected IntentSender(IIntentSender target) { + mTarget = target; + } + + protected IntentSender(IBinder target) { + mTarget = IIntentSender.Stub.asInterface(target); + } +} diff --git a/core/java/android/content/SyncStorageEngine.java b/core/java/android/content/SyncStorageEngine.java index 9c25e73b0cf3d10348d7f3a549ae624f5fa777c8..f781e0d0e12dff81a646cb9beeaf2b65a2d37d98 100644 --- a/core/java/android/content/SyncStorageEngine.java +++ b/core/java/android/content/SyncStorageEngine.java @@ -24,6 +24,7 @@ import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; +import android.backup.IBackupManager; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteException; @@ -35,6 +36,7 @@ import android.os.Message; import android.os.Parcel; import android.os.RemoteCallbackList; import android.os.RemoteException; +import android.os.ServiceManager; import android.util.Log; import android.util.SparseArray; import android.util.Xml; @@ -351,8 +353,18 @@ public class SyncStorageEngine extends Handler { } } } + // Inform the backup manager about a data change + IBackupManager ibm = IBackupManager.Stub.asInterface( + ServiceManager.getService(Context.BACKUP_SERVICE)); + if (ibm != null) { + try { + ibm.dataChanged("com.android.providers.settings"); + } catch (RemoteException e) { + // Try again later + } + } } - + public boolean getSyncProviderAutomatically(String account, String providerName) { synchronized (mAuthorities) { if (account != null) { diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java index 85d877a070170a5006465d77caaf2859fb3a098c..27783efea9dff42301e48885fc25b1a61cabf38d 100644 --- a/core/java/android/content/pm/ActivityInfo.java +++ b/core/java/android/content/pm/ActivityInfo.java @@ -233,6 +233,12 @@ public class ActivityInfo extends ComponentInfo * {@link android.R.attr#configChanges} attribute. */ public static final int CONFIG_ORIENTATION = 0x0080; + /** + * Bit in {@link #configChanges} that indicates that the activity + * can itself handle changes to the screen layout. Set from the + * {@link android.R.attr#configChanges} attribute. + */ + public static final int CONFIG_SCREEN_LAYOUT = 0x0100; /** * Bit in {@link #configChanges} that indicates that the activity * can itself handle changes to the font scaling factor. Set from the @@ -248,8 +254,8 @@ public class ActivityInfo extends ComponentInfo * Contains any combination of {@link #CONFIG_FONT_SCALE}, * {@link #CONFIG_MCC}, {@link #CONFIG_MNC}, * {@link #CONFIG_LOCALE}, {@link #CONFIG_TOUCHSCREEN}, - * {@link #CONFIG_KEYBOARD}, {@link #CONFIG_NAVIGATION}, and - * {@link #CONFIG_ORIENTATION}. Set from the + * {@link #CONFIG_KEYBOARD}, {@link #CONFIG_NAVIGATION}, + * {@link #CONFIG_ORIENTATION}, and {@link #CONFIG_SCREEN_LAYOUT}. Set from the * {@link android.R.attr#configChanges} attribute. */ public int configChanges; diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index 88ac04c24e6df912e60ea73655c3ef8aa66eeaf2..bcf95b6f574eba477921bab51c6c153a1999137c 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -58,10 +58,21 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { * Class implementing the Application's manage space * functionality. From the "manageSpaceActivity" * attribute. This is an optional attribute and will be null if - * application's dont specify it in their manifest + * applications don't specify it in their manifest */ public String manageSpaceActivityName; + /** + * Class implementing the Application's backup functionality. From + * the "backupAgent" attribute. This is an optional attribute and + * will be null if the application does not specify it in its manifest. + * + *

      If android:allowBackup is set to false, this attribute is ignored. + * + * {@hide} + */ + public String backupAgentName; + /** * Value for {@link #flags}: if set, this application is installed in the * device's system image. @@ -93,7 +104,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { public static final int FLAG_PERSISTENT = 1<<3; /** - * Value for {@link #flags}: set to true iif this application holds the + * Value for {@link #flags}: set to true if this application holds the * {@link android.Manifest.permission#FACTORY_TEST} permission and the * device is running in factory test mode. */ @@ -123,13 +134,46 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { * Value for {@link #flags}: this is set of the application has set * its android:targetSdkVersion to something >= the current SDK version. */ - public static final int FLAG_TARGETS_SDK = 1<<8; + public static final int FLAG_TEST_ONLY = 1<<8; /** - * Value for {@link #flags}: this is set of the application has set - * its android:targetSdkVersion to something >= the current SDK version. + * Value for {@link #flags}: true when the application's window can be + * reduced in size for smaller screens. Corresponds to + * {@link android.R.styleable#AndroidManifestSupportsScreens_smallScreens + * android:smallScreens}. */ - public static final int FLAG_TEST_ONLY = 1<<9; + public static final int FLAG_SUPPORTS_SMALL_SCREENS = 1<<9; + + /** + * Value for {@link #flags}: true when the application's window can be + * displayed on normal screens. Corresponds to + * {@link android.R.styleable#AndroidManifestSupportsScreens_normalScreens + * android:normalScreens}. + */ + public static final int FLAG_SUPPORTS_NORMAL_SCREENS = 1<<10; + + /** + * Value for {@link #flags}: true when the application's window can be + * increased in size for larger screens. Corresponds to + * {@link android.R.styleable#AndroidManifestSupportsScreens_largeScreens + * android:smallScreens}. + */ + public static final int FLAG_SUPPORTS_LARGE_SCREENS = 1<<11; + + /** + * Value for {@link #flags}: this is false if the application has set + * its android:allowBackup to false, true otherwise. + * + * {@hide} + */ + public static final int FLAG_ALLOW_BACKUP = 1<<12; + + /** + * Indicates that the application supports any densities; + * {@hide} + */ + public static final int ANY_DENSITY = -1; + private static final int[] ANY_DENSITIES_ARRAY = { ANY_DENSITY }; /** * Flags associated with the application. Any combination of @@ -137,7 +181,9 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { * {@link #FLAG_PERSISTENT}, {@link #FLAG_FACTORY_TEST}, and * {@link #FLAG_ALLOW_TASK_REPARENTING} * {@link #FLAG_ALLOW_CLEAR_USER_DATA}, {@link #FLAG_UPDATED_SYSTEM_APP}, - * {@link #FLAG_TARGETS_SDK}. + * {@link #FLAG_TEST_ONLY}, {@link #FLAG_SUPPORTS_SMALL_SCREENS}, + * {@link #FLAG_SUPPORTS_NORMAL_SCREENS}, + * {@link #FLAG_SUPPORTS_LARGE_SCREENS}. */ public int flags = 0; @@ -173,7 +219,6 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { */ public int uid; - /** * The list of densities in DPI that application supprots. This * field is only set if the {@link PackageManager#GET_SUPPORTS_DENSITIES} flag was @@ -181,6 +226,16 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { */ public int[] supportsDensities; + /** + * The minimum SDK version this application targets. It may run on earilier + * versions, but it knows how to work with any new behavior added at this + * version. Will be {@link android.os.Build.VERSION_CODES#CUR_DEVELOPMENT} + * if this is a development build and the app is targeting that. You should + * compare that this number is >= the SDK version number at which your + * behavior was introduced. + */ + public int targetSdkVersion; + /** * When false, indicates that all components within this application are * considered disabled, regardless of their individually set enabled status. @@ -200,6 +255,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { pw.println(prefix + "publicSourceDir=" + publicSourceDir); pw.println(prefix + "sharedLibraryFiles=" + sharedLibraryFiles); pw.println(prefix + "dataDir=" + dataDir); + pw.println(prefix + "targetSdkVersion=" + targetSdkVersion); pw.println(prefix + "enabled=" + enabled); pw.println(prefix + "manageSpaceActivityName="+manageSpaceActivityName); pw.println(prefix + "description=0x"+Integer.toHexString(descriptionRes)); @@ -246,6 +302,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { sharedLibraryFiles = orig.sharedLibraryFiles; dataDir = orig.dataDir; uid = orig.uid; + targetSdkVersion = orig.targetSdkVersion; enabled = orig.enabled; manageSpaceActivityName = orig.manageSpaceActivityName; descriptionRes = orig.descriptionRes; @@ -276,8 +333,10 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { dest.writeStringArray(sharedLibraryFiles); dest.writeString(dataDir); dest.writeInt(uid); + dest.writeInt(targetSdkVersion); dest.writeInt(enabled ? 1 : 0); dest.writeString(manageSpaceActivityName); + dest.writeString(backupAgentName); dest.writeInt(descriptionRes); dest.writeIntArray(supportsDensities); } @@ -305,8 +364,10 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { sharedLibraryFiles = source.readStringArray(); dataDir = source.readString(); uid = source.readInt(); + targetSdkVersion = source.readInt(); enabled = source.readInt() != 0; manageSpaceActivityName = source.readString(); + backupAgentName = source.readString(); descriptionRes = source.readInt(); supportsDensities = source.createIntArray(); } @@ -331,4 +392,14 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { } return null; } + + /** + * Disable compatibility mode + * + * @hide + */ + public void disableCompatibilityMode() { + flags |= FLAG_SUPPORTS_LARGE_SCREENS; + supportsDensities = ANY_DENSITIES_ARRAY; + } } diff --git a/core/java/android/content/pm/ConfigurationInfo.java b/core/java/android/content/pm/ConfigurationInfo.java index dcc746331b0b89a7ea4aba3882c66ef929c455c5..fb7a47fbd1bd3438345df93f61fcc5c94ec91eb9 100755 --- a/core/java/android/content/pm/ConfigurationInfo.java +++ b/core/java/android/content/pm/ConfigurationInfo.java @@ -22,7 +22,7 @@ import android.os.Parcelable; /** * Information you can retrieve about hardware configuration preferences * declared by an application. This corresponds to information collected from the - * AndroidManifest.xml's <uses-configuration> tags. + * AndroidManifest.xml's <uses-configuration> and the <uses-feature>tags. */ public class ConfigurationInfo implements Parcelable { /** @@ -70,6 +70,16 @@ public class ConfigurationInfo implements Parcelable { */ public int reqInputFeatures = 0; + /** + * Default value for {@link #reqGlEsVersion}; + */ + public static final int GL_ES_VERSION_UNDEFINED = 0; + /** + * The GLES version used by an application. The upper order 16 bits represent the + * major version and the lower order 16 bits the minor version. + */ + public int reqGlEsVersion; + public ConfigurationInfo() { } @@ -78,6 +88,7 @@ public class ConfigurationInfo implements Parcelable { reqKeyboardType = orig.reqKeyboardType; reqNavigation = orig.reqNavigation; reqInputFeatures = orig.reqInputFeatures; + reqGlEsVersion = orig.reqGlEsVersion; } public String toString() { @@ -86,7 +97,8 @@ public class ConfigurationInfo implements Parcelable { + ", touchscreen = " + reqTouchScreen + "}" + ", inputMethod = " + reqKeyboardType + "}" + ", navigation = " + reqNavigation + "}" - + ", reqInputFeatures = " + reqInputFeatures + "}"; + + ", reqInputFeatures = " + reqInputFeatures + "}" + + ", reqGlEsVersion = " + reqGlEsVersion + "}"; } public int describeContents() { @@ -98,6 +110,7 @@ public class ConfigurationInfo implements Parcelable { dest.writeInt(reqKeyboardType); dest.writeInt(reqNavigation); dest.writeInt(reqInputFeatures); + dest.writeInt(reqGlEsVersion); } public static final Creator CREATOR = @@ -115,5 +128,18 @@ public class ConfigurationInfo implements Parcelable { reqKeyboardType = source.readInt(); reqNavigation = source.readInt(); reqInputFeatures = source.readInt(); + reqGlEsVersion = source.readInt(); + } + + /** + * This method extracts the major and minor version of reqGLEsVersion attribute + * and returns it as a string. Say reqGlEsVersion value of 0x00010002 is returned + * as 1.2 + * @return String representation of the reqGlEsVersion attribute + */ + public String getGlEsVersion() { + int major = ((reqGlEsVersion & 0xffff0000) >> 16); + int minor = reqGlEsVersion & 0x0000ffff; + return String.valueOf(major)+"."+String.valueOf(minor); } } diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index c199619c57a92ba6168c9e6d223b411fc008e44a..bf2a8959c7f4cb9f84789675d2d883becd44311c 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -34,7 +34,7 @@ import android.content.pm.PermissionInfo; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.net.Uri; -import android.app.PendingIntent; +import android.content.IntentSender; /** * See {@link PackageManager} for documentation on most of the APIs @@ -164,7 +164,12 @@ interface IPackageManager { void addPreferredActivity(in IntentFilter filter, int match, in ComponentName[] set, in ComponentName activity); + + void replacePreferredActivity(in IntentFilter filter, int match, + in ComponentName[] set, in ComponentName activity); + void clearPackagePreferredActivities(String packageName); + int getPreferredActivities(out List outFilters, out List outActivities, String packageName); @@ -229,12 +234,12 @@ interface IPackageManager { * and the current free storage is YY, * if XX is less than YY, just return. if not free XX-YY number * of bytes if possible. - * @param opFinishedIntent PendingIntent call back used to + * @param pi IntentSender call back used to * notify when the operation is completed.May be null * to indicate that no call back is desired. */ void freeStorage(in long freeStorageSize, - in PendingIntent opFinishedIntent); + in IntentSender pi); /** * Delete all the cache files in an applications cache directory @@ -271,4 +276,11 @@ interface IPackageManager { boolean isSafeMode(); void systemReady(); boolean hasSystemUidErrors(); + + /** + * Ask the package manager to perform dex-opt (if needed) on the given + * package, if it already hasn't done mode. Only does this if running + * in the special development "no pre-dexopt" mode. + */ + boolean performDexOpt(String packageName); } diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 3a192f7eb7493168da4d0e9ce42446032dac89df..941ca9e5bb74efc8e73e630f932ddaca217e4c20 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -16,12 +16,11 @@ package android.content.pm; - -import android.app.PendingIntent; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.IntentSender; import android.content.res.Resources; import android.content.res.XmlResourceParser; import android.graphics.drawable.Drawable; @@ -397,6 +396,15 @@ public abstract class PackageManager { */ public static final int INSTALL_FAILED_TEST_ONLY = -15; + /** + * Installation return code: this is passed to the {@link IPackageInstallObserver} by + * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if + * the package being installed contains native code, but none that is + * compatible with the the device's CPU_ABI. + * @hide + */ + public static final int INSTALL_FAILED_CPU_ABI_INCOMPATIBLE = -16; + /** * Installation parse return code: this is passed to the {@link IPackageInstallObserver} by * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} @@ -563,9 +571,8 @@ public abstract class PackageManager { * launch the main activity in the package, or null if the package does * not contain such an activity. */ - public abstract Intent getLaunchIntentForPackage(String packageName) - throws NameNotFoundException; - + public abstract Intent getLaunchIntentForPackage(String packageName); + /** * Return an array of all of the secondary group-ids that have been * assigned to a package. @@ -1491,7 +1498,7 @@ public abstract class PackageManager { * @hide */ public abstract void freeStorageAndNotify(long freeStorageSize, IPackageDataObserver observer); - + /** * Free storage by deleting LRU sorted list of cache files across * all applications. If the currently available free storage @@ -1509,13 +1516,13 @@ public abstract class PackageManager { * and the current free storage is YY, * if XX is less than YY, just return. if not free XX-YY number * of bytes if possible. - * @param opFinishedIntent PendingIntent call back used to + * @param pi IntentSender call back used to * notify when the operation is completed.May be null * to indicate that no call back is desired. * * @hide */ - public abstract void freeStorage(long freeStorageSize, PendingIntent opFinishedIntent); + public abstract void freeStorage(long freeStorageSize, IntentSender pi); /** * Retrieve the size information for a package. @@ -1604,6 +1611,26 @@ public abstract class PackageManager { public abstract void addPreferredActivity(IntentFilter filter, int match, ComponentName[] set, ComponentName activity); + /** + * Replaces an existing preferred activity mapping to the system, and if that were not present + * adds a new preferred activity. This will be used + * to automatically select the given activity component when + * {@link Context#startActivity(Intent) Context.startActivity()} finds + * multiple matching activities and also matches the given filter. + * + * @param filter The set of intents under which this activity will be + * made preferred. + * @param match The IntentFilter match category that this preference + * applies to. + * @param set The set of activities that the user was picking from when + * this preference was made. + * @param activity The component name of the activity that is to be + * preferred. + * @hide + */ + public abstract void replacePreferredActivity(IntentFilter filter, int match, + ComponentName[] set, ComponentName activity); + /** * Remove all preferred activity mappings, previously added with * {@link #addPreferredActivity}, from the diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 88907c180117f15546c410151cabcbf2eb927890..558b0c3e369a109dd8d7dbc36a2a32b58eb29677 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -55,6 +55,32 @@ import java.util.jar.JarFile; * {@hide} */ public class PackageParser { + /** @hide */ + public static class NewPermissionInfo { + public final String name; + public final int sdkVersion; + public final int fileVersion; + + public NewPermissionInfo(String name, int sdkVersion, int fileVersion) { + this.name = name; + this.sdkVersion = sdkVersion; + this.fileVersion = fileVersion; + } + } + + /** + * List of new permissions that have been added since 1.0. + * NOTE: These must be declared in SDK version order, with permissions + * added to older SDKs appearing before those added to newer SDKs. + * @hide + */ + public static final PackageParser.NewPermissionInfo NEW_PERMISSIONS[] = + new PackageParser.NewPermissionInfo[] { + new PackageParser.NewPermissionInfo(android.Manifest.permission.WRITE_EXTERNAL_STORAGE, + android.os.Build.VERSION_CODES.DONUT, 0), + new PackageParser.NewPermissionInfo(android.Manifest.permission.READ_PHONE_STATE, + android.os.Build.VERSION_CODES.DONUT, 0) + }; private String mArchiveSourcePath; private String[] mSeparateProcesses; @@ -616,7 +642,6 @@ public class PackageParser { final Package pkg = new Package(pkgName); boolean foundApp = false; - boolean targetsSdk = false; TypedArray sa = res.obtainAttributes(attrs, com.android.internal.R.styleable.AndroidManifest); @@ -643,6 +668,11 @@ public class PackageParser { } sa.recycle(); + // Resource boolean are -1, so 1 means we don't know the value. + int supportsSmallScreens = 1; + int supportsNormalScreens = 1; + int supportsLargeScreens = 1; + int outerDepth = parser.getDepth(); while ((type=parser.next()) != parser.END_DOCUMENT && (type != parser.END_TAG || parser.getDepth() > outerDepth)) { @@ -723,6 +753,18 @@ public class PackageParser { XmlUtils.skipCurrentTag(parser); + } else if (tagName.equals("uses-feature")) { + ConfigurationInfo cPref = new ConfigurationInfo(); + sa = res.obtainAttributes(attrs, + com.android.internal.R.styleable.AndroidManifestUsesFeature); + cPref.reqGlEsVersion = sa.getInt( + com.android.internal.R.styleable.AndroidManifestUsesFeature_glEsVersion, + ConfigurationInfo.GL_ES_VERSION_UNDEFINED); + sa.recycle(); + pkg.configPreferences.add(cPref); + + XmlUtils.skipCurrentTag(parser); + } else if (tagName.equals("uses-sdk")) { if (mSdkVersion > 0) { sa = res.obtainAttributes(attrs, @@ -740,7 +782,7 @@ public class PackageParser { targetCode = minCode = val.string.toString(); } else { // If it's not a string, it's an integer. - minVers = val.data; + targetVers = minVers = val.data; } } @@ -761,6 +803,25 @@ public class PackageParser { sa.recycle(); + if (minCode != null) { + if (!minCode.equals(mSdkCodename)) { + if (mSdkCodename != null) { + outError[0] = "Requires development platform " + minCode + + " (current platform is " + mSdkCodename + ")"; + } else { + outError[0] = "Requires development platform " + minCode + + " but this is a release platform."; + } + mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK; + return null; + } + } else if (minVers > mSdkVersion) { + outError[0] = "Requires newer sdk version #" + minVers + + " (current version is #" + mSdkVersion + ")"; + mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK; + return null; + } + if (targetCode != null) { if (!targetCode.equals(mSdkCodename)) { if (mSdkCodename != null) { @@ -774,18 +835,10 @@ public class PackageParser { return null; } // If the code matches, it definitely targets this SDK. - targetsSdk = true; - } else if (targetVers >= mSdkVersion) { - // If they have explicitly targeted our current version - // or something after it, then note this. - targetsSdk = true; - } - - if (minVers > mSdkVersion) { - outError[0] = "Requires newer sdk version #" + minVers - + " (current version is #" + mSdkVersion + ")"; - mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK; - return null; + pkg.applicationInfo.targetSdkVersion + = android.os.Build.VERSION_CODES.CUR_DEVELOPMENT; + } else { + pkg.applicationInfo.targetSdkVersion = targetVers; } if (maxVers < mSdkVersion) { @@ -811,6 +864,42 @@ public class PackageParser { + parser.getName(); mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; return null; + + + } else if (tagName.equals("supports-density")) { + sa = res.obtainAttributes(attrs, + com.android.internal.R.styleable.AndroidManifestSupportsDensity); + + int density = sa.getInteger( + com.android.internal.R.styleable.AndroidManifestSupportsDensity_density, -1); + + sa.recycle(); + + if (density != -1 && !pkg.supportsDensityList.contains(density)) { + pkg.supportsDensityList.add(density); + } + + XmlUtils.skipCurrentTag(parser); + + } else if (tagName.equals("supports-screens")) { + sa = res.obtainAttributes(attrs, + com.android.internal.R.styleable.AndroidManifestSupportsScreens); + + // This is a trick to get a boolean and still able to detect + // if a value was actually set. + supportsSmallScreens = sa.getInteger( + com.android.internal.R.styleable.AndroidManifestSupportsScreens_smallScreens, + supportsSmallScreens); + supportsNormalScreens = sa.getInteger( + com.android.internal.R.styleable.AndroidManifestSupportsScreens_normalScreens, + supportsNormalScreens); + supportsLargeScreens = sa.getInteger( + com.android.internal.R.styleable.AndroidManifestSupportsScreens_largeScreens, + supportsLargeScreens); + + sa.recycle(); + + XmlUtils.skipCurrentTag(parser); } else { Log.w(TAG, "Bad element under : " + parser.getName()); @@ -824,15 +913,39 @@ public class PackageParser { mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_EMPTY; } - if (targetsSdk) { - pkg.applicationInfo.flags |= ApplicationInfo.FLAG_TARGETS_SDK; + final int NP = PackageParser.NEW_PERMISSIONS.length; + for (int ip=0; ip= npi.sdkVersion) { + break; + } + if (!pkg.requestedPermissions.contains(npi.name)) { + Log.i(TAG, "Impliciting adding " + npi.name + " to old pkg " + + pkg.packageName); + pkg.requestedPermissions.add(npi.name); + } } if (pkg.usesLibraries.size() > 0) { pkg.usesLibraryFiles = new String[pkg.usesLibraries.size()]; pkg.usesLibraries.toArray(pkg.usesLibraryFiles); } - + + if (supportsSmallScreens < 0 || (supportsSmallScreens > 0 + && pkg.applicationInfo.targetSdkVersion + >= android.os.Build.VERSION_CODES.CUR_DEVELOPMENT)) { + pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS; + } + if (supportsNormalScreens != 0) { + pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_NORMAL_SCREENS; + } + if (supportsLargeScreens < 0 || (supportsLargeScreens > 0 + && pkg.applicationInfo.targetSdkVersion + >= android.os.Build.VERSION_CODES.CUR_DEVELOPMENT)) { + pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS; + } + int size = pkg.supportsDensityList.size(); if (size > 0) { int densities[] = pkg.supportsDensities = new int[size]; @@ -1142,6 +1255,19 @@ public class PackageParser { outError); } + boolean allowBackup = sa.getBoolean( + com.android.internal.R.styleable.AndroidManifestApplication_allowBackup, true); + if (allowBackup) { + ai.flags |= ApplicationInfo.FLAG_ALLOW_BACKUP; + String backupAgent = sa.getNonResourceString( + com.android.internal.R.styleable.AndroidManifestApplication_backupAgent); + if (backupAgent != null) { + ai.backupAgentName = buildClassName(pkgName, backupAgent, outError); + Log.v(TAG, "android:backupAgent = " + ai.backupAgentName + + " from " + pkgName + "+" + backupAgent); + } + } + TypedValue v = sa.peekValue( com.android.internal.R.styleable.AndroidManifestApplication_label); if (v != null && (ai.labelRes=v.resourceId) == 0) { @@ -1298,21 +1424,6 @@ public class PackageParser { XmlUtils.skipCurrentTag(parser); - } else if (tagName.equals("supports-density")) { - sa = res.obtainAttributes(attrs, - com.android.internal.R.styleable.AndroidManifestSupportsDensity); - - int density = sa.getInteger( - com.android.internal.R.styleable.AndroidManifestSupportsDensity_density, -1); - - sa.recycle(); - - if (density != -1 && !owner.supportsDensityList.contains(density)) { - owner.supportsDensityList.add(density); - } - - XmlUtils.skipCurrentTag(parser); - } else { if (!RIGID_PARSER) { Log.w(TAG, "Problem in package " + mArchiveSourcePath + ":"); @@ -2219,6 +2330,17 @@ public class PackageParser { // preferred up order. public int mPreferredOrder = 0; + // For use by package manager service to keep track of which apps + // have been installed with forward locking. + public boolean mForwardLocked; + + // For use by the package manager to keep track of the path to the + // file an app came from. + public String mScanPath; + + // For use by package manager to keep track of where it has done dexopt. + public boolean mDidDexOpt; + // Additional data supplied by callers. public Object mExtras; @@ -2368,7 +2490,7 @@ public class PackageParser { return true; } if ((flags & PackageManager.GET_SUPPORTS_DENSITIES) != 0 - && p.supportsDensities != null) { + && p.supportsDensities != null) { return true; } return false; diff --git a/core/java/android/content/res/AssetFileDescriptor.java b/core/java/android/content/res/AssetFileDescriptor.java index 231e3e24a27c8036c736b29fe48ad040f9aadce7..a37e4e8cc3bf31286675c97b02d6d62b47c1e4b3 100644 --- a/core/java/android/content/res/AssetFileDescriptor.java +++ b/core/java/android/content/res/AssetFileDescriptor.java @@ -16,6 +16,7 @@ package android.content.res; +import android.os.MemoryFile; import android.os.Parcel; import android.os.ParcelFileDescriptor; import android.os.Parcelable; @@ -24,6 +25,8 @@ import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; +import java.io.InputStream; +import java.nio.channels.FileChannel; /** * File descriptor of an entry in the AssetManager. This provides your own @@ -123,6 +126,13 @@ public class AssetFileDescriptor implements Parcelable { mFd.close(); } + /** + * Checks whether this file descriptor is for a memory file. + */ + private boolean isMemoryFile() throws IOException { + return MemoryFile.isMemoryFile(mFd.getFileDescriptor()); + } + /** * Create and return a new auto-close input stream for this asset. This * will either return a full asset {@link AutoCloseInputStream}, or @@ -132,6 +142,12 @@ public class AssetFileDescriptor implements Parcelable { * should only call this once for a particular asset. */ public FileInputStream createInputStream() throws IOException { + if (isMemoryFile()) { + if (mLength > Integer.MAX_VALUE) { + throw new IOException("File length too large for a memory file: " + mLength); + } + return new AutoCloseMemoryFileInputStream(mFd, (int)mLength); + } if (mLength < 0) { return new ParcelFileDescriptor.AutoCloseInputStream(mFd); } @@ -261,6 +277,66 @@ public class AssetFileDescriptor implements Parcelable { } } + /** + * An input stream that reads from a MemoryFile and closes it when the stream is closed. + * This extends FileInputStream just because {@link #createInputStream} returns + * a FileInputStream. All the FileInputStream methods are + * overridden to use the MemoryFile instead. + */ + private static class AutoCloseMemoryFileInputStream extends FileInputStream { + private ParcelFileDescriptor mParcelFd; + private MemoryFile mFile; + private InputStream mStream; + + public AutoCloseMemoryFileInputStream(ParcelFileDescriptor fd, int length) + throws IOException { + super(fd.getFileDescriptor()); + mParcelFd = fd; + mFile = new MemoryFile(fd.getFileDescriptor(), length, "r"); + mStream = mFile.getInputStream(); + } + + @Override + public int available() throws IOException { + return mStream.available(); + } + + @Override + public void close() throws IOException { + mParcelFd.close(); // must close ParcelFileDescriptor, not just the file descriptor, + // since it could be a subclass of ParcelFileDescriptor. + // E.g. ContentResolver.ParcelFileDescriptorInner.close() releases + // a content provider + mFile.close(); // to unmap the memory file from the address space. + mStream.close(); // doesn't actually do anything + } + + @Override + public FileChannel getChannel() { + return null; + } + + @Override + public int read() throws IOException { + return mStream.read(); + } + + @Override + public int read(byte[] buffer, int offset, int count) throws IOException { + return mStream.read(buffer, offset, count); + } + + @Override + public int read(byte[] buffer) throws IOException { + return mStream.read(buffer); + } + + @Override + public long skip(long count) throws IOException { + return mStream.skip(count); + } + } + /** * An OutputStream you can create on a ParcelFileDescriptor, which will * take care of calling {@link ParcelFileDescriptor#close @@ -345,4 +421,16 @@ public class AssetFileDescriptor implements Parcelable { return new AssetFileDescriptor[size]; } }; + + /** + * Creates an AssetFileDescriptor from a memory file. + * + * @hide + */ + public static AssetFileDescriptor fromMemoryFile(MemoryFile memoryFile) + throws IOException { + ParcelFileDescriptor fd = memoryFile.getParcelFileDescriptor(); + return new AssetFileDescriptor(fd, 0, memoryFile.length()); + } + } diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java index 1c9173694d2bccb668b99d96e89df9ad2d6422c2..5c7b01fa0ccb0d5034f09dd24d984636a7385705 100644 --- a/core/java/android/content/res/AssetManager.java +++ b/core/java/android/content/res/AssetManager.java @@ -601,7 +601,7 @@ public final class AssetManager { public native final void setConfiguration(int mcc, int mnc, String locale, int orientation, int touchscreen, int density, int keyboard, int keyboardHidden, int navigation, int screenWidth, int screenHeight, - int majorVersion); + int screenLayout, int majorVersion); /** * Retrieve the resource identifier for the given resource name. diff --git a/core/java/android/content/res/CompatibilityInfo.java b/core/java/android/content/res/CompatibilityInfo.java new file mode 100644 index 0000000000000000000000000000000000000000..dfe304d1593d193d85d8936f8112e03d576a4e71 --- /dev/null +++ b/core/java/android/content/res/CompatibilityInfo.java @@ -0,0 +1,491 @@ +/* + * 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.content.res; + +import android.content.pm.ApplicationInfo; +import android.graphics.Canvas; +import android.graphics.Rect; +import android.graphics.Region; +import android.util.DisplayMetrics; +import android.util.Log; +import android.view.Gravity; +import android.view.MotionEvent; +import android.view.WindowManager; +import android.view.WindowManager.LayoutParams; + +/** + * CompatibilityInfo class keeps the information about compatibility mode that the application is + * running under. + * + * {@hide} + */ +public class CompatibilityInfo { + private static final boolean DBG = false; + private static final String TAG = "CompatibilityInfo"; + + /** default compatibility info object for compatible applications */ + public static final CompatibilityInfo DEFAULT_COMPATIBILITY_INFO = new CompatibilityInfo(); + + /** + * The default width of the screen in portrait mode. + */ + public static final int DEFAULT_PORTRAIT_WIDTH = 320; + + /** + * The default height of the screen in portrait mode. + */ + public static final int DEFAULT_PORTRAIT_HEIGHT = 480; + + /** + * The x-shift mode that controls the position of the content or the window under + * compatibility mode. + * {@see getTranslator} + * {@see Translator#mShiftMode} + */ + private static final int X_SHIFT_NONE = 0; + private static final int X_SHIFT_CONTENT = 1; + private static final int X_SHIFT_AND_CLIP_CONTENT = 2; + private static final int X_SHIFT_WINDOW = 3; + + + /** + * A compatibility flags + */ + private int mCompatibilityFlags; + + /** + * A flag mask to tell if the application needs scaling (when mApplicationScale != 1.0f) + * {@see compatibilityFlag} + */ + private static final int SCALING_REQUIRED = 1; + + /** + * A flag mask to indicates that the application can expand over the original size. + * The flag is set to true if + * 1) Application declares its expandable in manifest file using or + * 2) The screen size is same as (320 x 480) * density. + * {@see compatibilityFlag} + */ + private static final int EXPANDABLE = 2; + + /** + * A flag mask to tell if the application is configured to be expandable. This differs + * from EXPANDABLE in that the application that is not expandable will be + * marked as expandable if it runs in (320x 480) * density screen size. + */ + private static final int CONFIGURED_EXPANDABLE = 4; + + private static final int SCALING_EXPANDABLE_MASK = SCALING_REQUIRED | EXPANDABLE; + + /** + * Application's scale. + */ + public final float applicationScale; + + /** + * Application's inverted scale. + */ + public final float applicationInvertedScale; + + /** + * The flags from ApplicationInfo. + */ + public final int appFlags; + + /** + * Window size in Compatibility Mode, in real pixels. This is updated by + * {@link DisplayMetrics#updateMetrics}. + */ + private int mWidth; + private int mHeight; + + /** + * The x offset to center the window content. In X_SHIFT_WINDOW mode, the offset is added + * to the window's layout. In X_SHIFT_CONTENT/X_SHIFT_AND_CLIP_CONTENT mode, the offset + * is used to translate the Canvas. + */ + private int mXOffset; + + public CompatibilityInfo(ApplicationInfo appInfo) { + appFlags = appInfo.flags; + + if ((appInfo.flags & ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS) != 0) { + mCompatibilityFlags = EXPANDABLE | CONFIGURED_EXPANDABLE; + } + + float packageDensityScale = -1.0f; + if (appInfo.supportsDensities != null) { + int minDiff = Integer.MAX_VALUE; + for (int density : appInfo.supportsDensities) { + if (density == ApplicationInfo.ANY_DENSITY) { + packageDensityScale = 1.0f; + break; + } + int tmpDiff = Math.abs(DisplayMetrics.DEVICE_DENSITY - density); + if (tmpDiff == 0) { + packageDensityScale = 1.0f; + break; + } + // prefer higher density (appScale>1.0), unless that's only option. + if (tmpDiff < minDiff && packageDensityScale < 1.0f) { + packageDensityScale = DisplayMetrics.DEVICE_DENSITY / (float) density; + minDiff = tmpDiff; + } + } + } + if (packageDensityScale > 0.0f) { + applicationScale = packageDensityScale; + } else { + applicationScale = + DisplayMetrics.DEVICE_DENSITY / (float) DisplayMetrics.DEFAULT_DENSITY; + } + applicationInvertedScale = 1.0f / applicationScale; + if (applicationScale != 1.0f) { + mCompatibilityFlags |= SCALING_REQUIRED; + } + } + + private CompatibilityInfo(int appFlags, int compFlags, float scale, float invertedScale) { + this.appFlags = appFlags; + mCompatibilityFlags = compFlags; + applicationScale = scale; + applicationInvertedScale = invertedScale; + } + + private CompatibilityInfo() { + this(ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS + | ApplicationInfo.FLAG_SUPPORTS_NORMAL_SCREENS + | ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS, + EXPANDABLE | CONFIGURED_EXPANDABLE, + 1.0f, + 1.0f); + } + + /** + * Returns the copy of this instance. + */ + public CompatibilityInfo copy() { + CompatibilityInfo info = new CompatibilityInfo(appFlags, mCompatibilityFlags, + applicationScale, applicationInvertedScale); + info.setVisibleRect(mXOffset, mWidth, mHeight); + return info; + } + + /** + * Sets the application's visible rect in compatibility mode. + * @param xOffset the application's x offset that is added to center the content. + * @param widthPixels the application's width in real pixels on the screen. + * @param heightPixels the application's height in real pixels on the screen. + */ + public void setVisibleRect(int xOffset, int widthPixels, int heightPixels) { + this.mXOffset = xOffset; + mWidth = widthPixels; + mHeight = heightPixels; + } + + /** + * Sets expandable bit in the compatibility flag. + */ + public void setExpandable(boolean expandable) { + if (expandable) { + mCompatibilityFlags |= CompatibilityInfo.EXPANDABLE; + } else { + mCompatibilityFlags &= ~CompatibilityInfo.EXPANDABLE; + } + } + + /** + * @return true if the application is configured to be expandable. + */ + public boolean isConfiguredExpandable() { + return (mCompatibilityFlags & CompatibilityInfo.CONFIGURED_EXPANDABLE) != 0; + } + + /** + * @return true if the scaling is required + */ + public boolean isScalingRequired() { + return (mCompatibilityFlags & SCALING_REQUIRED) != 0; + } + + @Override + public String toString() { + return "CompatibilityInfo{scale=" + applicationScale + + ", compatibility flag=" + mCompatibilityFlags + "}"; + } + + /** + * Returns the translator which can translate the coordinates of the window. + * There are five different types of Translator. + * + * 1) {@link CompatibilityInfo#X_SHIFT_AND_CLIP_CONTENT} + * Shift and clip the content of the window at drawing time. Used for activities' + * main window (with no gravity). + * 2) {@link CompatibilityInfo#X_SHIFT_CONTENT} + * Shift the content of the window at drawing time. Used for windows that is created by + * an application and expected to be aligned with the application window. + * 3) {@link CompatibilityInfo#X_SHIFT_WINDOW} + * Create the window with adjusted x- coordinates. This is typically used + * in popup window, where it has to be placed relative to main window. + * 4) {@link CompatibilityInfo#X_SHIFT_NONE} + * No adjustment required, such as dialog. + * 5) Same as X_SHIFT_WINDOW, but no scaling. This is used by {@link SurfaceView}, which + * does not require scaling, but its window's location has to be adjusted. + * + * @param params the window's parameter + */ + public Translator getTranslator(WindowManager.LayoutParams params) { + if ( (mCompatibilityFlags & CompatibilityInfo.SCALING_EXPANDABLE_MASK) + == CompatibilityInfo.EXPANDABLE) { + if (DBG) Log.d(TAG, "no translation required"); + return null; + } + + if ((mCompatibilityFlags & CompatibilityInfo.EXPANDABLE) == 0) { + if ((params.flags & WindowManager.LayoutParams.FLAG_NO_COMPATIBILITY_SCALING) != 0) { + if (DBG) Log.d(TAG, "translation for surface view selected"); + return new Translator(X_SHIFT_WINDOW, false, 1.0f, 1.0f); + } else { + int shiftMode; + if (params.gravity == Gravity.NO_GRAVITY) { + // For Regular Application window + shiftMode = X_SHIFT_AND_CLIP_CONTENT; + if (DBG) Log.d(TAG, "shift and clip translator"); + } else if (params.width == WindowManager.LayoutParams.FILL_PARENT) { + // For Regular Application window + shiftMode = X_SHIFT_CONTENT; + if (DBG) Log.d(TAG, "shift content translator"); + } else if ((params.gravity & Gravity.LEFT) != 0 && params.x > 0) { + shiftMode = X_SHIFT_WINDOW; + if (DBG) Log.d(TAG, "shift window translator"); + } else { + shiftMode = X_SHIFT_NONE; + if (DBG) Log.d(TAG, "no content/window translator"); + } + return new Translator(shiftMode); + } + } else if (isScalingRequired()) { + return new Translator(); + } else { + return null; + } + } + + /** + * A helper object to translate the screen and window coordinates back and forth. + * @hide + */ + public class Translator { + final private int mShiftMode; + final public boolean scalingRequired; + final public float applicationScale; + final public float applicationInvertedScale; + + private Rect mContentInsetsBuffer = null; + private Rect mVisibleInsets = null; + + Translator(int shiftMode, boolean scalingRequired, float applicationScale, + float applicationInvertedScale) { + mShiftMode = shiftMode; + this.scalingRequired = scalingRequired; + this.applicationScale = applicationScale; + this.applicationInvertedScale = applicationInvertedScale; + } + + Translator(int shiftMode) { + this(shiftMode, + isScalingRequired(), + CompatibilityInfo.this.applicationScale, + CompatibilityInfo.this.applicationInvertedScale); + } + + Translator() { + this(X_SHIFT_NONE); + } + + /** + * Translate the screen rect to the application frame. + */ + public void translateRectInScreenToAppWinFrame(Rect rect) { + if (rect.isEmpty()) return; // skip if the window size is empty. + switch (mShiftMode) { + case X_SHIFT_AND_CLIP_CONTENT: + rect.intersect(0, 0, mWidth, mHeight); + break; + case X_SHIFT_CONTENT: + rect.intersect(0, 0, mWidth + mXOffset, mHeight); + break; + case X_SHIFT_WINDOW: + case X_SHIFT_NONE: + break; + } + if (scalingRequired) { + rect.scale(applicationInvertedScale); + } + } + + /** + * Translate the region in window to screen. + */ + public void translateRegionInWindowToScreen(Region transparentRegion) { + switch (mShiftMode) { + case X_SHIFT_AND_CLIP_CONTENT: + case X_SHIFT_CONTENT: + transparentRegion.scale(applicationScale); + transparentRegion.translate(mXOffset, 0); + break; + case X_SHIFT_WINDOW: + case X_SHIFT_NONE: + transparentRegion.scale(applicationScale); + } + } + + /** + * Apply translation to the canvas that is necessary to draw the content. + */ + public void translateCanvas(Canvas canvas) { + if (mShiftMode == X_SHIFT_CONTENT || + mShiftMode == X_SHIFT_AND_CLIP_CONTENT) { + // TODO: clear outside when rotation is changed. + + // Translate x-offset only when the content is shifted. + canvas.translate(mXOffset, 0); + } + if (scalingRequired) { + canvas.scale(applicationScale, applicationScale); + } + } + + /** + * Translate the motion event captured on screen to the application's window. + */ + public void translateEventInScreenToAppWindow(MotionEvent event) { + if (mShiftMode == X_SHIFT_CONTENT || + mShiftMode == X_SHIFT_AND_CLIP_CONTENT) { + event.translate(-mXOffset, 0); + } + if (scalingRequired) { + event.scale(applicationInvertedScale); + } + } + + /** + * Translate the window's layout parameter, from application's view to + * Screen's view. + */ + public void translateWindowLayout(WindowManager.LayoutParams params) { + switch (mShiftMode) { + case X_SHIFT_NONE: + case X_SHIFT_AND_CLIP_CONTENT: + case X_SHIFT_CONTENT: + params.scale(applicationScale); + break; + case X_SHIFT_WINDOW: + params.scale(applicationScale); + params.x += mXOffset; + break; + } + } + + /** + * Translate a Rect in application's window to screen. + */ + public void translateRectInAppWindowToScreen(Rect rect) { + // TODO Auto-generated method stub + if (scalingRequired) { + rect.scale(applicationScale); + } + switch(mShiftMode) { + case X_SHIFT_NONE: + case X_SHIFT_WINDOW: + break; + case X_SHIFT_CONTENT: + case X_SHIFT_AND_CLIP_CONTENT: + rect.offset(mXOffset, 0); + break; + } + } + + /** + * Translate a Rect in screen coordinates into the app window's coordinates. + */ + public void translateRectInScreenToAppWindow(Rect rect) { + switch (mShiftMode) { + case X_SHIFT_NONE: + case X_SHIFT_WINDOW: + break; + case X_SHIFT_CONTENT: { + rect.intersects(mXOffset, 0, rect.right, rect.bottom); + int dx = Math.min(mXOffset, rect.left); + rect.offset(-dx, 0); + break; + } + case X_SHIFT_AND_CLIP_CONTENT: { + rect.intersects(mXOffset, 0, mWidth + mXOffset, mHeight); + int dx = Math.min(mXOffset, rect.left); + rect.offset(-dx, 0); + break; + } + } + if (scalingRequired) { + rect.scale(applicationInvertedScale); + } + } + + /** + * Translate the location of the sub window. + * @param params + */ + public void translateLayoutParamsInAppWindowToScreen(LayoutParams params) { + if (scalingRequired) { + params.scale(applicationScale); + } + switch (mShiftMode) { + // the window location on these mode does not require adjustmenet. + case X_SHIFT_NONE: + case X_SHIFT_WINDOW: + break; + case X_SHIFT_CONTENT: + case X_SHIFT_AND_CLIP_CONTENT: + params.x += mXOffset; + break; + } + } + + /** + * Translate the content insets in application window to Screen. This uses + * the internal buffer for content insets to avoid extra object allocation. + */ + public Rect getTranslatedContentInsets(Rect contentInsets) { + if (mContentInsetsBuffer == null) mContentInsetsBuffer = new Rect(); + mContentInsetsBuffer.set(contentInsets); + translateRectInAppWindowToScreen(mContentInsetsBuffer); + return mContentInsetsBuffer; + } + + /** + * Translate the visible insets in application window to Screen. This uses + * the internal buffer for content insets to avoid extra object allocation. + */ + public Rect getTranslatedVisbileInsets(Rect visibleInsets) { + if (mVisibleInsets == null) mVisibleInsets = new Rect(); + mVisibleInsets.set(visibleInsets); + translateRectInAppWindowToScreen(mVisibleInsets); + return mVisibleInsets; + } + } +} diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java index bb3486c205545776e0c3bbbfe568384aa58c9f12..577aa600a19e1e576a004ee30f274bc589d75190 100644 --- a/core/java/android/content/res/Configuration.java +++ b/core/java/android/content/res/Configuration.java @@ -116,6 +116,18 @@ public final class Configuration implements Parcelable, Comparable SparseArray emptySparseArray() { - return (SparseArray) EMPTY_ARRAY; + private static LongSparseArray emptySparseArray() { + return (LongSparseArray) EMPTY_ARRAY; } /** @@ -126,26 +129,59 @@ public class Resources { */ public Resources(AssetManager assets, DisplayMetrics metrics, Configuration config) { - this(assets, metrics, config, true); + this(assets, metrics, config, (ApplicationInfo) null); } /** - * Create a resource with an additional flag for preloaded - * drawable cache. Used by {@link ActivityThread}. - * + * Creates a new Resources object with ApplicationInfo. + * + * @param assets Previously created AssetManager. + * @param metrics Current display metrics to consider when + * selecting/computing resource values. + * @param config Desired device configuration to consider when + * selecting/computing resource values (optional). + * @param appInfo this resource's application info. * @hide */ public Resources(AssetManager assets, DisplayMetrics metrics, - Configuration config, boolean usePreloadedCache) { + Configuration config, ApplicationInfo appInfo) { mAssets = assets; mConfiguration.setToDefaults(); mMetrics.setToDefaults(); + if (appInfo != null) { + mCompatibilityInfo = new CompatibilityInfo(appInfo); + if (DEBUG_CONFIG) { + Log.d(TAG, "compatibility for " + appInfo.packageName + " : " + mCompatibilityInfo); + } + } else { + mCompatibilityInfo = CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO; + } updateConfiguration(config, metrics); assets.ensureStringBlocks(); - if (usePreloadedCache) { - mPreloadedDrawables = sPreloadedDrawables; + if (mCompatibilityInfo.isScalingRequired()) { + mPreloadedDrawables = emptySparseArray(); } else { + mPreloadedDrawables = sPreloadedDrawables; + } + } + + /** + * Creates a new resources that uses the given compatibility info. Used to create + * a context for widgets using the container's compatibility info. + * {@see ApplicationContext#createPackageCotnext}. + * @hide + */ + public Resources(AssetManager assets, DisplayMetrics metrics, + Configuration config, CompatibilityInfo info) { + mAssets = assets; + mMetrics.setToDefaults(); + mCompatibilityInfo = info; + updateConfiguration(config, metrics); + assets.ensureStringBlocks(); + if (mCompatibilityInfo.isScalingRequired()) { mPreloadedDrawables = emptySparseArray(); + } else { + mPreloadedDrawables = sPreloadedDrawables; } } @@ -1238,7 +1274,7 @@ public class Resources { return array; } - + /** * Store the newly updated configuration. */ @@ -1251,6 +1287,8 @@ public class Resources { } if (metrics != null) { mMetrics.setTo(metrics); + mMetrics.updateMetrics(mCompatibilityInfo, + mConfiguration.orientation, mConfiguration.screenLayout); } mMetrics.scaledDensity = mMetrics.density * mConfiguration.fontScale; @@ -1282,7 +1320,7 @@ public class Resources { mConfiguration.touchscreen, (int)(mMetrics.density*160), mConfiguration.keyboard, keyboardHidden, mConfiguration.navigation, width, height, - sSdkVersion); + mConfiguration.screenLayout, sSdkVersion); int N = mDrawableCache.size(); if (DEBUG_CONFIG) { Log.d(TAG, "Cleaning up drawables config changes: 0x" @@ -1297,14 +1335,14 @@ public class Resources { configChanges, cs.getChangingConfigurations())) { if (DEBUG_CONFIG) { Log.d(TAG, "FLUSHING #0x" - + Integer.toHexString(mDrawableCache.keyAt(i)) + + Long.toHexString(mDrawableCache.keyAt(i)) + " / " + cs + " with changes: 0x" + Integer.toHexString(cs.getChangingConfigurations())); } mDrawableCache.setValueAt(i, null); } else if (DEBUG_CONFIG) { Log.d(TAG, "(Keeping #0x" - + Integer.toHexString(mDrawableCache.keyAt(i)) + + Long.toHexString(mDrawableCache.keyAt(i)) + " / " + cs + " with changes: 0x" + Integer.toHexString(cs.getChangingConfigurations()) + ")"); @@ -1356,6 +1394,17 @@ public class Resources { public Configuration getConfiguration() { return mConfiguration; } + + /** + * Return the compatibility mode information for the application. + * The returned object should be treated as read-only. + * + * @return compatibility info. null if the app does not require compatibility mode. + * @hide + */ + public CompatibilityInfo getCompatibilityInfo() { + return mCompatibilityInfo; + } /** * Return a resource identifier for the given resource name. A fully @@ -1624,7 +1673,7 @@ public class Resources { } } - final int key = (value.assetCookie << 24) | value.data; + final long key = (((long) value.assetCookie) << 32) | value.data; Drawable dr = getCachedDrawable(key); if (dr != null) { @@ -1704,7 +1753,7 @@ public class Resources { return dr; } - private Drawable getCachedDrawable(int key) { + private Drawable getCachedDrawable(long key) { synchronized (mTmpValue) { WeakReference wr = mDrawableCache.get(key); if (wr != null) { // we have the key @@ -1920,5 +1969,6 @@ public class Resources { updateConfiguration(null, null); mAssets.ensureStringBlocks(); mPreloadedDrawables = sPreloadedDrawables; + mCompatibilityInfo = CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO; } } diff --git a/core/java/android/database/BulkCursorToCursorAdaptor.java b/core/java/android/database/BulkCursorToCursorAdaptor.java index c26810a6248feaa11f4b771a2e082cb1789fa0e5..cf30dd9a9112dc4eb637cdc330990997d518a339 100644 --- a/core/java/android/database/BulkCursorToCursorAdaptor.java +++ b/core/java/android/database/BulkCursorToCursorAdaptor.java @@ -247,9 +247,11 @@ public final class BulkCursorToCursorAdaptor extends AbstractWindowedCursor { try { return mBulkCursor.respond(extras); } catch (RemoteException e) { - // This should never happen because the system kills processes that are using remote - // cursors when the provider process is killed. - throw new RuntimeException(e); + // the system kills processes that are using remote cursors when the provider process + // is killed, but this can still happen if this is being called from the system process, + // so, better to log and return an empty bundle. + Log.w(TAG, "respond() threw RemoteException, returning an empty bundle.", e); + return Bundle.EMPTY; } } } diff --git a/core/java/android/database/sqlite/SQLiteContentHelper.java b/core/java/android/database/sqlite/SQLiteContentHelper.java new file mode 100644 index 0000000000000000000000000000000000000000..2800d86279b1ef352961c2c16b43844ee595dd70 --- /dev/null +++ b/core/java/android/database/sqlite/SQLiteContentHelper.java @@ -0,0 +1,92 @@ +/* + * 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.database.sqlite; + +import android.content.res.AssetFileDescriptor; +import android.database.Cursor; +import android.os.MemoryFile; + +import java.io.FileNotFoundException; +import java.io.IOException; + +/** + * Some helper functions for using SQLite database to implement content providers. + * + * @hide + */ +public class SQLiteContentHelper { + + /** + * Runs an SQLite query and returns an AssetFileDescriptor for the + * blob in column 0 of the first row. If the first column does + * not contain a blob, an unspecified exception is thrown. + * + * @param db Handle to a readable database. + * @param sql SQL query, possibly with query arguments. + * @param selectionArgs Query argument values, or {@code null} for no argument. + * @return If no exception is thrown, a non-null AssetFileDescriptor is returned. + * @throws FileNotFoundException If the query returns no results or the + * value of column 0 is NULL, or if there is an error creating the + * asset file descriptor. + */ + public static AssetFileDescriptor getBlobColumnAsAssetFile(SQLiteDatabase db, String sql, + String[] selectionArgs) throws FileNotFoundException { + try { + MemoryFile file = simpleQueryForBlobMemoryFile(db, sql, selectionArgs); + if (file == null) { + throw new FileNotFoundException("No results."); + } + return AssetFileDescriptor.fromMemoryFile(file); + } catch (IOException ex) { + throw new FileNotFoundException(ex.toString()); + } + } + + /** + * Runs an SQLite query and returns a MemoryFile for the + * blob in column 0 of the first row. If the first column does + * not contain a blob, an unspecified exception is thrown. + * + * @return A memory file, or {@code null} if the query returns no results + * or the value column 0 is NULL. + * @throws IOException If there is an error creating the memory file. + */ + // TODO: make this native and use the SQLite blob API to reduce copying + private static MemoryFile simpleQueryForBlobMemoryFile(SQLiteDatabase db, String sql, + String[] selectionArgs) throws IOException { + Cursor cursor = db.rawQuery(sql, selectionArgs); + if (cursor == null) { + return null; + } + try { + if (!cursor.moveToFirst()) { + return null; + } + byte[] bytes = cursor.getBlob(0); + if (bytes == null) { + return null; + } + MemoryFile file = new MemoryFile(null, bytes.length); + file.writeBytes(bytes, 0, 0, bytes.length); + file.deactivate(); + return file; + } finally { + cursor.close(); + } + } + +} diff --git a/core/java/android/database/sqlite/SQLiteQueryBuilder.java b/core/java/android/database/sqlite/SQLiteQueryBuilder.java index ab7c827cc7c86e0853f0dba203b785e149a981f8..8a639196ee7a414ad93af7571cf8b6f0747ee5b1 100644 --- a/core/java/android/database/sqlite/SQLiteQueryBuilder.java +++ b/core/java/android/database/sqlite/SQLiteQueryBuilder.java @@ -18,16 +18,15 @@ package android.database.sqlite; import android.database.Cursor; import android.database.DatabaseUtils; -import android.database.sqlite.SQLiteDatabase; import android.provider.BaseColumns; import android.text.TextUtils; -import android.util.Config; import android.util.Log; import java.util.Iterator; import java.util.Map; import java.util.Set; import java.util.Map.Entry; +import java.util.regex.Pattern; /** * This is a convience class that helps build SQL queries to be sent to @@ -36,10 +35,12 @@ import java.util.Map.Entry; public class SQLiteQueryBuilder { private static final String TAG = "SQLiteQueryBuilder"; + private static final Pattern sLimitPattern = + Pattern.compile("\\s*\\d+\\s*(,\\s*\\d+\\s*)?"); private Map mProjectionMap = null; private String mTables = ""; - private StringBuilder mWhereClause = new StringBuilder(64); + private final StringBuilder mWhereClause = new StringBuilder(64); private boolean mDistinct; private SQLiteDatabase.CursorFactory mFactory; @@ -169,6 +170,9 @@ public class SQLiteQueryBuilder throw new IllegalArgumentException( "HAVING clauses are only permitted when using a groupBy clause"); } + if (!TextUtils.isEmpty(limit) && !sLimitPattern.matcher(limit).matches()) { + throw new IllegalArgumentException("invalid LIMIT clauses:" + limit); + } StringBuilder query = new StringBuilder(120); @@ -187,7 +191,7 @@ public class SQLiteQueryBuilder appendClause(query, " GROUP BY ", groupBy); appendClause(query, " HAVING ", having); appendClause(query, " ORDER BY ", orderBy); - appendClauseEscapeClause(query, " LIMIT ", limit); + appendClause(query, " LIMIT ", limit); return query.toString(); } diff --git a/core/java/android/gesture/Gesture.java b/core/java/android/gesture/Gesture.java new file mode 100755 index 0000000000000000000000000000000000000000..2262477a93c4fcd9d88194cef80afa63bfd14293 --- /dev/null +++ b/core/java/android/gesture/Gesture.java @@ -0,0 +1,341 @@ +/* + * Copyright (C) 2008-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.gesture; + +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.RectF; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Log; + +import java.io.IOException; +import java.io.DataOutputStream; +import java.io.DataInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ByteArrayInputStream; +import java.util.ArrayList; + +/** + * A gesture can have a single or multiple strokes + */ + +public class Gesture implements Parcelable { + private static final long GESTURE_ID_BASE = System.currentTimeMillis(); + + private static final int BITMAP_RENDERING_WIDTH = 2; + + private static final boolean BITMAP_RENDERING_ANTIALIAS = true; + private static final boolean BITMAP_RENDERING_DITHER = true; + + private static int sGestureCount = 0; + + private final RectF mBoundingBox = new RectF(); + + // the same as its instance ID + private long mGestureID; + + private final ArrayList mStrokes = new ArrayList(); + + public Gesture() { + mGestureID = GESTURE_ID_BASE + sGestureCount++; + } + + void recycle() { + mStrokes.clear(); + mBoundingBox.setEmpty(); + } + + /** + * @return all the strokes of the gesture + */ + public ArrayList getStrokes() { + return mStrokes; + } + + /** + * @return the number of strokes included by this gesture + */ + public int getStrokesCount() { + return mStrokes.size(); + } + + /** + * Add a stroke to the gesture + * + * @param stroke + */ + public void addStroke(GestureStroke stroke) { + mStrokes.add(stroke); + mBoundingBox.union(stroke.boundingBox); + } + + /** + * Get the total length of the gesture. When there are multiple strokes in + * the gesture, this returns the sum of the lengths of all the strokes + * + * @return the length of the gesture + */ + public float getLength() { + int len = 0; + final ArrayList strokes = mStrokes; + final int count = strokes.size(); + + for (int i = 0; i < count; i++) { + len += strokes.get(i).length; + } + + return len; + } + + /** + * @return the bounding box of the gesture + */ + public RectF getBoundingBox() { + return mBoundingBox; + } + + public Path toPath() { + return toPath(null); + } + + public Path toPath(Path path) { + if (path == null) path = new Path(); + + final ArrayList strokes = mStrokes; + final int count = strokes.size(); + + for (int i = 0; i < count; i++) { + path.addPath(strokes.get(i).getPath()); + } + + return path; + } + + public Path toPath(int width, int height, int edge, int numSample) { + return toPath(null, width, height, edge, numSample); + } + + public Path toPath(Path path, int width, int height, int edge, int numSample) { + if (path == null) path = new Path(); + + final ArrayList strokes = mStrokes; + final int count = strokes.size(); + + for (int i = 0; i < count; i++) { + path.addPath(strokes.get(i).toPath(width - 2 * edge, height - 2 * edge, numSample)); + } + + return path; + } + + /** + * Set the id of the gesture + * + * @param id + */ + void setID(long id) { + mGestureID = id; + } + + /** + * @return the id of the gesture + */ + public long getID() { + return mGestureID; + } + + /** + * draw the gesture + * + * @param canvas + */ + void draw(Canvas canvas, Paint paint) { + final ArrayList strokes = mStrokes; + final int count = strokes.size(); + + for (int i = 0; i < count; i++) { + strokes.get(i).draw(canvas, paint); + } + } + + /** + * Create a bitmap of the gesture with a transparent background + * + * @param width width of the target bitmap + * @param height height of the target bitmap + * @param edge the edge + * @param numSample + * @param color + * @return the bitmap + */ + public Bitmap toBitmap(int width, int height, int edge, int numSample, int color) { + final Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); + final Canvas canvas = new Canvas(bitmap); + + canvas.translate(edge, edge); + + final Paint paint = new Paint(); + paint.setAntiAlias(BITMAP_RENDERING_ANTIALIAS); + paint.setDither(BITMAP_RENDERING_DITHER); + paint.setColor(color); + paint.setStyle(Paint.Style.STROKE); + paint.setStrokeJoin(Paint.Join.ROUND); + paint.setStrokeCap(Paint.Cap.ROUND); + paint.setStrokeWidth(BITMAP_RENDERING_WIDTH); + + final ArrayList strokes = mStrokes; + final int count = strokes.size(); + + for (int i = 0; i < count; i++) { + Path path = strokes.get(i).toPath(width - 2 * edge, height - 2 * edge, numSample); + canvas.drawPath(path, paint); + } + + return bitmap; + } + + /** + * Create a bitmap of the gesture with a transparent background + * + * @param width + * @param height + * @param inset + * @param color + * @return the bitmap + */ + public Bitmap toBitmap(int width, int height, int inset, int color) { + final Bitmap bitmap = Bitmap.createBitmap(width, height, + Bitmap.Config.ARGB_8888); + final Canvas canvas = new Canvas(bitmap); + + final Paint paint = new Paint(); + paint.setAntiAlias(BITMAP_RENDERING_ANTIALIAS); + paint.setDither(BITMAP_RENDERING_DITHER); + paint.setColor(color); + paint.setStyle(Paint.Style.STROKE); + paint.setStrokeJoin(Paint.Join.ROUND); + paint.setStrokeCap(Paint.Cap.ROUND); + paint.setStrokeWidth(BITMAP_RENDERING_WIDTH); + + final Path path = toPath(); + final RectF bounds = new RectF(); + path.computeBounds(bounds, true); + + final float sx = (width - 2 * inset) / bounds.width(); + final float sy = (height - 2 * inset) / bounds.height(); + final float scale = sx > sy ? sy : sx; + paint.setStrokeWidth(2.0f / scale); + + path.offset(-bounds.left + (width - bounds.width() * scale) / 2.0f, + -bounds.top + (height - bounds.height() * scale) / 2.0f); + + canvas.translate(inset, inset); + canvas.scale(scale, scale); + + canvas.drawPath(path, paint); + + return bitmap; + } + + void serialize(DataOutputStream out) throws IOException { + final ArrayList strokes = mStrokes; + final int count = strokes.size(); + + // Write gesture ID + out.writeLong(mGestureID); + // Write number of strokes + out.writeInt(count); + + for (int i = 0; i < count; i++) { + strokes.get(i).serialize(out); + } + } + + static Gesture deserialize(DataInputStream in) throws IOException { + final Gesture gesture = new Gesture(); + + // Gesture ID + gesture.mGestureID = in.readLong(); + // Number of strokes + final int count = in.readInt(); + + for (int i = 0; i < count; i++) { + gesture.addStroke(GestureStroke.deserialize(in)); + } + + return gesture; + } + + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + public Gesture createFromParcel(Parcel in) { + Gesture gesture = null; + final long gestureID = in.readLong(); + + final DataInputStream inStream = new DataInputStream( + new ByteArrayInputStream(in.createByteArray())); + + try { + gesture = deserialize(inStream); + } catch (IOException e) { + Log.e(GestureConstants.LOG_TAG, "Error reading Gesture from parcel:", e); + } finally { + GestureUtilities.closeStream(inStream); + } + + if (gesture != null) { + gesture.mGestureID = gestureID; + } + + return gesture; + } + + public Gesture[] newArray(int size) { + return new Gesture[size]; + } + }; + + public void writeToParcel(Parcel out, int flags) { + out.writeLong(mGestureID); + + boolean result = false; + final ByteArrayOutputStream byteStream = + new ByteArrayOutputStream(GestureConstants.IO_BUFFER_SIZE); + final DataOutputStream outStream = new DataOutputStream(byteStream); + + try { + serialize(outStream); + result = true; + } catch (IOException e) { + Log.e(GestureConstants.LOG_TAG, "Error writing Gesture to parcel:", e); + } finally { + GestureUtilities.closeStream(outStream); + GestureUtilities.closeStream(byteStream); + } + + if (result) { + out.writeByteArray(byteStream.toByteArray()); + } + } + + public int describeContents() { + return 0; + } +} + diff --git a/core/java/android/gesture/GestureConstants.java b/core/java/android/gesture/GestureConstants.java new file mode 100644 index 0000000000000000000000000000000000000000..230db0c00c50162cc7081213bc5bdb4b7a61ec2a --- /dev/null +++ b/core/java/android/gesture/GestureConstants.java @@ -0,0 +1,26 @@ +/* + * 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.gesture; + +interface GestureConstants { + static final int STROKE_STRING_BUFFER_SIZE = 1024; + static final int STROKE_POINT_BUFFER_SIZE = 100; // number of points + + static final int IO_BUFFER_SIZE = 32 * 1024; // 32K + + static final String LOG_TAG = "Gestures"; +} diff --git a/core/java/android/gesture/GestureLibraries.java b/core/java/android/gesture/GestureLibraries.java new file mode 100644 index 0000000000000000000000000000000000000000..6d6c156def9a432f58b99fa4d301e28625c77095 --- /dev/null +++ b/core/java/android/gesture/GestureLibraries.java @@ -0,0 +1,143 @@ +/* + * 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.gesture; + +import android.util.Log; +import static android.gesture.GestureConstants.*; +import android.content.Context; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.FileInputStream; +import java.io.InputStream; +import java.lang.ref.WeakReference; + +public final class GestureLibraries { + private GestureLibraries() { + } + + public static GestureLibrary fromFile(String path) { + return fromFile(new File(path)); + } + + public static GestureLibrary fromFile(File path) { + return new FileGestureLibrary(path); + } + + public static GestureLibrary fromPrivateFile(Context context, String name) { + return fromFile(context.getFileStreamPath(name)); + } + + public static GestureLibrary fromRawResource(Context context, int resourceId) { + return new ResourceGestureLibrary(context, resourceId); + } + + private static class FileGestureLibrary extends GestureLibrary { + private final File mPath; + + public FileGestureLibrary(File path) { + mPath = path; + } + + @Override + public boolean isReadOnly() { + return !mPath.canWrite(); + } + + public boolean save() { + if (!mStore.hasChanged()) return true; + + final File file = mPath; + + final File parentFile = file.getParentFile(); + if (!parentFile.exists()) { + if (!parentFile.mkdirs()) { + return false; + } + } + + boolean result = false; + try { + //noinspection ResultOfMethodCallIgnored + file.createNewFile(); + mStore.save(new FileOutputStream(file), true); + result = true; + } catch (FileNotFoundException e) { + Log.d(LOG_TAG, "Could not save the gesture library in " + mPath, e); + } catch (IOException e) { + Log.d(LOG_TAG, "Could not save the gesture library in " + mPath, e); + } + + return result; + } + + public boolean load() { + boolean result = false; + final File file = mPath; + if (file.exists() && file.canRead()) { + try { + mStore.load(new FileInputStream(file), true); + result = true; + } catch (FileNotFoundException e) { + Log.d(LOG_TAG, "Could not load the gesture library from " + mPath, e); + } catch (IOException e) { + Log.d(LOG_TAG, "Could not load the gesture library from " + mPath, e); + } + } + + return result; + } + } + + private static class ResourceGestureLibrary extends GestureLibrary { + private final WeakReference mContext; + private final int mResourceId; + + public ResourceGestureLibrary(Context context, int resourceId) { + mContext = new WeakReference(context); + mResourceId = resourceId; + } + + @Override + public boolean isReadOnly() { + return true; + } + + public boolean save() { + return false; + } + + public boolean load() { + boolean result = false; + final Context context = mContext.get(); + if (context != null) { + final InputStream in = context.getResources().openRawResource(mResourceId); + try { + mStore.load(in, true); + result = true; + } catch (IOException e) { + Log.d(LOG_TAG, "Could not load the gesture library from raw resource " + + context.getResources().getResourceName(mResourceId), e); + } + } + + return result; + } + } +} diff --git a/core/java/android/gesture/GestureLibrary.java b/core/java/android/gesture/GestureLibrary.java new file mode 100644 index 0000000000000000000000000000000000000000..a29c2c83cfbf030279eb62ff2572e0d283607b49 --- /dev/null +++ b/core/java/android/gesture/GestureLibrary.java @@ -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. + */ + + +package android.gesture; + +import java.util.Set; +import java.util.ArrayList; + +public abstract class GestureLibrary { + protected final GestureStore mStore; + + protected GestureLibrary() { + mStore = new GestureStore(); + } + + public abstract boolean save(); + + public abstract boolean load(); + + public boolean isReadOnly() { + return false; + } + + public Learner getLearner() { + return mStore.getLearner(); + } + + public void setOrientationStyle(int style) { + mStore.setOrientationStyle(style); + } + + public int getOrientationStyle() { + return mStore.getOrientationStyle(); + } + + public void setSequenceType(int type) { + mStore.setSequenceType(type); + } + + public int getSequenceType() { + return mStore.getSequenceType(); + } + + public Set getGestureEntries() { + return mStore.getGestureEntries(); + } + + public ArrayList recognize(Gesture gesture) { + return mStore.recognize(gesture); + } + + public void addGesture(String entryName, Gesture gesture) { + mStore.addGesture(entryName, gesture); + } + + public void removeGesture(String entryName, Gesture gesture) { + mStore.removeGesture(entryName, gesture); + } + + public void removeEntry(String entryName) { + mStore.removeEntry(entryName); + } + + public ArrayList getGestures(String entryName) { + return mStore.getGestures(entryName); + } +} diff --git a/core/java/android/gesture/GestureOverlayView.java b/core/java/android/gesture/GestureOverlayView.java new file mode 100755 index 0000000000000000000000000000000000000000..5bfdcc4a5578c6abe4f409838aa937578ddc3a0c --- /dev/null +++ b/core/java/android/gesture/GestureOverlayView.java @@ -0,0 +1,793 @@ +/* + * 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.gesture; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.Rect; +import android.graphics.RectF; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.animation.AnimationUtils; +import android.view.animation.AccelerateDecelerateInterpolator; +import android.widget.FrameLayout; +import android.os.SystemClock; +import com.android.internal.R; + +import java.util.ArrayList; + +/** + * A transparent overlay for gesture input that can be placed on top of other + * widgets or contain other widgets. + * + * @attr ref android.R.styleable#GestureOverlayView_eventsInterceptionEnabled + * @attr ref android.R.styleable#GestureOverlayView_fadeDuration + * @attr ref android.R.styleable#GestureOverlayView_fadeOffset + * @attr ref android.R.styleable#GestureOverlayView_fadeEnabled + * @attr ref android.R.styleable#GestureOverlayView_gestureStrokeWidth + * @attr ref android.R.styleable#GestureOverlayView_gestureStrokeAngleThreshold + * @attr ref android.R.styleable#GestureOverlayView_gestureStrokeLengthThreshold + * @attr ref android.R.styleable#GestureOverlayView_gestureStrokeSquarenessThreshold + * @attr ref android.R.styleable#GestureOverlayView_gestureStrokeType + * @attr ref android.R.styleable#GestureOverlayView_gestureColor + * @attr ref android.R.styleable#GestureOverlayView_orientation + * @attr ref android.R.styleable#GestureOverlayView_uncertainGestureColor + */ +public class GestureOverlayView extends FrameLayout { + public static final int GESTURE_STROKE_TYPE_SINGLE = 0; + public static final int GESTURE_STROKE_TYPE_MULTIPLE = 1; + + public static final int ORIENTATION_HORIZONTAL = 0; + public static final int ORIENTATION_VERTICAL = 1; + + private static final int FADE_ANIMATION_RATE = 16; + private static final boolean GESTURE_RENDERING_ANTIALIAS = true; + private static final boolean DITHER_FLAG = true; + + private final Paint mGesturePaint = new Paint(); + + private long mFadeDuration = 150; + private long mFadeOffset = 420; + private long mFadingStart; + private boolean mFadingHasStarted; + private boolean mFadeEnabled = true; + + private int mCurrentColor; + private int mCertainGestureColor = 0xFFFFFF00; + private int mUncertainGestureColor = 0x48FFFF00; + private float mGestureStrokeWidth = 12.0f; + private int mInvalidateExtraBorder = 10; + + private int mGestureStrokeType = GESTURE_STROKE_TYPE_SINGLE; + private float mGestureStrokeLengthThreshold = 50.0f; + private float mGestureStrokeSquarenessTreshold = 0.275f; + private float mGestureStrokeAngleThreshold = 40.0f; + + private int mOrientation = ORIENTATION_VERTICAL; + + private final Rect mInvalidRect = new Rect(); + private final Path mPath = new Path(); + private boolean mGestureVisible = true; + + private float mX; + private float mY; + + private float mCurveEndX; + private float mCurveEndY; + + private float mTotalLength; + private boolean mIsGesturing = false; + private boolean mPreviousWasGesturing = false; + private boolean mInterceptEvents = true; + private boolean mIsListeningForGestures; + private boolean mResetGesture; + + // current gesture + private Gesture mCurrentGesture; + private final ArrayList mStrokeBuffer = new ArrayList(100); + + // TODO: Make this a list of WeakReferences + private final ArrayList mOnGestureListeners = + new ArrayList(); + // TODO: Make this a list of WeakReferences + private final ArrayList mOnGesturePerformedListeners = + new ArrayList(); + // TODO: Make this a list of WeakReferences + private final ArrayList mOnGesturingListeners = + new ArrayList(); + + private boolean mHandleGestureActions; + + // fading out effect + private boolean mIsFadingOut = false; + private float mFadingAlpha = 1.0f; + private final AccelerateDecelerateInterpolator mInterpolator = + new AccelerateDecelerateInterpolator(); + + private final FadeOutRunnable mFadingOut = new FadeOutRunnable(); + + public GestureOverlayView(Context context) { + super(context); + init(); + } + + public GestureOverlayView(Context context, AttributeSet attrs) { + this(context, attrs, com.android.internal.R.attr.gestureOverlayViewStyle); + } + + public GestureOverlayView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + + TypedArray a = context.obtainStyledAttributes(attrs, + R.styleable.GestureOverlayView, defStyle, 0); + + mGestureStrokeWidth = a.getFloat(R.styleable.GestureOverlayView_gestureStrokeWidth, + mGestureStrokeWidth); + mInvalidateExtraBorder = Math.max(1, ((int) mGestureStrokeWidth) - 1); + mCertainGestureColor = a.getColor(R.styleable.GestureOverlayView_gestureColor, + mCertainGestureColor); + mUncertainGestureColor = a.getColor(R.styleable.GestureOverlayView_uncertainGestureColor, + mUncertainGestureColor); + mFadeDuration = a.getInt(R.styleable.GestureOverlayView_fadeDuration, (int) mFadeDuration); + mFadeOffset = a.getInt(R.styleable.GestureOverlayView_fadeOffset, (int) mFadeOffset); + mGestureStrokeType = a.getInt(R.styleable.GestureOverlayView_gestureStrokeType, + mGestureStrokeType); + mGestureStrokeLengthThreshold = a.getFloat( + R.styleable.GestureOverlayView_gestureStrokeLengthThreshold, + mGestureStrokeLengthThreshold); + mGestureStrokeAngleThreshold = a.getFloat( + R.styleable.GestureOverlayView_gestureStrokeAngleThreshold, + mGestureStrokeAngleThreshold); + mGestureStrokeSquarenessTreshold = a.getFloat( + R.styleable.GestureOverlayView_gestureStrokeSquarenessThreshold, + mGestureStrokeSquarenessTreshold); + mInterceptEvents = a.getBoolean(R.styleable.GestureOverlayView_eventsInterceptionEnabled, + mInterceptEvents); + mFadeEnabled = a.getBoolean(R.styleable.GestureOverlayView_fadeEnabled, + mFadeEnabled); + mOrientation = a.getInt(R.styleable.GestureOverlayView_orientation, mOrientation); + + a.recycle(); + + init(); + } + + private void init() { + setWillNotDraw(false); + + final Paint gesturePaint = mGesturePaint; + gesturePaint.setAntiAlias(GESTURE_RENDERING_ANTIALIAS); + gesturePaint.setColor(mCertainGestureColor); + gesturePaint.setStyle(Paint.Style.STROKE); + gesturePaint.setStrokeJoin(Paint.Join.ROUND); + gesturePaint.setStrokeCap(Paint.Cap.ROUND); + gesturePaint.setStrokeWidth(mGestureStrokeWidth); + gesturePaint.setDither(DITHER_FLAG); + + mCurrentColor = mCertainGestureColor; + setPaintAlpha(255); + } + + public ArrayList getCurrentStroke() { + return mStrokeBuffer; + } + + public int getOrientation() { + return mOrientation; + } + + public void setOrientation(int orientation) { + mOrientation = orientation; + } + + public void setGestureColor(int color) { + mCertainGestureColor = color; + } + + public void setUncertainGestureColor(int color) { + mUncertainGestureColor = color; + } + + public int getUncertainGestureColor() { + return mUncertainGestureColor; + } + + public int getGestureColor() { + return mCertainGestureColor; + } + + public float getGestureStrokeWidth() { + return mGestureStrokeWidth; + } + + public void setGestureStrokeWidth(float gestureStrokeWidth) { + mGestureStrokeWidth = gestureStrokeWidth; + mInvalidateExtraBorder = Math.max(1, ((int) gestureStrokeWidth) - 1); + mGesturePaint.setStrokeWidth(gestureStrokeWidth); + } + + public int getGestureStrokeType() { + return mGestureStrokeType; + } + + public void setGestureStrokeType(int gestureStrokeType) { + mGestureStrokeType = gestureStrokeType; + } + + public float getGestureStrokeLengthThreshold() { + return mGestureStrokeLengthThreshold; + } + + public void setGestureStrokeLengthThreshold(float gestureStrokeLengthThreshold) { + mGestureStrokeLengthThreshold = gestureStrokeLengthThreshold; + } + + public float getGestureStrokeSquarenessTreshold() { + return mGestureStrokeSquarenessTreshold; + } + + public void setGestureStrokeSquarenessTreshold(float gestureStrokeSquarenessTreshold) { + mGestureStrokeSquarenessTreshold = gestureStrokeSquarenessTreshold; + } + + public float getGestureStrokeAngleThreshold() { + return mGestureStrokeAngleThreshold; + } + + public void setGestureStrokeAngleThreshold(float gestureStrokeAngleThreshold) { + mGestureStrokeAngleThreshold = gestureStrokeAngleThreshold; + } + + public boolean isEventsInterceptionEnabled() { + return mInterceptEvents; + } + + public void setEventsInterceptionEnabled(boolean enabled) { + mInterceptEvents = enabled; + } + + public boolean isFadeEnabled() { + return mFadeEnabled; + } + + public void setFadeEnabled(boolean fadeEnabled) { + mFadeEnabled = fadeEnabled; + } + + public Gesture getGesture() { + return mCurrentGesture; + } + + public void setGesture(Gesture gesture) { + if (mCurrentGesture != null) { + clear(false); + } + + setCurrentColor(mCertainGestureColor); + mCurrentGesture = gesture; + + final Path path = mCurrentGesture.toPath(); + final RectF bounds = new RectF(); + path.computeBounds(bounds, true); + + // TODO: The path should also be scaled to fit inside this view + mPath.rewind(); + mPath.addPath(path, -bounds.left + (getWidth() - bounds.width()) / 2.0f, + -bounds.top + (getHeight() - bounds.height()) / 2.0f); + + mResetGesture = true; + + invalidate(); + } + + public Path getGesturePath() { + return mPath; + } + + public Path getGesturePath(Path path) { + path.set(mPath); + return path; + } + + public boolean isGestureVisible() { + return mGestureVisible; + } + + public void setGestureVisible(boolean visible) { + mGestureVisible = visible; + } + + public long getFadeOffset() { + return mFadeOffset; + } + + public void setFadeOffset(long fadeOffset) { + mFadeOffset = fadeOffset; + } + + public void addOnGestureListener(OnGestureListener listener) { + mOnGestureListeners.add(listener); + } + + public void removeOnGestureListener(OnGestureListener listener) { + mOnGestureListeners.remove(listener); + } + + public void removeAllOnGestureListeners() { + mOnGestureListeners.clear(); + } + + public void addOnGesturePerformedListener(OnGesturePerformedListener listener) { + mOnGesturePerformedListeners.add(listener); + if (mOnGesturePerformedListeners.size() > 0) { + mHandleGestureActions = true; + } + } + + public void removeOnGesturePerformedListener(OnGesturePerformedListener listener) { + mOnGesturePerformedListeners.remove(listener); + if (mOnGesturePerformedListeners.size() <= 0) { + mHandleGestureActions = false; + } + } + + public void removeAllOnGesturePerformedListeners() { + mOnGesturePerformedListeners.clear(); + mHandleGestureActions = false; + } + + public void addOnGesturingListener(OnGesturingListener listener) { + mOnGesturingListeners.add(listener); + } + + public void removeOnGesturingListener(OnGesturingListener listener) { + mOnGesturingListeners.remove(listener); + } + + public void removeAllOnGesturingListeners() { + mOnGesturingListeners.clear(); + } + + public boolean isGesturing() { + return mIsGesturing; + } + + private void setCurrentColor(int color) { + mCurrentColor = color; + if (mFadingHasStarted) { + setPaintAlpha((int) (255 * mFadingAlpha)); + } else { + setPaintAlpha(255); + } + invalidate(); + } + + /** + * @hide + */ + public Paint getGesturePaint() { + return mGesturePaint; + } + + @Override + public void draw(Canvas canvas) { + super.draw(canvas); + + if (mCurrentGesture != null && mGestureVisible) { + canvas.drawPath(mPath, mGesturePaint); + } + } + + private void setPaintAlpha(int alpha) { + alpha += alpha >> 7; + final int baseAlpha = mCurrentColor >>> 24; + final int useAlpha = baseAlpha * alpha >> 8; + mGesturePaint.setColor((mCurrentColor << 8 >>> 8) | (useAlpha << 24)); + } + + public void clear(boolean animated) { + clear(animated, false, true); + } + + private void clear(boolean animated, boolean fireActionPerformed, boolean immediate) { + setPaintAlpha(255); + removeCallbacks(mFadingOut); + mResetGesture = false; + mFadingOut.fireActionPerformed = fireActionPerformed; + mFadingOut.resetMultipleStrokes = false; + + if (animated && mCurrentGesture != null) { + mFadingAlpha = 1.0f; + mIsFadingOut = true; + mFadingHasStarted = false; + mFadingStart = AnimationUtils.currentAnimationTimeMillis() + mFadeOffset; + + postDelayed(mFadingOut, mFadeOffset); + } else { + mFadingAlpha = 1.0f; + mIsFadingOut = false; + mFadingHasStarted = false; + + if (immediate) { + mCurrentGesture = null; + mPath.rewind(); + invalidate(); + } else if (fireActionPerformed) { + postDelayed(mFadingOut, mFadeOffset); + } else if (mGestureStrokeType == GESTURE_STROKE_TYPE_MULTIPLE) { + mFadingOut.resetMultipleStrokes = true; + postDelayed(mFadingOut, mFadeOffset); + } else { + mCurrentGesture = null; + mPath.rewind(); + invalidate(); + } + } + } + + public void cancelClearAnimation() { + setPaintAlpha(255); + mIsFadingOut = false; + mFadingHasStarted = false; + removeCallbacks(mFadingOut); + mPath.rewind(); + mCurrentGesture = null; + } + + public void cancelGesture() { + mIsListeningForGestures = false; + + // add the stroke to the current gesture + mCurrentGesture.addStroke(new GestureStroke(mStrokeBuffer)); + + // pass the event to handlers + final long now = SystemClock.uptimeMillis(); + final MotionEvent event = MotionEvent.obtain(now, now, + MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0); + + final ArrayList listeners = mOnGestureListeners; + int count = listeners.size(); + for (int i = 0; i < count; i++) { + listeners.get(i).onGestureCancelled(this, event); + } + + event.recycle(); + + clear(false); + mIsGesturing = false; + mPreviousWasGesturing = false; + mStrokeBuffer.clear(); + + final ArrayList otherListeners = mOnGesturingListeners; + count = otherListeners.size(); + for (int i = 0; i < count; i++) { + otherListeners.get(i).onGesturingEnded(this); + } + } + + @Override + protected void onDetachedFromWindow() { + cancelClearAnimation(); + } + + @Override + public boolean dispatchTouchEvent(MotionEvent event) { + if (isEnabled()) { + final boolean cancelDispatch = (mIsGesturing || (mCurrentGesture != null && + mCurrentGesture.getStrokesCount() > 0 && mPreviousWasGesturing)) && + mInterceptEvents; + + processEvent(event); + + if (cancelDispatch) { + event.setAction(MotionEvent.ACTION_CANCEL); + } + + super.dispatchTouchEvent(event); + + return true; + } + + return super.dispatchTouchEvent(event); + } + + private boolean processEvent(MotionEvent event) { + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + touchDown(event); + invalidate(); + return true; + case MotionEvent.ACTION_MOVE: + if (mIsListeningForGestures) { + Rect rect = touchMove(event); + if (rect != null) { + invalidate(rect); + } + return true; + } + break; + case MotionEvent.ACTION_UP: + if (mIsListeningForGestures) { + touchUp(event, false); + invalidate(); + return true; + } + break; + case MotionEvent.ACTION_CANCEL: + if (mIsListeningForGestures) { + touchUp(event, true); + invalidate(); + return true; + } + } + + return false; + } + + private void touchDown(MotionEvent event) { + mIsListeningForGestures = true; + + float x = event.getX(); + float y = event.getY(); + + mX = x; + mY = y; + + mTotalLength = 0; + mIsGesturing = false; + + if (mGestureStrokeType == GESTURE_STROKE_TYPE_SINGLE || mResetGesture) { + if (mHandleGestureActions) setCurrentColor(mUncertainGestureColor); + mResetGesture = false; + mCurrentGesture = null; + mPath.rewind(); + } else if (mCurrentGesture == null || mCurrentGesture.getStrokesCount() == 0) { + if (mHandleGestureActions) setCurrentColor(mUncertainGestureColor); + } + + // if there is fading out going on, stop it. + if (mFadingHasStarted) { + cancelClearAnimation(); + } else if (mIsFadingOut) { + setPaintAlpha(255); + mIsFadingOut = false; + mFadingHasStarted = false; + removeCallbacks(mFadingOut); + } + + if (mCurrentGesture == null) { + mCurrentGesture = new Gesture(); + } + + mStrokeBuffer.add(new GesturePoint(x, y, event.getEventTime())); + mPath.moveTo(x, y); + + final int border = mInvalidateExtraBorder; + mInvalidRect.set((int) x - border, (int) y - border, (int) x + border, (int) y + border); + + mCurveEndX = x; + mCurveEndY = y; + + // pass the event to handlers + final ArrayList listeners = mOnGestureListeners; + final int count = listeners.size(); + for (int i = 0; i < count; i++) { + listeners.get(i).onGestureStarted(this, event); + } + } + + private Rect touchMove(MotionEvent event) { + Rect areaToRefresh = null; + + final float x = event.getX(); + final float y = event.getY(); + + final float previousX = mX; + final float previousY = mY; + + final float dx = Math.abs(x - previousX); + final float dy = Math.abs(y - previousY); + + if (dx >= GestureStroke.TOUCH_TOLERANCE || dy >= GestureStroke.TOUCH_TOLERANCE) { + areaToRefresh = mInvalidRect; + + // start with the curve end + final int border = mInvalidateExtraBorder; + areaToRefresh.set((int) mCurveEndX - border, (int) mCurveEndY - border, + (int) mCurveEndX + border, (int) mCurveEndY + border); + + float cX = mCurveEndX = (x + previousX) / 2; + float cY = mCurveEndY = (y + previousY) / 2; + + mPath.quadTo(previousX, previousY, cX, cY); + + // union with the control point of the new curve + areaToRefresh.union((int) previousX - border, (int) previousY - border, + (int) previousX + border, (int) previousY + border); + + // union with the end point of the new curve + areaToRefresh.union((int) cX - border, (int) cY - border, + (int) cX + border, (int) cY + border); + + mX = x; + mY = y; + + mStrokeBuffer.add(new GesturePoint(x, y, event.getEventTime())); + + if (mHandleGestureActions && !mIsGesturing) { + mTotalLength += (float) Math.sqrt(dx * dx + dy * dy); + + if (mTotalLength > mGestureStrokeLengthThreshold) { + final OrientedBoundingBox box = + GestureUtilities.computeOrientedBoundingBox(mStrokeBuffer); + + float angle = Math.abs(box.orientation); + if (angle > 90) { + angle = 180 - angle; + } + + if (box.squareness > mGestureStrokeSquarenessTreshold || + (mOrientation == ORIENTATION_VERTICAL ? + angle < mGestureStrokeAngleThreshold : + angle > mGestureStrokeAngleThreshold)) { + + mIsGesturing = true; + setCurrentColor(mCertainGestureColor); + + final ArrayList listeners = mOnGesturingListeners; + int count = listeners.size(); + for (int i = 0; i < count; i++) { + listeners.get(i).onGesturingStarted(this); + } + } + } + } + + // pass the event to handlers + final ArrayList listeners = mOnGestureListeners; + final int count = listeners.size(); + for (int i = 0; i < count; i++) { + listeners.get(i).onGesture(this, event); + } + } + + return areaToRefresh; + } + + private void touchUp(MotionEvent event, boolean cancel) { + mIsListeningForGestures = false; + + // A gesture wasn't started or was cancelled + if (mCurrentGesture != null) { + // add the stroke to the current gesture + mCurrentGesture.addStroke(new GestureStroke(mStrokeBuffer)); + + if (!cancel) { + // pass the event to handlers + final ArrayList listeners = mOnGestureListeners; + int count = listeners.size(); + for (int i = 0; i < count; i++) { + listeners.get(i).onGestureEnded(this, event); + } + + clear(mHandleGestureActions && mFadeEnabled, mHandleGestureActions && mIsGesturing, + false); + } else { + cancelGesture(event); + + } + } else { + cancelGesture(event); + } + + mStrokeBuffer.clear(); + mPreviousWasGesturing = mIsGesturing; + mIsGesturing = false; + + final ArrayList listeners = mOnGesturingListeners; + int count = listeners.size(); + for (int i = 0; i < count; i++) { + listeners.get(i).onGesturingEnded(this); + } + } + + private void cancelGesture(MotionEvent event) { + // pass the event to handlers + final ArrayList listeners = mOnGestureListeners; + final int count = listeners.size(); + for (int i = 0; i < count; i++) { + listeners.get(i).onGestureCancelled(this, event); + } + + clear(false); + } + + private void fireOnGesturePerformed() { + final ArrayList actionListeners = mOnGesturePerformedListeners; + final int count = actionListeners.size(); + for (int i = 0; i < count; i++) { + actionListeners.get(i).onGesturePerformed(GestureOverlayView.this, mCurrentGesture); + } + } + + private class FadeOutRunnable implements Runnable { + boolean fireActionPerformed; + boolean resetMultipleStrokes; + + public void run() { + if (mIsFadingOut) { + final long now = AnimationUtils.currentAnimationTimeMillis(); + final long duration = now - mFadingStart; + + if (duration > mFadeDuration) { + if (fireActionPerformed) { + fireOnGesturePerformed(); + } + + mPreviousWasGesturing = false; + mIsFadingOut = false; + mFadingHasStarted = false; + mPath.rewind(); + mCurrentGesture = null; + setPaintAlpha(255); + } else { + mFadingHasStarted = true; + float interpolatedTime = Math.max(0.0f, + Math.min(1.0f, duration / (float) mFadeDuration)); + mFadingAlpha = 1.0f - mInterpolator.getInterpolation(interpolatedTime); + setPaintAlpha((int) (255 * mFadingAlpha)); + postDelayed(this, FADE_ANIMATION_RATE); + } + } else if (resetMultipleStrokes) { + mResetGesture = true; + } else { + fireOnGesturePerformed(); + + mFadingHasStarted = false; + mPath.rewind(); + mCurrentGesture = null; + mPreviousWasGesturing = false; + setPaintAlpha(255); + } + + invalidate(); + } + } + + public static interface OnGesturingListener { + void onGesturingStarted(GestureOverlayView overlay); + + void onGesturingEnded(GestureOverlayView overlay); + } + + public static interface OnGestureListener { + void onGestureStarted(GestureOverlayView overlay, MotionEvent event); + + void onGesture(GestureOverlayView overlay, MotionEvent event); + + void onGestureEnded(GestureOverlayView overlay, MotionEvent event); + + void onGestureCancelled(GestureOverlayView overlay, MotionEvent event); + } + + public static interface OnGesturePerformedListener { + void onGesturePerformed(GestureOverlayView overlay, Gesture gesture); + } +} diff --git a/core/java/android/gesture/GesturePoint.java b/core/java/android/gesture/GesturePoint.java new file mode 100644 index 0000000000000000000000000000000000000000..3698011fc7fcd578eb433f9046e6ae142dd78a0c --- /dev/null +++ b/core/java/android/gesture/GesturePoint.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2008-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.gesture; + +import java.io.DataInputStream; +import java.io.IOException; + +/** + * A timed point of a gesture stroke + */ + +public class GesturePoint { + public final float x; + public final float y; + + public final long timestamp; + + public GesturePoint(float x, float y, long t) { + this.x = x; + this.y = y; + timestamp = t; + } + + static GesturePoint deserialize(DataInputStream in) throws IOException { + // Read X and Y + final float x = in.readFloat(); + final float y = in.readFloat(); + // Read timestamp + final long timeStamp = in.readLong(); + return new GesturePoint(x, y, timeStamp); + } +} diff --git a/core/java/android/gesture/GestureStore.java b/core/java/android/gesture/GestureStore.java new file mode 100644 index 0000000000000000000000000000000000000000..5f1a44503604e436183daa6c22c4da50a1c9fc1e --- /dev/null +++ b/core/java/android/gesture/GestureStore.java @@ -0,0 +1,330 @@ +/* + * Copyright (C) 2008-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.gesture; + +import android.util.Log; +import android.os.SystemClock; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.IOException; +import java.io.DataOutputStream; +import java.io.DataInputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Set; +import java.util.Map; + +import static android.gesture.GestureConstants.LOG_TAG; + +/** + * GestureLibrary maintains gesture examples and makes predictions on a new + * gesture + */ +// +// File format for GestureStore: +// +// Nb. bytes Java type Description +// ----------------------------------- +// Header +// 2 bytes short File format version number +// 4 bytes int Number of entries +// Entry +// X bytes UTF String Entry name +// 4 bytes int Number of gestures +// Gesture +// 8 bytes long Gesture ID +// 4 bytes int Number of strokes +// Stroke +// 4 bytes int Number of points +// Point +// 4 bytes float X coordinate of the point +// 4 bytes float Y coordinate of the point +// 8 bytes long Time stamp +// +public class GestureStore { + public static final int SEQUENCE_INVARIANT = 1; + // when SEQUENCE_SENSITIVE is used, only single stroke gestures are currently allowed + public static final int SEQUENCE_SENSITIVE = 2; + + // ORIENTATION_SENSITIVE and ORIENTATION_INVARIANT are only for SEQUENCE_SENSITIVE gestures + public static final int ORIENTATION_INVARIANT = 1; + public static final int ORIENTATION_SENSITIVE = 2; + + private static final short FILE_FORMAT_VERSION = 1; + + private static final boolean PROFILE_LOADING_SAVING = false; + + private int mSequenceType = SEQUENCE_SENSITIVE; + private int mOrientationStyle = ORIENTATION_SENSITIVE; + + private final HashMap> mNamedGestures = + new HashMap>(); + + private Learner mClassifier; + + private boolean mChanged = false; + + public GestureStore() { + mClassifier = new InstanceLearner(); + } + + /** + * Specify how the gesture library will handle orientation. + * Use ORIENTATION_INVARIANT or ORIENTATION_SENSITIVE + * + * @param style + */ + public void setOrientationStyle(int style) { + mOrientationStyle = style; + } + + public int getOrientationStyle() { + return mOrientationStyle; + } + + /** + * @param type SEQUENCE_INVARIANT or SEQUENCE_SENSITIVE + */ + public void setSequenceType(int type) { + mSequenceType = type; + } + + /** + * @return SEQUENCE_INVARIANT or SEQUENCE_SENSITIVE + */ + public int getSequenceType() { + return mSequenceType; + } + + /** + * Get all the gesture entry names in the library + * + * @return a set of strings + */ + public Set getGestureEntries() { + return mNamedGestures.keySet(); + } + + /** + * Recognize a gesture + * + * @param gesture the query + * @return a list of predictions of possible entries for a given gesture + */ + public ArrayList recognize(Gesture gesture) { + Instance instance = Instance.createInstance(mSequenceType, + mOrientationStyle, gesture, null); + return mClassifier.classify(mSequenceType, instance.vector); + } + + /** + * Add a gesture for the entry + * + * @param entryName entry name + * @param gesture + */ + public void addGesture(String entryName, Gesture gesture) { + if (entryName == null || entryName.length() == 0) { + return; + } + ArrayList gestures = mNamedGestures.get(entryName); + if (gestures == null) { + gestures = new ArrayList(); + mNamedGestures.put(entryName, gestures); + } + gestures.add(gesture); + mClassifier.addInstance( + Instance.createInstance(mSequenceType, mOrientationStyle, gesture, entryName)); + mChanged = true; + } + + /** + * Remove a gesture from the library. If there are no more gestures for the + * given entry, the gesture entry will be removed. + * + * @param entryName entry name + * @param gesture + */ + public void removeGesture(String entryName, Gesture gesture) { + ArrayList gestures = mNamedGestures.get(entryName); + if (gestures == null) { + return; + } + + gestures.remove(gesture); + + // if there are no more samples, remove the entry automatically + if (gestures.isEmpty()) { + mNamedGestures.remove(entryName); + } + + mClassifier.removeInstance(gesture.getID()); + + mChanged = true; + } + + /** + * Remove a entry of gestures + * + * @param entryName the entry name + */ + public void removeEntry(String entryName) { + mNamedGestures.remove(entryName); + mClassifier.removeInstances(entryName); + mChanged = true; + } + + /** + * Get all the gestures of an entry + * + * @param entryName + * @return the list of gestures that is under this name + */ + public ArrayList getGestures(String entryName) { + ArrayList gestures = mNamedGestures.get(entryName); + if (gestures != null) { + return new ArrayList(gestures); + } else { + return null; + } + } + + public boolean hasChanged() { + return mChanged; + } + + /** + * Save the gesture library + */ + public void save(OutputStream stream) throws IOException { + save(stream, false); + } + + public void save(OutputStream stream, boolean closeStream) throws IOException { + DataOutputStream out = null; + + try { + long start; + if (PROFILE_LOADING_SAVING) { + start = SystemClock.elapsedRealtime(); + } + + final HashMap> maps = mNamedGestures; + + out = new DataOutputStream((stream instanceof BufferedOutputStream) ? stream : + new BufferedOutputStream(stream, GestureConstants.IO_BUFFER_SIZE)); + // Write version number + out.writeShort(FILE_FORMAT_VERSION); + // Write number of entries + out.writeInt(maps.size()); + + for (Map.Entry> entry : maps.entrySet()) { + final String key = entry.getKey(); + final ArrayList examples = entry.getValue(); + final int count = examples.size(); + + // Write entry name + out.writeUTF(key); + // Write number of examples for this entry + out.writeInt(count); + + for (int i = 0; i < count; i++) { + examples.get(i).serialize(out); + } + } + + out.flush(); + + if (PROFILE_LOADING_SAVING) { + long end = SystemClock.elapsedRealtime(); + Log.d(LOG_TAG, "Saving gestures library = " + (end - start) + " ms"); + } + + mChanged = false; + } finally { + if (closeStream) GestureUtilities.closeStream(out); + } + } + + /** + * Load the gesture library + */ + public void load(InputStream stream) throws IOException { + load(stream, false); + } + + public void load(InputStream stream, boolean closeStream) throws IOException { + DataInputStream in = null; + try { + in = new DataInputStream((stream instanceof BufferedInputStream) ? stream : + new BufferedInputStream(stream, GestureConstants.IO_BUFFER_SIZE)); + + long start; + if (PROFILE_LOADING_SAVING) { + start = SystemClock.elapsedRealtime(); + } + + // Read file format version number + final short versionNumber = in.readShort(); + switch (versionNumber) { + case 1: + readFormatV1(in); + break; + } + + if (PROFILE_LOADING_SAVING) { + long end = SystemClock.elapsedRealtime(); + Log.d(LOG_TAG, "Loading gestures library = " + (end - start) + " ms"); + } + } finally { + if (closeStream) GestureUtilities.closeStream(in); + } + } + + private void readFormatV1(DataInputStream in) throws IOException { + final Learner classifier = mClassifier; + final HashMap> namedGestures = mNamedGestures; + namedGestures.clear(); + + // Number of entries in the library + final int entriesCount = in.readInt(); + + for (int i = 0; i < entriesCount; i++) { + // Entry name + final String name = in.readUTF(); + // Number of gestures + final int gestureCount = in.readInt(); + + final ArrayList gestures = new ArrayList(gestureCount); + for (int j = 0; j < gestureCount; j++) { + final Gesture gesture = Gesture.deserialize(in); + gestures.add(gesture); + classifier.addInstance( + Instance.createInstance(mSequenceType, mOrientationStyle, gesture, name)); + } + + namedGestures.put(name, gestures); + } + } + + Learner getLearner() { + return mClassifier; + } +} diff --git a/core/java/android/gesture/GestureStroke.java b/core/java/android/gesture/GestureStroke.java new file mode 100644 index 0000000000000000000000000000000000000000..598eb8534ffc56b3e7a1a1649b553d057328c61a --- /dev/null +++ b/core/java/android/gesture/GestureStroke.java @@ -0,0 +1,229 @@ +/* + * 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.gesture; + +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.RectF; + +import java.io.IOException; +import java.io.DataOutputStream; +import java.io.DataInputStream; +import java.util.ArrayList; + +/** + * A gesture stroke started on a touch down and ended on a touch up. + */ +public class GestureStroke { + static final float TOUCH_TOLERANCE = 8; + + public final RectF boundingBox; + + public final float length; + public final float[] points; + + private final long[] timestamps; + private Path mCachedPath; + + /** + * Construct a gesture stroke from a list of gesture points + * + * @param points + */ + public GestureStroke(ArrayList points) { + final int count = points.size(); + final float[] tmpPoints = new float[count * 2]; + final long[] times = new long[count]; + + RectF bx = null; + float len = 0; + int index = 0; + + for (int i = 0; i < count; i++) { + final GesturePoint p = points.get(i); + tmpPoints[i * 2] = p.x; + tmpPoints[i * 2 + 1] = p.y; + times[index] = p.timestamp; + + if (bx == null) { + bx = new RectF(); + bx.top = p.y; + bx.left = p.x; + bx.right = p.x; + bx.bottom = p.y; + len = 0; + } else { + len += Math.sqrt(Math.pow(p.x - tmpPoints[(i - 1) * 2], 2) + + Math.pow(p.y - tmpPoints[(i -1 ) * 2 + 1], 2)); + bx.union(p.x, p.y); + } + index++; + } + + timestamps = times; + this.points = tmpPoints; + boundingBox = bx; + length = len; + } + + /** + * Draw the gesture with a given canvas and paint + * + * @param canvas + */ + void draw(Canvas canvas, Paint paint) { + if (mCachedPath == null) { + makePath(); + } + + canvas.drawPath(mCachedPath, paint); + } + + public Path getPath() { + if (mCachedPath == null) { + makePath(); + } + + return mCachedPath; + } + + private void makePath() { + final float[] localPoints = points; + final int count = localPoints.length; + + Path path = null; + + float mX = 0; + float mY = 0; + + for (int i = 0; i < count; i += 2) { + float x = localPoints[i]; + float y = localPoints[i + 1]; + if (path == null) { + path = new Path(); + path.moveTo(x, y); + mX = x; + mY = y; + } else { + float dx = Math.abs(x - mX); + float dy = Math.abs(y - mY); + if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) { + path.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2); + mX = x; + mY = y; + } + } + } + + mCachedPath = path; + } + + /** + * Convert the stroke to a Path based on the number of points + * + * @param width the width of the bounding box of the target path + * @param height the height of the bounding box of the target path + * @param numSample the number of points needed + * + * @return the path + */ + public Path toPath(float width, float height, int numSample) { + final float[] pts = GestureUtilities.temporalSampling(this, numSample); + final RectF rect = boundingBox; + + GestureUtilities.translate(pts, -rect.left, -rect.top); + + float sx = width / rect.width(); + float sy = height / rect.height(); + float scale = sx > sy ? sy : sx; + GestureUtilities.scale(pts, scale, scale); + + float mX = 0; + float mY = 0; + + Path path = null; + + final int count = pts.length; + + for (int i = 0; i < count; i += 2) { + float x = pts[i]; + float y = pts[i + 1]; + if (path == null) { + path = new Path(); + path.moveTo(x, y); + mX = x; + mY = y; + } else { + float dx = Math.abs(x - mX); + float dy = Math.abs(y - mY); + if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) { + path.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2); + mX = x; + mY = y; + } + } + } + + return path; + } + + void serialize(DataOutputStream out) throws IOException { + final float[] pts = points; + final long[] times = timestamps; + final int count = points.length; + + // Write number of points + out.writeInt(count / 2); + + for (int i = 0; i < count; i += 2) { + // Write X + out.writeFloat(pts[i]); + // Write Y + out.writeFloat(pts[i + 1]); + // Write timestamp + out.writeLong(times[i / 2]); + } + } + + static GestureStroke deserialize(DataInputStream in) throws IOException { + // Number of points + final int count = in.readInt(); + + final ArrayList points = new ArrayList(count); + for (int i = 0; i < count; i++) { + points.add(GesturePoint.deserialize(in)); + } + + return new GestureStroke(points); + } + + /** + * Invalidate the cached path that is used to render the stroke + */ + public void clearPath() { + if (mCachedPath != null) mCachedPath.rewind(); + } + + /** + * Compute an oriented bounding box of the stroke + * @return OrientedBoundingBox + */ + public OrientedBoundingBox computeOrientedBoundingBox() { + return GestureUtilities.computeOrientedBoundingBox(points); + } +} diff --git a/core/java/android/gesture/GestureUtilities.java b/core/java/android/gesture/GestureUtilities.java new file mode 100755 index 0000000000000000000000000000000000000000..40d70295fc9b220567cb38bdc535d33c45f9043e --- /dev/null +++ b/core/java/android/gesture/GestureUtilities.java @@ -0,0 +1,475 @@ +/* + * Copyright (C) 2008-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.gesture; + +import android.graphics.RectF; +import android.util.Log; + +import java.util.ArrayList; +import java.util.Arrays; +import java.io.Closeable; +import java.io.IOException; + +import static android.gesture.GestureConstants.*; + +final class GestureUtilities { + private static final int TEMPORAL_SAMPLING_RATE = 16; + + private GestureUtilities() { + } + + /** + * Closes the specified stream. + * + * @param stream The stream to close. + */ + static void closeStream(Closeable stream) { + if (stream != null) { + try { + stream.close(); + } catch (IOException e) { + Log.e(LOG_TAG, "Could not close stream", e); + } + } + } + + static float[] spatialSampling(Gesture gesture, int sampleMatrixDimension) { + final float targetPatchSize = sampleMatrixDimension - 1; // edge inclusive + float[] sample = new float[sampleMatrixDimension * sampleMatrixDimension]; + Arrays.fill(sample, 0); + + RectF rect = gesture.getBoundingBox(); + float sx = targetPatchSize / rect.width(); + float sy = targetPatchSize / rect.height(); + float scale = sx < sy ? sx : sy; + + float preDx = -rect.centerX(); + float preDy = -rect.centerY(); + float postDx = targetPatchSize / 2; + float postDy = targetPatchSize / 2; + + final ArrayList strokes = gesture.getStrokes(); + final int count = strokes.size(); + + int size; + float xpos; + float ypos; + + for (int index = 0; index < count; index++) { + final GestureStroke stroke = strokes.get(index); + float[] strokepoints = stroke.points; + size = strokepoints.length; + + final float[] pts = new float[size]; + + for (int i = 0; i < size; i += 2) { + pts[i] = (strokepoints[i] + preDx) * scale + postDx; + pts[i + 1] = (strokepoints[i + 1] + preDy) * scale + postDy; + } + + float segmentEndX = -1; + float segmentEndY = -1; + + for (int i = 0; i < size; i += 2) { + + float segmentStartX = pts[i] < 0 ? 0 : pts[i]; + float segmentStartY = pts[i + 1] < 0 ? 0 : pts[i + 1]; + + if (segmentStartX > targetPatchSize) { + segmentStartX = targetPatchSize; + } + + if (segmentStartY > targetPatchSize) { + segmentStartY = targetPatchSize; + } + + plot(segmentStartX, segmentStartY, sample, sampleMatrixDimension); + + if (segmentEndX != -1) { + // evaluate horizontally + if (segmentEndX > segmentStartX) { + xpos = (float) Math.ceil(segmentStartX); + float slope = (segmentEndY - segmentStartY) / (segmentEndX - segmentStartX); + while (xpos < segmentEndX) { + ypos = slope * (xpos - segmentStartX) + segmentStartY; + plot(xpos, ypos, sample, sampleMatrixDimension); + xpos++; + } + } else if (segmentEndX < segmentStartX){ + xpos = (float) Math.ceil(segmentEndX); + float slope = (segmentEndY - segmentStartY) / (segmentEndX - segmentStartX); + while (xpos < segmentStartX) { + ypos = slope * (xpos - segmentStartX) + segmentStartY; + plot(xpos, ypos, sample, sampleMatrixDimension); + xpos++; + } + } + + // evaluating vertically + if (segmentEndY > segmentStartY) { + ypos = (float) Math.ceil(segmentStartY); + float invertSlope = (segmentEndX - segmentStartX) / (segmentEndY - segmentStartY); + while (ypos < segmentEndY) { + xpos = invertSlope * (ypos - segmentStartY) + segmentStartX; + plot(xpos, ypos, sample, sampleMatrixDimension); + ypos++; + } + } else if (segmentEndY < segmentStartY) { + ypos = (float) Math.ceil(segmentEndY); + float invertSlope = (segmentEndX - segmentStartX) / (segmentEndY - segmentStartY); + while (ypos < segmentStartY) { + xpos = invertSlope * (ypos - segmentStartY) + segmentStartX; + plot(xpos, ypos, sample, sampleMatrixDimension); + ypos++; + } + } + } + + segmentEndX = segmentStartX; + segmentEndY = segmentStartY; + } + } + + + return sample; + } + + private static void plot(float x, float y, float[] sample, int sampleSize) { + x = x < 0 ? 0 : x; + y = y < 0 ? 0 : y; + int xFloor = (int) Math.floor(x); + int xCeiling = (int) Math.ceil(x); + int yFloor = (int) Math.floor(y); + int yCeiling = (int) Math.ceil(y); + + // if it's an integer + if (x == xFloor && y == yFloor) { + int index = yCeiling * sampleSize + xCeiling; + if (sample[index] < 1){ + sample[index] = 1; + } + } else { + double topLeft = Math.sqrt(Math.pow(xFloor - x, 2) + Math.pow(yFloor - y, 2)); + double topRight = Math.sqrt(Math.pow(xCeiling - x, 2) + Math.pow(yFloor - y, 2)); + double btmLeft = Math.sqrt(Math.pow(xFloor - x, 2) + Math.pow(yCeiling - y, 2)); + double btmRight = Math.sqrt(Math.pow(xCeiling - x, 2) + Math.pow(yCeiling - y, 2)); + double sum = topLeft + topRight + btmLeft + btmRight; + + double value = topLeft / sum; + int index = yFloor * sampleSize + xFloor; + if (value > sample[index]){ + sample[index] = (float) value; + } + + value = topRight / sum; + index = yFloor * sampleSize + xCeiling; + if (value > sample[index]){ + sample[index] = (float) value; + } + + value = btmLeft / sum; + index = yCeiling * sampleSize + xFloor; + if (value > sample[index]){ + sample[index] = (float) value; + } + + value = btmRight / sum; + index = yCeiling * sampleSize + xCeiling; + if (value > sample[index]){ + sample[index] = (float) value; + } + } + } + + /** + * Featurize a stroke into a vector of a given number of elements + * + * @param stroke + * @param sampleSize + * @return a float array + */ + static float[] temporalSampling(GestureStroke stroke, int sampleSize) { + final float increment = stroke.length / (sampleSize - 1); + int vectorLength = sampleSize * 2; + float[] vector = new float[vectorLength]; + float distanceSoFar = 0; + float[] pts = stroke.points; + float lstPointX = pts[0]; + float lstPointY = pts[1]; + int index = 0; + float currentPointX = Float.MIN_VALUE; + float currentPointY = Float.MIN_VALUE; + vector[index] = lstPointX; + index++; + vector[index] = lstPointY; + index++; + int i = 0; + int count = pts.length / 2; + while (i < count) { + if (currentPointX == Float.MIN_VALUE) { + i++; + if (i >= count) { + break; + } + currentPointX = pts[i * 2]; + currentPointY = pts[i * 2 + 1]; + } + float deltaX = currentPointX - lstPointX; + float deltaY = currentPointY - lstPointY; + float distance = (float) Math.sqrt(deltaX * deltaX + deltaY * deltaY); + if (distanceSoFar + distance >= increment) { + float ratio = (increment - distanceSoFar) / distance; + float nx = lstPointX + ratio * deltaX; + float ny = lstPointY + ratio * deltaY; + vector[index] = nx; + index++; + vector[index] = ny; + index++; + lstPointX = nx; + lstPointY = ny; + distanceSoFar = 0; + } else { + lstPointX = currentPointX; + lstPointY = currentPointY; + currentPointX = Float.MIN_VALUE; + currentPointY = Float.MIN_VALUE; + distanceSoFar += distance; + } + } + + for (i = index; i < vectorLength; i += 2) { + vector[i] = lstPointX; + vector[i + 1] = lstPointY; + } + return vector; + } + + /** + * Calculate the centroid + * + * @param points + * @return the centroid + */ + static float[] computeCentroid(float[] points) { + float centerX = 0; + float centerY = 0; + int count = points.length; + for (int i = 0; i < count; i++) { + centerX += points[i]; + i++; + centerY += points[i]; + } + float[] center = new float[2]; + center[0] = 2 * centerX / count; + center[1] = 2 * centerY / count; + + return center; + } + + /** + * calculate the variance-covariance matrix, treat each point as a sample + * + * @param points + * @return the covariance matrix + */ + private static double[][] computeCoVariance(float[] points) { + double[][] array = new double[2][2]; + array[0][0] = 0; + array[0][1] = 0; + array[1][0] = 0; + array[1][1] = 0; + int count = points.length; + for (int i = 0; i < count; i++) { + float x = points[i]; + i++; + float y = points[i]; + array[0][0] += x * x; + array[0][1] += x * y; + array[1][0] = array[0][1]; + array[1][1] += y * y; + } + array[0][0] /= (count / 2); + array[0][1] /= (count / 2); + array[1][0] /= (count / 2); + array[1][1] /= (count / 2); + + return array; + } + + static float computeTotalLength(float[] points) { + float sum = 0; + int count = points.length - 4; + for (int i = 0; i < count; i += 2) { + float dx = points[i + 2] - points[i]; + float dy = points[i + 3] - points[i + 1]; + sum += Math.sqrt(dx * dx + dy * dy); + } + return sum; + } + + static double computeStraightness(float[] points) { + float totalLen = computeTotalLength(points); + float dx = points[2] - points[0]; + float dy = points[3] - points[1]; + return Math.sqrt(dx * dx + dy * dy) / totalLen; + } + + static double computeStraightness(float[] points, float totalLen) { + float dx = points[2] - points[0]; + float dy = points[3] - points[1]; + return Math.sqrt(dx * dx + dy * dy) / totalLen; + } + + /** + * Calculate the squared Euclidean distance between two vectors + * + * @param vector1 + * @param vector2 + * @return the distance + */ + static double squaredEuclideanDistance(float[] vector1, float[] vector2) { + double squaredDistance = 0; + int size = vector1.length; + for (int i = 0; i < size; i++) { + float difference = vector1[i] - vector2[i]; + squaredDistance += difference * difference; + } + return squaredDistance / size; + } + + /** + * Calculate the cosine distance between two instances + * + * @param vector1 + * @param vector2 + * @return the distance between 0 and Math.PI + */ + static double cosineDistance(float[] vector1, float[] vector2) { + float sum = 0; + int len = vector1.length; + for (int i = 0; i < len; i++) { + sum += vector1[i] * vector2[i]; + } + return Math.acos(sum); + } + + static OrientedBoundingBox computeOrientedBoundingBox(ArrayList pts) { + GestureStroke stroke = new GestureStroke(pts); + float[] points = temporalSampling(stroke, TEMPORAL_SAMPLING_RATE); + return computeOrientedBoundingBox(points); + } + + static OrientedBoundingBox computeOrientedBoundingBox(float[] points) { + float[] meanVector = computeCentroid(points); + return computeOrientedBoundingBox(points, meanVector); + } + + static OrientedBoundingBox computeOrientedBoundingBox(float[] points, float[] centroid) { + translate(points, -centroid[0], -centroid[1]); + + double[][] array = computeCoVariance(points); + double[] targetVector = computeOrientation(array); + + float angle; + if (targetVector[0] == 0 && targetVector[1] == 0) { + angle = (float) -Math.PI/2; + } else { // -PI maxx) { + maxx = points[i]; + } + i++; + if (points[i] < miny) { + miny = points[i]; + } + if (points[i] > maxy) { + maxy = points[i]; + } + } + + return new OrientedBoundingBox((float) (angle * 180 / Math.PI), centroid[0], centroid[1], maxx - minx, maxy - miny); + } + + private static double[] computeOrientation(double[][] covarianceMatrix) { + double[] targetVector = new double[2]; + if (covarianceMatrix[0][1] == 0 || covarianceMatrix[1][0] == 0) { + targetVector[0] = 1; + targetVector[1] = 0; + } + + double a = -covarianceMatrix[0][0] - covarianceMatrix[1][1]; + double b = covarianceMatrix[0][0] * covarianceMatrix[1][1] - covarianceMatrix[0][1] + * covarianceMatrix[1][0]; + double value = a / 2; + double rightside = Math.sqrt(Math.pow(value, 2) - b); + double lambda1 = -value + rightside; + double lambda2 = -value - rightside; + if (lambda1 == lambda2) { + targetVector[0] = 0; + targetVector[1] = 0; + } else { + double lambda = lambda1 > lambda2 ? lambda1 : lambda2; + targetVector[0] = 1; + targetVector[1] = (lambda - covarianceMatrix[0][0]) / covarianceMatrix[0][1]; + } + return targetVector; + } + + + static float[] rotate(float[] points, double angle) { + double cos = Math.cos(angle); + double sin = Math.sin(angle); + int size = points.length; + for (int i = 0; i < size; i += 2) { + float x = (float) (points[i] * cos - points[i + 1] * sin); + float y = (float) (points[i] * sin + points[i + 1] * cos); + points[i] = x; + points[i + 1] = y; + } + return points; + } + + static float[] translate(float[] points, float dx, float dy) { + int size = points.length; + for (int i = 0; i < size; i += 2) { + points[i] += dx; + points[i + 1] += dy; + } + return points; + } + + static float[] scale(float[] points, float sx, float sy) { + int size = points.length; + for (int i = 0; i < size; i += 2) { + points[i] *= sx; + points[i + 1] *= sy; + } + return points; + } +} diff --git a/core/java/android/gesture/Instance.java b/core/java/android/gesture/Instance.java new file mode 100755 index 0000000000000000000000000000000000000000..ef208acf4ec793167bc43dde35fa7973f57fc47f --- /dev/null +++ b/core/java/android/gesture/Instance.java @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2008-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.gesture; + + +/** + * An instance represents a sample if the label is available or a query if the + * label is null. + */ +class Instance { + private static final int SEQUENCE_SAMPLE_SIZE = 16; + + private static final int PATCH_SAMPLE_SIZE = 16; + + private final static float[] ORIENTATIONS = { + 0, (float) (Math.PI / 4), (float) (Math.PI / 2), (float) (Math.PI * 3 / 4), + (float) Math.PI, -0, (float) (-Math.PI / 4), (float) (-Math.PI / 2), + (float) (-Math.PI * 3 / 4), (float) -Math.PI + }; + + // the feature vector + final float[] vector; + + // the label can be null + final String label; + + // the id of the instance + final long id; + + private Instance(long id, float[] sample, String sampleName) { + this.id = id; + vector = sample; + label = sampleName; + } + + private void normalize() { + float[] sample = vector; + float sum = 0; + + int size = sample.length; + for (int i = 0; i < size; i++) { + sum += sample[i] * sample[i]; + } + + float magnitude = (float)Math.sqrt(sum); + for (int i = 0; i < size; i++) { + sample[i] /= magnitude; + } + } + + /** + * create a learning instance for a single stroke gesture + * + * @param gesture + * @param label + * @return the instance + */ + static Instance createInstance(int sequenceType, int orientationType, Gesture gesture, String label) { + float[] pts; + Instance instance; + if (sequenceType == GestureStore.SEQUENCE_SENSITIVE) { + pts = temporalSampler(orientationType, gesture); + instance = new Instance(gesture.getID(), pts, label); + instance.normalize(); + } else { + pts = spatialSampler(gesture); + instance = new Instance(gesture.getID(), pts, label); + } + return instance; + } + + private static float[] spatialSampler(Gesture gesture) { + return GestureUtilities.spatialSampling(gesture, PATCH_SAMPLE_SIZE); + } + + private static float[] temporalSampler(int orientationType, Gesture gesture) { + float[] pts = GestureUtilities.temporalSampling(gesture.getStrokes().get(0), + SEQUENCE_SAMPLE_SIZE); + float[] center = GestureUtilities.computeCentroid(pts); + float orientation = (float)Math.atan2(pts[1] - center[1], pts[0] - center[0]); + + float adjustment = -orientation; + if (orientationType == GestureStore.ORIENTATION_SENSITIVE) { + int count = ORIENTATIONS.length; + for (int i = 0; i < count; i++) { + float delta = ORIENTATIONS[i] - orientation; + if (Math.abs(delta) < Math.abs(adjustment)) { + adjustment = delta; + } + } + } + + GestureUtilities.translate(pts, -center[0], -center[1]); + GestureUtilities.rotate(pts, adjustment); + + return pts; + } + +} diff --git a/core/java/android/gesture/InstanceLearner.java b/core/java/android/gesture/InstanceLearner.java new file mode 100644 index 0000000000000000000000000000000000000000..b93b76fa9a87692cde56f292b2b9958347f2b449 --- /dev/null +++ b/core/java/android/gesture/InstanceLearner.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2008-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.gesture; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.TreeMap; + +/** + * An implementation of an instance-based learner + */ + +class InstanceLearner extends Learner { + private static final Comparator sComparator = new Comparator() { + public int compare(Prediction object1, Prediction object2) { + double score1 = object1.score; + double score2 = object2.score; + if (score1 > score2) { + return -1; + } else if (score1 < score2) { + return 1; + } else { + return 0; + } + } + }; + + @Override + ArrayList classify(int sequenceType, float[] vector) { + ArrayList predictions = new ArrayList(); + ArrayList instances = getInstances(); + int count = instances.size(); + TreeMap label2score = new TreeMap(); + for (int i = 0; i < count; i++) { + Instance sample = instances.get(i); + if (sample.vector.length != vector.length) { + continue; + } + double distance; + if (sequenceType == GestureStore.SEQUENCE_SENSITIVE) { + distance = GestureUtilities.cosineDistance(sample.vector, vector); + } else { + distance = GestureUtilities.squaredEuclideanDistance(sample.vector, vector); + } + double weight; + if (distance == 0) { + weight = Double.MAX_VALUE; + } else { + weight = 1 / distance; + } + Double score = label2score.get(sample.label); + if (score == null || weight > score) { + label2score.put(sample.label, weight); + } + } + +// double sum = 0; + for (String name : label2score.keySet()) { + double score = label2score.get(name); +// sum += score; + predictions.add(new Prediction(name, score)); + } + + // normalize +// for (Prediction prediction : predictions) { +// prediction.score /= sum; +// } + + Collections.sort(predictions, sComparator); + + return predictions; + } +} diff --git a/core/java/android/gesture/Learner.java b/core/java/android/gesture/Learner.java new file mode 100755 index 0000000000000000000000000000000000000000..feacde5f97365b209c330f567b962955fed7950d --- /dev/null +++ b/core/java/android/gesture/Learner.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2008-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.gesture; + +import java.util.ArrayList; + +/** + * The abstract class of a gesture learner + */ +abstract class Learner { + private final ArrayList mInstances = new ArrayList(); + + /** + * Add an instance to the learner + * + * @param instance + */ + void addInstance(Instance instance) { + mInstances.add(instance); + } + + /** + * Retrieve all the instances + * + * @return instances + */ + ArrayList getInstances() { + return mInstances; + } + + /** + * Remove an instance based on its id + * + * @param id + */ + void removeInstance(long id) { + ArrayList instances = mInstances; + int count = instances.size(); + for (int i = 0; i < count; i++) { + Instance instance = instances.get(i); + if (id == instance.id) { + instances.remove(instance); + return; + } + } + } + + /** + * Remove all the instances of a category + * + * @param name the category name + */ + void removeInstances(String name) { + final ArrayList toDelete = new ArrayList(); + final ArrayList instances = mInstances; + final int count = instances.size(); + + for (int i = 0; i < count; i++) { + final Instance instance = instances.get(i); + // the label can be null, as specified in Instance + if ((instance.label == null && name == null) || instance.label.equals(name)) { + toDelete.add(instance); + } + } + instances.removeAll(toDelete); + } + + abstract ArrayList classify(int gestureType, float[] vector); +} diff --git a/core/java/android/gesture/OrientedBoundingBox.java b/core/java/android/gesture/OrientedBoundingBox.java new file mode 100644 index 0000000000000000000000000000000000000000..f1335ee122322918576cadc17d3e43a3cff38612 --- /dev/null +++ b/core/java/android/gesture/OrientedBoundingBox.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2008-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.gesture; + +import android.graphics.Matrix; +import android.graphics.Path; + +/** + * An oriented bounding box + */ +public class OrientedBoundingBox { + public final float squareness; + + public final float width; + public final float height; + + public final float orientation; + + public final float centerX; + public final float centerY; + + OrientedBoundingBox(float angle, float cx, float cy, float w, float h) { + orientation = angle; + width = w; + height = h; + centerX = cx; + centerY = cy; + float ratio = w / h; + if (ratio > 1) { + squareness = 1 / ratio; + } else { + squareness = ratio; + } + } + + /** + * Currently used for debugging purpose only. + * + * @hide + */ + public Path toPath() { + Path path = new Path(); + float[] point = new float[2]; + point[0] = -width / 2; + point[1] = height / 2; + Matrix matrix = new Matrix(); + matrix.setRotate(orientation); + matrix.postTranslate(centerX, centerY); + matrix.mapPoints(point); + path.moveTo(point[0], point[1]); + + point[0] = -width / 2; + point[1] = -height / 2; + matrix.mapPoints(point); + path.lineTo(point[0], point[1]); + + point[0] = width / 2; + point[1] = -height / 2; + matrix.mapPoints(point); + path.lineTo(point[0], point[1]); + + point[0] = width / 2; + point[1] = height / 2; + matrix.mapPoints(point); + path.lineTo(point[0], point[1]); + + path.close(); + + return path; + } +} diff --git a/core/java/android/gesture/Prediction.java b/core/java/android/gesture/Prediction.java new file mode 100755 index 0000000000000000000000000000000000000000..ce6ad5745df6ce3789692b27b97dc0334ffa6f9a --- /dev/null +++ b/core/java/android/gesture/Prediction.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2008-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.gesture; + +public class Prediction { + public final String name; + + public double score; + + Prediction(String label, double predictionScore) { + name = label; + score = predictionScore; + } + + @Override + public String toString() { + return name; + } +} diff --git a/core/java/android/gesture/package.html b/core/java/android/gesture/package.html new file mode 100644 index 0000000000000000000000000000000000000000..a54a01713c91f7700ed9af4c57a14e3841025c91 --- /dev/null +++ b/core/java/android/gesture/package.html @@ -0,0 +1,5 @@ + + +@hide + + diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java index ca579b6b501b6965588612078eb02876befd1fb6..091bc1700988e5e66517622c139bdd4f6cd4a9b3 100644 --- a/core/java/android/hardware/Camera.java +++ b/core/java/android/hardware/Camera.java @@ -39,13 +39,16 @@ import android.os.Message; public class Camera { private static final String TAG = "Camera"; - // These match the enum in libs/android_runtime/android_hardware_Camera.cpp - private static final int SHUTTER_CALLBACK = 0; - private static final int RAW_PICTURE_CALLBACK = 1; - private static final int JPEG_PICTURE_CALLBACK = 2; - private static final int PREVIEW_CALLBACK = 3; - private static final int AUTOFOCUS_CALLBACK = 4; - private static final int ERROR_CALLBACK = 5; + // These match the enums in frameworks/base/include/ui/Camera.h + private static final int CAMERA_MSG_ERROR = 0; + private static final int CAMERA_MSG_SHUTTER = 1; + private static final int CAMERA_MSG_FOCUS = 2; + private static final int CAMERA_MSG_ZOOM = 3; + private static final int CAMERA_MSG_PREVIEW_FRAME = 4; + private static final int CAMERA_MSG_VIDEO_FRAME = 5; + private static final int CAMERA_MSG_POSTVIEW_FRAME = 6; + private static final int CAMERA_MSG_RAW_IMAGE = 7; + private static final int CAMERA_MSG_COMPRESSED_IMAGE = 8; private int mNativeContext; // accessed by native methods private EventHandler mEventHandler; @@ -152,7 +155,11 @@ public class Camera { * @throws IOException if the method fails. */ public final void setPreviewDisplay(SurfaceHolder holder) throws IOException { - setPreviewDisplay(holder.getSurface()); + if (holder != null) { + setPreviewDisplay(holder.getSurface()); + } else { + setPreviewDisplay((Surface)null); + } } private native final void setPreviewDisplay(Surface surface); @@ -231,22 +238,23 @@ public class Camera { @Override public void handleMessage(Message msg) { switch(msg.what) { - case SHUTTER_CALLBACK: + case CAMERA_MSG_SHUTTER: if (mShutterCallback != null) { mShutterCallback.onShutter(); } return; - case RAW_PICTURE_CALLBACK: + + case CAMERA_MSG_RAW_IMAGE: if (mRawImageCallback != null) mRawImageCallback.onPictureTaken((byte[])msg.obj, mCamera); return; - case JPEG_PICTURE_CALLBACK: + case CAMERA_MSG_COMPRESSED_IMAGE: if (mJpegCallback != null) mJpegCallback.onPictureTaken((byte[])msg.obj, mCamera); return; - case PREVIEW_CALLBACK: + case CAMERA_MSG_PREVIEW_FRAME: if (mPreviewCallback != null) { mPreviewCallback.onPreviewFrame((byte[])msg.obj, mCamera); if (mOneShot) { @@ -255,12 +263,12 @@ public class Camera { } return; - case AUTOFOCUS_CALLBACK: + case CAMERA_MSG_FOCUS: if (mAutoFocusCallback != null) mAutoFocusCallback.onAutoFocus(msg.arg1 == 0 ? false : true, mCamera); return; - case ERROR_CALLBACK: + case CAMERA_MSG_ERROR : Log.e(TAG, "Error " + msg.arg1); if (mErrorCallback != null) mErrorCallback.onError(msg.arg1, mCamera); @@ -363,7 +371,7 @@ public class Camera { } private native final void native_takePicture(); - // These match the enum in libs/android_runtime/android_hardware_Camera.cpp + // These match the enum in include/ui/Camera.h /** Unspecified camerar error. @see #ErrorCallback */ public static final int CAMERA_ERROR_UNKNOWN = 1; /** Media server died. In this case, the application must release the diff --git a/core/java/android/hardware/ISensorService.aidl b/core/java/android/hardware/ISensorService.aidl index 04af2aec45239f6d160eab888b0ce152de6d7f11..67180bd9006408ce3949fc43a3d1c2e868c346ca 100644 --- a/core/java/android/hardware/ISensorService.aidl +++ b/core/java/android/hardware/ISensorService.aidl @@ -17,13 +17,13 @@ package android.hardware; -import android.os.ParcelFileDescriptor; +import android.os.Bundle; /** * {@hide} */ interface ISensorService { - ParcelFileDescriptor getDataChanel(); + Bundle getDataChannel(); 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 67df23b270a4b16ab804e62c517cd4ab95178ba3..bf945ec7bae33525decaeed3067266719b977303 100644 --- a/core/java/android/hardware/SensorManager.java +++ b/core/java/android/hardware/SensorManager.java @@ -18,7 +18,9 @@ package android.hardware; import android.content.Context; import android.os.Binder; +import android.os.Bundle; import android.os.Looper; +import android.os.Parcelable; import android.os.ParcelFileDescriptor; import android.os.Process; import android.os.RemoteException; @@ -280,8 +282,8 @@ public class SensorManager void startLocked(ISensorService service) { try { if (mThread == null) { - ParcelFileDescriptor fd = service.getDataChanel(); - mThread = new Thread(new SensorThreadRunnable(fd), + Bundle dataChannel = service.getDataChannel(); + mThread = new Thread(new SensorThreadRunnable(dataChannel), SensorThread.class.getName()); mThread.start(); } @@ -291,10 +293,52 @@ public class SensorManager } private class SensorThreadRunnable implements Runnable { - private ParcelFileDescriptor mSensorDataFd; - SensorThreadRunnable(ParcelFileDescriptor fd) { - mSensorDataFd = fd; + private Bundle mDataChannel; + SensorThreadRunnable(Bundle dataChannel) { + mDataChannel = dataChannel; } + + private boolean open() { + if (mDataChannel == null) { + Log.e(TAG, "mDataChannel == NULL, exiting"); + synchronized (sListeners) { + mThread = null; + } + return false; + } + + // this thread is guaranteed to be unique + Parcelable[] pfds = mDataChannel.getParcelableArray("fds"); + FileDescriptor[] fds; + if (pfds != null) { + int length = pfds.length; + fds = new FileDescriptor[length]; + for (int i = 0; i < length; i++) { + ParcelFileDescriptor pfd = (ParcelFileDescriptor)pfds[i]; + fds[i] = pfd.getFileDescriptor(); + } + } else { + fds = null; + } + int[] ints = mDataChannel.getIntArray("ints"); + sensors_data_open(fds, ints); + if (pfds != null) { + try { + // close our copies of the file descriptors, + // since we are just passing these to the JNI code and not using them here. + for (int i = pfds.length - 1; i >= 0; i--) { + ParcelFileDescriptor pfd = (ParcelFileDescriptor)pfds[i]; + pfd.close(); + } + } catch (IOException e) { + // *shrug* + Log.e(TAG, "IOException: ", e); + } + } + mDataChannel = null; + return true; + } + public void run() { //Log.d(TAG, "entering main sensor thread"); final float[] values = new float[3]; @@ -302,23 +346,9 @@ public class SensorManager final long timestamp[] = new long[1]; Process.setThreadPriority(Process.THREAD_PRIORITY_DISPLAY); - if (mSensorDataFd == null) { - Log.e(TAG, "mSensorDataFd == NULL, exiting"); - synchronized (sListeners) { - mThread = null; - } + if (!open()) { return; } - // this thread is guaranteed to be unique - sensors_data_open(mSensorDataFd.getFileDescriptor()); - try { - mSensorDataFd.close(); - } catch (IOException e) { - // *shrug* - Log.e(TAG, "IOException: ", e); - } - mSensorDataFd = null; - while (true) { // wait for an event @@ -1469,7 +1499,7 @@ public class SensorManager // Used within this module from outside SensorManager, don't make private static native int sensors_data_init(); static native int sensors_data_uninit(); - static native int sensors_data_open(FileDescriptor fd); + static native int sensors_data_open(FileDescriptor[] fds, int[] ints); static native int sensors_data_close(); static native int sensors_data_poll(float[] values, int[] status, long[] timestamp); } diff --git a/core/java/android/net/http/RequestHandle.java b/core/java/android/net/http/RequestHandle.java index c4ee5b0da0e2bfe43082616c705cff3d0dbdc072..6a97951fecbd47bbb0e92d39fec75138f1159e5e 100644 --- a/core/java/android/net/http/RequestHandle.java +++ b/core/java/android/net/http/RequestHandle.java @@ -159,11 +159,11 @@ public class RequestHandle { e.printStackTrace(); } - // update the "cookie" header based on the redirected url - mHeaders.remove("cookie"); + // update the "Cookie" header based on the redirected url + mHeaders.remove("Cookie"); String cookie = CookieManager.getInstance().getCookie(mUri); if (cookie != null && cookie.length() > 0) { - mHeaders.put("cookie", cookie); + mHeaders.put("Cookie", cookie); } if ((statusCode == 302 || statusCode == 303) && mMethod.equals("POST")) { diff --git a/core/java/android/os/AsyncTask.java b/core/java/android/os/AsyncTask.java index 6c135825a99fb52f44cf051175e627fcfce6a3ae..abfb27412de0c333d45d451c1ff4c5867cbe5373 100644 --- a/core/java/android/os/AsyncTask.java +++ b/core/java/android/os/AsyncTask.java @@ -127,12 +127,12 @@ import java.util.concurrent.atomic.AtomicInteger; public abstract class AsyncTask { private static final String LOG_TAG = "AsyncTask"; - private static final int CORE_POOL_SIZE = 1; - private static final int MAXIMUM_POOL_SIZE = 10; + private static final int CORE_POOL_SIZE = 5; + private static final int MAXIMUM_POOL_SIZE = 128; private static final int KEEP_ALIVE = 10; private static final BlockingQueue sWorkQueue = - new LinkedBlockingQueue(MAXIMUM_POOL_SIZE); + new LinkedBlockingQueue(10); private static final ThreadFactory sThreadFactory = new ThreadFactory() { private final AtomicInteger mCount = new AtomicInteger(1); diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index 8a0fd58530b04f0b84dfa394f277494f5ce7ba0b..528def5c401182c4e5c934dd6f0390af18d924f7 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -68,6 +68,20 @@ public abstract class BatteryStats implements Parcelable { */ public static final int WIFI_MULTICAST_ENABLED = 7; + /** + * A constant indicating an audio turn on timer + * + * {@hide} + */ + public static final int AUDIO_TURNED_ON = 7; + + /** + * A constant indicating a video turn on timer + * + * {@hide} + */ + public static final int VIDEO_TURNED_ON = 8; + /** * Include all of the data in the stats, including previously saved data. */ @@ -164,7 +178,7 @@ public abstract class BatteryStats implements Parcelable { * @return a time in microseconds */ public abstract long getTotalTimeLocked(long batteryRealtime, int which); - + /** * Temporary for debugging. */ @@ -234,11 +248,17 @@ public abstract class BatteryStats implements Parcelable { public abstract void noteScanWifiLockReleasedLocked(); public abstract void noteWifiMulticastEnabledLocked(); public abstract void noteWifiMulticastDisabledLocked(); + public abstract void noteAudioTurnedOnLocked(); + public abstract void noteAudioTurnedOffLocked(); + public abstract void noteVideoTurnedOnLocked(); + public abstract void noteVideoTurnedOffLocked(); public abstract long getWifiTurnedOnTime(long batteryRealtime, int which); public abstract long getFullWifiLockTime(long batteryRealtime, int which); public abstract long getScanWifiLockTime(long batteryRealtime, int which); public abstract long getWifiMulticastTime(long batteryRealtime, int which); + public abstract long getAudioTurnedOnTime(long batteryRealtime, int which); + public abstract long getVideoTurnedOnTime(long batteryRealtime, int which); /** * Note that these must match the constants in android.os.LocalPowerManager. @@ -287,6 +307,13 @@ public abstract class BatteryStats implements Parcelable { * @param which one of STATS_TOTAL, STATS_LAST, or STATS_CURRENT. */ public abstract int getStarts(int which); + + /** + * Returns the cpu time spent in microseconds while the process was in the foreground. + * @param which one of STATS_TOTAL, STATS_LAST, STATS_CURRENT or STATS_UNPLUGGED + * @return foreground cpu time in microseconds + */ + public abstract long getForegroundTime(int which); } /** @@ -344,7 +371,7 @@ public abstract class BatteryStats implements Parcelable { public abstract int getStartCount(); /** - * Returns the time in milliseconds that the screen has been on while the device was + * Returns the time in microseconds that the screen has been on while the device was * running on battery. * * {@hide} @@ -364,7 +391,7 @@ public abstract class BatteryStats implements Parcelable { public static final int NUM_SCREEN_BRIGHTNESS_BINS = 5; /** - * Returns the time in milliseconds that the screen has been on with + * Returns the time in microseconds that the screen has been on with * the given brightness * * {@hide} @@ -375,7 +402,7 @@ public abstract class BatteryStats implements Parcelable { public abstract int getInputEventCount(int which); /** - * Returns the time in milliseconds that the phone has been on while the device was + * Returns the time in microseconds that the phone has been on while the device was * running on battery. * * {@hide} @@ -395,7 +422,7 @@ public abstract class BatteryStats implements Parcelable { public static final int NUM_SIGNAL_STRENGTH_BINS = 5; /** - * Returns the time in milliseconds that the phone has been running with + * Returns the time in microseconds that the phone has been running with * the given signal strength. * * {@hide} @@ -423,7 +450,7 @@ public abstract class BatteryStats implements Parcelable { public static final int NUM_DATA_CONNECTION_TYPES = 5; /** - * Returns the time in milliseconds that the phone has been running with + * Returns the time in microseconds that the phone has been running with * the given data connection. * * {@hide} @@ -440,7 +467,7 @@ public abstract class BatteryStats implements Parcelable { public abstract int getPhoneDataConnectionCount(int dataType, int which); /** - * Returns the time in milliseconds that wifi has been on while the device was + * Returns the time in microseconds that wifi has been on while the device was * running on battery. * * {@hide} @@ -448,7 +475,7 @@ public abstract class BatteryStats implements Parcelable { public abstract long getWifiOnTime(long batteryRealtime, int which); /** - * Returns the time in milliseconds that wifi has been on and the driver has + * Returns the time in microseconds that wifi has been on and the driver has * been in the running state while the device was running on battery. * * {@hide} @@ -456,7 +483,7 @@ public abstract class BatteryStats implements Parcelable { public abstract long getWifiRunningTime(long batteryRealtime, int which); /** - * Returns the time in milliseconds that bluetooth has been on while the device was + * Returns the time in microseconds that bluetooth has been on while the device was * running on battery. * * {@hide} diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index 5487c545c12adb6bac35818b8df994f44e25565c..830b0bd7ceb1a565146699c7f727ead880e1865e 100644 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -38,6 +38,12 @@ public class Build { /** The name of the underlying board, like "goldfish". */ public static final String BOARD = getString("ro.product.board"); + /** The name of the instruction set (CPU type + ABI convention) of native code. */ + public static final String CPU_ABI = getString("ro.product.cpu.abi"); + + /** The manufacturer of the product/hardware. */ + public static final String MANUFACTURER = getString("ro.product.manufacturer"); + /** The brand (e.g., carrier) the software is customized for, if any. */ public static final String BRAND = getString("ro.product.brand"); @@ -86,6 +92,12 @@ public class Build { * increment monotonically with each official platform release. */ public static class VERSION_CODES { + /** + * Magic version number for a current development build, which has + * not yet turned into an official release. + */ + public static final int CUR_DEVELOPMENT = 10000; + /** * October 2008: The original, first, version of Android. Yay! */ @@ -98,6 +110,19 @@ public class Build { * May 2009: Android 1.5. */ public static final int CUPCAKE = 3; + /** + * Current work on "Donut" development branch. + * + *

      Applications targeting this or a later release will get these + * new changes in behavior:

      + *
        + *
      • They must explicitly request the + * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} permission to be + * able to modify the contents of the SD card. (Apps targeting + * earlier versions will always request the permission.) + *
      + */ + public static final int DONUT = CUR_DEVELOPMENT; } /** The type of build, like "user" or "eng". */ diff --git a/core/java/android/os/Bundle.java b/core/java/android/os/Bundle.java index b669fa2d695f53105ca1ef46db51a3e3b17df2ae..a91655f8e8e91d30b9875441bb57e1db85105722 100644 --- a/core/java/android/os/Bundle.java +++ b/core/java/android/os/Bundle.java @@ -78,6 +78,10 @@ public final class Bundle implements Parcelable, Cloneable { readFromParcel(parcelledData); } + /* package */ Bundle(Parcel parcelledData, int length) { + readFromParcelInner(parcelledData, length); + } + /** * Constructs a new, empty Bundle that uses a specific ClassLoader for * instantiating Parcelable and Serializable objects. @@ -155,13 +159,14 @@ public final class Bundle implements Parcelable, Cloneable { return; } - mParcelledData.setDataPosition(0); - Bundle b = mParcelledData.readBundleUnpacked(mClassLoader); - mMap = b.mMap; - - mHasFds = mParcelledData.hasFileDescriptors(); - mFdsKnown = true; - + int N = mParcelledData.readInt(); + if (N < 0) { + return; + } + if (mMap == null) { + mMap = new HashMap(); + } + mParcelledData.readMapInternal(mMap, N, mClassLoader); mParcelledData.recycle(); mParcelledData = null; } @@ -1427,7 +1432,25 @@ public final class Bundle implements Parcelable, Cloneable { * @param parcel The parcel to copy this bundle to. */ public void writeToParcel(Parcel parcel, int flags) { - parcel.writeBundle(this); + if (mParcelledData != null) { + int length = mParcelledData.dataSize(); + parcel.writeInt(length); + parcel.writeInt(0x4C444E42); // 'B' 'N' 'D' 'L' + parcel.appendFrom(mParcelledData, 0, length); + } else { + parcel.writeInt(-1); // dummy, will hold length + parcel.writeInt(0x4C444E42); // 'B' 'N' 'D' 'L' + + int oldPos = parcel.dataPosition(); + parcel.writeMapInternal(mMap); + int newPos = parcel.dataPosition(); + + // Backpatch length + parcel.setDataPosition(oldPos - 8); + int length = newPos - oldPos; + parcel.writeInt(length); + parcel.setDataPosition(newPos); + } } /** @@ -1436,8 +1459,33 @@ public final class Bundle implements Parcelable, Cloneable { * @param parcel The parcel to overwrite this bundle from. */ public void readFromParcel(Parcel parcel) { - mParcelledData = parcel; - mHasFds = mParcelledData.hasFileDescriptors(); + int length = parcel.readInt(); + if (length < 0) { + throw new RuntimeException("Bad length in parcel: " + length); + } + readFromParcelInner(parcel, length); + } + + void readFromParcelInner(Parcel parcel, int length) { + int magic = parcel.readInt(); + if (magic != 0x4C444E42) { + //noinspection ThrowableInstanceNeverThrown + String st = Log.getStackTraceString(new RuntimeException()); + Log.e("Bundle", "readBundle: bad magic number"); + Log.e("Bundle", "readBundle: trace = " + st); + } + + // Advance within this Parcel + int offset = parcel.dataPosition(); + parcel.setDataPosition(offset + length); + + Parcel p = Parcel.obtain(); + p.setDataPosition(0); + p.appendFrom(parcel, offset, length); + p.setDataPosition(0); + + mParcelledData = p; + mHasFds = p.hasFileDescriptors(); mFdsKnown = true; } diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java index 8fcb4d7a40a3054b3a1082ecc5f9a58fd67438ef..d40ea6b300c5516c87987402195a3b809d72336c 100644 --- a/core/java/android/os/Debug.java +++ b/core/java/android/os/Debug.java @@ -21,6 +21,7 @@ import com.android.internal.util.TypedProperties; import android.util.Config; import android.util.Log; +import java.io.FileDescriptor; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FileReader; @@ -377,6 +378,20 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo VMDebug.startMethodTracing(pathName, bufferSize, flags); } + /** + * Like startMethodTracing(String, int, int), but taking an already-opened + * FileDescriptor in which the trace is written. The file name is also + * supplied simply for logging. Makes a dup of the file descriptor. + * + * Not exposed in the SDK unless we are really comfortable with supporting + * this and find it would be useful. + * @hide + */ + public static void startMethodTracing(String traceName, FileDescriptor fd, + int bufferSize, int flags) { + VMDebug.startMethodTracing(traceName, fd, bufferSize, flags); + } + /** * Determine whether method tracing is currently active. * @hide diff --git a/core/java/android/os/MemoryFile.java b/core/java/android/os/MemoryFile.java index 76e4f47f8d2b55094bf057e8c1b57dd905c8de51..c14925cd38394f91807ef05cd82dee6514c53376 100644 --- a/core/java/android/os/MemoryFile.java +++ b/core/java/android/os/MemoryFile.java @@ -18,6 +18,7 @@ package android.os; import android.util.Log; +import java.io.FileDescriptor; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -35,48 +36,120 @@ import java.io.OutputStream; public class MemoryFile { private static String TAG = "MemoryFile"; - - // returns fd - private native int native_open(String name, int length); + + // mmap(2) protection flags from + private static final int PROT_READ = 0x1; + private static final int PROT_WRITE = 0x2; + + private static native FileDescriptor native_open(String name, int length) throws IOException; // returns memory address for ashmem region - private native int native_mmap(int fd, int length); - private native void native_close(int fd); - private native int native_read(int fd, int address, byte[] buffer, - int srcOffset, int destOffset, int count, boolean isUnpinned); - private native void native_write(int fd, int address, byte[] buffer, - int srcOffset, int destOffset, int count, boolean isUnpinned); - private native void native_pin(int fd, boolean pin); - - private int mFD; // ashmem file descriptor + private static native int native_mmap(FileDescriptor fd, int length, int mode) + throws IOException; + private static native void native_munmap(int addr, int length) throws IOException; + private static native void native_close(FileDescriptor fd); + private static native int native_read(FileDescriptor fd, int address, byte[] buffer, + int srcOffset, int destOffset, int count, boolean isUnpinned) throws IOException; + private static native void native_write(FileDescriptor fd, int address, byte[] buffer, + int srcOffset, int destOffset, int count, boolean isUnpinned) throws IOException; + private static native void native_pin(FileDescriptor fd, boolean pin) throws IOException; + private static native boolean native_is_ashmem_region(FileDescriptor fd) throws IOException; + + private FileDescriptor mFD; // ashmem file descriptor private int mAddress; // address of ashmem memory private int mLength; // total length of our ashmem region private boolean mAllowPurging = false; // true if our ashmem region is unpinned + private final boolean mOwnsRegion; // false if this is a ref to an existing ashmem region /** - * MemoryFile constructor. + * Allocates a new ashmem region. The region is initially not purgable. * * @param name optional name for the file (can be null). * @param length of the memory file in bytes. + * @throws IOException if the memory file could not be created. */ - public MemoryFile(String name, int length) { + public MemoryFile(String name, int length) throws IOException { mLength = length; mFD = native_open(name, length); - mAddress = native_mmap(mFD, length); + mAddress = native_mmap(mFD, length, PROT_READ | PROT_WRITE); + mOwnsRegion = true; } /** - * Closes and releases all resources for the memory file. + * Creates a reference to an existing memory file. Changes to the original file + * will be available through this reference. + * Calls to {@link #allowPurging(boolean)} on the returned MemoryFile will fail. + * + * @param fd File descriptor for an existing memory file, as returned by + * {@link #getFileDescriptor()}. This file descriptor will be closed + * by {@link #close()}. + * @param length Length of the memory file in bytes. + * @param mode File mode. Currently only "r" for read-only access is supported. + * @throws NullPointerException if fd is null. + * @throws IOException If fd does not refer to an existing memory file, + * or if the file mode of the existing memory file is more restrictive + * than mode. + * + * @hide + */ + public MemoryFile(FileDescriptor fd, int length, String mode) throws IOException { + if (fd == null) { + throw new NullPointerException("File descriptor is null."); + } + if (!isMemoryFile(fd)) { + throw new IllegalArgumentException("Not a memory file."); + } + mLength = length; + mFD = fd; + mAddress = native_mmap(mFD, length, modeToProt(mode)); + mOwnsRegion = false; + } + + /** + * Closes the memory file. If there are no other open references to the memory + * file, it will be deleted. */ public void close() { - if (mFD > 0) { + deactivate(); + if (!isClosed()) { native_close(mFD); - mFD = 0; } } + /** + * Unmaps the memory file from the process's memory space, but does not close it. + * After this method has been called, read and write operations through this object + * will fail, but {@link #getFileDescriptor()} will still return a valid file descriptor. + * + * @hide + */ + public void deactivate() { + if (!isDeactivated()) { + try { + native_munmap(mAddress, mLength); + mAddress = 0; + } catch (IOException ex) { + Log.e(TAG, ex.toString()); + } + } + } + + /** + * Checks whether the memory file has been deactivated. + */ + private boolean isDeactivated() { + return mAddress == 0; + } + + /** + * Checks whether the memory file has been closed. + */ + private boolean isClosed() { + return !mFD.valid(); + } + @Override protected void finalize() { - if (mFD > 0) { + if (!isClosed()) { Log.e(TAG, "MemoryFile.finalize() called while ashmem still open"); close(); } @@ -108,6 +181,9 @@ public class MemoryFile * @return previous value of allowPurging */ synchronized public boolean allowPurging(boolean allowPurging) throws IOException { + if (!mOwnsRegion) { + throw new IOException("Only the owner can make ashmem regions purgable."); + } boolean oldValue = mAllowPurging; if (oldValue != allowPurging) { native_pin(mFD, !allowPurging); @@ -131,7 +207,6 @@ public class MemoryFile @return OutputStream */ public OutputStream getOutputStream() { - return new MemoryOutputStream(); } @@ -144,9 +219,13 @@ public class MemoryFile * @param destOffset offset into the byte array buffer to read into. * @param count number of bytes to read. * @return number of bytes read. + * @throws IOException if the memory file has been purged or deactivated. */ public int readBytes(byte[] buffer, int srcOffset, int destOffset, int count) throws IOException { + if (isDeactivated()) { + throw new IOException("Can't read from deactivated memory file."); + } if (destOffset < 0 || destOffset > buffer.length || count < 0 || count > buffer.length - destOffset || srcOffset < 0 || srcOffset > mLength @@ -164,9 +243,13 @@ public class MemoryFile * @param srcOffset offset into the byte array buffer to write from. * @param destOffset offset into the memory file to write to. * @param count number of bytes to write. + * @throws IOException if the memory file has been purged or deactivated. */ public void writeBytes(byte[] buffer, int srcOffset, int destOffset, int count) throws IOException { + if (isDeactivated()) { + throw new IOException("Can't write to deactivated memory file."); + } if (srcOffset < 0 || srcOffset > buffer.length || count < 0 || count > buffer.length - srcOffset || destOffset < 0 || destOffset > mLength @@ -176,6 +259,64 @@ public class MemoryFile native_write(mFD, mAddress, buffer, srcOffset, destOffset, count, mAllowPurging); } + /** + * Gets a ParcelFileDescriptor for the memory file. See {@link #getFileDescriptor()} + * for caveats. This must be here to allow classes outside android.osfd is not a valid file descriptor. + * + * @hide + */ + public static boolean isMemoryFile(FileDescriptor fd) throws IOException { + return native_is_ashmem_region(fd); + } + + /** + * Converts a file mode string to a prot value as expected by + * native_mmap(). + * + * @throws IllegalArgumentException if the file mode is invalid. + */ + private static int modeToProt(String mode) { + if ("r".equals(mode)) { + return PROT_READ; + } else { + throw new IllegalArgumentException("Unsupported file mode: '" + mode + "'"); + } + } + private class MemoryInputStream extends InputStream { private int mMark = 0; @@ -212,13 +353,22 @@ public class MemoryFile } int result = read(mSingleByte, 0, 1); if (result != 1) { - throw new IOException("read() failed"); + return -1; } return mSingleByte[0]; } @Override public int read(byte buffer[], int offset, int count) throws IOException { + if (offset < 0 || count < 0 || offset + count > buffer.length) { + // readBytes() also does this check, but we need to do it before + // changing count. + throw new IndexOutOfBoundsException(); + } + count = Math.min(count, available()); + if (count < 1) { + return -1; + } int result = readBytes(buffer, mOffset, offset, count); if (result > 0) { mOffset += result; diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java index 9a71f6e0f2d42abcaf099ec0fd5205db42ac02fd..6cfcceedfa467d29c7a248e6a39f43232051ea27 100644 --- a/core/java/android/os/Parcel.java +++ b/core/java/android/os/Parcel.java @@ -457,7 +457,7 @@ public final class Parcel { * Flatten a Map into the parcel at the current dataPosition(), * growing dataCapacity() if needed. The Map keys must be String objects. */ - private void writeMapInternal(Map val) { + /* package */ void writeMapInternal(Map val) { if (val == null) { writeInt(-1); return; @@ -480,23 +480,7 @@ public final class Parcel { return; } - if (val.mParcelledData != null) { - int length = val.mParcelledData.dataSize(); - appendFrom(val.mParcelledData, 0, length); - } else { - writeInt(-1); // dummy, will hold length - int oldPos = dataPosition(); - writeInt(0x4C444E42); // 'B' 'N' 'D' 'L' - - writeMapInternal(val.mMap); - int newPos = dataPosition(); - - // Backpatch length - setDataPosition(oldPos - 4); - int length = newPos - oldPos; - writeInt(length); - setDataPosition(newPos); - } + val.writeToParcel(this, 0); } /** @@ -1352,60 +1336,18 @@ public final class Parcel { * Returns null if the previously written Bundle object was null. */ public final Bundle readBundle(ClassLoader loader) { - int offset = dataPosition(); int length = readInt(); if (length < 0) { return null; } - int magic = readInt(); - if (magic != 0x4C444E42) { - //noinspection ThrowableInstanceNeverThrown - String st = Log.getStackTraceString(new RuntimeException()); - Log.e("Bundle", "readBundle: bad magic number"); - Log.e("Bundle", "readBundle: trace = " + st); - } - - // Advance within this Parcel - setDataPosition(offset + length + 4); - - Parcel p = new Parcel(0); - p.setDataPosition(0); - p.appendFrom(this, offset, length + 4); - p.setDataPosition(0); - final Bundle bundle = new Bundle(p); + + final Bundle bundle = new Bundle(this, length); if (loader != null) { bundle.setClassLoader(loader); } return bundle; } - /** - * Read and return a new Bundle object from the parcel at the current - * dataPosition(). Returns null if the previously written Bundle object was - * null. The returned bundle will have its contents fully unpacked using - * the given ClassLoader. - */ - /* package */ Bundle readBundleUnpacked(ClassLoader loader) { - int length = readInt(); - if (length == -1) { - return null; - } - int magic = readInt(); - if (magic != 0x4C444E42) { - //noinspection ThrowableInstanceNeverThrown - String st = Log.getStackTraceString(new RuntimeException()); - Log.e("Bundle", "readBundleUnpacked: bad magic number"); - Log.e("Bundle", "readBundleUnpacked: trace = " + st); - } - Bundle m = new Bundle(loader); - int N = readInt(); - if (N < 0) { - return null; - } - readMapInternal(m.mMap, N, loader); - return m; - } - /** * Read and return a byte[] object from the parcel. */ @@ -1998,7 +1940,7 @@ public final class Parcel { private native void init(int obj); private native void destroy(); - private void readMapInternal(Map outVal, int N, + /* package */ void readMapInternal(Map outVal, int N, ClassLoader loader) { while (N > 0) { Object key = readValue(loader); diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java index 30acef9ee7b581606bf7b38e669bb94916f0b198..1214abcad634a537b7d41dfd66dd5b193ae08da8 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -573,7 +573,21 @@ public class Process { * directly to a gid. */ public static final native int getGidForName(String name); - + + /** + * Returns a uid for a currently running process. + * @param pid the process id + * @return the uid of the process, or -1 if the process is not running. + * @hide pending API council review + */ + public static final int getUidForPid(int pid) { + String[] procStatusLabels = { "Uid:" }; + long[] procStatusValues = new long[1]; + procStatusValues[0] = -1; + Process.readProcLines("/proc/" + pid + "/status", procStatusLabels, procStatusValues); + return (int) procStatusValues[0]; + } + /** * Set the priority of a thread, based on Linux priorities. * @@ -604,6 +618,20 @@ public class Process { */ public static final native void setThreadGroup(int tid, int group) throws IllegalArgumentException, SecurityException; + /** + * Sets the scheduling group for a process and all child threads + * @hide + * @param pid The indentifier of the process to change. + * @param group The target group for this process. + * + * @throws IllegalArgumentException Throws IllegalArgumentException if + * tid does not exist. + * @throws SecurityException Throws SecurityException if your process does + * not have permission to modify the given thread, or to use the given + * priority. + */ + public static final native void setProcessGroup(int pid, int group) + throws IllegalArgumentException, SecurityException; /** * Set the priority of the calling thread, based on Linux priorities. See diff --git a/core/java/android/pim/EventRecurrence.java b/core/java/android/pim/EventRecurrence.java index edf69eea8a86d182ada45958d93dbe557a375c62..3ea9b4a89bbc806512f280888e7afe3ae40efe60 100644 --- a/core/java/android/pim/EventRecurrence.java +++ b/core/java/android/pim/EventRecurrence.java @@ -408,13 +408,13 @@ public class EventRecurrence private String dayToString(Resources r, int day) { switch (day) { - case SU: return r.getString(com.android.internal.R.string.sunday); - case MO: return r.getString(com.android.internal.R.string.monday); - case TU: return r.getString(com.android.internal.R.string.tuesday); - case WE: return r.getString(com.android.internal.R.string.wednesday); - case TH: return r.getString(com.android.internal.R.string.thursday); - case FR: return r.getString(com.android.internal.R.string.friday); - case SA: return r.getString(com.android.internal.R.string.saturday); + case SU: return r.getString(com.android.internal.R.string.day_of_week_long_sunday); + case MO: return r.getString(com.android.internal.R.string.day_of_week_long_monday); + case TU: return r.getString(com.android.internal.R.string.day_of_week_long_tuesday); + case WE: return r.getString(com.android.internal.R.string.day_of_week_long_wednesday); + case TH: return r.getString(com.android.internal.R.string.day_of_week_long_thursday); + case FR: return r.getString(com.android.internal.R.string.day_of_week_long_friday); + case SA: return r.getString(com.android.internal.R.string.day_of_week_long_saturday); default: throw new IllegalArgumentException("bad day argument: " + day); } } diff --git a/core/java/android/preference/CheckBoxPreference.java b/core/java/android/preference/CheckBoxPreference.java index 1e9b7aed59ccdf89ddcc2c88ebff0f0e96f60eab..cf5664c3814e9c8b071c621d81b51d5297684ff4 100644 --- a/core/java/android/preference/CheckBoxPreference.java +++ b/core/java/android/preference/CheckBoxPreference.java @@ -16,6 +16,7 @@ package android.preference; +import android.app.Service; import android.content.Context; import android.content.SharedPreferences; import android.content.res.TypedArray; @@ -23,6 +24,8 @@ import android.os.Parcel; import android.os.Parcelable; import android.util.AttributeSet; import android.view.View; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityManager; import android.widget.Checkable; import android.widget.TextView; @@ -42,6 +45,9 @@ public class CheckBoxPreference extends Preference { private CharSequence mSummaryOff; private boolean mChecked; + private boolean mSendAccessibilityEventViewClickedType; + + private AccessibilityManager mAccessibilityManager; private boolean mDisableDependentsState; @@ -55,6 +61,9 @@ public class CheckBoxPreference extends Preference { mDisableDependentsState = a.getBoolean( com.android.internal.R.styleable.CheckBoxPreference_disableDependentsState, false); a.recycle(); + + mAccessibilityManager = + (AccessibilityManager) getContext().getSystemService(Service.ACCESSIBILITY_SERVICE); } public CheckBoxPreference(Context context, AttributeSet attrs) { @@ -64,14 +73,26 @@ public class CheckBoxPreference extends Preference { public CheckBoxPreference(Context context) { this(context, null); } - + @Override protected void onBindView(View view) { super.onBindView(view); - + View checkboxView = view.findViewById(com.android.internal.R.id.checkbox); if (checkboxView != null && checkboxView instanceof Checkable) { ((Checkable) checkboxView).setChecked(mChecked); + + // send an event to announce the value change of the CheckBox and is done here + // because clicking a preference does not immediately change the checked state + // for example when enabling the WiFi + if (mSendAccessibilityEventViewClickedType && + mAccessibilityManager.isEnabled() && + checkboxView.isEnabled()) { + mSendAccessibilityEventViewClickedType = false; + + int eventType = AccessibilityEvent.TYPE_VIEW_CLICKED; + checkboxView.sendAccessibilityEventUnchecked(AccessibilityEvent.obtain(eventType)); + } } // Sync the summary view @@ -85,7 +106,7 @@ public class CheckBoxPreference extends Preference { summaryView.setText(mSummaryOff); useDefaultSummary = false; } - + if (useDefaultSummary) { final CharSequence summary = getSummary(); if (summary != null) { @@ -111,6 +132,10 @@ public class CheckBoxPreference extends Preference { boolean newValue = !isChecked(); + // in onBindView() an AccessibilityEventViewClickedType is sent to announce the change + // not sending + mSendAccessibilityEventViewClickedType = true; + if (!callChangeListener(newValue)) { return; } @@ -124,10 +149,11 @@ public class CheckBoxPreference extends Preference { * @param checked The checked state. */ public void setChecked(boolean checked) { + mChecked = checked; persistBoolean(checked); - + notifyDependencyChange(shouldDisableDependents()); notifyChanged(); diff --git a/core/java/android/preference/PreferenceScreen.java b/core/java/android/preference/PreferenceScreen.java index 5353b531dd068645d20b63b7b2a267ea2d412cc0..95e54324f4458debc0c51bd3e35f1b8ac44985f2 100644 --- a/core/java/android/preference/PreferenceScreen.java +++ b/core/java/android/preference/PreferenceScreen.java @@ -22,6 +22,7 @@ import android.content.DialogInterface; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; +import android.text.TextUtils; import android.util.AttributeSet; import android.view.View; import android.widget.Adapter; @@ -147,13 +148,20 @@ public final class PreferenceScreen extends PreferenceGroup implements AdapterVi ListView listView = new ListView(context); bind(listView); - Dialog dialog = mDialog = new Dialog(context, com.android.internal.R.style.Theme_NoTitleBar); + // Set the title bar if title is available, else no title bar + final CharSequence title = getTitle(); + Dialog dialog = mDialog = new Dialog(context, TextUtils.isEmpty(title) + ? com.android.internal.R.style.Theme_NoTitleBar + : com.android.internal.R.style.Theme); dialog.setContentView(listView); + if (!TextUtils.isEmpty(title)) { + dialog.setTitle(title); + } dialog.setOnDismissListener(this); if (state != null) { dialog.onRestoreInstanceState(state); } - + // Add the screen to the list of preferences screens opened as dialogs getPreferenceManager().addPreferencesScreen(dialog); diff --git a/core/java/android/provider/Browser.java b/core/java/android/provider/Browser.java index c597b3c62125dcbe8455b032e89f469ad043b91a..1ba5e25e1d54c3a201f6673c17b18542c5727e21 100644 --- a/core/java/android/provider/Browser.java +++ b/core/java/android/provider/Browser.java @@ -33,6 +33,12 @@ public class Browser { public static final Uri BOOKMARKS_URI = Uri.parse("content://browser/bookmarks"); + /** + * The inline scheme to show embedded content in a browser. + * @hide + */ + public static final Uri INLINE_URI = Uri.parse("inline:"); + /** * The name of extra data when starting Browser with ACTION_VIEW or * ACTION_SEARCH intent. @@ -53,7 +59,48 @@ public class Browser { * identifier. */ public static final String EXTRA_APPLICATION_ID = - "com.android.browser.application_id"; + "com.android.browser.application_id"; + + /** + * The content to be rendered when url's scheme is inline. + * @hide + */ + public static final String EXTRA_INLINE_CONTENT ="com.android.browser.inline.content"; + + /** + * The encoding of the inlined content for inline scheme. + * @hide + */ + public static final String EXTRA_INLINE_ENCODING ="com.android.browser.inline.encoding"; + + /** + * The url used when the inline content is falied to render. + * @hide + */ + public static final String EXTRA_INLINE_FAILURL ="com.android.browser.inline.failurl"; + + /** + * The name of the extra data in the VIEW intent. The data is in boolean. + *

      + * If the Browser is handling the intent and the setting for + * USE_LOCATION_FOR_SERVICES is allow, the Browser will send the location in + * the POST data if this extra data is presented and it is true. + *

      + * pending api approval + * @hide + */ + public static final String EXTRA_APPEND_LOCATION = "com.android.browser.append_location"; + + /** + * The name of the extra data in the VIEW intent. The data is in the format of + * a byte array. + *

      + * Any value sent here will be passed in the http request to the provided url as post data. + *

      + * pending api approval + * @hide + */ + public static final String EXTRA_POST_DATA = "com.android.browser.post_data"; /* if you change column order you must also change indices below */ @@ -132,6 +179,7 @@ public class Browser { /** * Return a cursor pointing to a list of all the bookmarks. + * Requires {@link android.Manifest.permission#READ_HISTORY_BOOKMARKS} * @param cr The ContentResolver used to access the database. */ public static final Cursor getAllBookmarks(ContentResolver cr) throws @@ -143,6 +191,7 @@ public class Browser { /** * Return a cursor pointing to a list of all visited site urls. + * Requires {@link android.Manifest.permission#READ_HISTORY_BOOKMARKS} * @param cr The ContentResolver used to access the database. */ public static final Cursor getAllVisitedUrls(ContentResolver cr) throws @@ -154,6 +203,8 @@ public class Browser { /** * Update the visited history to acknowledge that a site has been * visited. + * Requires {@link android.Manifest.permission#READ_HISTORY_BOOKMARKS} + * Requires {@link android.Manifest.permission#WRITE_HISTORY_BOOKMARKS} * @param cr The ContentResolver used to access the database. * @param url The site being visited. * @param real Whether this is an actual visit, and should be added to the @@ -203,6 +254,8 @@ public class Browser { * of them. This is used to keep our history table to a * reasonable size. Note: it does not prune bookmarks. If the * user wants 1000 bookmarks, the user gets 1000 bookmarks. + * Requires {@link android.Manifest.permission#READ_HISTORY_BOOKMARKS} + * Requires {@link android.Manifest.permission#WRITE_HISTORY_BOOKMARKS} * * @param cr The ContentResolver used to access the database. */ @@ -236,6 +289,7 @@ public class Browser { /** * Returns whether there is any history to clear. + * Requires {@link android.Manifest.permission#READ_HISTORY_BOOKMARKS} * @param cr The ContentResolver used to access the database. * @return boolean True if the history can be cleared. */ @@ -261,6 +315,7 @@ public class Browser { /** * Delete all entries from the bookmarks/history table which are * not bookmarks. Also set all visited bookmarks to unvisited. + * Requires {@link android.Manifest.permission#WRITE_HISTORY_BOOKMARKS} * @param cr The ContentResolver used to access the database. */ public static final void clearHistory(ContentResolver cr) { @@ -270,6 +325,8 @@ public class Browser { /** * Helper function to delete all history items and revert all * bookmarks to zero visits which meet the criteria provided. + * Requires {@link android.Manifest.permission#READ_HISTORY_BOOKMARKS} + * Requires {@link android.Manifest.permission#WRITE_HISTORY_BOOKMARKS} * @param cr The ContentResolver used to access the database. * @param whereClause String to limit the items affected. * null means all items. @@ -332,6 +389,7 @@ public class Browser { /** * Delete all history items from begin to end. + * Requires {@link android.Manifest.permission#WRITE_HISTORY_BOOKMARKS} * @param cr The ContentResolver used to access the database. * @param begin First date to remove. If -1, all dates before end. * Inclusive. @@ -359,6 +417,7 @@ public class Browser { /** * Remove a specific url from the history database. + * Requires {@link android.Manifest.permission#WRITE_HISTORY_BOOKMARKS} * @param cr The ContentResolver used to access the database. * @param url url to remove. */ @@ -372,6 +431,8 @@ public class Browser { /** * Add a search string to the searches database. + * Requires {@link android.Manifest.permission#READ_HISTORY_BOOKMARKS} + * Requires {@link android.Manifest.permission#WRITE_HISTORY_BOOKMARKS} * @param cr The ContentResolver used to access the database. * @param search The string to add to the searches database. */ @@ -401,6 +462,7 @@ public class Browser { } /** * Remove all searches from the search database. + * Requires {@link android.Manifest.permission#WRITE_HISTORY_BOOKMARKS} * @param cr The ContentResolver used to access the database. */ public static final void clearSearches(ContentResolver cr) { @@ -415,6 +477,7 @@ public class Browser { /** * Request all icons from the database. + * Requires {@link android.Manifest.permission#READ_HISTORY_BOOKMARKS} * @param cr The ContentResolver used to access the database. * @param where Clause to be used to limit the query from the database. * Must be an allowable string to be passed into a database query. diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java index abd6934a86b3932d9d7b844273bba140cdbb953a..7d03801d7bd10b43f4159e41c0dcf49e050a4d4e 100644 --- a/core/java/android/provider/CallLog.java +++ b/core/java/android/provider/CallLog.java @@ -151,6 +151,9 @@ public class CallLog { int presentation, int callType, long start, int duration) { final ContentResolver resolver = context.getContentResolver(); + // TODO(Moto): Which is correct: original code, this only changes the + // number if the number is empty and never changes the caller info name. + if (false) { if (TextUtils.isEmpty(number)) { if (presentation == Connection.PRESENTATION_RESTRICTED) { number = CallerInfo.PRIVATE_NUMBER; @@ -160,7 +163,22 @@ public class CallLog { number = CallerInfo.UNKNOWN_NUMBER; } } - + } else { + // NEWCODE: From Motorola + + //If this is a private number then set the number to Private, otherwise check + //if the number field is empty and set the number to Unavailable + if (presentation == Connection.PRESENTATION_RESTRICTED) { + number = CallerInfo.PRIVATE_NUMBER; + ci.name = ""; + } else if (presentation == Connection.PRESENTATION_PAYPHONE) { + number = CallerInfo.PAYPHONE_NUMBER; + ci.name = ""; + } else if (TextUtils.isEmpty(number) || presentation == Connection.PRESENTATION_UNKNOWN) { + number = CallerInfo.UNKNOWN_NUMBER; + ci.name = ""; + } + } ContentValues values = new ContentValues(5); values.put(NUMBER, number); diff --git a/core/java/android/provider/Checkin.java b/core/java/android/provider/Checkin.java index 3c23db03ac0af2337a679b781df9fc046bb17ec0..f2c275e6993168e1c86febb140c7d6b42c1444d1 100644 --- a/core/java/android/provider/Checkin.java +++ b/core/java/android/provider/Checkin.java @@ -137,6 +137,8 @@ public final class Checkin { CRASHES_TRUNCATED, ELAPSED_REALTIME_SEC, ELAPSED_UPTIME_SEC, + HTTP_REQUEST, + HTTP_REUSED, HTTP_STATUS, PHONE_GSM_REGISTERED, PHONE_GPRS_ATTEMPTED, @@ -351,6 +353,3 @@ public final class Checkin { } } } - - - diff --git a/core/java/android/provider/Contacts.java b/core/java/android/provider/Contacts.java index 3141f1aede53348a9b7bbfac75e460eae3a31d42..84fe1841888ec686c02a52db264bce1315a25338 100644 --- a/core/java/android/provider/Contacts.java +++ b/core/java/android/provider/Contacts.java @@ -340,27 +340,33 @@ public class Contacts { } /** - * Adds a person to the My Contacts group. - * - * @param resolver the resolver to use - * @param personId the person to add to the group - * @return the URI of the group membership row - * @throws IllegalStateException if the My Contacts group can't be found + * @hide Used in vCard parser code. */ - public static Uri addToMyContactsGroup(ContentResolver resolver, long personId) { - long groupId = 0; + public static long tryGetMyContactsGroupId(ContentResolver resolver) { Cursor groupsCursor = resolver.query(Groups.CONTENT_URI, GROUPS_PROJECTION, Groups.SYSTEM_ID + "='" + Groups.GROUP_MY_CONTACTS + "'", null, null); if (groupsCursor != null) { try { if (groupsCursor.moveToFirst()) { - groupId = groupsCursor.getLong(0); + return groupsCursor.getLong(0); } } finally { groupsCursor.close(); } } + return 0; + } + /** + * Adds a person to the My Contacts group. + * + * @param resolver the resolver to use + * @param personId the person to add to the group + * @return the URI of the group membership row + * @throws IllegalStateException if the My Contacts group can't be found + */ + public static Uri addToMyContactsGroup(ContentResolver resolver, long personId) { + long groupId = tryGetMyContactsGroupId(resolver); if (groupId == 0) { throw new IllegalStateException("Failed to find the My Contacts group"); } @@ -868,6 +874,17 @@ public class Contacts { public static final int TYPE_WORK = 2; public static final int TYPE_OTHER = 3; + /** + * @hide This is temporal. TYPE_MOBILE should be added to TYPE in the future. + */ + public static final int MOBILE_EMAIL_TYPE_INDEX = 2; + + /** + * @hide This is temporal. TYPE_MOBILE should be added to TYPE in the future. + * This is not "mobile" but "CELL" since vCard uses it for identifying mobile phone. + */ + public static final String MOBILE_EMAIL_TYPE_NAME = "_AUTO_CELL"; + /** * The user defined label for the the contact method. *

      Type: TEXT

      @@ -1005,7 +1022,13 @@ public class Contacts { } } else { if (!TextUtils.isEmpty(label)) { - display = label; + if (label.toString().equals(MOBILE_EMAIL_TYPE_NAME)) { + display = + context.getString( + com.android.internal.R.string.mobileEmailTypeName); + } else { + display = label; + } } } break; diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java index b6f96c4e0d4bcf79a4be730443a68493b2024b73..21e5865fef70129748942da94cf852f4098df41a 100644 --- a/core/java/android/provider/MediaStore.java +++ b/core/java/android/provider/MediaStore.java @@ -344,7 +344,10 @@ public final class MediaStore // Check if file exists with a FileInputStream FileInputStream stream = new FileInputStream(imagePath); try { - return insertImage(cr, BitmapFactory.decodeFile(imagePath), name, description); + Bitmap bm = BitmapFactory.decodeFile(imagePath); + String ret = insertImage(cr, bm, name, description); + bm.recycle(); + return ret; } finally { try { stream.close(); @@ -719,9 +722,15 @@ public final class MediaStore */ public static String keyFor(String name) { if (name != null) { + boolean sortfirst = false; if (name.equals(android.media.MediaFile.UNKNOWN_STRING)) { return "\001"; } + // Check if the first character is \001. We use this to + // force sorting of certain special files, like the silent ringtone. + if (name.startsWith("\001")) { + sortfirst = true; + } name = name.trim().toLowerCase(); if (name.startsWith("the ")) { name = name.substring(4); @@ -737,7 +746,7 @@ public final class MediaStore name.endsWith(", a") || name.endsWith(",a")) { name = name.substring(0, name.lastIndexOf(',')); } - name = name.replaceAll("[\\[\\]\\(\\)'.,?!]", "").trim(); + name = name.replaceAll("[\\[\\]\\(\\)\"'.,?!]", "").trim(); if (name.length() > 0) { // Insert a separator between the characters to avoid // matches on a partial character. If we ever change @@ -750,7 +759,11 @@ public final class MediaStore b.append('.'); } name = b.toString(); - return DatabaseUtils.getCollationKey(name); + String key = DatabaseUtils.getCollationKey(name); + if (sortfirst) { + key = "\001" + key; + } + return key; } else { return ""; } @@ -797,7 +810,7 @@ public final class MediaStore /** * The default sort order for this table */ - public static final String DEFAULT_SORT_ORDER = TITLE; + public static final String DEFAULT_SORT_ORDER = TITLE_KEY; /** * Activity Action: Start SoundRecorder application. @@ -894,7 +907,7 @@ public final class MediaStore /** * The default sort order for this table */ - public static final String DEFAULT_SORT_ORDER = TITLE; + public static final String DEFAULT_SORT_ORDER = TITLE_KEY; /** * The ID of the audio file diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 4dd6524fd2c08b0682fcf0e738f3feedf7c01d3c..aa583ac35b5e40d2a7763ebd942f37c4d7b2924e 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -148,7 +148,7 @@ public final class Settings { @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_WIFI_SETTINGS = "android.settings.WIFI_SETTINGS"; - + /** * Activity Action: Show settings to allow configuration of a static IP * address for Wi-Fi. @@ -305,7 +305,7 @@ public final class Settings { @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_QUICK_LAUNCH_SETTINGS = "android.settings.QUICK_LAUNCH_SETTINGS"; - + /** * Activity Action: Show settings to manage installed applications. *

      @@ -319,7 +319,7 @@ public final class Settings { @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_MANAGE_APPLICATIONS_SETTINGS = "android.settings.MANAGE_APPLICATIONS_SETTINGS"; - + /** * Activity Action: Show settings for system update functionality. *

      @@ -329,7 +329,7 @@ public final class Settings { * Input: Nothing. *

      * Output: Nothing. - * + * * @hide */ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) @@ -349,7 +349,7 @@ public final class Settings { @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_SYNC_SETTINGS = "android.settings.SYNC_SETTINGS"; - + /** * Activity Action: Show settings for selecting the network operator. *

      @@ -404,7 +404,7 @@ public final class Settings { @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_MEMORY_CARD_SETTINGS = "android.settings.MEMORY_CARD_SETTINGS"; - + // End of Intent actions for Settings private static final String JID_RESOURCE_PREFIX = "android"; @@ -495,7 +495,7 @@ public final class Settings { public static final String SYS_PROP_SETTING_VERSION = "sys.settings_system_version"; private static volatile NameValueCache mNameValueCache = null; - + private static final HashSet MOVED_TO_SECURE; static { MOVED_TO_SECURE = new HashSet(30); @@ -901,12 +901,12 @@ public final class Settings { * plugged in. */ public static final int WIFI_SLEEP_POLICY_NEVER_WHILE_PLUGGED = 1; - + /** * Value for {@link #WIFI_SLEEP_POLICY} to never go to sleep. */ public static final int WIFI_SLEEP_POLICY_NEVER = 2; - + /** * Whether to use static IP and other static network attributes. *

      @@ -1024,6 +1024,14 @@ public final class Settings { */ public static final String SCREEN_OFF_TIMEOUT = "screen_off_timeout"; + /** + * If 0, the compatibility mode is off for all applications. + * If 1, older applications run under compatibility mode. + * TODO: remove this settings before code freeze (bug/1907571) + * @hide + */ + public static final String COMPATIBILITY_MODE = "compatibility_mode"; + /** * The screen backlight brightness between 0 and 255. */ @@ -1115,12 +1123,12 @@ public final class Settings { * Note: This is a one-off setting that will be removed in the future * when there is profile support. For this reason, it is kept hidden * from the public APIs. - * + * * @hide */ - public static final String NOTIFICATIONS_USE_RING_VOLUME = + public static final String NOTIFICATIONS_USE_RING_VOLUME = "notifications_use_ring_volume"; - + /** * The mapping of stream type (integer) to its setting. */ @@ -1188,7 +1196,7 @@ public final class Settings { * feature converts two spaces to a "." and space. */ public static final String TEXT_AUTO_PUNCTUATE = "auto_punctuate"; - + /** * Setting to showing password characters in text editors. 1 = On, 0 = Off */ @@ -1265,18 +1273,126 @@ public final class Settings { */ public static final String DTMF_TONE_WHEN_DIALING = "dtmf_tone"; + /** + * CDMA only settings + * DTMF tone type played by the dialer when dialing. + * 0 = Normal + * 1 = Long + * @hide + */ + public static final String DTMF_TONE_TYPE_WHEN_DIALING = "dtmf_tone_type"; + + /** + * CDMA only settings + * Emergency Tone 0 = Off + * 1 = Alert + * 2 = Vibrate + * @hide + */ + public static final String EMERGENCY_TONE = "emergency_tone"; + + /** + * CDMA only settings + * Whether the auto retry is enabled. The value is + * boolean (1 or 0). + * @hide + */ + public static final String CALL_AUTO_RETRY = "call_auto_retry"; + + /** + * Whether the hearing aid is enabled. The value is + * boolean (1 or 0). + * @hide + */ + public static final String HEARING_AID = "hearing_aid"; + + /** + * CDMA only settings + * TTY Mode + * 0 = OFF + * 1 = FULL + * 2 = VCO + * 3 = HCO + * @hide + */ + public static final String TTY_MODE = "tty_mode"; + /** * Whether the sounds effects (key clicks, lid open ...) are enabled. The value is * boolean (1 or 0). */ public static final String SOUND_EFFECTS_ENABLED = "sound_effects_enabled"; - + /** * Whether the haptic feedback (long presses, ...) are enabled. The value is * boolean (1 or 0). */ public static final String HAPTIC_FEEDBACK_ENABLED = "haptic_feedback_enabled"; + /** + * Whether live web suggestions while the user types into search dialogs are + * enabled. Browsers and other search UIs should respect this, as it allows + * a user to avoid sending partial queries to a search engine, if it poses + * any privacy concern. The value is boolean (1 or 0). + */ + public static final String SHOW_WEB_SUGGESTIONS = "show_web_suggestions"; + + /** + * Settings to backup. This is here so that it's in the same place as the settings + * keys and easy to update. + * @hide + */ + public static final String[] SETTINGS_TO_BACKUP = { + STAY_ON_WHILE_PLUGGED_IN, + END_BUTTON_BEHAVIOR, + WIFI_SLEEP_POLICY, + WIFI_USE_STATIC_IP, + WIFI_STATIC_IP, + WIFI_STATIC_GATEWAY, + WIFI_STATIC_NETMASK, + WIFI_STATIC_DNS1, + WIFI_STATIC_DNS2, + BLUETOOTH_DISCOVERABILITY, + BLUETOOTH_DISCOVERABILITY_TIMEOUT, + DIM_SCREEN, + SCREEN_OFF_TIMEOUT, + SCREEN_BRIGHTNESS, + VIBRATE_ON, + NOTIFICATIONS_USE_RING_VOLUME, + MODE_RINGER, + MODE_RINGER_STREAMS_AFFECTED, + MUTE_STREAMS_AFFECTED, + VOLUME_VOICE, + VOLUME_SYSTEM, + VOLUME_RING, + VOLUME_MUSIC, + VOLUME_ALARM, + VOLUME_NOTIFICATION, + VOLUME_VOICE + APPEND_FOR_LAST_AUDIBLE, + VOLUME_SYSTEM + APPEND_FOR_LAST_AUDIBLE, + VOLUME_RING + APPEND_FOR_LAST_AUDIBLE, + VOLUME_MUSIC + APPEND_FOR_LAST_AUDIBLE, + VOLUME_ALARM + APPEND_FOR_LAST_AUDIBLE, + VOLUME_NOTIFICATION + APPEND_FOR_LAST_AUDIBLE, + TEXT_AUTO_REPLACE, + TEXT_AUTO_CAPS, + TEXT_AUTO_PUNCTUATE, + TEXT_SHOW_PASSWORD, + AUTO_TIME, + TIME_12_24, + DATE_FORMAT, + ACCELEROMETER_ROTATION, + DTMF_TONE_WHEN_DIALING, + DTMF_TONE_TYPE_WHEN_DIALING, + EMERGENCY_TONE, + CALL_AUTO_RETRY, + HEARING_AID, + TTY_MODE, + SOUND_EFFECTS_ENABLED, + HAPTIC_FEEDBACK_ENABLED, + SHOW_WEB_SUGGESTIONS + }; + // Settings moved to Settings.Secure /** @@ -1321,7 +1437,7 @@ public final class Settings { */ @Deprecated public static final String INSTALL_NON_MARKET_APPS = Secure.INSTALL_NON_MARKET_APPS; - + /** * @deprecated Use {@link android.provider.Settings.Secure#LOCATION_PROVIDERS_ALLOWED} * instead @@ -1334,7 +1450,7 @@ public final class Settings { */ @Deprecated public static final String LOGGING_ID = Secure.LOGGING_ID; - + /** * @deprecated Use {@link android.provider.Settings.Secure#NETWORK_PREFERENCE} instead */ @@ -1374,7 +1490,7 @@ public final class Settings { */ @Deprecated public static final String USB_MASS_STORAGE_ENABLED = Secure.USB_MASS_STORAGE_ENABLED; - + /** * @deprecated Use {@link android.provider.Settings.Secure#USE_GOOGLE_MAIL} instead */ @@ -1412,7 +1528,7 @@ public final class Settings { @Deprecated public static final String WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY = Secure.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY; - + /** * @deprecated Use {@link android.provider.Settings.Secure#WIFI_NUM_OPEN_NETWORKS_KEPT} * instead @@ -1448,7 +1564,7 @@ public final class Settings { @Deprecated public static final String WIFI_WATCHDOG_BACKGROUND_CHECK_DELAY_MS = Secure.WIFI_WATCHDOG_BACKGROUND_CHECK_DELAY_MS; - + /** * @deprecated Use * {@link android.provider.Settings.Secure#WIFI_WATCHDOG_BACKGROUND_CHECK_ENABLED} instead @@ -1824,19 +1940,19 @@ public final class Settings { * Whether the device has been provisioned (0 = false, 1 = true) */ public static final String DEVICE_PROVISIONED = "device_provisioned"; - + /** * List of input methods that are currently enabled. This is a string * containing the IDs of all enabled input methods, each ID separated * by ':'. */ public static final String ENABLED_INPUT_METHODS = "enabled_input_methods"; - + /** * Host name and port for a user-selected proxy. */ public static final String HTTP_PROXY = "http_proxy"; - + /** * Whether the package installer should allow installation of apps downloaded from * sources other than the Android Market (vending machine). @@ -1845,12 +1961,12 @@ public final class Settings { * 0 = only allow installing from the Android Market */ public static final String INSTALL_NON_MARKET_APPS = "install_non_market_apps"; - + /** * Comma-separated list of location providers that activities may access. */ public static final String LOCATION_PROVIDERS_ALLOWED = "location_providers_allowed"; - + /** * The Logging ID (a unique 64-bit value) as a hex string. * Used as a pseudonymous identifier for logging. @@ -1872,19 +1988,19 @@ public final class Settings { * connectivity service should touch this. */ public static final String NETWORK_PREFERENCE = "network_preference"; - - /** + + /** */ public static final String PARENTAL_CONTROL_ENABLED = "parental_control_enabled"; - - /** + + /** */ public static final String PARENTAL_CONTROL_LAST_UPDATE = "parental_control_last_update"; - - /** + + /** */ public static final String PARENTAL_CONTROL_REDIRECT_URL = "parental_control_redirect_url"; - + /** * Settings classname to launch when Settings is clicked from All * Applications. Needed because of user testing between the old @@ -1892,18 +2008,67 @@ public final class Settings { */ // TODO: 881807 public static final String SETTINGS_CLASSNAME = "settings_classname"; - + /** * USB Mass Storage Enabled */ public static final String USB_MASS_STORAGE_ENABLED = "usb_mass_storage_enabled"; - + /** * If this setting is set (to anything), then all references * to Gmail on the device must change to Google Mail. */ public static final String USE_GOOGLE_MAIL = "use_google_mail"; - + + /** + * If accessibility is enabled. + */ + public static final String ACCESSIBILITY_ENABLED = "accessibility_enabled"; + + /** + * List of the enabled accessibility providers. + */ + public static final String ENABLED_ACCESSIBILITY_SERVICES = + "enabled_accessibility_services"; + + /** + * Setting to always use the default text-to-speech settings regardless + * of the application settings. + * 1 = override application settings, + * 0 = use application settings (if specified). + */ + public static final String TTS_USE_DEFAULTS = "tts_use_defaults"; + + /** + * Default text-to-speech engine speech rate. 100 = 1x + */ + public static final String TTS_DEFAULT_RATE = "tts_default_rate"; + + /** + * Default text-to-speech engine pitch. 100 = 1x + */ + public static final String TTS_DEFAULT_PITCH = "tts_default_pitch"; + + /** + * Default text-to-speech engine. + */ + public static final String TTS_DEFAULT_SYNTH = "tts_default_synth"; + + /** + * Default text-to-speech language. + */ + public static final String TTS_DEFAULT_LANG = "tts_default_lang"; + + /** + * Default text-to-speech country. + */ + public static final String TTS_DEFAULT_COUNTRY = "tts_default_country"; + + /** + * Default text-to-speech locale variant. + */ + public static final String TTS_DEFAULT_VARIANT = "tts_default_variant"; + /** * Whether to notify the user of open networks. *

      @@ -1915,64 +2080,64 @@ public final class Settings { */ public static final String WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON = "wifi_networks_available_notification_on"; - + /** * Delay (in seconds) before repeating the Wi-Fi networks available notification. * Connecting to a network will reset the timer. */ public static final String WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY = "wifi_networks_available_repeat_delay"; - + /** * The number of radio channels that are allowed in the local * 802.11 regulatory domain. * @hide */ public static final String WIFI_NUM_ALLOWED_CHANNELS = "wifi_num_allowed_channels"; - + /** * When the number of open networks exceeds this number, the * least-recently-used excess networks will be removed. */ public static final String WIFI_NUM_OPEN_NETWORKS_KEPT = "wifi_num_open_networks_kept"; - + /** * Whether the Wi-Fi should be on. Only the Wi-Fi service should touch this. */ public static final String WIFI_ON = "wifi_on"; - + /** * The acceptable packet loss percentage (range 0 - 100) before trying * another AP on the same network. */ public static final String WIFI_WATCHDOG_ACCEPTABLE_PACKET_LOSS_PERCENTAGE = "wifi_watchdog_acceptable_packet_loss_percentage"; - + /** * The number of access points required for a network in order for the * watchdog to monitor it. */ public static final String WIFI_WATCHDOG_AP_COUNT = "wifi_watchdog_ap_count"; - + /** * The delay between background checks. */ public static final String WIFI_WATCHDOG_BACKGROUND_CHECK_DELAY_MS = "wifi_watchdog_background_check_delay_ms"; - + /** * Whether the Wi-Fi watchdog is enabled for background checking even * after it thinks the user has connected to a good access point. */ public static final String WIFI_WATCHDOG_BACKGROUND_CHECK_ENABLED = "wifi_watchdog_background_check_enabled"; - + /** * The timeout for a background ping */ public static final String WIFI_WATCHDOG_BACKGROUND_CHECK_TIMEOUT_MS = "wifi_watchdog_background_check_timeout_ms"; - + /** * The number of initial pings to perform that *may* be ignored if they * fail. Again, if these fail, they will *not* be used in packet loss @@ -1981,7 +2146,7 @@ public final class Settings { */ public static final String WIFI_WATCHDOG_INITIAL_IGNORED_PING_COUNT = "wifi_watchdog_initial_ignored_ping_count"; - + /** * The maximum number of access points (per network) to attempt to test. * If this number is reached, the watchdog will no longer monitor the @@ -1989,7 +2154,7 @@ public final class Settings { * networks containing multiple APs whose DNS does not respond to pings. */ public static final String WIFI_WATCHDOG_MAX_AP_CHECKS = "wifi_watchdog_max_ap_checks"; - + /** * Whether the Wi-Fi watchdog is enabled. */ @@ -2004,24 +2169,24 @@ public final class Settings { * The number of pings to test if an access point is a good connection. */ public static final String WIFI_WATCHDOG_PING_COUNT = "wifi_watchdog_ping_count"; - + /** * The delay between pings. */ public static final String WIFI_WATCHDOG_PING_DELAY_MS = "wifi_watchdog_ping_delay_ms"; - + /** * The timeout per ping. */ public static final String WIFI_WATCHDOG_PING_TIMEOUT_MS = "wifi_watchdog_ping_timeout_ms"; - + /** * The maximum number of times we will retry a connection to an access * point for which we have failed in acquiring an IP address from DHCP. * A value of N means that we will make N+1 connection attempts in all. */ public static final String WIFI_MAX_DHCP_RETRY_COUNT = "wifi_max_dhcp_retry_count"; - + /** * Maximum amount of time in milliseconds to hold a wakelock while waiting for mobile * data connectivity to be established after a disconnect from Wi-Fi. @@ -2051,20 +2216,29 @@ public final class Settings { public static final String CDMA_SUBSCRIPTION_MODE = "subscription_mode"; /** - * represents current active phone class - * 1 = GSM-Phone, 0 = CDMA-Phone + * The preferred network mode 7 = Global + * 6 = EvDo only + * 5 = CDMA w/o EvDo + * 4 = CDMA / EvDo auto + * 3 = GSM / WCDMA auto + * 2 = WCDMA only + * 1 = GSM only + * 0 = GSM / WCDMA preferred * @hide */ - public static final String CURRENT_ACTIVE_PHONE = "current_active_phone"; + public static final String PREFERRED_NETWORK_MODE = + "preferred_network_mode"; /** - * The preferred network mode 7 = Global, CDMA default - * 4 = CDMA only - * 3 = GSM/UMTS only + * The preferred TTY mode 0 = TTy Off, CDMA default + * 1 = TTY Full + * 2 = TTY HCO + * 3 = TTY VCO * @hide */ - public static final String PREFERRED_NETWORK_MODE = - "preferred_network_mode"; + public static final String PREFERRED_TTY_MODE = + "preferred_tty_mode"; + /** * CDMA Cell Broadcast SMS @@ -2099,6 +2273,71 @@ public final class Settings { */ public static final String TTY_MODE_ENABLED = "tty_mode_enabled"; + /** + * Flag for allowing service provider to use location information to improve products and + * services. + * Type: int ( 0 = disallow, 1 = allow ) + * @hide + */ + public static final String USE_LOCATION_FOR_SERVICES = "use_location"; + + /** + * Controls whether settings backup is enabled. + * Type: int ( 0 = disabled, 1 = enabled ) + * @hide + */ + public static final String BACKUP_ENABLED = "backup_enabled"; + + /** + * Indicates whether settings backup has been fully provisioned. + * Type: int ( 0 = unprovisioned, 1 = fully provisioned ) + * @hide + */ + public static final String BACKUP_PROVISIONED = "backup_provisioned"; + + /** + * Component of the transport to use for backup/restore. + * @hide + */ + public static final String BACKUP_TRANSPORT = "backup_transport"; + + /** + * Version for which the setup wizard was last shown. Bumped for + * each release when there is new setup information to show. + * @hide + */ + public static final String LAST_SETUP_SHOWN = "last_setup_shown"; + + /** + * @hide + */ + public static final String[] SETTINGS_TO_BACKUP = { + ADB_ENABLED, + ALLOW_MOCK_LOCATION, + INSTALL_NON_MARKET_APPS, + PARENTAL_CONTROL_ENABLED, + PARENTAL_CONTROL_REDIRECT_URL, + USB_MASS_STORAGE_ENABLED, + ACCESSIBILITY_ENABLED, + ENABLED_ACCESSIBILITY_SERVICES, + TTS_USE_DEFAULTS, + TTS_DEFAULT_RATE, + TTS_DEFAULT_PITCH, + TTS_DEFAULT_SYNTH, + TTS_DEFAULT_LANG, + TTS_DEFAULT_COUNTRY, + WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, + WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY, + WIFI_NUM_ALLOWED_CHANNELS, + WIFI_NUM_OPEN_NETWORKS_KEPT, + BACKGROUND_DATA, + PREFERRED_NETWORK_MODE, + PREFERRED_TTY_MODE, + CDMA_CELL_BROADCAST_SMS, + PREFERRED_CDMA_SUBSCRIPTION, + ENHANCED_VOICE_PRIVACY_ENABLED + }; + /** * Helper method for determining if a location provider is enabled. * @param cr the content resolver to use @@ -2115,7 +2354,7 @@ public final class Settings { allowedProviders.startsWith(provider + ",") || allowedProviders.endsWith("," + provider)); } - return false; + return false; } /** @@ -2139,7 +2378,7 @@ public final class Settings { putString(cr, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, provider); } } - + /** * Gservices settings, containing the network names for Google's * various services. This table holds simple name/addr pairs. @@ -2160,6 +2399,13 @@ public final class Settings { public static final String CHANGED_ACTION = "com.google.gservices.intent.action.GSERVICES_CHANGED"; + /** + * Intent action to override Gservices for testing. (Requires WRITE_GSERVICES permission.) + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String OVERRIDE_ACTION = + "com.google.gservices.intent.action.GSERVICES_OVERRIDE"; + private static volatile NameValueCache mNameValueCache = null; private static final Object mNameValueCacheLock = new Object(); @@ -2260,7 +2506,7 @@ 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. */ @@ -2427,12 +2673,34 @@ public final class Settings { */ public static final String GMAIL_BUFFER_SERVER_RESPONSE = "gmail_buffer_server_response"; + /** + * The maximum size in bytes allowed for the provider to gzip a protocol buffer uploaded to + * the server. + */ + public static final String GMAIL_MAX_GZIP_SIZE = "gmail_max_gzip_size_bytes"; + /** * Controls whether Gmail will discard uphill operations that repeatedly fail. Value must be * an integer where non-zero means true. Defaults to 1. */ public static final String GMAIL_DISCARD_ERROR_UPHILL_OP = "gmail_discard_error_uphill_op"; + /** + * Controls how many attempts Gmail will try to upload an uphill operations before it + * abandons the operation. Defaults to 20. + */ + public static final String GMAIL_NUM_RETRY_UPHILL_OP = "gmail_discard_error_uphill_op"; + + /** + * the transcoder URL for mobile devices. + */ + public static final String TRANSCODER_URL = "mobile_transcoder_url"; + + /** + * URL that points to the privacy terms of the Google Talk service. + */ + public static final String GTALK_TERMS_OF_SERVICE_URL = "gtalk_terms_of_service_url"; + /** * Hostname of the GTalk server. */ @@ -2560,6 +2828,21 @@ public final class Settings { public static final String GTALK_SSL_HANDSHAKE_TIMEOUT_MS = "gtalk_ssl_handshake_timeout_ms"; + /** + * Compress the gtalk stream. + */ + public static final String GTALK_COMPRESS = "gtalk_compress"; + + /** + * This is the timeout for which Google Talk will send the message using bareJID. In a + * established chat between two XMPP endpoints, Google Talk uses fullJID in the format + * of user@domain/resource in order to send the message to the specific client. However, + * if Google Talk hasn't received a message from that client after some time, it would + * fall back to use the bareJID, which would broadcast the message to all clients for + * the other user. + */ + public static final String GTALK_USE_BARE_JID_TIMEOUT_MS = "gtalk_use_barejid_timeout_ms"; + /** * Enable use of ssl session caching. * 'db' - save each session in a (per process) database @@ -2656,6 +2939,20 @@ public final class Settings { */ public static final String VENDING_TAB_2_TITLE = "vending_tab_2_title"; + /** + * Frequency in milliseconds at which we should request MCS heartbeats + * from the Vending Machine client. + */ + public static final String VENDING_HEARTBEAT_FREQUENCY_MS = + "vending_heartbeat_frequency_ms"; + + /** + * Frequency in milliseconds at which we should resend pending download + * requests to the API Server from the Vending Machine client. + */ + public static final String VENDING_PENDING_DOWNLOAD_RESEND_FREQUENCY_MS = + "vending_pd_resend_frequency_ms"; + /** * URL that points to the legal terms of service to display in Settings. *

      @@ -2796,12 +3093,12 @@ public final class Settings { * out without asking for use permit, to limit the un-authorized SMS * usage. */ - public static final String SMS_OUTGOING_CEHCK_INTERVAL_MS = + public static final String SMS_OUTGOING_CHECK_INTERVAL_MS = "sms_outgoing_check_interval_ms"; /** * The number of outgoing SMS sent without asking for user permit - * (of {@link #SMS_OUTGOING_CEHCK_INTERVAL_MS} + * (of {@link #SMS_OUTGOING_CHECK_INTERVAL_MS} */ public static final String SMS_OUTGOING_CEHCK_MAX_COUNT = "sms_outgoing_check_max_count"; @@ -2950,12 +3247,20 @@ public final class Settings { public static final String BATTERY_DISCHARGE_DURATION_THRESHOLD = "battery_discharge_duration_threshold"; public static final String BATTERY_DISCHARGE_THRESHOLD = "battery_discharge_threshold"; - + /** * An email address that anr bugreports should be sent to. */ public static final String ANR_BUGREPORT_RECIPIENT = "anr_bugreport_recipient"; + /** + * Flag for allowing service provider to use location information to improve products and + * services. + * Type: int ( 0 = disallow, 1 = allow ) + * @deprecated + */ + public static final String USE_LOCATION_FOR_SERVICES = "use_location"; + /** * @deprecated * @hide @@ -3094,7 +3399,7 @@ public final class Settings { /** * Add a new bookmark to the system. - * + * * @param cr The ContentResolver to query. * @param intent The desired target of the bookmark. * @param title Bookmark title that is shown to the user; null if none @@ -3159,7 +3464,7 @@ public final class Settings { /** * Return the title as it should be displayed to the user. This takes * care of localizing bookmarks that point to activities. - * + * * @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} @@ -3174,24 +3479,24 @@ public final class Settings { throw new IllegalArgumentException( "The cursor must contain the TITLE and INTENT columns."); } - + String title = cursor.getString(titleColumn); if (!TextUtils.isEmpty(title)) { return title; } - + String intentUri = cursor.getString(intentColumn); if (TextUtils.isEmpty(intentUri)) { return ""; } - + Intent intent; try { intent = Intent.getIntent(intentUri); } catch (URISyntaxException e) { return ""; } - + PackageManager packageManager = context.getPackageManager(); ResolveInfo info = packageManager.resolveActivity(intent, 0); return info != null ? info.loadLabel(packageManager) : ""; @@ -3247,4 +3552,3 @@ public final class Settings { return "android-" + Long.toHexString(androidId); } } - diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java index a4145c487696b0267dcf35e8ae1f94c8cc862acb..4078fa6d5a734020b30c766460261a3dc0f4f443 100644 --- a/core/java/android/provider/Telephony.java +++ b/core/java/android/provider/Telephony.java @@ -465,6 +465,24 @@ public final class Telephony { * Contains info about SMS related Intents that are broadcast. */ public static final class Intents { + /** + * Set by BroadcastReceiver. Indicates the message was handled + * successfully. + */ + public static final int RESULT_SMS_HANDLED = 1; + + /** + * Set by BroadcastReceiver. Indicates a generic error while + * processing the message. + */ + public static final int RESULT_SMS_GENERIC_ERROR = 2; + + /** + * Set by BroadcastReceiver. Indicates insufficient memory to store + * the message. + */ + public static final int RESULT_SMS_OUT_OF_MEMORY = 3; + /** * Broadcast Action: A new text based SMS message has been received * by the device. The intent will have the following extra @@ -476,7 +494,10 @@ public final class Telephony { * * *

      The extra values can be extracted using - * {@link #getMessagesFromIntent(Intent)}

      + * {@link #getMessagesFromIntent(Intent)}.

      + * + *

      If a BroadcastReceiver encounters an error while processing + * this intent it should set the result code appropriately.

      */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String SMS_RECEIVED_ACTION = @@ -493,7 +514,10 @@ public final class Telephony { * * *

      The extra values can be extracted using - * {@link #getMessagesFromIntent(Intent)}

      + * {@link #getMessagesFromIntent(Intent)}.

      + * + *

      If a BroadcastReceiver encounters an error while processing + * this intent it should set the result code appropriately.

      */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String DATA_SMS_RECEIVED_ACTION = @@ -510,6 +534,9 @@ public final class Telephony { *
    15. pduType (Integer) - The WAP PDU type
    16. *
    17. data - The data payload of the message
    18. * + * + *

      If a BroadcastReceiver encounters an error while processing + * this intent it should set the result code appropriately.

      */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String WAP_PUSH_RECEIVED_ACTION = diff --git a/core/java/android/server/BluetoothDeviceService.java b/core/java/android/server/BluetoothDeviceService.java index 8e5cee9bbfba9bc6786a4b563b7c9b2468e304c2..8c843efd7e4ebc5e7d923fdb054c99127e4a5a6a 100644 --- a/core/java/android/server/BluetoothDeviceService.java +++ b/core/java/android/server/BluetoothDeviceService.java @@ -372,6 +372,10 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { mEventLoop.onModeChanged(getModeNative()); } + if (mIsAirplaneSensitive && isAirplaneModeOn()) { + disable(false); + } + } } @@ -1220,6 +1224,8 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { break; } pw.println("getHeadsetAddress() = " + headset.getHeadsetAddress()); + pw.println("getBatteryUsageHint() = " + headset.getBatteryUsageHint()); + headset.close(); } diff --git a/core/java/android/server/search/SearchManagerService.java b/core/java/android/server/search/SearchManagerService.java index 03623d6bf411307d3e56e687504dbbeafcc3b02a..373e61ff97f086db2e2b73a7ad7ba6d97dcc2385 100644 --- a/core/java/android/server/search/SearchManagerService.java +++ b/core/java/android/server/search/SearchManagerService.java @@ -17,48 +17,69 @@ package android.server.search; import android.app.ISearchManager; +import android.app.ISearchManagerCallback; +import android.app.SearchDialog; +import android.app.SearchManager; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; +import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; +import android.content.res.Configuration; +import android.os.Bundle; import android.os.Handler; +import android.os.RemoteException; +import android.os.SystemProperties; +import android.text.TextUtils; +import android.util.Log; import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.FutureTask; /** * This is a simplified version of the Search Manager service. It no longer handles - * presentation (UI). Its function is to maintain the map & list of "searchable" + * presentation (UI). Its function is to maintain the map & list of "searchable" * items, which provides a mapping from individual activities (where a user might have * invoked search) to specific searchable activities (where the search will be dispatched). */ public class SearchManagerService extends ISearchManager.Stub + implements DialogInterface.OnCancelListener, DialogInterface.OnDismissListener { // general debugging support private static final String TAG = "SearchManagerService"; - private static final boolean DEBUG = false; - - // configuration choices - private static final boolean IMMEDIATE_SEARCHABLES_UPDATE = true; + private static final boolean DBG = false; // class maintenance and general shared data private final Context mContext; private final Handler mHandler; private boolean mSearchablesDirty; - private Searchables mSearchables; - + private final Searchables mSearchables; + + final SearchDialog mSearchDialog; + ISearchManagerCallback mCallback = null; + + private final boolean mDisabledOnBoot; + + private static final String DISABLE_SEARCH_PROPERTY = "dev.disablesearchdialog"; + /** * Initializes the Search Manager service in the provided system context. * Only one instance of this object should be created! * * @param context to use for accessing DB, window manager, etc. */ - public SearchManagerService(Context context) { + public SearchManagerService(Context context) { mContext = context; mHandler = new Handler(); mSearchablesDirty = true; mSearchables = new Searchables(context); - + mSearchDialog = new SearchDialog(context); + mSearchDialog.setOnCancelListener(this); + mSearchDialog.setOnDismissListener(this); + // Setup the infrastructure for updating and maintaining the list // of searchable activities. IntentFilter filter = new IntentFilter(); @@ -67,17 +88,18 @@ public class SearchManagerService extends ISearchManager.Stub filter.addAction(Intent.ACTION_PACKAGE_CHANGED); filter.addDataScheme("package"); mContext.registerReceiver(mIntentReceiver, filter, null, mHandler); - + // After startup settles down, preload the searchables list, // which will reduce the delay when the search UI is invoked. - if (IMMEDIATE_SEARCHABLES_UPDATE) { - mHandler.post(mRunUpdateSearchable); - } + mHandler.post(mRunUpdateSearchable); + + // allows disabling of search dialog for stress testing runs + mDisabledOnBoot = !TextUtils.isEmpty(SystemProperties.get(DISABLE_SEARCH_PROPERTY)); } - + /** * Listens for intent broadcasts. - * + * * The primary purpose here is to refresh the "searchables" list * if packages are added/removed. */ @@ -85,29 +107,25 @@ public class SearchManagerService extends ISearchManager.Stub @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); - + // First, test for intents that matter at any time if (action.equals(Intent.ACTION_PACKAGE_ADDED) || action.equals(Intent.ACTION_PACKAGE_REMOVED) || action.equals(Intent.ACTION_PACKAGE_CHANGED)) { mSearchablesDirty = true; - if (IMMEDIATE_SEARCHABLES_UPDATE) { - mHandler.post(mRunUpdateSearchable); - } + mHandler.post(mRunUpdateSearchable); return; } } }; - + /** * This runnable (for the main handler / UI thread) will update the searchables list. */ private Runnable mRunUpdateSearchable = new Runnable() { public void run() { - if (mSearchablesDirty) { - updateSearchables(); - } - } + updateSearchablesIfDirty(); + } }; /** @@ -115,42 +133,251 @@ public class SearchManagerService extends ISearchManager.Stub * a package add/remove broadcast message. */ private void updateSearchables() { + if (DBG) debug("updateSearchables()"); mSearchables.buildSearchableList(); mSearchablesDirty = false; } + /** + * Updates the list of searchables if needed. + */ + private void updateSearchablesIfDirty() { + if (mSearchablesDirty) { + updateSearchables(); + } + } + /** * Returns the SearchableInfo for a given activity * * @param launchActivity The activity from which we're launching this search. * @param globalSearch If false, this will only launch the search that has been specifically - * defined by the application (which is usually defined as a local search). If no default + * defined by the application (which is usually defined as a local search). If no default * search is defined in the current application or activity, no search will be launched. * If true, this will always launch a platform-global (e.g. web-based) search instead. * @return Returns a SearchableInfo record describing the parameters of the search, * or null if no searchable metadata was available. */ public SearchableInfo getSearchableInfo(ComponentName launchActivity, boolean globalSearch) { - // final check. however we should try to avoid this, because - // it slows down the entry into the UI. - if (mSearchablesDirty) { - updateSearchables(); - } + updateSearchablesIfDirty(); SearchableInfo si = null; if (globalSearch) { si = mSearchables.getDefaultSearchable(); } else { + if (launchActivity == null) { + Log.e(TAG, "getSearchableInfo(), activity == null"); + return null; + } si = mSearchables.getSearchableInfo(launchActivity); } return si; } - + /** * Returns a list of the searchable activities that can be included in global search. */ public List getSearchablesInGlobalSearch() { + updateSearchablesIfDirty(); return mSearchables.getSearchablesInGlobalSearchList(); } + /** + * Launches the search UI on the main thread of the service. + * + * @see SearchManager#startSearch(String, boolean, ComponentName, Bundle, boolean) + */ + public void startSearch(final String initialQuery, + final boolean selectInitialQuery, + final ComponentName launchActivity, + final Bundle appSearchData, + final boolean globalSearch, + final ISearchManagerCallback searchManagerCallback) { + if (DBG) debug("startSearch()"); + Runnable task = new Runnable() { + public void run() { + performStartSearch(initialQuery, + selectInitialQuery, + launchActivity, + appSearchData, + globalSearch, + searchManagerCallback); + } + }; + mHandler.post(task); + } + + /** + * Actually launches the search. This must be called on the service UI thread. + */ + /*package*/ void performStartSearch(String initialQuery, + boolean selectInitialQuery, + ComponentName launchActivity, + Bundle appSearchData, + boolean globalSearch, + ISearchManagerCallback searchManagerCallback) { + if (DBG) debug("performStartSearch()"); + + if (mDisabledOnBoot) { + Log.d(TAG, "ignoring start search request because " + DISABLE_SEARCH_PROPERTY + + " system property is set."); + return; + } + + mSearchDialog.show(initialQuery, selectInitialQuery, launchActivity, appSearchData, + globalSearch); + if (searchManagerCallback != null) { + mCallback = searchManagerCallback; + } + } + + /** + * Cancels the search dialog. Can be called from any thread. + */ + public void stopSearch() { + if (DBG) debug("stopSearch()"); + mHandler.post(new Runnable() { + public void run() { + performStopSearch(); + } + }); + } + + /** + * Cancels the search dialog. Must be called from the service UI thread. + */ + /*package*/ void performStopSearch() { + if (DBG) debug("performStopSearch()"); + mSearchDialog.cancel(); + } + + /** + * Determines if the Search UI is currently displayed. + * + * @see SearchManager#isVisible() + */ + public boolean isVisible() { + return postAndWait(mIsShowing, false, "isShowing()"); + } + + private final Callable mIsShowing = new Callable() { + public Boolean call() { + return mSearchDialog.isShowing(); + } + }; + + public Bundle onSaveInstanceState() { + return postAndWait(mOnSaveInstanceState, null, "onSaveInstanceState()"); + } + + private final Callable mOnSaveInstanceState = new Callable() { + public Bundle call() { + if (mSearchDialog.isShowing()) { + return mSearchDialog.onSaveInstanceState(); + } else { + return null; + } + } + }; + + public void onRestoreInstanceState(final Bundle searchDialogState) { + if (searchDialogState != null) { + mHandler.post(new Runnable() { + public void run() { + mSearchDialog.onRestoreInstanceState(searchDialogState); + } + }); + } + } + + public void onConfigurationChanged(final Configuration newConfig) { + mHandler.post(new Runnable() { + public void run() { + if (mSearchDialog.isShowing()) { + mSearchDialog.onConfigurationChanged(newConfig); + } + } + }); + } + + /** + * Called by {@link SearchDialog} when it goes away. + */ + public void onDismiss(DialogInterface dialog) { + if (DBG) debug("onDismiss()"); + if (mCallback != null) { + try { + mCallback.onDismiss(); + } catch (RemoteException ex) { + Log.e(TAG, "onDismiss() failed: " + ex); + } + } + } + + /** + * Called by {@link SearchDialog} when the user or activity cancels search. + * When this is called, {@link #onDismiss} is called too. + */ + public void onCancel(DialogInterface dialog) { + if (DBG) debug("onCancel()"); + if (mCallback != null) { + try { + mCallback.onCancel(); + } catch (RemoteException ex) { + Log.e(TAG, "onCancel() failed: " + ex); + } + } + } + + /** + * Returns a list of the searchable activities that handle web searches. + */ + public List getSearchablesForWebSearch() { + updateSearchablesIfDirty(); + return mSearchables.getSearchablesForWebSearchList(); + } + + /** + * Returns the default searchable activity for web searches. + */ + public SearchableInfo getDefaultSearchableForWebSearch() { + updateSearchablesIfDirty(); + return mSearchables.getDefaultSearchableForWebSearch(); + } + + /** + * Sets the default searchable activity for web searches. + */ + public void setDefaultWebSearch(ComponentName component) { + mSearchables.setDefaultWebSearch(component); + } + + /** + * Runs an operation on the handler for the service, blocks until it returns, + * and returns the value returned by the operation. + * + * @param Return value type. + * @param callable Operation to run. + * @param errorResult Value to return if the operations throws an exception. + * @param name Operation name to include in error log messages. + * @return The value returned by the operation. + */ + private V postAndWait(Callable callable, V errorResult, String name) { + FutureTask task = new FutureTask(callable); + mHandler.post(task); + try { + return task.get(); + } catch (InterruptedException ex) { + Log.e(TAG, "Error calling " + name + ": " + ex); + return errorResult; + } catch (ExecutionException ex) { + Log.e(TAG, "Error calling " + name + ": " + ex); + return errorResult; + } + } + + private static void debug(String msg) { + Thread thread = Thread.currentThread(); + Log.d(TAG, msg + " (" + thread.getName() + "-" + thread.getId() + ")"); + } } diff --git a/core/java/android/server/search/SearchableInfo.java b/core/java/android/server/search/SearchableInfo.java index 842fc7573aefdf145809010867e9df347634b775..8ef1f154d72d92b8ec1aa3c0e5a5f87bd15b6d7a 100644 --- a/core/java/android/server/search/SearchableInfo.java +++ b/core/java/android/server/search/SearchableInfo.java @@ -40,7 +40,7 @@ import java.util.HashMap; public final class SearchableInfo implements Parcelable { // general debugging support - private static final boolean DBG = true; + private static final boolean DBG = false; private static final String LOG_TAG = "SearchableInfo"; // static strings used for XML lookups. @@ -66,6 +66,8 @@ public final class SearchableInfo implements Parcelable { private final int mSearchInputType; private final int mSearchImeOptions; private final boolean mIncludeInGlobalSearch; + private final boolean mQueryAfterZeroResults; + private final String mSettingsDescription; private final String mSuggestAuthority; private final String mSuggestPath; private final String mSuggestSelection; @@ -133,6 +135,14 @@ public final class SearchableInfo implements Parcelable { public boolean shouldRewriteQueryFromText() { return 0 != (mSearchMode & SEARCH_MODE_QUERY_REWRITE_FROM_TEXT); } + + /** + * Gets the description to use for this source in system search settings, or null if + * none has been specified. + */ + public String getSettingsDescription() { + return mSettingsDescription; + } /** * Retrieve the path for obtaining search suggestions. @@ -276,7 +286,11 @@ public final class SearchableInfo implements Parcelable { EditorInfo.IME_ACTION_SEARCH); mIncludeInGlobalSearch = a.getBoolean( com.android.internal.R.styleable.Searchable_includeInGlobalSearch, false); + mQueryAfterZeroResults = a.getBoolean( + com.android.internal.R.styleable.Searchable_queryAfterZeroResults, false); + mSettingsDescription = a.getString( + com.android.internal.R.styleable.Searchable_searchSettingsDescription); mSuggestAuthority = a.getString( com.android.internal.R.styleable.Searchable_searchSuggestAuthority); mSuggestPath = a.getString( @@ -317,7 +331,7 @@ public final class SearchableInfo implements Parcelable { // for now, implement some form of rules - minimal data if (mLabelId == 0) { - throw new IllegalArgumentException("No label."); + throw new IllegalArgumentException("Search label must be a resource reference."); } } @@ -438,13 +452,18 @@ public final class SearchableInfo implements Parcelable { xml.close(); if (DBG) { - Log.d(LOG_TAG, "Checked " + activityInfo.name - + ",label=" + searchable.getLabelId() - + ",icon=" + searchable.getIconId() - + ",suggestAuthority=" + searchable.getSuggestAuthority() - + ",target=" + searchable.getSearchActivity().getClassName() - + ",global=" + searchable.shouldIncludeInGlobalSearch() - + ",threshold=" + searchable.getSuggestThreshold()); + if (searchable != null) { + Log.d(LOG_TAG, "Checked " + activityInfo.name + + ",label=" + searchable.getLabelId() + + ",icon=" + searchable.getIconId() + + ",suggestAuthority=" + searchable.getSuggestAuthority() + + ",target=" + searchable.getSearchActivity().getClassName() + + ",global=" + searchable.shouldIncludeInGlobalSearch() + + ",settingsDescription=" + searchable.getSettingsDescription() + + ",threshold=" + searchable.getSuggestThreshold()); + } else { + Log.d(LOG_TAG, "Checked " + activityInfo.name + ", no searchable meta-data"); + } } return searchable; } @@ -636,6 +655,17 @@ public final class SearchableInfo implements Parcelable { return mIncludeInGlobalSearch; } + /** + * Checks whether this searchable activity should be invoked after a query returned zero + * results. + * + * @return The value of the queryAfterZeroResults attribute, + * or false if the attribute is not set. + */ + public boolean queryAfterZeroResults() { + return mQueryAfterZeroResults; + } + /** * Support for parcelable and aidl operations. */ @@ -667,7 +697,9 @@ public final class SearchableInfo implements Parcelable { mSearchInputType = in.readInt(); mSearchImeOptions = in.readInt(); mIncludeInGlobalSearch = in.readInt() != 0; - + mQueryAfterZeroResults = in.readInt() != 0; + + mSettingsDescription = in.readString(); mSuggestAuthority = in.readString(); mSuggestPath = in.readString(); mSuggestSelection = in.readString(); @@ -702,7 +734,9 @@ public final class SearchableInfo implements Parcelable { dest.writeInt(mSearchInputType); dest.writeInt(mSearchImeOptions); dest.writeInt(mIncludeInGlobalSearch ? 1 : 0); + dest.writeInt(mQueryAfterZeroResults ? 1 : 0); + dest.writeString(mSettingsDescription); dest.writeString(mSuggestAuthority); dest.writeString(mSuggestPath); dest.writeString(mSuggestSelection); diff --git a/core/java/android/server/search/Searchables.java b/core/java/android/server/search/Searchables.java index 9586d56e8e8d6811c9234eb151da9833e4736c09..c7cc8edf108d818839138401d5abf76d66e5c234 100644 --- a/core/java/android/server/search/Searchables.java +++ b/core/java/android/server/search/Searchables.java @@ -16,49 +16,64 @@ package android.server.search; +import com.android.internal.app.ResolverActivity; +import com.android.internal.R; + import android.app.SearchManager; 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.ResolveInfo; +import android.content.res.Resources; import android.os.Bundle; +import android.util.Log; import java.util.ArrayList; import java.util.HashMap; import java.util.List; /** - * This class maintains the information about all searchable activities. + * This class maintains the information about all searchable activities. */ public class Searchables { + private static final String LOG_TAG = "Searchables"; + // static strings used for XML lookups, etc. - // TODO how should these be documented for the developer, in a more structured way than + // TODO how should these be documented for the developer, in a more structured way than // the current long wordy javadoc in SearchManager.java ? private static final String MD_LABEL_DEFAULT_SEARCHABLE = "android.app.default_searchable"; private static final String MD_SEARCHABLE_SYSTEM_SEARCH = "*"; - + private Context mContext; - + private HashMap mSearchablesMap = null; private ArrayList mSearchablesList = null; private ArrayList mSearchablesInGlobalSearchList = null; + private ArrayList mSearchablesForWebSearchList = null; private SearchableInfo mDefaultSearchable = null; - + private SearchableInfo mDefaultSearchableForWebSearch = null; + + public static String GOOGLE_SEARCH_COMPONENT_NAME = + "com.android.googlesearch/.GoogleSearch"; + public static String ENHANCED_GOOGLE_SEARCH_COMPONENT_NAME = + "com.google.android.providers.enhancedgooglesearch/.Launcher"; + /** - * + * * @param context Context to use for looking up activities etc. */ public Searchables (Context context) { mContext = context; } - + /** * Look up, or construct, based on the activity. - * - * The activities fall into three cases, based on meta-data found in + * + * The activities fall into three cases, based on meta-data found in * the manifest entry: *
        *
      1. The activity itself implements search. This is indicated by the @@ -70,16 +85,16 @@ public class Searchables { * case the factory will "redirect" and return the searchable data.
      2. *
      3. No searchability data is provided. We return null here and other * code will insert the "default" (e.g. contacts) search. - * + * * TODO: cache the result in the map, and check the map first. * TODO: it might make sense to implement the searchable reference as * an application meta-data entry. This way we don't have to pepper each * and every activity. * TODO: can we skip the constructor step if it's a non-searchable? - * TODO: does it make sense to plug the default into a slot here for + * TODO: does it make sense to plug the default into a slot here for * automatic return? Probably not, but it's one way to do it. * - * @param activity The name of the current activity, or null if the + * @param activity The name of the current activity, or null if the * activity does not define any explicit searchable metadata. */ public SearchableInfo getSearchableInfo(ComponentName activity) { @@ -89,18 +104,18 @@ public class Searchables { result = mSearchablesMap.get(activity); if (result != null) return result; } - + // Step 2. See if the current activity references a searchable. // Note: Conceptually, this could be a while(true) loop, but there's - // no point in implementing reference chaining here and risking a loop. + // no point in implementing reference chaining here and risking a loop. // References must point directly to searchable activities. - + ActivityInfo ai = null; try { ai = mContext.getPackageManager(). getActivityInfo(activity, PackageManager.GET_META_DATA ); String refActivityName = null; - + // First look for activity-specific reference Bundle md = ai.metaData; if (md != null) { @@ -113,11 +128,11 @@ public class Searchables { refActivityName = md.getString(MD_LABEL_DEFAULT_SEARCHABLE); } } - + // Irrespective of source, if a reference was found, follow it. if (refActivityName != null) { - // An app or activity can declare that we should simply launch + // An app or activity can declare that we should simply launch // "system default search" if search is invoked. if (refActivityName.equals(MD_SEARCHABLE_SYSTEM_SEARCH)) { return getDefaultSearchable(); @@ -143,95 +158,212 @@ public class Searchables { } catch (PackageManager.NameNotFoundException e) { // case 3: no metadata } - + // Step 3. None found. Return null. return null; - + } - + /** * Provides the system-default search activity, which you can use * whenever getSearchableInfo() returns null; - * + * * @return Returns the system-default search activity, null if never defined */ public synchronized SearchableInfo getDefaultSearchable() { return mDefaultSearchable; } - + public synchronized boolean isDefaultSearchable(SearchableInfo searchable) { return searchable == mDefaultSearchable; } - + /** - * Builds an entire list (suitable for display) of - * activities that are searchable, by iterating the entire set of - * ACTION_SEARCH intents. - * + * Builds an entire list (suitable for display) of + * activities that are searchable, by iterating the entire set of + * ACTION_SEARCH & ACTION_WEB_SEARCH intents. + * * Also clears the hash of all activities -> searches which will * refill as the user clicks "search". - * + * * This should only be done at startup and again if we know that the * list has changed. - * + * * TODO: every activity that provides a ACTION_SEARCH intent should * also provide searchability meta-data. There are a bunch of checks here * that, if data is not found, silently skip to the next activity. This * won't help a developer trying to figure out why their activity isn't * showing up in the list, but an exception here is too rough. I would * like to find a better notification mechanism. - * + * * TODO: sort the list somehow? UI choice. */ public void buildSearchableList() { - // These will become the new values at the end of the method - HashMap newSearchablesMap + HashMap newSearchablesMap = new HashMap(); ArrayList newSearchablesList = new ArrayList(); ArrayList newSearchablesInGlobalSearchList = new ArrayList(); + ArrayList newSearchablesForWebSearchList + = new ArrayList(); final PackageManager pm = mContext.getPackageManager(); - - // use intent resolver to generate list of ACTION_SEARCH receivers - List infoList; + + // Use intent resolver to generate list of ACTION_SEARCH & ACTION_WEB_SEARCH receivers. + List searchList; final Intent intent = new Intent(Intent.ACTION_SEARCH); - infoList = pm.queryIntentActivities(intent, PackageManager.GET_META_DATA); - + searchList = pm.queryIntentActivities(intent, PackageManager.GET_META_DATA); + + List webSearchInfoList; + final Intent webSearchIntent = new Intent(Intent.ACTION_WEB_SEARCH); + webSearchInfoList = pm.queryIntentActivities(webSearchIntent, PackageManager.GET_META_DATA); + // analyze each one, generate a Searchables record, and record - if (infoList != null) { - int count = infoList.size(); + if (searchList != null || webSearchInfoList != null) { + int search_count = (searchList == null ? 0 : searchList.size()); + int web_search_count = (webSearchInfoList == null ? 0 : webSearchInfoList.size()); + int count = search_count + web_search_count; for (int ii = 0; ii < count; ii++) { // for each component, try to find metadata - ResolveInfo info = infoList.get(ii); + ResolveInfo info = (ii < search_count) + ? searchList.get(ii) + : webSearchInfoList.get(ii - search_count); ActivityInfo ai = info.activityInfo; - SearchableInfo searchable = SearchableInfo.getActivityMetaData(mContext, ai); - if (searchable != null) { - newSearchablesList.add(searchable); - newSearchablesMap.put(searchable.getSearchActivity(), searchable); - if (searchable.shouldIncludeInGlobalSearch()) { - newSearchablesInGlobalSearchList.add(searchable); + // Check first to avoid duplicate entries. + if (newSearchablesMap.get(new ComponentName(ai.packageName, ai.name)) == null) { + SearchableInfo searchable = SearchableInfo.getActivityMetaData(mContext, ai); + if (searchable != null) { + newSearchablesList.add(searchable); + newSearchablesMap.put(searchable.getSearchActivity(), searchable); + if (searchable.shouldIncludeInGlobalSearch()) { + newSearchablesInGlobalSearchList.add(searchable); + } } } } } - + + if (webSearchInfoList != null) { + for (int i = 0; i < webSearchInfoList.size(); ++i) { + ActivityInfo ai = webSearchInfoList.get(i).activityInfo; + ComponentName component = new ComponentName(ai.packageName, ai.name); + newSearchablesForWebSearchList.add(newSearchablesMap.get(component)); + } + } + // Find the global search provider Intent globalSearchIntent = new Intent(SearchManager.INTENT_ACTION_GLOBAL_SEARCH); ComponentName globalSearchActivity = globalSearchIntent.resolveActivity(pm); SearchableInfo newDefaultSearchable = newSearchablesMap.get(globalSearchActivity); + if (newDefaultSearchable == null) { + Log.w(LOG_TAG, "No searchable info found for new default searchable activity " + + globalSearchActivity); + } + + // Find the default web search provider. + ComponentName webSearchActivity = getPreferredWebSearchActivity(); + SearchableInfo newDefaultSearchableForWebSearch = null; + if (webSearchActivity != null) { + newDefaultSearchableForWebSearch = newSearchablesMap.get(webSearchActivity); + } + if (newDefaultSearchableForWebSearch == null) { + Log.w(LOG_TAG, "No searchable info found for new default web search activity " + + webSearchActivity); + } + // Store a consistent set of new values synchronized (this) { mSearchablesMap = newSearchablesMap; mSearchablesList = newSearchablesList; mSearchablesInGlobalSearchList = newSearchablesInGlobalSearchList; + mSearchablesForWebSearchList = newSearchablesForWebSearchList; mDefaultSearchable = newDefaultSearchable; + mDefaultSearchableForWebSearch = newDefaultSearchableForWebSearch; + } + + // Inform all listeners that the list of searchables has been updated. + mContext.sendBroadcast(new Intent(SearchManager.INTENT_ACTION_SEARCHABLES_CHANGED)); + } + + /** + * Checks if the given activity component is present in the system and if so makes it the + * preferred activity for handling ACTION_WEB_SEARCH. + * @param component Name of the component to check and set as preferred. + * @param action Intent action for which this activity is to be set as preferred. + * @return true if component was detected and set as preferred activity, false if not. + */ + private boolean setPreferredActivity(ComponentName component, String action) { + Log.d(LOG_TAG, "Checking component " + component); + PackageManager pm = mContext.getPackageManager(); + ActivityInfo ai; + try { + ai = pm.getActivityInfo(component, 0); + } catch (PackageManager.NameNotFoundException e) { + return false; + } + + // The code here to find the value for bestMatch is heavily inspired by the code + // in ResolverActivity where the preferred activity is set. + Intent intent = new Intent(action); + intent.addCategory(Intent.CATEGORY_DEFAULT); + List webSearchActivities = pm.queryIntentActivities(intent, 0); + ComponentName set[] = new ComponentName[webSearchActivities.size()]; + int bestMatch = 0; + for (int i = 0; i < webSearchActivities.size(); ++i) { + ResolveInfo ri = webSearchActivities.get(i); + set[i] = new ComponentName(ri.activityInfo.packageName, + ri.activityInfo.name); + if (ri.match > bestMatch) bestMatch = ri.match; + } + + Log.d(LOG_TAG, "Setting preferred web search activity to " + component); + IntentFilter filter = new IntentFilter(action); + filter.addCategory(Intent.CATEGORY_DEFAULT); + pm.replacePreferredActivity(filter, bestMatch, set, component); + return true; + } + + public ComponentName getPreferredWebSearchActivity() { + // Check if we have a preferred web search activity. + Intent intent = new Intent(Intent.ACTION_WEB_SEARCH); + PackageManager pm = mContext.getPackageManager(); + ResolveInfo ri = pm.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY); + + if (ri == null || ri.activityInfo.name.equals(ResolverActivity.class.getName())) { + Log.d(LOG_TAG, "No preferred activity set for action web search."); + + // The components in the providers array are checked in the order of declaration so the + // first one has the highest priority. If the component exists in the system it is set + // as the preferred activity to handle intent action web search. + String[] preferredActivities = mContext.getResources().getStringArray( + com.android.internal.R.array.default_web_search_providers); + for (String componentName : preferredActivities) { + ComponentName component = ComponentName.unflattenFromString(componentName); + if (setPreferredActivity(component, Intent.ACTION_WEB_SEARCH)) { + return component; + } + } + } else { + // If the current preferred activity is GoogleSearch, and we detect + // EnhancedGoogleSearch installed as well, set the latter as preferred since that + // is a superset and provides more functionality. + ComponentName cn = new ComponentName(ri.activityInfo.packageName, ri.activityInfo.name); + if (cn.flattenToShortString().equals(GOOGLE_SEARCH_COMPONENT_NAME)) { + ComponentName enhancedGoogleSearch = ComponentName.unflattenFromString( + ENHANCED_GOOGLE_SEARCH_COMPONENT_NAME); + if (setPreferredActivity(enhancedGoogleSearch, Intent.ACTION_WEB_SEARCH)) { + return enhancedGoogleSearch; + } + } } + + if (ri == null) return null; + return new ComponentName(ri.activityInfo.packageName, ri.activityInfo.name); } - + /** * Returns the list of searchable activities. */ @@ -239,11 +371,33 @@ public class Searchables { ArrayList result = new ArrayList(mSearchablesList); return result; } - + /** * Returns a list of the searchable activities that can be included in global search. */ public synchronized ArrayList getSearchablesInGlobalSearchList() { return new ArrayList(mSearchablesInGlobalSearchList); } + + /** + * Returns a list of the searchable activities that handle web searches. + */ + public synchronized ArrayList getSearchablesForWebSearchList() { + return new ArrayList(mSearchablesForWebSearchList); + } + + /** + * Returns the default searchable activity for web searches. + */ + public synchronized SearchableInfo getDefaultSearchableForWebSearch() { + return mDefaultSearchableForWebSearch; + } + + /** + * Sets the default searchable activity for web searches. + */ + public synchronized void setDefaultWebSearch(ComponentName component) { + setPreferredActivity(component, Intent.ACTION_WEB_SEARCH); + buildSearchableList(); + } } diff --git a/core/java/android/speech/IRecognitionListener.aidl b/core/java/android/speech/IRecognitionListener.aidl index 6ed32b502b424084e48e6d7101ae223c7e16623d..2da2258b4ee1272734d2f5a643ef2dd71b49954f 100644 --- a/core/java/android/speech/IRecognitionListener.aidl +++ b/core/java/android/speech/IRecognitionListener.aidl @@ -17,6 +17,7 @@ package android.speech; import android.os.Bundle; +import android.speech.RecognitionResult; /** * Listener for speech recognition events, used with RecognitionService. @@ -43,13 +44,17 @@ interface IRecognitionListener { /** Called after the user stops speaking. */ void onEndOfSpeech(); - /** A network or recognition error occurred. */ - void onError(in String error); + /** + * A network or recognition error occurred. The code is defined in + * {@link android.speech.RecognitionResult} + */ + void onError(in int error); /** - * Called when recognition transcripts are ready. - * results: an ordered list of the most likely transcripts (N-best list). - * @hide + * Called when recognition results are ready. + * @param results: an ordered list of the most likely results (N-best list). + * @param key: a key associated with the results. The same results can + * be retrieved asynchronously later using the key, if available. */ - void onResults(in List results); + void onResults(in List results, long key); } diff --git a/core/java/android/speech/IRecognitionService.aidl b/core/java/android/speech/IRecognitionService.aidl index 36d12e9aae6437c01d39def2956ab737c28c399d..a18c380c9e707d8ddb01c2f7da4516a768b816ba 100644 --- a/core/java/android/speech/IRecognitionService.aidl +++ b/core/java/android/speech/IRecognitionService.aidl @@ -18,6 +18,7 @@ package android.speech; import android.content.Intent; import android.speech.IRecognitionListener; +import android.speech.RecognitionResult; // A Service interface to speech recognition. Call startListening when // you want to begin capturing audio; RecognitionService will automatically @@ -29,6 +30,8 @@ interface IRecognitionService { // see RecognizerIntent.java for constants used to specify the intent. void startListening(in Intent recognizerIntent, in IRecognitionListener listener); + + List getRecognitionResults(in long key); void cancel(); } diff --git a/core/java/android/speech/RecognitionResult.aidl b/core/java/android/speech/RecognitionResult.aidl new file mode 100644 index 0000000000000000000000000000000000000000..59e53ab86fd18d5b28fc6e70b497f9cb46fc7d81 --- /dev/null +++ b/core/java/android/speech/RecognitionResult.aidl @@ -0,0 +1,19 @@ +/* + * 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.speech; + +parcelable RecognitionResult; diff --git a/core/java/android/speech/RecognitionResult.java b/core/java/android/speech/RecognitionResult.java new file mode 100644 index 0000000000000000000000000000000000000000..8d031fcd32739c15f2ca795054c526d4311c385c --- /dev/null +++ b/core/java/android/speech/RecognitionResult.java @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2008 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 android.speech; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * RecognitionResult is a passive object that stores a single recognized + * query and its search result. + * TODO: revisit and improve. May be we should have a separate result + * object for each type, and put them (type/value) in bundle? + * + * {@hide} + */ +public class RecognitionResult implements Parcelable { + /** + * Status of the recognize request. + */ + public static final int NETWORK_TIMEOUT = 1; // Network operation timed out. + public static final int NETWORK_ERROR = 2; // Other networkrelated errors. + public static final int AUDIO_ERROR = 3; // Audio recording error. + public static final int SERVER_ERROR = 4; // Server sends error status. + public static final int CLIENT_ERROR = 5; // Other client side errors. + public static final int SPEECH_TIMEOUT = 6; // No speech input + public static final int NO_MATCH = 7; // No recognition result matched. + public static final int SERVICE_BUSY = 8; // RecognitionService busy. + + /** + * Type of the recognition results. + */ + public static final int RAW_RECOGNITION_RESULT = 0; + public static final int WEB_SEARCH_RESULT = 1; + public static final int CONTACT_RESULT = 2; + + /** + * A factory method to create a raw RecognitionResult + * + * @param sentence the recognized text. + */ + public static RecognitionResult newRawRecognitionResult(String sentence) { + return new RecognitionResult(RAW_RECOGNITION_RESULT, sentence, null, null); + } + + /** + * A factory method to create RecognitionResult for contacts. + * + * @param contact the contact name. + * @param phoneType the phone type. + */ + public static RecognitionResult newContactResult(String contact, int phoneType) { + return new RecognitionResult(CONTACT_RESULT, contact, phoneType); + } + + /** + * A factory method to create a RecognitionResult for Web Search Query. + * + * @param query the query string. + * @param html the html page of the search result. + * @param url the url that performs the search with the query. + */ + public static RecognitionResult newWebResult(String query, String html, String url) { + return new RecognitionResult(WEB_SEARCH_RESULT, query, html, url); + } + + public static final Parcelable.Creator CREATOR + = new Parcelable.Creator() { + + public RecognitionResult createFromParcel(Parcel in) { + return new RecognitionResult(in); + } + + public RecognitionResult[] newArray(int size) { + return new RecognitionResult[size]; + } + }; + + /** + * Result type. + */ + public final int mResultType; + + /** + * The recognized string when mResultType is WEB_SEARCH_RESULT. + * The name of the contact when mResultType is CONTACT_RESULT. + */ + public final String mText; + + /** + * The HTML result page for the query. If this is null, then the + * application must use the url field to get the HTML result page. + */ + public final String mHtml; + + /** + * The url to get the result page for the query string. The + * application must use this url instead of performing the search + * with the query. + */ + public final String mUrl; + + /** Phone number type. This is valid only when mResultType == CONTACT_RESULT */ + public final int mPhoneType; + + private RecognitionResult(int type, String query, String html, String url) { + mResultType = type; + mText = query; + mHtml = html; + mUrl = url; + mPhoneType = -1; + } + + private RecognitionResult(int type, String query, int at) { + mResultType = type; + mText = query; + mPhoneType = at; + mHtml = null; + mUrl = null; + } + + private RecognitionResult(Parcel in) { + mResultType = in.readInt(); + mText = in.readString(); + mHtml= in.readString(); + mUrl= in.readString(); + mPhoneType = in.readInt(); + } + + public void writeToParcel(Parcel out, int flags) { + out.writeInt(mResultType); + out.writeString(mText); + out.writeString(mHtml); + out.writeString(mUrl); + out.writeInt(mPhoneType); + } + + + @Override + public String toString() { + String resultType[] = { "RAW", "WEB", "CONTACT" }; + return "[type=" + resultType[mResultType] + + ", text=" + mText+ ", mUrl=" + mUrl + ", html=" + mHtml + "]"; + } + + public int describeContents() { + // no special description + return 0; + } +} diff --git a/core/java/android/speech/RecognitionServiceUtil.java b/core/java/android/speech/RecognitionServiceUtil.java index 650c0fd2488179922fae54ef9b71d32db6eecb93..a8c78684f6cdc99f2fa9ca5e8eb1d6ce6459225e 100644 --- a/core/java/android/speech/RecognitionServiceUtil.java +++ b/core/java/android/speech/RecognitionServiceUtil.java @@ -21,6 +21,9 @@ import android.content.Intent; import android.content.ServiceConnection; import android.os.Bundle; import android.os.IBinder; +import android.os.RemoteException; +import android.speech.RecognitionResult; +import android.util.Log; import java.util.List; @@ -56,6 +59,11 @@ public class RecognitionServiceUtil { public static final Intent sDefaultIntent = new Intent( RecognizerIntent.ACTION_RECOGNIZE_SPEECH); + // Recognize request parameters + public static final String USE_LOCATION = "useLocation"; + public static final String CONTACT_AUTH_TOKEN = "contactAuthToken"; + + // Bundles public static final String NOISE_LEVEL = "NoiseLevel"; public static final String SIGNAL_NOISE_RATIO = "SignalNoiseRatio"; @@ -72,8 +80,8 @@ public class RecognitionServiceUtil { public void onRmsChanged(float rmsdB) {} public void onBufferReceived(byte[] buf) {} public void onEndOfSpeech() {} - public void onError(String error) {} - public void onResults(List results) {} + public void onError(int error) {} + public void onResults(List results, long key) {} } /** diff --git a/core/java/android/speech/tts/ITts.aidl b/core/java/android/speech/tts/ITts.aidl new file mode 100755 index 0000000000000000000000000000000000000000..c9a6180d4e6945962ed2fcc6dc5932b00a4c6a68 --- /dev/null +++ b/core/java/android/speech/tts/ITts.aidl @@ -0,0 +1,63 @@ +/* + * 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.speech.tts; + +import android.speech.tts.ITtsCallback; + +import android.content.Intent; + +/** + * AIDL for the TTS Service + * ITts.java is autogenerated from this. + * + * {@hide} + */ +interface ITts { + int setSpeechRate(in int speechRate); + + int setPitch(in int pitch); + + int speak(in String text, in int queueMode, in String[] params); + + boolean isSpeaking(); + + int stop(); + + void addSpeech(in String text, in String packageName, in int resId); + + void addSpeechFile(in String text, in String filename); + + String[] getLanguage(); + + int isLanguageAvailable(in String language, in String country, in String variant); + + int setLanguage(in String language, in String country, in String variant); + + boolean synthesizeToFile(in String text, in String[] params, in String outputDirectory); + + int playEarcon(in String earcon, in int queueMode, in String[] params); + + void addEarcon(in String earcon, in String packageName, in int resId); + + void addEarconFile(in String earcon, in String filename); + + void registerCallback(ITtsCallback cb); + + void unregisterCallback(ITtsCallback cb); + + int playSilence(in long duration, in int queueMode, in String[] params); +} diff --git a/core/java/android/speech/tts/ITtsCallback.aidl b/core/java/android/speech/tts/ITtsCallback.aidl new file mode 100755 index 0000000000000000000000000000000000000000..48ed73e021658f0f949abe3622c7d45cadcc0dfa --- /dev/null +++ b/core/java/android/speech/tts/ITtsCallback.aidl @@ -0,0 +1,27 @@ +/* + * 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.speech.tts; + +/** + * AIDL for the callback from the TTS Service + * ITtsCallback.java is autogenerated from this. + * + * {@hide} + */ +oneway interface ITtsCallback { + void markReached(String mark); +} diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java new file mode 100644 index 0000000000000000000000000000000000000000..616b3f113ffb7c00e0cedea5c543008ff63254a5 --- /dev/null +++ b/core/java/android/speech/tts/TextToSpeech.java @@ -0,0 +1,719 @@ +/* + * Copyright (C) 2009 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 android.speech.tts; + +import android.speech.tts.ITts; +import android.speech.tts.ITtsCallback; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Log; + +import java.util.HashMap; +import java.util.Locale; + +/** + * + * Synthesizes speech from text for immediate playback or to create a sound file. + * + */ +//TODO complete javadoc + add links to constants +public class TextToSpeech { + + /** + * Denotes a successful operation. + */ + public static final int TTS_SUCCESS = 0; + /** + * Denotes a generic operation failure. + */ + public static final int TTS_ERROR = -1; + + /** + * Queue mode where all entries in the playback queue (media to be played + * and text to be synthesized) are dropped and replaced by the new entry. + */ + public static final int TTS_QUEUE_FLUSH = 0; + /** + * Queue mode where the new entry is added at the end of the playback queue. + */ + public static final int TTS_QUEUE_ADD = 1; + + + /** + * Denotes the language is available exactly as specified by the locale + */ + public static final int TTS_LANG_COUNTRY_VAR_AVAILABLE = 2; + + + /** + * Denotes the language is available for the language and country specified + * by the locale, but not the variant. + */ + public static final int TTS_LANG_COUNTRY_AVAILABLE = 1; + + + /** + * Denotes the language is available for the language by the locale, + * but not the country and variant. + */ + public static final int TTS_LANG_AVAILABLE = 0; + + /** + * Denotes the language data is missing. + */ + public static final int TTS_LANG_MISSING_DATA = -1; + + /** + * Denotes the language is not supported by the current TTS engine. + */ + public static final int TTS_LANG_NOT_SUPPORTED = -2; + + + /** + * Called when the TTS has initialized. + * + * The InitListener must implement the onInit function. onInit is passed a + * status code indicating the result of the TTS initialization. + */ + public interface OnInitListener { + public void onInit(int status); + } + + /** + * Internal constants for the TTS functionality + * + * {@hide} + */ + public class Engine { + // default values for a TTS engine when settings are not found in the provider + public static final int FALLBACK_TTS_DEFAULT_RATE = 100; // 1x + public static final int FALLBACK_TTS_DEFAULT_PITCH = 100;// 1x + public static final int FALLBACK_TTS_USE_DEFAULTS = 0; // false + public static final String FALLBACK_TTS_DEFAULT_LANG = "eng"; + public static final String FALLBACK_TTS_DEFAULT_COUNTRY = ""; + public static final String FALLBACK_TTS_DEFAULT_VARIANT = ""; + + // return codes for a TTS engine's check data activity + public static final int CHECK_VOICE_DATA_PASS = 1; + public static final int CHECK_VOICE_DATA_FAIL = 0; + public static final int CHECK_VOICE_DATA_BAD_DATA = -1; + public static final int CHECK_VOICE_DATA_MISSING_DATA = -2; + public static final int CHECK_VOICE_DATA_MISSING_DATA_NO_SDCARD = -3; + + // return codes for a TTS engine's check data activity + public static final String VOICE_DATA_ROOT_DIRECTORY = "dataRoot"; + public static final String VOICE_DATA_FILES = "dataFiles"; + public static final String VOICE_DATA_FILES_INFO = "dataFilesInfo"; + + // keys for the parameters passed with speak commands + public static final String TTS_KEY_PARAM_RATE = "rate"; + public static final String TTS_KEY_PARAM_LANGUAGE = "language"; + public static final String TTS_KEY_PARAM_COUNTRY = "country"; + public static final String TTS_KEY_PARAM_VARIANT = "variant"; + public static final int TTS_PARAM_POSITION_RATE = 0; + public static final int TTS_PARAM_POSITION_LANGUAGE = 2; + public static final int TTS_PARAM_POSITION_COUNTRY = 4; + public static final int TTS_PARAM_POSITION_VARIANT = 6; + } + + /** + * Connection needed for the TTS. + */ + private ServiceConnection mServiceConnection; + + private ITts mITts = null; + private Context mContext = null; + private OnInitListener mInitListener = null; + private boolean mStarted = false; + private final Object mStartLock = new Object(); + /** + * Used to store the cached parameters sent along with each synthesis request to the + * TTS service. + */ + private String[] mCachedParams; + + /** + * The constructor for the TTS. + * + * @param context + * The context + * @param listener + * The InitListener that will be called when the TTS has + * initialized successfully. + */ + public TextToSpeech(Context context, OnInitListener listener) { + mContext = context; + mInitListener = listener; + + mCachedParams = new String[2*4]; // 4 parameters, store key and value + mCachedParams[Engine.TTS_PARAM_POSITION_RATE] = Engine.TTS_KEY_PARAM_RATE; + mCachedParams[Engine.TTS_PARAM_POSITION_LANGUAGE] = Engine.TTS_KEY_PARAM_LANGUAGE; + mCachedParams[Engine.TTS_PARAM_POSITION_COUNTRY] = Engine.TTS_KEY_PARAM_COUNTRY; + mCachedParams[Engine.TTS_PARAM_POSITION_VARIANT] = Engine.TTS_KEY_PARAM_VARIANT; + + mCachedParams[Engine.TTS_PARAM_POSITION_RATE + 1] = + String.valueOf(Engine.FALLBACK_TTS_DEFAULT_RATE); + // initialize the language cached parameters with the current Locale + Locale defaultLoc = Locale.getDefault(); + mCachedParams[Engine.TTS_PARAM_POSITION_LANGUAGE + 1] = defaultLoc.getISO3Language(); + mCachedParams[Engine.TTS_PARAM_POSITION_COUNTRY + 1] = defaultLoc.getISO3Country(); + mCachedParams[Engine.TTS_PARAM_POSITION_VARIANT + 1] = defaultLoc.getVariant(); + + initTts(); + } + + + private void initTts() { + mStarted = false; + + // Initialize the TTS, run the callback after the binding is successful + mServiceConnection = new ServiceConnection() { + public void onServiceConnected(ComponentName name, IBinder service) { + synchronized(mStartLock) { + mITts = ITts.Stub.asInterface(service); + mStarted = true; + if (mInitListener != null) { + // TODO manage failures and missing resources + mInitListener.onInit(TTS_SUCCESS); + } + } + } + + public void onServiceDisconnected(ComponentName name) { + synchronized(mStartLock) { + mITts = null; + mInitListener = null; + mStarted = false; + } + } + }; + + Intent intent = new Intent("android.intent.action.START_TTS_SERVICE"); + intent.addCategory("android.intent.category.TTS"); + mContext.bindService(intent, mServiceConnection, + Context.BIND_AUTO_CREATE); + // TODO handle case where the binding works (should always work) but + // the plugin fails + } + + + /** + * Shuts down the TTS. It is good practice to call this in the onDestroy + * method of the Activity that is using the TTS so that the TTS is stopped + * cleanly. + */ + public void shutdown() { + try { + mContext.unbindService(mServiceConnection); + } catch (IllegalArgumentException e) { + // Do nothing and fail silently since an error here indicates that + // binding never succeeded in the first place. + } + } + + + /** + * Adds a mapping between a string of text and a sound resource in a + * package. + * + * @see #TTS.speak(String text, int queueMode, String[] params) + * + * @param text + * Example: "south_south_east"
        + * + * @param packagename + * Pass the packagename of the application that contains the + * resource. If the resource is in your own application (this is + * the most common case), then put the packagename of your + * application here.
        + * Example: "com.google.marvin.compass"
        + * The packagename can be found in the AndroidManifest.xml of + * your application. + *

        + * <manifest xmlns:android="..." + * package="com.google.marvin.compass"> + *

        + * + * @param resourceId + * Example: R.raw.south_south_east + * + * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS. + */ + public int addSpeech(String text, String packagename, int resourceId) { + synchronized(mStartLock) { + if (!mStarted) { + return TTS_ERROR; + } + try { + mITts.addSpeech(text, packagename, resourceId); + return TTS_SUCCESS; + } catch (RemoteException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } catch (NullPointerException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } catch (IllegalStateException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } + return TTS_ERROR; + } + } + + + /** + * Adds a mapping between a string of text and a sound file. Using this, it + * is possible to add custom pronounciations for text. + * + * @param text + * The string of text + * @param filename + * The full path to the sound file (for example: + * "/sdcard/mysounds/hello.wav") + * + * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS. + */ + public int addSpeech(String text, String filename) { + synchronized (mStartLock) { + if (!mStarted) { + return TTS_ERROR; + } + try { + mITts.addSpeechFile(text, filename); + return TTS_SUCCESS; + } catch (RemoteException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } catch (NullPointerException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } catch (IllegalStateException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } + return TTS_ERROR; + } + } + + + /** + * Speaks the string using the specified queuing strategy and speech + * parameters. Note that the speech parameters are not universally supported + * by all engines and will be treated as a hint. The TTS library will try to + * fulfill these parameters as much as possible, but there is no guarantee + * that the voice used will have the properties specified. + * + * @param text + * The string of text to be spoken. + * @param queueMode + * The queuing strategy to use. + * See TTS_QUEUE_ADD and TTS_QUEUE_FLUSH. + * @param params + * The hashmap of speech parameters to be used. + * + * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS. + */ + public int speak(String text, int queueMode, HashMap params) + { + synchronized (mStartLock) { + int result = TTS_ERROR; + Log.i("TTS received: ", text); + if (!mStarted) { + return result; + } + try { + // TODO support extra parameters, passing cache of current parameters for the moment + result = mITts.speak(text, queueMode, mCachedParams); + } catch (RemoteException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } catch (NullPointerException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } catch (IllegalStateException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } finally { + return result; + } + } + } + + + /** + * Plays the earcon using the specified queueing mode and parameters. + * + * @param earcon + * The earcon that should be played + * @param queueMode + * See TTS_QUEUE_ADD and TTS_QUEUE_FLUSH. + * @param params + * The hashmap of parameters to be used. + * + * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS. + */ + public int playEarcon(String earcon, int queueMode, + HashMap params) { + synchronized (mStartLock) { + int result = TTS_ERROR; + if (!mStarted) { + return result; + } + try { + // TODO support extra parameters, passing null for the moment + result = mITts.playEarcon(earcon, queueMode, null); + } catch (RemoteException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } catch (NullPointerException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } catch (IllegalStateException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } finally { + return result; + } + } + } + + /** + * Plays silence for the specified amount of time using the specified + * queue mode. + * + * @param durationInMs + * A long that indicates how long the silence should last. + * @param queueMode + * See TTS_QUEUE_ADD and TTS_QUEUE_FLUSH. + * + * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS. + */ + public int playSilence(long durationInMs, int queueMode) { + synchronized (mStartLock) { + int result = TTS_ERROR; + if (!mStarted) { + return result; + } + try { + // TODO support extra parameters, passing cache of current parameters for the moment + result = mITts.playSilence(durationInMs, queueMode, mCachedParams); + } catch (RemoteException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } catch (NullPointerException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } catch (IllegalStateException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } finally { + return result; + } + } + } + + + /** + * Returns whether or not the TTS is busy speaking. + * + * @return Whether or not the TTS is busy speaking. + */ + public boolean isSpeaking() { + synchronized (mStartLock) { + if (!mStarted) { + return false; + } + try { + return mITts.isSpeaking(); + } catch (RemoteException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } catch (NullPointerException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } catch (IllegalStateException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } + return false; + } + } + + + /** + * Stops speech from the TTS. + * + * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS. + */ + public int stop() { + synchronized (mStartLock) { + int result = TTS_ERROR; + if (!mStarted) { + return result; + } + try { + result = mITts.stop(); + } catch (RemoteException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } catch (NullPointerException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } catch (IllegalStateException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } finally { + return result; + } + } + } + + + /** + * Sets the speech rate for the TTS engine. + * + * Note that the speech rate is not universally supported by all engines and + * will be treated as a hint. The TTS library will try to use the specified + * speech rate, but there is no guarantee. + * This has no effect on any pre-recorded speech. + * + * @param speechRate + * The speech rate for the TTS engine. 1 is the normal speed, + * lower values slow down the speech (0.5 is half the normal speech rate), + * greater values accelerate it (2 is twice the normal speech rate). + * + * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS. + */ + public int setSpeechRate(float speechRate) { + synchronized (mStartLock) { + int result = TTS_ERROR; + if (!mStarted) { + return result; + } + try { + if (speechRate > 0) { + int rate = (int)(speechRate*100); + mCachedParams[Engine.TTS_PARAM_POSITION_RATE + 1] = String.valueOf(rate); + result = mITts.setSpeechRate(rate); + } + } catch (RemoteException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } finally { + return result; + } + } + } + + + /** + * Sets the speech pitch for the TTS engine. + * + * Note that the pitch is not universally supported by all engines and + * will be treated as a hint. The TTS library will try to use the specified + * pitch, but there is no guarantee. + * This has no effect on any pre-recorded speech. + * + * @param pitch + * The pitch for the TTS engine. 1 is the normal pitch, + * lower values lower the tone of the synthesized voice, + * greater values increase it. + * + * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS. + */ + public int setPitch(float pitch) { + synchronized (mStartLock) { + int result = TTS_ERROR; + if (!mStarted) { + return result; + } + try { + if (pitch > 0) { + result = mITts.setPitch((int)(pitch*100)); + } + } catch (RemoteException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } finally { + return result; + } + } + } + + + /** + * Sets the language for the TTS engine. + * + * Note that the language is not universally supported by all engines and + * will be treated as a hint. The TTS library will try to use the specified + * language as represented by the Locale, but there is no guarantee. + * + * @param loc + * The locale describing the language to be used. + * + * @return Code indicating the support status for the locale. See the TTS_LANG_ codes. + */ + public int setLanguage(Locale loc) { + synchronized (mStartLock) { + int result = TTS_LANG_NOT_SUPPORTED; + if (!mStarted) { + return result; + } + try { + mCachedParams[Engine.TTS_PARAM_POSITION_LANGUAGE + 1] = loc.getISO3Language(); + mCachedParams[Engine.TTS_PARAM_POSITION_COUNTRY + 1] = loc.getISO3Country(); + mCachedParams[Engine.TTS_PARAM_POSITION_VARIANT + 1] = loc.getVariant(); + result = mITts.setLanguage(mCachedParams[Engine.TTS_PARAM_POSITION_LANGUAGE + 1], + mCachedParams[Engine.TTS_PARAM_POSITION_COUNTRY + 1], + mCachedParams[Engine.TTS_PARAM_POSITION_VARIANT + 1] ); + // TTS died; restart it. + mStarted = false; + initTts(); + } finally { + return result; + } + } + } + + + /** + * Returns a Locale instance describing the language currently being used by the TTS engine. + * @return language, country (if any) and variant (if any) used by the engine stored in a Locale + * instance, or null is the TTS engine has failed. + */ + public Locale getLanguage() { + synchronized (mStartLock) { + if (!mStarted) { + return null; + } + try { + String[] locStrings = mITts.getLanguage(); + if (locStrings.length == 3) { + return new Locale(locStrings[0], locStrings[1], locStrings[2]); + } else { + return null; + } + } catch (RemoteException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } + return null; + } + } + + /** + * Checks if the specified language as represented by the Locale is available. + * + * @param loc + * The Locale describing the language to be used. + * + * @return one of TTS_LANG_NOT_SUPPORTED, TTS_LANG_MISSING_DATA, TTS_LANG_AVAILABLE, + * TTS_LANG_COUNTRY_AVAILABLE, TTS_LANG_COUNTRY_VAR_AVAILABLE. + */ + public int isLanguageAvailable(Locale loc) { + synchronized (mStartLock) { + int result = TTS_LANG_NOT_SUPPORTED; + if (!mStarted) { + return result; + } + try { + result = mITts.isLanguageAvailable(loc.getISO3Language(), + loc.getISO3Country(), loc.getVariant()); + } catch (RemoteException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } finally { + return result; + } + } + } + + + /** + * Synthesizes the given text to a file using the specified parameters. + * + * @param text + * The String of text that should be synthesized + * @param params + * A hashmap of parameters. + * @param filename + * The string that gives the full output filename; it should be + * something like "/sdcard/myappsounds/mysound.wav". + * + * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS. + */ + public int synthesizeToFile(String text, HashMap params, + String filename) { + synchronized (mStartLock) { + int result = TTS_ERROR; + if (!mStarted) { + return result; + } + try { + // TODO support extra parameters, passing null for the moment + if (mITts.synthesizeToFile(text, null, filename)){ + result = TTS_SUCCESS; + } + } catch (RemoteException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } catch (NullPointerException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } catch (IllegalStateException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } finally { + return result; + } + } + } + +} diff --git a/core/java/android/syncml/pim/PropertyNode.java b/core/java/android/syncml/pim/PropertyNode.java index cc5249905efe571ec90440e47b11319ee2bcb6c5..983ecb8a37f2e2a225ce08eb66707e57fa3fc77d 100644 --- a/core/java/android/syncml/pim/PropertyNode.java +++ b/core/java/android/syncml/pim/PropertyNode.java @@ -17,12 +17,16 @@ package android.syncml.pim; import android.content.ContentValues; -import android.util.Log; +import org.apache.commons.codec.binary.Base64; + +import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.Map.Entry; +import java.util.regex.Pattern; public class PropertyNode { @@ -52,7 +56,9 @@ public class PropertyNode { public Set propGroupSet; public PropertyNode() { + propName = ""; propValue = ""; + propValue_vector = new ArrayList(); paramMap = new ContentValues(); paramMap_TYPE = new HashSet(); propGroupSet = new HashSet(); @@ -62,13 +68,21 @@ public class PropertyNode { String propName, String propValue, List propValue_vector, byte[] propValue_bytes, ContentValues paramMap, Set paramMap_TYPE, Set propGroupSet) { - this.propName = propName; + if (propName != null) { + this.propName = propName; + } else { + this.propName = ""; + } if (propValue != null) { this.propValue = propValue; } else { this.propValue = ""; } - this.propValue_vector = propValue_vector; + if (propValue_vector != null) { + this.propValue_vector = propValue_vector; + } else { + this.propValue_vector = new ArrayList(); + } this.propValue_bytes = propValue_bytes; if (paramMap != null) { this.paramMap = paramMap; @@ -117,17 +131,9 @@ public class PropertyNode { // decoded by BASE64 or QUOTED-PRINTABLE. When the size of propValue_vector // is 1, the encoded value is stored in propValue, so we do not have to // check it. - if (propValue_vector != null) { - // Log.d("@@@", "===" + propValue_vector + ", " + node.propValue_vector); - return (propValue_vector.equals(node.propValue_vector) || - (propValue_vector.size() == 1)); - } else if (node.propValue_vector != null) { - // Log.d("@@@", "===" + propValue_vector + ", " + node.propValue_vector); - return (node.propValue_vector.equals(propValue_vector) || - (node.propValue_vector.size() == 1)); - } else { - return true; - } + return (propValue_vector.equals(node.propValue_vector) || + propValue_vector.size() == 1 || + node.propValue_vector.size() == 1); } } @@ -154,4 +160,164 @@ public class PropertyNode { builder.append(propValue); return builder.toString(); } + + /** + * Encode this object into a string which can be decoded. + */ + public String encode() { + // PropertyNode#toString() is for reading, not for parsing in the future. + // We construct appropriate String here. + StringBuilder builder = new StringBuilder(); + if (propName.length() > 0) { + builder.append("propName:["); + builder.append(propName); + builder.append("],"); + } + int size = propGroupSet.size(); + if (size > 0) { + Set set = propGroupSet; + builder.append("propGroup:["); + int i = 0; + for (String group : set) { + // We do not need to double quote groups. + // group = 1*(ALPHA / DIGIT / "-") + builder.append(group); + if (i < size - 1) { + builder.append(","); + } + i++; + } + builder.append("],"); + } + + if (paramMap.size() > 0 || paramMap_TYPE.size() > 0) { + ContentValues values = paramMap; + builder.append("paramMap:["); + size = paramMap.size(); + int i = 0; + for (Entry entry : values.valueSet()) { + // Assuming param-key does not contain NON-ASCII nor symbols. + // + // According to vCard 3.0: + // param-name = iana-token / x-name + builder.append(entry.getKey()); + + // param-value may contain any value including NON-ASCIIs. + // We use the following replacing rule. + // \ -> \\ + // , -> \, + // In String#replaceAll(), "\\\\" means a single backslash. + builder.append("="); + builder.append(entry.getValue().toString() + .replaceAll("\\\\", "\\\\\\\\") + .replaceAll(",", "\\\\,")); + if (i < size -1) { + builder.append(","); + } + i++; + } + + Set set = paramMap_TYPE; + size = paramMap_TYPE.size(); + if (i > 0 && size > 0) { + builder.append(","); + } + i = 0; + for (String type : set) { + builder.append("TYPE="); + builder.append(type + .replaceAll("\\\\", "\\\\\\\\") + .replaceAll(",", "\\\\,")); + if (i < size - 1) { + builder.append(","); + } + i++; + } + builder.append("],"); + } + + size = propValue_vector.size(); + if (size > 0) { + builder.append("propValue:["); + List list = propValue_vector; + for (int i = 0; i < size; i++) { + builder.append(list.get(i) + .replaceAll("\\\\", "\\\\\\\\") + .replaceAll(",", "\\\\,")); + if (i < size -1) { + builder.append(","); + } + } + builder.append("],"); + } + + return builder.toString(); + } + + public static PropertyNode decode(String encodedString) { + PropertyNode propertyNode = new PropertyNode(); + String trimed = encodedString.trim(); + if (trimed.length() == 0) { + return propertyNode; + } + String[] elems = trimed.split("],"); + + for (String elem : elems) { + int index = elem.indexOf('['); + String name = elem.substring(0, index - 1); + Pattern pattern = Pattern.compile("(? paramMap_TYPE = propertyNode.paramMap_TYPE; + for (String value : values) { + String[] tmp = value.split("=", 2); + String mapKey = tmp[0]; + // \, -> , + // \\ -> \ + // In String#replaceAll(), "\\\\" means a single backslash. + String mapValue = + tmp[1].replaceAll("\\\\,", ",").replaceAll("\\\\\\\\", "\\\\"); + if (mapKey.equalsIgnoreCase("TYPE")) { + paramMap_TYPE.add(mapValue); + } else { + paramMap.put(mapKey, mapValue); + } + } + } else if (name.equals("propValue")) { + StringBuilder builder = new StringBuilder(); + List list = propertyNode.propValue_vector; + int length = values.length; + for (int i = 0; i < length; i++) { + String normValue = values[i] + .replaceAll("\\\\,", ",") + .replaceAll("\\\\\\\\", "\\\\"); + list.add(normValue); + builder.append(normValue); + if (i < length - 1) { + builder.append(";"); + } + } + propertyNode.propValue = builder.toString(); + } + } + + // At this time, QUOTED-PRINTABLE is already decoded to Java String. + // We just need to decode BASE64 String to binary. + String encoding = propertyNode.paramMap.getAsString("ENCODING"); + if (encoding != null && + (encoding.equalsIgnoreCase("BASE64") || + encoding.equalsIgnoreCase("B"))) { + propertyNode.propValue_bytes = + Base64.decodeBase64(propertyNode.propValue_vector.get(0).getBytes()); + } + + return propertyNode; + } } diff --git a/core/java/android/syncml/pim/VBuilderCollection.java b/core/java/android/syncml/pim/VBuilderCollection.java new file mode 100644 index 0000000000000000000000000000000000000000..f09c1c49445c4c193cd94a2555599cb21ce58ede --- /dev/null +++ b/core/java/android/syncml/pim/VBuilderCollection.java @@ -0,0 +1,100 @@ +/* + * 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.syncml.pim; + +import java.util.Collection; +import java.util.List; + +public class VBuilderCollection implements VBuilder { + + private final Collection mVBuilderCollection; + + public VBuilderCollection(Collection vBuilderCollection) { + mVBuilderCollection = vBuilderCollection; + } + + public Collection getVBuilderCollection() { + return mVBuilderCollection; + } + + public void start() { + for (VBuilder builder : mVBuilderCollection) { + builder.start(); + } + } + + public void end() { + for (VBuilder builder : mVBuilderCollection) { + builder.end(); + } + } + + public void startRecord(String type) { + for (VBuilder builder : mVBuilderCollection) { + builder.startRecord(type); + } + } + + public void endRecord() { + for (VBuilder builder : mVBuilderCollection) { + builder.endRecord(); + } + } + + public void startProperty() { + for (VBuilder builder : mVBuilderCollection) { + builder.startProperty(); + } + } + + + public void endProperty() { + for (VBuilder builder : mVBuilderCollection) { + builder.endProperty(); + } + } + + public void propertyGroup(String group) { + for (VBuilder builder : mVBuilderCollection) { + builder.propertyGroup(group); + } + } + + public void propertyName(String name) { + for (VBuilder builder : mVBuilderCollection) { + builder.propertyName(name); + } + } + + public void propertyParamType(String type) { + for (VBuilder builder : mVBuilderCollection) { + builder.propertyParamType(type); + } + } + + public void propertyParamValue(String value) { + for (VBuilder builder : mVBuilderCollection) { + builder.propertyParamValue(value); + } + } + + public void propertyValues(List values) { + for (VBuilder builder : mVBuilderCollection) { + builder.propertyValues(values); + } + } +} diff --git a/core/java/android/syncml/pim/VDataBuilder.java b/core/java/android/syncml/pim/VDataBuilder.java index 8c67cf5bfa035a2527dd035f31179b72fe6983ee..f6e5b653125f019b00af17db8df62e8023def9c8 100644 --- a/core/java/android/syncml/pim/VDataBuilder.java +++ b/core/java/android/syncml/pim/VDataBuilder.java @@ -17,8 +17,10 @@ package android.syncml.pim; import android.content.ContentValues; +import android.util.CharsetUtils; import android.util.Log; +import org.apache.commons.codec.DecoderException; import org.apache.commons.codec.binary.Base64; import org.apache.commons.codec.net.QuotedPrintableCodec; @@ -26,9 +28,7 @@ import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.util.ArrayList; -import java.util.Collection; import java.util.List; -import java.util.Vector; /** * Store the parse result to custom datastruct: VNode, PropertyNode @@ -38,7 +38,13 @@ import java.util.Vector; */ public class VDataBuilder implements VBuilder { static private String LOG_TAG = "VDATABuilder"; - + + /** + * If there's no other information available, this class uses this charset for encoding + * byte arrays. + */ + static public String DEFAULT_CHARSET = "UTF-8"; + /** type=VNode */ public List vNodeList = new ArrayList(); private int mNodeListPos = 0; @@ -47,34 +53,74 @@ public class VDataBuilder implements VBuilder { private String mCurrentParamType; /** - * Assumes that each String can be encoded into byte array using this encoding. + * The charset using which VParser parses the text. + */ + private String mSourceCharset; + + /** + * The charset with which byte array is encoded to String. */ - private String mCharset; + private String mTargetCharset; private boolean mStrictLineBreakParsing; public VDataBuilder() { - mCharset = "ISO-8859-1"; - mStrictLineBreakParsing = false; + this(VParser.DEFAULT_CHARSET, DEFAULT_CHARSET, false); } - public VDataBuilder(String encoding, boolean strictLineBreakParsing) { - mCharset = encoding; - mStrictLineBreakParsing = strictLineBreakParsing; + public VDataBuilder(String charset, boolean strictLineBreakParsing) { + this(null, charset, strictLineBreakParsing); } + /** + * @hide sourceCharset is temporal. + */ + public VDataBuilder(String sourceCharset, String targetCharset, + boolean strictLineBreakParsing) { + if (sourceCharset != null) { + mSourceCharset = sourceCharset; + } else { + mSourceCharset = VParser.DEFAULT_CHARSET; + } + if (targetCharset != null) { + mTargetCharset = targetCharset; + } else { + mTargetCharset = DEFAULT_CHARSET; + } + mStrictLineBreakParsing = strictLineBreakParsing; + } + public void start() { } public void end() { } + // Note: I guess that this code assumes the Record may nest like this: + // START:VPOS + // ... + // START:VPOS2 + // ... + // END:VPOS2 + // ... + // END:VPOS + // + // However the following code has a bug. + // When error occurs after calling startRecord(), the entry which is probably + // the cause of the error remains to be in vNodeList, while endRecord() is not called. + // + // I leave this code as is since I'm not familiar with vcalendar specification. + // But I believe we should refactor this code in the future. + // Until this, the last entry has to be removed when some error occurs. public void startRecord(String type) { + VNode vnode = new VNode(); vnode.parseStatus = 1; vnode.VName = type; + // I feel this should be done in endRecord(), but it cannot be done because of + // the reason above. vNodeList.add(vnode); - mNodeListPos = vNodeList.size()-1; + mNodeListPos = vNodeList.size() - 1; mCurrentVNode = vNodeList.get(mNodeListPos); } @@ -90,15 +136,14 @@ public class VDataBuilder implements VBuilder { } public void startProperty() { - // System.out.println("+ startProperty. "); + mCurrentPropNode = new PropertyNode(); } public void endProperty() { - // System.out.println("- endProperty. "); + mCurrentVNode.propList.add(mCurrentPropNode); } public void propertyName(String name) { - mCurrentPropNode = new PropertyNode(); mCurrentPropNode.propName = name; } @@ -122,139 +167,145 @@ public class VDataBuilder implements VBuilder { mCurrentParamType = null; } - private String encodeString(String originalString, String targetEncoding) { - Charset charset = Charset.forName(mCharset); + private String encodeString(String originalString, String targetCharset) { + if (mSourceCharset.equalsIgnoreCase(targetCharset)) { + return originalString; + } + Charset charset = Charset.forName(mSourceCharset); ByteBuffer byteBuffer = charset.encode(originalString); // byteBuffer.array() "may" return byte array which is larger than // byteBuffer.remaining(). Here, we keep on the safe side. byte[] bytes = new byte[byteBuffer.remaining()]; byteBuffer.get(bytes); try { - return new String(bytes, targetEncoding); + return new String(bytes, targetCharset); } catch (UnsupportedEncodingException e) { - return null; + Log.e(LOG_TAG, "Failed to encode: charset=" + targetCharset); + return new String(bytes); } } - public void propertyValues(List values) { - ContentValues paramMap = mCurrentPropNode.paramMap; - - String charsetString = paramMap.getAsString("CHARSET"); - - boolean setupParamValues = false; - //decode value string to propValue_bytes - if (paramMap.containsKey("ENCODING")) { - String encoding = paramMap.getAsString("ENCODING"); - if (encoding.equalsIgnoreCase("BASE64") || - encoding.equalsIgnoreCase("B")) { - if (values.size() > 1) { - Log.e(LOG_TAG, - ("BASE64 encoding is used while " + - "there are multiple values (" + values.size())); - } + private String handleOneValue(String value, String targetCharset, String encoding) { + if (encoding != null) { + if (encoding.equals("BASE64") || encoding.equals("B")) { + // Assume BASE64 is used only when the number of values is 1. mCurrentPropNode.propValue_bytes = - Base64.decodeBase64(values.get(0). - replaceAll(" ","").replaceAll("\t",""). - replaceAll("\r\n",""). - getBytes()); - } - - if(encoding.equalsIgnoreCase("QUOTED-PRINTABLE")){ - // if CHARSET is defined, we translate each String into the Charset. - List tmpValues = new ArrayList(); - Vector byteVector = new Vector(); - int size = 0; - try{ - for (String value : values) { - String quotedPrintable = value - .replaceAll("= ", " ").replaceAll("=\t", "\t"); - String[] lines; - if (mStrictLineBreakParsing) { - lines = quotedPrintable.split("\r\n"); - } else { - lines = quotedPrintable - .replace("\r\n", "\n").replace("\r", "\n").split("\n"); - } - StringBuilder builder = new StringBuilder(); - for (String line : lines) { - if (line.endsWith("=")) { - line = line.substring(0, line.length() - 1); - } - builder.append(line); - } - byte[] bytes = QuotedPrintableCodec.decodeQuotedPrintable( - builder.toString().getBytes()); - if (charsetString != null) { - try { - tmpValues.add(new String(bytes, charsetString)); - } catch (UnsupportedEncodingException e) { - Log.e(LOG_TAG, "Failed to encode: charset=" + charsetString); - tmpValues.add(new String(bytes)); + Base64.decodeBase64(value.getBytes()); + return value; + } else if (encoding.equals("QUOTED-PRINTABLE")) { + String quotedPrintable = value + .replaceAll("= ", " ").replaceAll("=\t", "\t"); + String[] lines; + if (mStrictLineBreakParsing) { + lines = quotedPrintable.split("\r\n"); + } else { + StringBuilder builder = new StringBuilder(); + int length = quotedPrintable.length(); + ArrayList list = new ArrayList(); + for (int i = 0; i < length; i++) { + char ch = quotedPrintable.charAt(i); + if (ch == '\n') { + list.add(builder.toString()); + builder = new StringBuilder(); + } else if (ch == '\r') { + list.add(builder.toString()); + builder = new StringBuilder(); + if (i < length - 1) { + char nextCh = quotedPrintable.charAt(i + 1); + if (nextCh == '\n') { + i++; + } } } else { - tmpValues.add(new String(bytes)); - } - byteVector.add(bytes); - size += bytes.length; - } // for (String value : values) { - mCurrentPropNode.propValue_vector = tmpValues; - mCurrentPropNode.propValue = listToString(tmpValues); - - mCurrentPropNode.propValue_bytes = new byte[size]; - - { - byte[] tmpBytes = mCurrentPropNode.propValue_bytes; - int index = 0; - for (byte[] bytes : byteVector) { - int length = bytes.length; - for (int i = 0; i < length; i++, index++) { - tmpBytes[index] = bytes[i]; - } + builder.append(ch); } } - setupParamValues = true; - } catch(Exception e) { - Log.e(LOG_TAG, "Failed to decode quoted-printable: " + e); + String finalLine = builder.toString(); + if (finalLine.length() > 0) { + list.add(finalLine); + } + lines = list.toArray(new String[0]); } - } // QUOTED-PRINTABLE - } // ENCODING - - if (!setupParamValues) { - // if CHARSET is defined, we translate each String into the Charset. - if (charsetString != null) { - List tmpValues = new ArrayList(); - for (String value : values) { - String result = encodeString(value, charsetString); - if (result != null) { - tmpValues.add(result); - } else { - Log.e(LOG_TAG, "Failed to encode: charset=" + charsetString); - tmpValues.add(value); + StringBuilder builder = new StringBuilder(); + for (String line : lines) { + if (line.endsWith("=")) { + line = line.substring(0, line.length() - 1); } + builder.append(line); + } + byte[] bytes; + try { + bytes = builder.toString().getBytes(mSourceCharset); + } catch (UnsupportedEncodingException e1) { + Log.e(LOG_TAG, "Failed to encode: charset=" + mSourceCharset); + bytes = builder.toString().getBytes(); + } + + try { + bytes = QuotedPrintableCodec.decodeQuotedPrintable(bytes); + } catch (DecoderException e) { + Log.e(LOG_TAG, "Failed to decode quoted-printable: " + e); + return ""; + } + + try { + return new String(bytes, targetCharset); + } catch (UnsupportedEncodingException e) { + Log.e(LOG_TAG, "Failed to encode: charset=" + targetCharset); + return new String(bytes); } - values = tmpValues; } - - mCurrentPropNode.propValue_vector = values; - mCurrentPropNode.propValue = listToString(values); + // Unknown encoding. Fall back to default. } - mCurrentVNode.propList.add(mCurrentPropNode); + return encodeString(value, targetCharset); } - - private String listToString(Collection list){ - StringBuilder typeListB = new StringBuilder(); - for (String type : list) { - typeListB.append(type).append(";"); + + public void propertyValues(List values) { + if (values == null || values.size() == 0) { + mCurrentPropNode.propValue_bytes = null; + mCurrentPropNode.propValue_vector.clear(); + mCurrentPropNode.propValue_vector.add(""); + mCurrentPropNode.propValue = ""; + return; + } + + ContentValues paramMap = mCurrentPropNode.paramMap; + + String targetCharset = CharsetUtils.nameForDefaultVendor(paramMap.getAsString("CHARSET")); + String encoding = paramMap.getAsString("ENCODING"); + + if (targetCharset == null || targetCharset.length() == 0) { + targetCharset = mTargetCharset; + } + + for (String value : values) { + mCurrentPropNode.propValue_vector.add( + handleOneValue(value, targetCharset, encoding)); } - int len = typeListB.length(); - if (len > 0 && typeListB.charAt(len - 1) == ';') { - return typeListB.substring(0, len - 1); + + mCurrentPropNode.propValue = listToString(mCurrentPropNode.propValue_vector); + } + + private String listToString(List list){ + int size = list.size(); + if (size > 1) { + StringBuilder typeListB = new StringBuilder(); + for (String type : list) { + typeListB.append(type).append(";"); + } + int len = typeListB.length(); + if (len > 0 && typeListB.charAt(len - 1) == ';') { + return typeListB.substring(0, len - 1); + } + return typeListB.toString(); + } else if (size == 1) { + return list.get(0); + } else { + return ""; } - return typeListB.toString(); } public String getResult(){ return null; } } - diff --git a/core/java/android/syncml/pim/VParser.java b/core/java/android/syncml/pim/VParser.java index df93f38e9e386190748fbc74c9fc2fc1c2ad5884..57c5f7a5cfd32c4aaa95fff21bd3a7c4d011feb4 100644 --- a/core/java/android/syncml/pim/VParser.java +++ b/core/java/android/syncml/pim/VParser.java @@ -26,6 +26,9 @@ import java.io.UnsupportedEncodingException; * */ abstract public class VParser { + // Assume that "iso-8859-1" is able to map "all" 8bit characters to some unicode and + // decode the unicode to the original charset. If not, this setting will cause some bug. + public static String DEFAULT_CHARSET = "iso-8859-1"; /** * The buffer used to store input stream @@ -95,6 +98,20 @@ abstract public class VParser { return (mBuffer.length() == sum); } + /** + * Parse the given stream with the default encoding. + * + * @param is + * The source to parse. + * @param builder + * The v builder which used to construct data. + * @return Return true for success, otherwise false. + * @throws IOException + */ + public boolean parse(InputStream is, VBuilder builder) throws IOException { + return parse(is, DEFAULT_CHARSET, builder); + } + /** * Copy the content of input stream and filter the "folding" */ diff --git a/core/java/android/syncml/pim/vcard/ContactStruct.java b/core/java/android/syncml/pim/vcard/ContactStruct.java index 8d9b7fab24fb8f4bfa9d99183bf198e288244540..ecd719da069d61efb48a553a4eb819eb7b6a5777 100644 --- a/core/java/android/syncml/pim/vcard/ContactStruct.java +++ b/core/java/android/syncml/pim/vcard/ContactStruct.java @@ -16,45 +16,103 @@ package android.syncml.pim.vcard; -import java.util.List; +import android.content.AbstractSyncableContentProvider; +import android.content.ContentResolver; +import android.content.ContentUris; +import android.content.ContentValues; +import android.net.Uri; +import android.provider.Contacts; +import android.provider.Contacts.ContactMethods; +import android.provider.Contacts.Extensions; +import android.provider.Contacts.GroupMembership; +import android.provider.Contacts.Organizations; +import android.provider.Contacts.People; +import android.provider.Contacts.Phones; +import android.provider.Contacts.Photos; +import android.syncml.pim.PropertyNode; +import android.syncml.pim.VNode; +import android.telephony.PhoneNumberUtils; +import android.text.TextUtils; +import android.util.Log; + import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Map.Entry; /** - * The parameter class of VCardCreator. + * The parameter class of VCardComposer. * This class standy by the person-contact in * Android system, we must use this class instance as parameter to transmit to - * VCardCreator so that create vCard string. + * VCardComposer so that create vCard string. */ // TODO: rename the class name, next step public class ContactStruct { - public String company; + private static final String LOG_TAG = "ContactStruct"; + + // Note: phonetic name probably should be "LAST FIRST MIDDLE" for European languages, and + // space should be added between each element while it should not be in Japanese. + // But unfortunately, we currently do not have the data and are not sure whether we should + // support European version of name ordering. + // + // TODO: Implement the logic described above if we really need European version of + // phonetic name handling. Also, adding the appropriate test case of vCard would be + // highly appreciated. + public static final int NAME_ORDER_TYPE_ENGLISH = 0; + public static final int NAME_ORDER_TYPE_JAPANESE = 1; + /** MUST exist */ public String name; + public String phoneticName; /** maybe folding */ - public String notes; + public List notes = new ArrayList(); /** maybe folding */ public String title; /** binary bytes of pic. */ public byte[] photoBytes; - /** mime_type col of images table */ + /** The type of Photo (e.g. JPEG, BMP, etc.) */ public String photoType; /** Only for GET. Use addPhoneList() to PUT. */ public List phoneList; /** Only for GET. Use addContactmethodList() to PUT. */ public List contactmethodList; + /** Only for GET. Use addOrgList() to PUT. */ + public List organizationList; + /** Only for GET. Use addExtension() to PUT */ + public Map> extensionMap; - public static class PhoneData{ + // Use organizationList instead when handling ORG. + @Deprecated + public String company; + + public static class PhoneData { + public int type; /** maybe folding */ public String data; - public String type; public String label; + public boolean isPrimary; } - public static class ContactMethod{ - public String kind; - public String type; + public static class ContactMethod { + // Contacts.KIND_EMAIL, Contacts.KIND_POSTAL + public int kind; + // e.g. Contacts.ContactMethods.TYPE_HOME, Contacts.PhoneColumns.TYPE_HOME + // If type == Contacts.PhoneColumns.TYPE_CUSTOM, label is used. + public int type; public String data; + // Used only when TYPE is TYPE_CUSTOM. public String label; + public boolean isPrimary; + } + + public static class OrganizationData { + public int type; + public String companyName; + public String positionName; + public boolean isPrimary; } /** @@ -63,29 +121,858 @@ public class ContactStruct { * @param type type col of content://contacts/phones * @param label lable col of content://contacts/phones */ - public void addPhone(String data, String type, String label){ - if(phoneList == null) + public void addPhone(int type, String data, String label, boolean isPrimary){ + if (phoneList == null) { phoneList = new ArrayList(); - PhoneData st = new PhoneData(); - st.data = data; - st.type = type; - st.label = label; - phoneList.add(st); + } + PhoneData phoneData = new PhoneData(); + phoneData.type = type; + + StringBuilder builder = new StringBuilder(); + String trimed = data.trim(); + int length = trimed.length(); + for (int i = 0; i < length; i++) { + char ch = trimed.charAt(i); + if (('0' <= ch && ch <= '9') || (i == 0 && ch == '+')) { + builder.append(ch); + } + } + phoneData.data = PhoneNumberUtils.formatNumber(builder.toString()); + phoneData.label = label; + phoneData.isPrimary = isPrimary; + phoneList.add(phoneData); } + /** * Add a contactmethod info to contactmethodList. - * @param data contact data + * @param kind integer value defined in Contacts.java + * (e.g. Contacts.KIND_EMAIL) * @param type type col of content://contacts/contact_methods + * @param data contact data + * @param label extra string used only when kind is Contacts.KIND_CUSTOM. */ - public void addContactmethod(String kind, String data, String type, - String label){ - if(contactmethodList == null) + public void addContactmethod(int kind, int type, String data, + String label, boolean isPrimary){ + if (contactmethodList == null) { contactmethodList = new ArrayList(); - ContactMethod st = new ContactMethod(); - st.kind = kind; - st.data = data; - st.type = type; - st.label = label; - contactmethodList.add(st); + } + ContactMethod contactMethod = new ContactMethod(); + contactMethod.kind = kind; + contactMethod.type = type; + contactMethod.data = data; + contactMethod.label = label; + contactMethod.isPrimary = isPrimary; + contactmethodList.add(contactMethod); + } + + /** + * Add a Organization info to organizationList. + */ + public void addOrganization(int type, String companyName, String positionName, + boolean isPrimary) { + if (organizationList == null) { + organizationList = new ArrayList(); + } + OrganizationData organizationData = new OrganizationData(); + organizationData.type = type; + organizationData.companyName = companyName; + organizationData.positionName = positionName; + organizationData.isPrimary = isPrimary; + organizationList.add(organizationData); + } + + /** + * Set "position" value to the appropriate data. If there's more than one + * OrganizationData objects, the value is set to the last one. If there's no + * OrganizationData object, a new OrganizationData is created, whose company name is + * empty. + * + * TODO: incomplete logic. fix this: + * + * e.g. This assumes ORG comes earlier, but TITLE may come earlier like this, though we do not + * know how to handle it in general cases... + * ---- + * TITLE:Software Engineer + * ORG:Google + * ---- + */ + public void setPosition(String positionValue) { + if (organizationList == null) { + organizationList = new ArrayList(); + } + int size = organizationList.size(); + if (size == 0) { + addOrganization(Contacts.OrganizationColumns.TYPE_OTHER, "", null, false); + size = 1; + } + OrganizationData lastData = organizationList.get(size - 1); + lastData.positionName = positionValue; + } + + public void addExtension(PropertyNode propertyNode) { + if (propertyNode.propValue.length() == 0) { + return; + } + // Now store the string into extensionMap. + List list; + String name = propertyNode.propName; + if (extensionMap == null) { + extensionMap = new HashMap>(); + } + if (!extensionMap.containsKey(name)){ + list = new ArrayList(); + extensionMap.put(name, list); + } else { + list = extensionMap.get(name); + } + + list.add(propertyNode.encode()); + } + + private static String getNameFromNProperty(List elems, int nameOrderType) { + // Family, Given, Middle, Prefix, Suffix. (1 - 5) + int size = elems.size(); + if (size > 1) { + StringBuilder builder = new StringBuilder(); + boolean builderIsEmpty = true; + // Prefix + if (size > 3 && elems.get(3).length() > 0) { + builder.append(elems.get(3)); + builderIsEmpty = false; + } + String first, second; + if (nameOrderType == NAME_ORDER_TYPE_JAPANESE) { + first = elems.get(0); + second = elems.get(1); + } else { + first = elems.get(1); + second = elems.get(0); + } + if (first.length() > 0) { + if (!builderIsEmpty) { + builder.append(' '); + } + builder.append(first); + builderIsEmpty = false; + } + // Middle name + if (size > 2 && elems.get(2).length() > 0) { + if (!builderIsEmpty) { + builder.append(' '); + } + builder.append(elems.get(2)); + builderIsEmpty = false; + } + if (second.length() > 0) { + if (!builderIsEmpty) { + builder.append(' '); + } + builder.append(second); + builderIsEmpty = false; + } + // Suffix + if (size > 4 && elems.get(4).length() > 0) { + if (!builderIsEmpty) { + builder.append(' '); + } + builder.append(elems.get(4)); + builderIsEmpty = false; + } + return builder.toString(); + } else if (size == 1) { + return elems.get(0); + } else { + return ""; + } + } + + public static ContactStruct constructContactFromVNode(VNode node, + int nameOrderType) { + if (!node.VName.equals("VCARD")) { + // Impossible in current implementation. Just for safety. + Log.e(LOG_TAG, "Non VCARD data is inserted."); + return null; + } + + // For name, there are three fields in vCard: FN, N, NAME. + // We prefer FN, which is a required field in vCard 3.0 , but not in vCard 2.1. + // Next, we prefer NAME, which is defined only in vCard 3.0. + // Finally, we use N, which is a little difficult to parse. + String fullName = null; + String nameFromNProperty = null; + + // Some vCard has "X-PHONETIC-FIRST-NAME", "X-PHONETIC-MIDDLE-NAME", and + // "X-PHONETIC-LAST-NAME" + String xPhoneticFirstName = null; + String xPhoneticMiddleName = null; + String xPhoneticLastName = null; + + ContactStruct contact = new ContactStruct(); + + // Each Column of four properties has ISPRIMARY field + // (See android.provider.Contacts) + // If false even after the following loop, we choose the first + // entry as a "primary" entry. + boolean prefIsSetAddress = false; + boolean prefIsSetPhone = false; + boolean prefIsSetEmail = false; + boolean prefIsSetOrganization = false; + + for (PropertyNode propertyNode: node.propList) { + String name = propertyNode.propName; + + if (TextUtils.isEmpty(propertyNode.propValue)) { + continue; + } + + if (name.equals("VERSION")) { + // vCard version. Ignore this. + } else if (name.equals("FN")) { + fullName = propertyNode.propValue; + } else if (name.equals("NAME") && fullName == null) { + // Only in vCard 3.0. Use this if FN does not exist. + // Though, note that vCard 3.0 requires FN. + fullName = propertyNode.propValue; + } else if (name.equals("N")) { + nameFromNProperty = getNameFromNProperty(propertyNode.propValue_vector, + nameOrderType); + } else if (name.equals("SORT-STRING")) { + contact.phoneticName = propertyNode.propValue; + } else if (name.equals("SOUND")) { + if (propertyNode.paramMap_TYPE.contains("X-IRMC-N") && + contact.phoneticName == null) { + // Some Japanese mobile phones use this field for phonetic name, + // since vCard 2.1 does not have "SORT-STRING" type. + // Also, in some cases, the field has some ';' in it. + // We remove them. + StringBuilder builder = new StringBuilder(); + String value = propertyNode.propValue; + int length = value.length(); + for (int i = 0; i < length; i++) { + char ch = value.charAt(i); + if (ch != ';') { + builder.append(ch); + } + } + contact.phoneticName = builder.toString(); + } else { + contact.addExtension(propertyNode); + } + } else if (name.equals("ADR")) { + List values = propertyNode.propValue_vector; + boolean valuesAreAllEmpty = true; + for (String value : values) { + if (value.length() > 0) { + valuesAreAllEmpty = false; + break; + } + } + if (valuesAreAllEmpty) { + continue; + } + + int kind = Contacts.KIND_POSTAL; + int type = -1; + String label = ""; + boolean isPrimary = false; + for (String typeString : propertyNode.paramMap_TYPE) { + if (typeString.equals("PREF") && !prefIsSetAddress) { + // Only first "PREF" is considered. + prefIsSetAddress = true; + isPrimary = true; + } else if (typeString.equalsIgnoreCase("HOME")) { + type = Contacts.ContactMethodsColumns.TYPE_HOME; + label = ""; + } else if (typeString.equalsIgnoreCase("WORK") || + typeString.equalsIgnoreCase("COMPANY")) { + // "COMPANY" seems emitted by Windows Mobile, which is not + // specifically supported by vCard 2.1. We assume this is same + // as "WORK". + type = Contacts.ContactMethodsColumns.TYPE_WORK; + label = ""; + } else if (typeString.equalsIgnoreCase("POSTAL")) { + kind = Contacts.KIND_POSTAL; + } else if (typeString.equalsIgnoreCase("PARCEL") || + typeString.equalsIgnoreCase("DOM") || + typeString.equalsIgnoreCase("INTL")) { + // We do not have a kind or type matching these. + // TODO: fix this. We may need to split entries into two. + // (e.g. entries for KIND_POSTAL and KIND_PERCEL) + } else if (typeString.toUpperCase().startsWith("X-") && + type < 0) { + type = Contacts.ContactMethodsColumns.TYPE_CUSTOM; + label = typeString.substring(2); + } else if (type < 0) { + // vCard 3.0 allows iana-token. Also some vCard 2.1 exporters + // emit non-standard types. We do not handle their values now. + type = Contacts.ContactMethodsColumns.TYPE_CUSTOM; + label = typeString; + } + } + // We use "HOME" as default + if (type < 0) { + type = Contacts.ContactMethodsColumns.TYPE_HOME; + } + + // adr-value = 0*6(text-value ";") text-value + // ; PO Box, Extended Address, Street, Locality, Region, Postal + // ; Code, Country Name + String address; + List list = propertyNode.propValue_vector; + int size = list.size(); + if (size > 1) { + StringBuilder builder = new StringBuilder(); + boolean builderIsEmpty = true; + if (Locale.getDefault().getCountry().equals(Locale.JAPAN.getCountry())) { + // In Japan, the order is reversed. + for (int i = size - 1; i >= 0; i--) { + String addressPart = list.get(i); + if (addressPart.length() > 0) { + if (!builderIsEmpty) { + builder.append(' '); + } + builder.append(addressPart); + builderIsEmpty = false; + } + } + } else { + for (int i = 0; i < size; i++) { + String addressPart = list.get(i); + if (addressPart.length() > 0) { + if (!builderIsEmpty) { + builder.append(' '); + } + builder.append(addressPart); + builderIsEmpty = false; + } + } + } + address = builder.toString().trim(); + } else { + address = propertyNode.propValue; + } + contact.addContactmethod(kind, type, address, label, isPrimary); + } else if (name.equals("ORG")) { + // vCard specification does not specify other types. + int type = Contacts.OrganizationColumns.TYPE_WORK; + boolean isPrimary = false; + + for (String typeString : propertyNode.paramMap_TYPE) { + if (typeString.equals("PREF") && !prefIsSetOrganization) { + // vCard specification officially does not have PREF in ORG. + // This is just for safety. + prefIsSetOrganization = true; + isPrimary = true; + } + // XXX: Should we cope with X- words? + } + + List list = propertyNode.propValue_vector; + int size = list.size(); + StringBuilder builder = new StringBuilder(); + for (Iterator iter = list.iterator(); iter.hasNext();) { + builder.append(iter.next()); + if (iter.hasNext()) { + builder.append(' '); + } + } + + contact.addOrganization(type, builder.toString(), "", isPrimary); + } else if (name.equals("TITLE")) { + contact.setPosition(propertyNode.propValue); + } else if (name.equals("ROLE")) { + contact.setPosition(propertyNode.propValue); + } else if (name.equals("PHOTO")) { + // We prefer PHOTO to LOGO. + String valueType = propertyNode.paramMap.getAsString("VALUE"); + if (valueType != null && valueType.equals("URL")) { + // TODO: do something. + } else { + // Assume PHOTO is stored in BASE64. In that case, + // data is already stored in propValue_bytes in binary form. + // It should be automatically done by VBuilder (VDataBuilder/VCardDatabuilder) + contact.photoBytes = propertyNode.propValue_bytes; + String type = propertyNode.paramMap.getAsString("TYPE"); + if (type != null) { + contact.photoType = type; + } + } + } else if (name.equals("LOGO")) { + // When PHOTO is not available this is not URL, + // we use this instead of PHOTO. + String valueType = propertyNode.paramMap.getAsString("VALUE"); + if (valueType != null && valueType.equals("URL")) { + // TODO: do something. + } else if (contact.photoBytes == null) { + contact.photoBytes = propertyNode.propValue_bytes; + String type = propertyNode.paramMap.getAsString("TYPE"); + if (type != null) { + contact.photoType = type; + } + } + } else if (name.equals("EMAIL")) { + int type = -1; + String label = null; + boolean isPrimary = false; + for (String typeString : propertyNode.paramMap_TYPE) { + if (typeString.equals("PREF") && !prefIsSetEmail) { + // Only first "PREF" is considered. + prefIsSetEmail = true; + isPrimary = true; + } else if (typeString.equalsIgnoreCase("HOME")) { + type = Contacts.ContactMethodsColumns.TYPE_HOME; + } else if (typeString.equalsIgnoreCase("WORK")) { + type = Contacts.ContactMethodsColumns.TYPE_WORK; + } else if (typeString.equalsIgnoreCase("CELL")) { + // We do not have Contacts.ContactMethodsColumns.TYPE_MOBILE yet. + type = Contacts.ContactMethodsColumns.TYPE_CUSTOM; + label = Contacts.ContactMethodsColumns.MOBILE_EMAIL_TYPE_NAME; + } else if (typeString.toUpperCase().startsWith("X-") && + type < 0) { + type = Contacts.ContactMethodsColumns.TYPE_CUSTOM; + label = typeString.substring(2); + } else if (type < 0) { + // vCard 3.0 allows iana-token. + // We may have INTERNET (specified in vCard spec), + // SCHOOL, etc. + type = Contacts.ContactMethodsColumns.TYPE_CUSTOM; + label = typeString; + } + } + // We use "OTHER" as default. + if (type < 0) { + type = Contacts.ContactMethodsColumns.TYPE_OTHER; + } + contact.addContactmethod(Contacts.KIND_EMAIL, + type, propertyNode.propValue,label, isPrimary); + } else if (name.equals("TEL")) { + int type = -1; + String label = null; + boolean isPrimary = false; + boolean isFax = false; + for (String typeString : propertyNode.paramMap_TYPE) { + if (typeString.equals("PREF") && !prefIsSetPhone) { + // Only first "PREF" is considered. + prefIsSetPhone = true; + isPrimary = true; + } else if (typeString.equalsIgnoreCase("HOME")) { + type = Contacts.PhonesColumns.TYPE_HOME; + } else if (typeString.equalsIgnoreCase("WORK")) { + type = Contacts.PhonesColumns.TYPE_WORK; + } else if (typeString.equalsIgnoreCase("CELL")) { + type = Contacts.PhonesColumns.TYPE_MOBILE; + } else if (typeString.equalsIgnoreCase("PAGER")) { + type = Contacts.PhonesColumns.TYPE_PAGER; + } else if (typeString.equalsIgnoreCase("FAX")) { + isFax = true; + } else if (typeString.equalsIgnoreCase("VOICE") || + typeString.equalsIgnoreCase("MSG")) { + // Defined in vCard 3.0. Ignore these because they + // conflict with "HOME", "WORK", etc. + // XXX: do something? + } else if (typeString.toUpperCase().startsWith("X-") && + type < 0) { + type = Contacts.PhonesColumns.TYPE_CUSTOM; + label = typeString.substring(2); + } else if (type < 0){ + // We may have MODEM, CAR, ISDN, etc... + type = Contacts.PhonesColumns.TYPE_CUSTOM; + label = typeString; + } + } + // We use "HOME" as default + if (type < 0) { + type = Contacts.PhonesColumns.TYPE_HOME; + } + if (isFax) { + if (type == Contacts.PhonesColumns.TYPE_HOME) { + type = Contacts.PhonesColumns.TYPE_FAX_HOME; + } else if (type == Contacts.PhonesColumns.TYPE_WORK) { + type = Contacts.PhonesColumns.TYPE_FAX_WORK; + } + } + + contact.addPhone(type, propertyNode.propValue, label, isPrimary); + } else if (name.equals("NOTE")) { + contact.notes.add(propertyNode.propValue); + } else if (name.equals("BDAY")) { + contact.addExtension(propertyNode); + } else if (name.equals("URL")) { + contact.addExtension(propertyNode); + } else if (name.equals("REV")) { + // Revision of this VCard entry. I think we can ignore this. + contact.addExtension(propertyNode); + } else if (name.equals("UID")) { + contact.addExtension(propertyNode); + } else if (name.equals("KEY")) { + // Type is X509 or PGP? I don't know how to handle this... + contact.addExtension(propertyNode); + } else if (name.equals("MAILER")) { + contact.addExtension(propertyNode); + } else if (name.equals("TZ")) { + contact.addExtension(propertyNode); + } else if (name.equals("GEO")) { + contact.addExtension(propertyNode); + } else if (name.equals("NICKNAME")) { + // vCard 3.0 only. + contact.addExtension(propertyNode); + } else if (name.equals("CLASS")) { + // vCard 3.0 only. + // e.g. CLASS:CONFIDENTIAL + contact.addExtension(propertyNode); + } else if (name.equals("PROFILE")) { + // VCard 3.0 only. Must be "VCARD". I think we can ignore this. + contact.addExtension(propertyNode); + } else if (name.equals("CATEGORIES")) { + // VCard 3.0 only. + // e.g. CATEGORIES:INTERNET,IETF,INDUSTRY,INFORMATION TECHNOLOGY + contact.addExtension(propertyNode); + } else if (name.equals("SOURCE")) { + // VCard 3.0 only. + contact.addExtension(propertyNode); + } else if (name.equals("PRODID")) { + // VCard 3.0 only. + // To specify the identifier for the product that created + // the vCard object. + contact.addExtension(propertyNode); + } else if (name.equals("X-PHONETIC-FIRST-NAME")) { + xPhoneticFirstName = propertyNode.propValue; + } else if (name.equals("X-PHONETIC-MIDDLE-NAME")) { + xPhoneticMiddleName = propertyNode.propValue; + } else if (name.equals("X-PHONETIC-LAST-NAME")) { + xPhoneticLastName = propertyNode.propValue; + } else { + // Unknown X- words and IANA token. + contact.addExtension(propertyNode); + } + } + + if (fullName != null) { + contact.name = fullName; + } else if(nameFromNProperty != null) { + contact.name = nameFromNProperty; + } else { + contact.name = ""; + } + + if (contact.phoneticName == null && + (xPhoneticFirstName != null || xPhoneticMiddleName != null || + xPhoneticLastName != null)) { + // Note: In Europe, this order should be "LAST FIRST MIDDLE". See the comment around + // NAME_ORDER_TYPE_* for more detail. + String first; + String second; + if (nameOrderType == NAME_ORDER_TYPE_JAPANESE) { + first = xPhoneticLastName; + second = xPhoneticFirstName; + } else { + first = xPhoneticFirstName; + second = xPhoneticLastName; + } + StringBuilder builder = new StringBuilder(); + if (first != null) { + builder.append(first); + } + if (xPhoneticMiddleName != null) { + builder.append(xPhoneticMiddleName); + } + if (second != null) { + builder.append(second); + } + contact.phoneticName = builder.toString(); + } + + // Remove unnecessary white spaces. + // It is found that some mobile phone emits phonetic name with just one white space + // when a user does not specify one. + // This logic is effective toward such kind of weird data. + if (contact.phoneticName != null) { + contact.phoneticName = contact.phoneticName.trim(); + } + + // If there is no "PREF", we choose the first entries as primary. + if (!prefIsSetPhone && + contact.phoneList != null && + contact.phoneList.size() > 0) { + contact.phoneList.get(0).isPrimary = true; + } + + if (!prefIsSetAddress && contact.contactmethodList != null) { + for (ContactMethod contactMethod : contact.contactmethodList) { + if (contactMethod.kind == Contacts.KIND_POSTAL) { + contactMethod.isPrimary = true; + break; + } + } + } + if (!prefIsSetEmail && contact.contactmethodList != null) { + for (ContactMethod contactMethod : contact.contactmethodList) { + if (contactMethod.kind == Contacts.KIND_EMAIL) { + contactMethod.isPrimary = true; + break; + } + } + } + if (!prefIsSetOrganization && + contact.organizationList != null && + contact.organizationList.size() > 0) { + contact.organizationList.get(0).isPrimary = true; + } + + return contact; + } + + public String displayString() { + if (name.length() > 0) { + return name; + } + if (contactmethodList != null && contactmethodList.size() > 0) { + for (ContactMethod contactMethod : contactmethodList) { + if (contactMethod.kind == Contacts.KIND_EMAIL && contactMethod.isPrimary) { + return contactMethod.data; + } + } + } + if (phoneList != null && phoneList.size() > 0) { + for (PhoneData phoneData : phoneList) { + if (phoneData.isPrimary) { + return phoneData.data; + } + } + } + return ""; + } + + private void pushIntoContentProviderOrResolver(Object contentSomething, + long myContactsGroupId) { + ContentResolver resolver = null; + AbstractSyncableContentProvider provider = null; + if (contentSomething instanceof ContentResolver) { + resolver = (ContentResolver)contentSomething; + } else if (contentSomething instanceof AbstractSyncableContentProvider) { + provider = (AbstractSyncableContentProvider)contentSomething; + } else { + Log.e(LOG_TAG, "Unsupported object came."); + return; + } + + ContentValues contentValues = new ContentValues(); + contentValues.put(People.NAME, name); + contentValues.put(People.PHONETIC_NAME, phoneticName); + + if (notes.size() > 1) { + StringBuilder builder = new StringBuilder(); + for (String note : notes) { + builder.append(note); + builder.append("\n"); + } + contentValues.put(People.NOTES, builder.toString()); + } else if (notes.size() == 1){ + contentValues.put(People.NOTES, notes.get(0)); + } + + Uri personUri; + long personId = 0; + if (resolver != null) { + personUri = Contacts.People.createPersonInMyContactsGroup( + resolver, contentValues); + if (personUri != null) { + personId = ContentUris.parseId(personUri); + } + } else { + personUri = provider.nonTransactionalInsert(People.CONTENT_URI, contentValues); + if (personUri != null) { + personId = ContentUris.parseId(personUri); + ContentValues values = new ContentValues(); + values.put(GroupMembership.PERSON_ID, personId); + values.put(GroupMembership.GROUP_ID, myContactsGroupId); + Uri resultUri = provider.nonTransactionalInsert( + GroupMembership.CONTENT_URI, values); + if (resultUri == null) { + Log.e(LOG_TAG, "Faild to insert the person to MyContact."); + provider.nonTransactionalDelete(personUri, null, null); + personUri = null; + } + } + } + + if (personUri == null) { + Log.e(LOG_TAG, "Failed to create the contact."); + return; + } + + if (photoBytes != null) { + if (resolver != null) { + People.setPhotoData(resolver, personUri, photoBytes); + } else { + Uri photoUri = Uri.withAppendedPath(personUri, Contacts.Photos.CONTENT_DIRECTORY); + ContentValues values = new ContentValues(); + values.put(Photos.DATA, photoBytes); + provider.update(photoUri, values, null, null); + } + } + + long primaryPhoneId = -1; + if (phoneList != null && phoneList.size() > 0) { + for (PhoneData phoneData : phoneList) { + ContentValues values = new ContentValues(); + values.put(Contacts.PhonesColumns.TYPE, phoneData.type); + if (phoneData.type == Contacts.PhonesColumns.TYPE_CUSTOM) { + values.put(Contacts.PhonesColumns.LABEL, phoneData.label); + } + // Already formatted. + values.put(Contacts.PhonesColumns.NUMBER, phoneData.data); + + // Not sure about Contacts.PhonesColumns.NUMBER_KEY ... + values.put(Contacts.PhonesColumns.ISPRIMARY, 1); + values.put(Contacts.Phones.PERSON_ID, personId); + Uri phoneUri; + if (resolver != null) { + phoneUri = resolver.insert(Phones.CONTENT_URI, values); + } else { + phoneUri = provider.nonTransactionalInsert(Phones.CONTENT_URI, values); + } + if (phoneData.isPrimary) { + primaryPhoneId = Long.parseLong(phoneUri.getLastPathSegment()); + } + } + } + + long primaryOrganizationId = -1; + if (organizationList != null && organizationList.size() > 0) { + for (OrganizationData organizationData : organizationList) { + ContentValues values = new ContentValues(); + // Currently, we do not use TYPE_CUSTOM. + values.put(Contacts.OrganizationColumns.TYPE, + organizationData.type); + values.put(Contacts.OrganizationColumns.COMPANY, + organizationData.companyName); + values.put(Contacts.OrganizationColumns.TITLE, + organizationData.positionName); + values.put(Contacts.OrganizationColumns.ISPRIMARY, 1); + values.put(Contacts.OrganizationColumns.PERSON_ID, personId); + + Uri organizationUri; + if (resolver != null) { + organizationUri = resolver.insert(Organizations.CONTENT_URI, values); + } else { + organizationUri = provider.nonTransactionalInsert( + Organizations.CONTENT_URI, values); + } + if (organizationData.isPrimary) { + primaryOrganizationId = Long.parseLong(organizationUri.getLastPathSegment()); + } + } + } + + long primaryEmailId = -1; + if (contactmethodList != null && contactmethodList.size() > 0) { + for (ContactMethod contactMethod : contactmethodList) { + ContentValues values = new ContentValues(); + values.put(Contacts.ContactMethodsColumns.KIND, contactMethod.kind); + values.put(Contacts.ContactMethodsColumns.TYPE, contactMethod.type); + if (contactMethod.type == Contacts.ContactMethodsColumns.TYPE_CUSTOM) { + values.put(Contacts.ContactMethodsColumns.LABEL, contactMethod.label); + } + values.put(Contacts.ContactMethodsColumns.DATA, contactMethod.data); + values.put(Contacts.ContactMethodsColumns.ISPRIMARY, 1); + values.put(Contacts.ContactMethods.PERSON_ID, personId); + + if (contactMethod.kind == Contacts.KIND_EMAIL) { + Uri emailUri; + if (resolver != null) { + emailUri = resolver.insert(ContactMethods.CONTENT_URI, values); + } else { + emailUri = provider.nonTransactionalInsert( + ContactMethods.CONTENT_URI, values); + } + if (contactMethod.isPrimary) { + primaryEmailId = Long.parseLong(emailUri.getLastPathSegment()); + } + } else { // probably KIND_POSTAL + if (resolver != null) { + resolver.insert(ContactMethods.CONTENT_URI, values); + } else { + provider.nonTransactionalInsert( + ContactMethods.CONTENT_URI, values); + } + } + } + } + + if (extensionMap != null && extensionMap.size() > 0) { + ArrayList contentValuesArray; + if (resolver != null) { + contentValuesArray = new ArrayList(); + } else { + contentValuesArray = null; + } + for (Entry> entry : extensionMap.entrySet()) { + String key = entry.getKey(); + List list = entry.getValue(); + for (String value : list) { + ContentValues values = new ContentValues(); + values.put(Extensions.NAME, key); + values.put(Extensions.VALUE, value); + values.put(Extensions.PERSON_ID, personId); + if (resolver != null) { + contentValuesArray.add(values); + } else { + provider.nonTransactionalInsert(Extensions.CONTENT_URI, values); + } + } + } + if (resolver != null) { + resolver.bulkInsert(Extensions.CONTENT_URI, + contentValuesArray.toArray(new ContentValues[0])); + } + } + + if (primaryPhoneId >= 0 || primaryOrganizationId >= 0 || primaryEmailId >= 0) { + ContentValues values = new ContentValues(); + if (primaryPhoneId >= 0) { + values.put(People.PRIMARY_PHONE_ID, primaryPhoneId); + } + if (primaryOrganizationId >= 0) { + values.put(People.PRIMARY_ORGANIZATION_ID, primaryOrganizationId); + } + if (primaryEmailId >= 0) { + values.put(People.PRIMARY_EMAIL_ID, primaryEmailId); + } + if (resolver != null) { + resolver.update(personUri, values, null, null); + } else { + provider.nonTransactionalUpdate(personUri, values, null, null); + } + } + } + + /** + * Push this object into database in the resolver. + */ + public void pushIntoContentResolver(ContentResolver resolver) { + pushIntoContentProviderOrResolver(resolver, 0); + } + + /** + * Push this object into AbstractSyncableContentProvider object. + */ + public void pushIntoAbstractSyncableContentProvider( + AbstractSyncableContentProvider provider, long myContactsGroupId) { + boolean successful = false; + provider.beginTransaction(); + try { + pushIntoContentProviderOrResolver(provider, myContactsGroupId); + successful = true; + } finally { + provider.endTransaction(successful); + } + } + + public boolean isIgnorable() { + return TextUtils.isEmpty(name) && + TextUtils.isEmpty(phoneticName) && + (phoneList == null || phoneList.size() == 0) && + (contactmethodList == null || contactmethodList.size() == 0); } } diff --git a/core/java/android/syncml/pim/vcard/VCardComposer.java b/core/java/android/syncml/pim/vcard/VCardComposer.java index 05e8f407d0e66293d2740c7a17dc92bcc5252643..192736ae59d9d14636928fc3c8e6d84c008c07ce 100644 --- a/core/java/android/syncml/pim/vcard/VCardComposer.java +++ b/core/java/android/syncml/pim/vcard/VCardComposer.java @@ -124,9 +124,9 @@ public class VCardComposer { mResult.append("ORG:").append(struct.company).append(mNewline); } - if (!isNull(struct.notes)) { + if (struct.notes.size() > 0 && !isNull(struct.notes.get(0))) { mResult.append("NOTE:").append( - foldingString(struct.notes, vcardversion)).append(mNewline); + foldingString(struct.notes.get(0), vcardversion)).append(mNewline); } if (!isNull(struct.title)) { @@ -190,7 +190,7 @@ public class VCardComposer { */ private void appendPhotoStr(byte[] bytes, String type, int version) throws VCardException { - String value, apptype, encodingStr; + String value, encodingStr; try { value = foldingString(new String(Base64.encodeBase64(bytes, true)), version); @@ -198,20 +198,23 @@ public class VCardComposer { throw new VCardException(e.getMessage()); } - if (isNull(type)) { - type = "image/jpeg"; - } - if (type.indexOf("jpeg") > 0) { - apptype = "JPEG"; - } else if (type.indexOf("gif") > 0) { - apptype = "GIF"; - } else if (type.indexOf("bmp") > 0) { - apptype = "BMP"; + if (isNull(type) || type.toUpperCase().indexOf("JPEG") >= 0) { + type = "JPEG"; + } else if (type.toUpperCase().indexOf("GIF") >= 0) { + type = "GIF"; + } else if (type.toUpperCase().indexOf("BMP") >= 0) { + type = "BMP"; } else { - apptype = type.substring(type.indexOf("/")).toUpperCase(); + // Handle the string like "image/tiff". + int indexOfSlash = type.indexOf("/"); + if (indexOfSlash >= 0) { + type = type.substring(indexOfSlash + 1).toUpperCase(); + } else { + type = type.toUpperCase(); + } } - mResult.append("LOGO;TYPE=").append(apptype); + mResult.append("LOGO;TYPE=").append(type); if (version == VERSION_VCARD21_INT) { encodingStr = ";ENCODING=BASE64:"; value = value + mNewline; @@ -281,7 +284,7 @@ public class VCardComposer { private String getPhoneTypeStr(PhoneData phone) { - int phoneType = Integer.parseInt(phone.type); + int phoneType = phone.type; String typeStr, label; if (phoneTypeMap.containsKey(phoneType)) { @@ -308,7 +311,7 @@ public class VCardComposer { String joinMark = version == VERSION_VCARD21_INT ? ";" : ","; for (ContactStruct.ContactMethod contactMethod : contactMList) { // same with v2.1 and v3.0 - switch (Integer.parseInt(contactMethod.kind)) { + switch (contactMethod.kind) { case Contacts.KIND_EMAIL: String mailType = "INTERNET"; if (!isNull(contactMethod.data)) { diff --git a/core/java/android/syncml/pim/vcard/VCardDataBuilder.java b/core/java/android/syncml/pim/vcard/VCardDataBuilder.java new file mode 100644 index 0000000000000000000000000000000000000000..a0513f1654f8b055ce17e372f87b824bb3b16108 --- /dev/null +++ b/core/java/android/syncml/pim/vcard/VCardDataBuilder.java @@ -0,0 +1,442 @@ +/* + * 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 android.syncml.pim.vcard; + +import android.app.ProgressDialog; +import android.content.AbstractSyncableContentProvider; +import android.content.ContentProvider; +import android.content.ContentResolver; +import android.content.ContentValues; +import android.content.IContentProvider; +import android.os.Handler; +import android.provider.Contacts; +import android.syncml.pim.PropertyNode; +import android.syncml.pim.VBuilder; +import android.syncml.pim.VNode; +import android.syncml.pim.VParser; +import android.util.CharsetUtils; +import android.util.Log; + +import org.apache.commons.codec.DecoderException; +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.codec.net.QuotedPrintableCodec; + +import java.io.UnsupportedEncodingException; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.List; + +/** + * VBuilder for VCard. VCard may contain big photo images encoded by BASE64, + * If we store all VNode entries in memory like VDataBuilder.java, + * OutOfMemoryError may be thrown. Thus, this class push each VCard entry into + * ContentResolver immediately. + */ +public class VCardDataBuilder implements VBuilder { + static private String LOG_TAG = "VCardDataBuilder"; + + /** + * If there's no other information available, this class uses this charset for encoding + * byte arrays. + */ + static public String DEFAULT_CHARSET = "UTF-8"; + + private class ProgressShower implements Runnable { + private ContactStruct mContact; + + public ProgressShower(ContactStruct contact) { + mContact = contact; + } + + public void run () { + mProgressDialog.setMessage(mProgressMessage + "\n" + + mContact.displayString()); + } + } + + /** type=VNode */ + private VNode mCurrentVNode; + private PropertyNode mCurrentPropNode; + private String mCurrentParamType; + + /** + * The charset using which VParser parses the text. + */ + private String mSourceCharset; + + /** + * The charset with which byte array is encoded to String. + */ + private String mTargetCharset; + private boolean mStrictLineBreakParsing; + private ContentResolver mContentResolver; + + // For letting VCardDataBuilder show the display name of VCard while handling it. + private Handler mHandler; + private ProgressDialog mProgressDialog; + private String mProgressMessage; + private Runnable mOnProgressRunnable; + private boolean mLastNameComesBeforeFirstName; + + // Just for testing. + private long mTimeCreateContactStruct; + private long mTimePushIntoContentResolver; + + // Ideally, this should be ContactsProvider but it seems Class loader cannot find it, + // even when it is subclass of ContactsProvider... + private AbstractSyncableContentProvider mProvider; + private long mMyContactsGroupId; + + public VCardDataBuilder(ContentResolver resolver) { + mTargetCharset = DEFAULT_CHARSET; + mContentResolver = resolver; + } + + /** + * Constructor which requires minimum requiredvariables. + * + * @param resolver insert each data into this ContentResolver + * @param progressDialog + * @param progressMessage + * @param handler if this importer works on the different thread than main one, + * set appropriate handler object. If not, it is ok to set this null. + */ + public VCardDataBuilder(ContentResolver resolver, + ProgressDialog progressDialog, + String progressMessage, + Handler handler) { + this(resolver, progressDialog, progressMessage, handler, + null, null, false, false); + } + + public VCardDataBuilder(ContentResolver resolver, + ProgressDialog progressDialog, + String progressMessage, + Handler handler, + String charset, + boolean strictLineBreakParsing, + boolean lastNameComesBeforeFirstName) { + this(resolver, progressDialog, progressMessage, handler, + null, charset, strictLineBreakParsing, + lastNameComesBeforeFirstName); + } + + /** + * @hide + */ + public VCardDataBuilder(ContentResolver resolver, + ProgressDialog progressDialog, + String progressMessage, + Handler handler, + String sourceCharset, + String targetCharset, + boolean strictLineBreakParsing, + boolean lastNameComesBeforeFirstName) { + if (sourceCharset != null) { + mSourceCharset = sourceCharset; + } else { + mSourceCharset = VParser.DEFAULT_CHARSET; + } + if (targetCharset != null) { + mTargetCharset = targetCharset; + } else { + mTargetCharset = DEFAULT_CHARSET; + } + mContentResolver = resolver; + mStrictLineBreakParsing = strictLineBreakParsing; + mHandler = handler; + mProgressDialog = progressDialog; + mProgressMessage = progressMessage; + mLastNameComesBeforeFirstName = lastNameComesBeforeFirstName; + + tryGetOriginalProvider(); + } + + private void tryGetOriginalProvider() { + final ContentResolver resolver = mContentResolver; + + if ((mMyContactsGroupId = Contacts.People.tryGetMyContactsGroupId(resolver)) == 0) { + Log.e(LOG_TAG, "Could not get group id of MyContact"); + return; + } + + IContentProvider iProviderForName = resolver.acquireProvider(Contacts.CONTENT_URI); + ContentProvider contentProvider = + ContentProvider.coerceToLocalContentProvider(iProviderForName); + if (contentProvider == null) { + Log.e(LOG_TAG, "Fail to get ContentProvider object."); + return; + } + + if (!(contentProvider instanceof AbstractSyncableContentProvider)) { + Log.e(LOG_TAG, + "Acquired ContentProvider object is not AbstractSyncableContentProvider."); + return; + } + + mProvider = (AbstractSyncableContentProvider)contentProvider; + } + + public void setOnProgressRunnable(Runnable runnable) { + mOnProgressRunnable = runnable; + } + + public void start() { + } + + public void end() { + } + + /** + * Assume that VCard is not nested. In other words, this code does not accept + */ + public void startRecord(String type) { + if (mCurrentVNode != null) { + // This means startRecord() is called inside startRecord() - endRecord() block. + // TODO: should throw some Exception + Log.e(LOG_TAG, "Nested VCard code is not supported now."); + } + mCurrentVNode = new VNode(); + mCurrentVNode.parseStatus = 1; + mCurrentVNode.VName = type; + } + + public void endRecord() { + mCurrentVNode.parseStatus = 0; + long start = System.currentTimeMillis(); + ContactStruct contact = ContactStruct.constructContactFromVNode(mCurrentVNode, + mLastNameComesBeforeFirstName ? ContactStruct.NAME_ORDER_TYPE_JAPANESE : + ContactStruct.NAME_ORDER_TYPE_ENGLISH); + mTimeCreateContactStruct += System.currentTimeMillis() - start; + if (!contact.isIgnorable()) { + if (mProgressDialog != null && mProgressMessage != null) { + if (mHandler != null) { + mHandler.post(new ProgressShower(contact)); + } else { + mProgressDialog.setMessage(mProgressMessage + "\n" + + contact.displayString()); + } + } + start = System.currentTimeMillis(); + if (mProvider != null) { + contact.pushIntoAbstractSyncableContentProvider( + mProvider, mMyContactsGroupId); + } else { + contact.pushIntoContentResolver(mContentResolver); + } + mTimePushIntoContentResolver += System.currentTimeMillis() - start; + } + if (mOnProgressRunnable != null) { + mOnProgressRunnable.run(); + } + mCurrentVNode = null; + } + + public void startProperty() { + mCurrentPropNode = new PropertyNode(); + } + + public void endProperty() { + mCurrentVNode.propList.add(mCurrentPropNode); + mCurrentPropNode = null; + } + + public void propertyName(String name) { + mCurrentPropNode.propName = name; + } + + public void propertyGroup(String group) { + mCurrentPropNode.propGroupSet.add(group); + } + + public void propertyParamType(String type) { + mCurrentParamType = type; + } + + public void propertyParamValue(String value) { + if (mCurrentParamType == null || + mCurrentParamType.equalsIgnoreCase("TYPE")) { + mCurrentPropNode.paramMap_TYPE.add(value); + } else { + mCurrentPropNode.paramMap.put(mCurrentParamType, value); + } + + mCurrentParamType = null; + } + + private String encodeString(String originalString, String targetCharset) { + if (mSourceCharset.equalsIgnoreCase(targetCharset)) { + return originalString; + } + Charset charset = Charset.forName(mSourceCharset); + ByteBuffer byteBuffer = charset.encode(originalString); + // byteBuffer.array() "may" return byte array which is larger than + // byteBuffer.remaining(). Here, we keep on the safe side. + byte[] bytes = new byte[byteBuffer.remaining()]; + byteBuffer.get(bytes); + try { + return new String(bytes, targetCharset); + } catch (UnsupportedEncodingException e) { + Log.e(LOG_TAG, "Failed to encode: charset=" + targetCharset); + return new String(bytes); + } + } + + private String handleOneValue(String value, String targetCharset, String encoding) { + if (encoding != null) { + if (encoding.equals("BASE64") || encoding.equals("B")) { + mCurrentPropNode.propValue_bytes = + Base64.decodeBase64(value.getBytes()); + return value; + } else if (encoding.equals("QUOTED-PRINTABLE")) { + // "= " -> " ", "=\t" -> "\t". + // Previous code had done this replacement. Keep on the safe side. + StringBuilder builder = new StringBuilder(); + int length = value.length(); + for (int i = 0; i < length; i++) { + char ch = value.charAt(i); + if (ch == '=' && i < length - 1) { + char nextCh = value.charAt(i + 1); + if (nextCh == ' ' || nextCh == '\t') { + + builder.append(nextCh); + i++; + continue; + } + } + builder.append(ch); + } + String quotedPrintable = builder.toString(); + + String[] lines; + if (mStrictLineBreakParsing) { + lines = quotedPrintable.split("\r\n"); + } else { + builder = new StringBuilder(); + length = quotedPrintable.length(); + ArrayList list = new ArrayList(); + for (int i = 0; i < length; i++) { + char ch = quotedPrintable.charAt(i); + if (ch == '\n') { + list.add(builder.toString()); + builder = new StringBuilder(); + } else if (ch == '\r') { + list.add(builder.toString()); + builder = new StringBuilder(); + if (i < length - 1) { + char nextCh = quotedPrintable.charAt(i + 1); + if (nextCh == '\n') { + i++; + } + } + } else { + builder.append(ch); + } + } + String finalLine = builder.toString(); + if (finalLine.length() > 0) { + list.add(finalLine); + } + lines = list.toArray(new String[0]); + } + + builder = new StringBuilder(); + for (String line : lines) { + if (line.endsWith("=")) { + line = line.substring(0, line.length() - 1); + } + builder.append(line); + } + byte[] bytes; + try { + bytes = builder.toString().getBytes(mSourceCharset); + } catch (UnsupportedEncodingException e1) { + Log.e(LOG_TAG, "Failed to encode: charset=" + mSourceCharset); + bytes = builder.toString().getBytes(); + } + + try { + bytes = QuotedPrintableCodec.decodeQuotedPrintable(bytes); + } catch (DecoderException e) { + Log.e(LOG_TAG, "Failed to decode quoted-printable: " + e); + return ""; + } + + try { + return new String(bytes, targetCharset); + } catch (UnsupportedEncodingException e) { + Log.e(LOG_TAG, "Failed to encode: charset=" + targetCharset); + return new String(bytes); + } + } + // Unknown encoding. Fall back to default. + } + return encodeString(value, targetCharset); + } + + public void propertyValues(List values) { + if (values == null || values.size() == 0) { + mCurrentPropNode.propValue_bytes = null; + mCurrentPropNode.propValue_vector.clear(); + mCurrentPropNode.propValue_vector.add(""); + mCurrentPropNode.propValue = ""; + return; + } + + ContentValues paramMap = mCurrentPropNode.paramMap; + + String targetCharset = CharsetUtils.nameForDefaultVendor(paramMap.getAsString("CHARSET")); + String encoding = paramMap.getAsString("ENCODING"); + + if (targetCharset == null || targetCharset.length() == 0) { + targetCharset = mTargetCharset; + } + + for (String value : values) { + mCurrentPropNode.propValue_vector.add( + handleOneValue(value, targetCharset, encoding)); + } + + mCurrentPropNode.propValue = listToString(mCurrentPropNode.propValue_vector); + } + + public void showDebugInfo() { + Log.d(LOG_TAG, "time for creating ContactStruct: " + mTimeCreateContactStruct + " ms"); + Log.d(LOG_TAG, "time for insert ContactStruct to database: " + + mTimePushIntoContentResolver + " ms"); + } + + private String listToString(List list){ + int size = list.size(); + if (size > 1) { + StringBuilder builder = new StringBuilder(); + int i = 0; + for (String type : list) { + builder.append(type); + if (i < size - 1) { + builder.append(";"); + } + } + return builder.toString(); + } else if (size == 1) { + return list.get(0); + } else { + return ""; + } + } +} diff --git a/core/java/android/syncml/pim/vcard/VCardEntryCounter.java b/core/java/android/syncml/pim/vcard/VCardEntryCounter.java new file mode 100644 index 0000000000000000000000000000000000000000..03cd1d9439fddb0b659bfcbc752ce4df48f76b75 --- /dev/null +++ b/core/java/android/syncml/pim/vcard/VCardEntryCounter.java @@ -0,0 +1,63 @@ +/* + * 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.syncml.pim.vcard; + +import java.util.List; + +import android.syncml.pim.VBuilder; + +public class VCardEntryCounter implements VBuilder { + private int mCount; + + public int getCount() { + return mCount; + } + + public void start() { + } + + public void end() { + } + + public void startRecord(String type) { + } + + public void endRecord() { + mCount++; + } + + public void startProperty() { + } + + public void endProperty() { + } + + public void propertyGroup(String group) { + } + + public void propertyName(String name) { + } + + public void propertyParamType(String type) { + } + + public void propertyParamValue(String value) { + } + + public void propertyValues(List values) { + } +} \ No newline at end of file diff --git a/core/java/android/syncml/pim/vcard/VCardNestedException.java b/core/java/android/syncml/pim/vcard/VCardNestedException.java new file mode 100644 index 0000000000000000000000000000000000000000..def6f3b785ffaecab38c225582285c65b782581d --- /dev/null +++ b/core/java/android/syncml/pim/vcard/VCardNestedException.java @@ -0,0 +1,27 @@ +/* + * 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.syncml.pim.vcard; + +/** + * VCardException thrown when VCard is nested without VCardParser's being notified. + */ +public class VCardNestedException extends VCardException { + public VCardNestedException() {} + public VCardNestedException(String message) { + super(message); + } +} diff --git a/core/java/android/syncml/pim/vcard/VCardParser_V21.java b/core/java/android/syncml/pim/vcard/VCardParser_V21.java index f853c5e383fa292bf79e72a466eea9d2b762897d..d86566811d85f33fe030f3fead9ac845431894eb 100644 --- a/core/java/android/syncml/pim/vcard/VCardParser_V21.java +++ b/core/java/android/syncml/pim/vcard/VCardParser_V21.java @@ -17,21 +17,26 @@ package android.syncml.pim.vcard; import android.syncml.pim.VBuilder; +import android.syncml.pim.VParser; +import android.util.Log; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.io.Reader; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; -import java.util.regex.Pattern; /** - * This class is used to parse vcard. Please refer to vCard Specification 2.1 + * This class is used to parse vcard. Please refer to vCard Specification 2.1. */ public class VCardParser_V21 { - + private static final String LOG_TAG = "VCardParser_V21"; + + public static final String DEFAULT_CHARSET = VParser.DEFAULT_CHARSET; + /** Store the known-type */ private static final HashSet sKnownTypeSet = new HashSet( Arrays.asList("DOM", "INTL", "POSTAL", "PARCEL", "HOME", "WORK", @@ -42,19 +47,17 @@ public class VCardParser_V21 { "CGM", "WMF", "BMP", "MET", "PMB", "DIB", "PICT", "TIFF", "PDF", "PS", "JPEG", "QTIME", "MPEG", "MPEG2", "AVI", "WAVE", "AIFF", "PCM", "X509", "PGP")); - + /** Store the known-value */ private static final HashSet sKnownValueSet = new HashSet( Arrays.asList("INLINE", "URL", "CONTENT-ID", "CID")); - /** Store the property name available in vCard 2.1 */ - // NICKNAME is not supported in vCard 2.1, but some vCard may contain. + /** Store the property names available in vCard 2.1 */ private static final HashSet sAvailablePropertyNameV21 = new HashSet(Arrays.asList( - "LOGO", "PHOTO", "LABEL", "FN", "TITLE", "SOUND", + "BEGIN", "LOGO", "PHOTO", "LABEL", "FN", "TITLE", "SOUND", "VERSION", "TEL", "EMAIL", "TZ", "GEO", "NOTE", "URL", - "BDAY", "ROLE", "REV", "UID", "KEY", "MAILER", - "NICKNAME")); + "BDAY", "ROLE", "REV", "UID", "KEY", "MAILER")); // Though vCard 2.1 specification does not allow "B" encoding, some data may have it. // We allow it for safety... @@ -76,6 +79,30 @@ public class VCardParser_V21 { // Should not directly read a line from this. Use getLine() instead. protected BufferedReader mReader; + private boolean mCanceled; + + // In some cases, vCard is nested. Currently, we only consider the most interior vCard data. + // See v21_foma_1.vcf in test directory for more information. + private int mNestCount; + + // In order to reduce warning message as much as possible, we hold the value which made Logger + // emit a warning message. + protected HashSet mWarningValueMap = new HashSet(); + + // Just for debugging + private long mTimeTotal; + private long mTimeStartRecord; + private long mTimeEndRecord; + private long mTimeStartProperty; + private long mTimeEndProperty; + private long mTimeParseItems; + private long mTimeParseItem1; + private long mTimeParseItem2; + private long mTimeParseItem3; + private long mTimeHandlePropertyValue1; + private long mTimeHandlePropertyValue2; + private long mTimeHandlePropertyValue3; + /** * Create a new VCard parser. */ @@ -83,12 +110,35 @@ public class VCardParser_V21 { super(); } + public VCardParser_V21(VCardSourceDetector detector) { + super(); + if (detector != null && detector.getType() == VCardSourceDetector.TYPE_FOMA) { + mNestCount = 1; + } + } + /** * Parse the file at the given position * vcard_file = [wsls] vcard [wsls] */ protected void parseVCardFile() throws IOException, VCardException { - while (parseOneVCard()) { + boolean firstReading = true; + while (true) { + if (mCanceled) { + break; + } + if (!parseOneVCard(firstReading)) { + break; + } + firstReading = false; + } + + if (mNestCount > 0) { + boolean useCache = true; + for (int i = 0; i < mNestCount; i++) { + readEndVCard(useCache, true); + useCache = false; + } } } @@ -100,7 +150,13 @@ public class VCardParser_V21 { * @return true when the propertyName is a valid property name. */ protected boolean isValidPropertyName(String propertyName) { - return sAvailablePropertyNameV21.contains(propertyName.toUpperCase()); + if (!(sAvailablePropertyNameV21.contains(propertyName.toUpperCase()) || + propertyName.startsWith("X-")) && + !mWarningValueMap.contains(propertyName)) { + mWarningValueMap.add(propertyName); + Log.w(LOG_TAG, "Property name unsupported by vCard 2.1: " + propertyName); + } + return true; } /** @@ -129,7 +185,7 @@ public class VCardParser_V21 { line = getLine(); if (line == null) { throw new VCardException("Reached end of buffer."); - } else if (line.trim().length() > 0) { + } else if (line.trim().length() > 0) { return line; } } @@ -140,12 +196,37 @@ public class VCardParser_V21 { * items *CRLF * "END" [ws] ":" [ws] "VCARD" */ - private boolean parseOneVCard() throws IOException, VCardException { - if (!readBeginVCard()) { + private boolean parseOneVCard(boolean firstReading) throws IOException, VCardException { + boolean allowGarbage = false; + if (firstReading) { + if (mNestCount > 0) { + for (int i = 0; i < mNestCount; i++) { + if (!readBeginVCard(allowGarbage)) { + return false; + } + allowGarbage = true; + } + } + } + + if (!readBeginVCard(allowGarbage)) { return false; } + long start; + if (mBuilder != null) { + start = System.currentTimeMillis(); + mBuilder.startRecord("VCARD"); + mTimeStartRecord += System.currentTimeMillis() - start; + } + start = System.currentTimeMillis(); parseItems(); - readEndVCard(); + mTimeParseItems += System.currentTimeMillis() - start; + readEndVCard(true, false); + if (mBuilder != null) { + start = System.currentTimeMillis(); + mBuilder.endRecord(); + mTimeEndRecord += System.currentTimeMillis() - start; + } return true; } @@ -154,46 +235,102 @@ public class VCardParser_V21 { * @throws IOException * @throws VCardException */ - protected boolean readBeginVCard() throws IOException, VCardException { + protected boolean readBeginVCard(boolean allowGarbage) + throws IOException, VCardException { String line; - while (true) { - line = getLine(); - if (line == null) { - return false; - } else if (line.trim().length() > 0) { - break; + do { + while (true) { + line = getLine(); + if (line == null) { + return false; + } else if (line.trim().length() > 0) { + break; + } } - } - String[] strArray = line.split(":", 2); - - // Though vCard specification does not allow lower cases, - // some data may have them, so we allow it. - if (!(strArray.length == 2 && - strArray[0].trim().equalsIgnoreCase("BEGIN") && - strArray[1].trim().equalsIgnoreCase("VCARD"))) { - throw new VCardException("BEGIN:VCARD != \"" + line + "\""); - } - - if (mBuilder != null) { - mBuilder.startRecord("VCARD"); - } + String[] strArray = line.split(":", 2); + int length = strArray.length; - return true; + // Though vCard 2.1/3.0 specification does not allow lower cases, + // some data may have them, so we allow it (Actually, previous code + // had explicitly allowed "BEGIN:vCard" though there's no example). + // + // TODO: ignore non vCard entry (e.g. vcalendar). + // XXX: Not sure, but according to VDataBuilder.java, vcalendar + // entry + // may be nested. Just seeking "END:SOMETHING" may not be enough. + // e.g. + // BEGIN:VCARD + // ... (Valid. Must parse this) + // END:VCARD + // BEGIN:VSOMETHING + // ... (Must ignore this) + // BEGIN:VSOMETHING2 + // ... (Must ignore this) + // END:VSOMETHING2 + // ... (Must ignore this!) + // END:VSOMETHING + // BEGIN:VCARD + // ... (Valid. Must parse this) + // END:VCARD + // INVALID_STRING (VCardException should be thrown) + if (length == 2 && + strArray[0].trim().equalsIgnoreCase("BEGIN") && + strArray[1].trim().equalsIgnoreCase("VCARD")) { + return true; + } else if (!allowGarbage) { + if (mNestCount > 0) { + mPreviousLine = line; + return false; + } else { + throw new VCardException( + "Expected String \"BEGIN:VCARD\" did not come " + + "(Instead, \"" + line + "\" came)"); + } + } + } while(allowGarbage); + + throw new VCardException("Reached where must not be reached."); } - - protected void readEndVCard() throws VCardException { - // Though vCard specification does not allow lower cases, - // some data may have them, so we allow it. - String[] strArray = mPreviousLine.split(":", 2); - if (!(strArray.length == 2 && - strArray[0].trim().equalsIgnoreCase("END") && - strArray[1].trim().equalsIgnoreCase("VCARD"))) { - throw new VCardException("END:VCARD != \"" + mPreviousLine + "\""); - } - - if (mBuilder != null) { - mBuilder.endRecord(); - } + + /** + * The arguments useCache and allowGarbase are usually true and false accordingly when + * this function is called outside this function itself. + * + * @param useCache When true, line is obtained from mPreviousline. Otherwise, getLine() + * is used. + * @param allowGarbage When true, ignore non "END:VCARD" line. + * @throws IOException + * @throws VCardException + */ + protected void readEndVCard(boolean useCache, boolean allowGarbage) + throws IOException, VCardException { + String line; + do { + if (useCache) { + // Though vCard specification does not allow lower cases, + // some data may have them, so we allow it. + line = mPreviousLine; + } else { + while (true) { + line = getLine(); + if (line == null) { + throw new VCardException("Expected END:VCARD was not found."); + } else if (line.trim().length() > 0) { + break; + } + } + } + + String[] strArray = line.split(":", 2); + if (strArray.length == 2 && + strArray[0].trim().equalsIgnoreCase("END") && + strArray[1].trim().equalsIgnoreCase("VCARD")) { + return; + } else if (!allowGarbage) { + throw new VCardException("END:VCARD != \"" + mPreviousLine + "\""); + } + useCache = false; + } while (allowGarbage); } /** @@ -205,32 +342,33 @@ public class VCardParser_V21 { boolean ended = false; if (mBuilder != null) { + long start = System.currentTimeMillis(); mBuilder.startProperty(); + mTimeStartProperty += System.currentTimeMillis() - start; } - - try { - ended = parseItem(); - } finally { - if (mBuilder != null) { - mBuilder.endProperty(); - } + ended = parseItem(); + if (mBuilder != null && !ended) { + long start = System.currentTimeMillis(); + mBuilder.endProperty(); + mTimeEndProperty += System.currentTimeMillis() - start; } while (!ended) { // follow VCARD ,it wont reach endProperty if (mBuilder != null) { + long start = System.currentTimeMillis(); mBuilder.startProperty(); + mTimeStartProperty += System.currentTimeMillis() - start; } - try { - ended = parseItem(); - } finally { - if (mBuilder != null) { - mBuilder.endProperty(); - } + ended = parseItem(); + if (mBuilder != null && !ended) { + long start = System.currentTimeMillis(); + mBuilder.endProperty(); + mTimeEndProperty += System.currentTimeMillis() - start; } } } - + /** * item = [groups "."] name [params] ":" value CRLF * / [groups "."] "ADR" [params] ":" addressparts CRLF @@ -241,57 +379,134 @@ public class VCardParser_V21 { protected boolean parseItem() throws IOException, VCardException { mEncoding = sDefaultEncoding; - // params = ";" [ws] paramlist String line = getNonEmptyLine(); - String[] strArray = line.split(":", 2); - if (strArray.length < 2) { - throw new VCardException("Invalid line(\":\" does not exist): " + line); - } - String propertyValue = strArray[1]; - String[] groupNameParamsArray = strArray[0].split(";"); - String groupAndName = groupNameParamsArray[0].trim(); - String[] groupNameArray = groupAndName.split("\\."); - int length = groupNameArray.length; - String propertyName = groupNameArray[length - 1]; - if (mBuilder != null) { - mBuilder.propertyName(propertyName); - for (int i = 0; i < length - 1; i++) { - mBuilder.propertyGroup(groupNameArray[i]); - } - } - if (propertyName.equalsIgnoreCase("END")) { - mPreviousLine = line; + long start = System.currentTimeMillis(); + + String[] propertyNameAndValue = separateLineAndHandleGroup(line); + if (propertyNameAndValue == null) { return true; } - - length = groupNameParamsArray.length; - for (int i = 1; i < length; i++) { - handleParams(groupNameParamsArray[i]); + if (propertyNameAndValue.length != 2) { + throw new VCardException("Invalid line \"" + line + "\""); } - - if (isValidPropertyName(propertyName) || - propertyName.startsWith("X-")) { - if (propertyName.equals("VERSION") && - !propertyValue.equals(getVersion())) { - throw new VCardVersionException("Incompatible version: " + - propertyValue + " != " + getVersion()); - } - handlePropertyValue(propertyName, propertyValue); - return false; - } else if (propertyName.equals("ADR") || + String propertyName = propertyNameAndValue[0].toUpperCase(); + String propertyValue = propertyNameAndValue[1]; + + mTimeParseItem1 += System.currentTimeMillis() - start; + + if (propertyName.equals("ADR") || propertyName.equals("ORG") || propertyName.equals("N")) { + start = System.currentTimeMillis(); handleMultiplePropertyValue(propertyName, propertyValue); + mTimeParseItem3 += System.currentTimeMillis() - start; return false; } else if (propertyName.equals("AGENT")) { handleAgent(propertyValue); return false; + } else if (isValidPropertyName(propertyName)) { + if (propertyName.equals("BEGIN")) { + if (propertyValue.equals("VCARD")) { + throw new VCardNestedException("This vCard has nested vCard data in it."); + } else { + throw new VCardException("Unknown BEGIN type: " + propertyValue); + } + } else if (propertyName.equals("VERSION") && + !propertyValue.equals(getVersion())) { + throw new VCardVersionException("Incompatible version: " + + propertyValue + " != " + getVersion()); + } + start = System.currentTimeMillis(); + handlePropertyValue(propertyName, propertyValue); + mTimeParseItem2 += System.currentTimeMillis() - start; + return false; } throw new VCardException("Unknown property name: \"" + propertyName + "\""); } + static private final int STATE_GROUP_OR_PROPNAME = 0; + static private final int STATE_PARAMS = 1; + // vCard 3.1 specification allows double-quoted param-value, while vCard 2.1 does not. + // This is just for safety. + static private final int STATE_PARAMS_IN_DQUOTE = 2; + + protected String[] separateLineAndHandleGroup(String line) throws VCardException { + int length = line.length(); + int state = STATE_GROUP_OR_PROPNAME; + int nameIndex = 0; + + String[] propertyNameAndValue = new String[2]; + + for (int i = 0; i < length; i++) { + char ch = line.charAt(i); + switch (state) { + case STATE_GROUP_OR_PROPNAME: + if (ch == ':') { + String propertyName = line.substring(nameIndex, i); + if (propertyName.equalsIgnoreCase("END")) { + mPreviousLine = line; + return null; + } + if (mBuilder != null) { + mBuilder.propertyName(propertyName); + } + propertyNameAndValue[0] = propertyName; + if (i < length - 1) { + propertyNameAndValue[1] = line.substring(i + 1); + } else { + propertyNameAndValue[1] = ""; + } + return propertyNameAndValue; + } else if (ch == '.') { + String groupName = line.substring(nameIndex, i); + if (mBuilder != null) { + mBuilder.propertyGroup(groupName); + } + nameIndex = i + 1; + } else if (ch == ';') { + String propertyName = line.substring(nameIndex, i); + if (propertyName.equalsIgnoreCase("END")) { + mPreviousLine = line; + return null; + } + if (mBuilder != null) { + mBuilder.propertyName(propertyName); + } + propertyNameAndValue[0] = propertyName; + nameIndex = i + 1; + state = STATE_PARAMS; + } + break; + case STATE_PARAMS: + if (ch == '"') { + state = STATE_PARAMS_IN_DQUOTE; + } else if (ch == ';') { + handleParams(line.substring(nameIndex, i)); + nameIndex = i + 1; + } else if (ch == ':') { + handleParams(line.substring(nameIndex, i)); + if (i < length - 1) { + propertyNameAndValue[1] = line.substring(i + 1); + } else { + propertyNameAndValue[1] = ""; + } + return propertyNameAndValue; + } + break; + case STATE_PARAMS_IN_DQUOTE: + if (ch == '"') { + state = STATE_PARAMS; + } + break; + } + } + + throw new VCardException("Invalid line: \"" + line + "\""); + } + + /** * params = ";" [ws] paramlist * paramlist = paramlist [ws] ";" [ws] param @@ -330,18 +545,19 @@ public class VCardParser_V21 { } /** - * typeval = knowntype / "X-" word + * ptypeval = knowntype / "X-" word */ - protected void handleType(String ptypeval) throws VCardException { - if (sKnownTypeSet.contains(ptypeval.toUpperCase()) || - ptypeval.startsWith("X-")) { - if (mBuilder != null) { - mBuilder.propertyParamType("TYPE"); - mBuilder.propertyParamValue(ptypeval.toUpperCase()); - } - } else { - throw new VCardException("Unknown type: \"" + ptypeval + "\""); - } + protected void handleType(String ptypeval) { + String upperTypeValue = ptypeval; + if (!(sKnownTypeSet.contains(upperTypeValue) || upperTypeValue.startsWith("X-")) && + !mWarningValueMap.contains(ptypeval)) { + mWarningValueMap.add(ptypeval); + Log.w(LOG_TAG, "Type unsupported by vCard 2.1: " + ptypeval); + } + if (mBuilder != null) { + mBuilder.propertyParamType("TYPE"); + mBuilder.propertyParamValue(upperTypeValue); + } } /** @@ -427,31 +643,48 @@ public class VCardParser_V21 { protected void handlePropertyValue( String propertyName, String propertyValue) throws IOException, VCardException { - if (mEncoding == null || mEncoding.equalsIgnoreCase("7BIT") - || mEncoding.equalsIgnoreCase("8BIT") - || mEncoding.toUpperCase().startsWith("X-")) { - if (mBuilder != null) { - ArrayList v = new ArrayList(); - v.add(maybeUnescapeText(propertyValue)); - mBuilder.propertyValues(v); - } - } else if (mEncoding.equalsIgnoreCase("QUOTED-PRINTABLE")) { + if (mEncoding.equalsIgnoreCase("QUOTED-PRINTABLE")) { + long start = System.currentTimeMillis(); String result = getQuotedPrintable(propertyValue); if (mBuilder != null) { ArrayList v = new ArrayList(); v.add(result); mBuilder.propertyValues(v); } + mTimeHandlePropertyValue2 += System.currentTimeMillis() - start; } else if (mEncoding.equalsIgnoreCase("BASE64") || mEncoding.equalsIgnoreCase("B")) { - String result = getBase64(propertyValue); + long start = System.currentTimeMillis(); + // It is very rare, but some BASE64 data may be so big that + // OutOfMemoryError occurs. To ignore such cases, use try-catch. + try { + String result = getBase64(propertyValue); + if (mBuilder != null) { + ArrayList v = new ArrayList(); + v.add(result); + mBuilder.propertyValues(v); + } + } catch (OutOfMemoryError error) { + Log.e(LOG_TAG, "OutOfMemoryError happened during parsing BASE64 data!"); + if (mBuilder != null) { + mBuilder.propertyValues(null); + } + } + mTimeHandlePropertyValue3 += System.currentTimeMillis() - start; + } else { + if (!(mEncoding == null || mEncoding.equalsIgnoreCase("7BIT") + || mEncoding.equalsIgnoreCase("8BIT") + || mEncoding.toUpperCase().startsWith("X-"))) { + Log.w(LOG_TAG, "The encoding unsupported by vCard spec: \"" + mEncoding + "\"."); + } + + long start = System.currentTimeMillis(); if (mBuilder != null) { ArrayList v = new ArrayList(); - v.add(result); + v.add(maybeUnescapeText(propertyValue)); mBuilder.propertyValues(v); - } - } else { - throw new VCardException("Unknown encoding: \"" + mEncoding + "\""); + } + mTimeHandlePropertyValue1 += System.currentTimeMillis() - start; } } @@ -546,57 +779,51 @@ public class VCardParser_V21 { if (mEncoding.equalsIgnoreCase("QUOTED-PRINTABLE")) { propertyValue = getQuotedPrintable(propertyValue); } - - if (propertyValue.endsWith("\\")) { + + if (mBuilder != null) { + // TODO: limit should be set in accordance with propertyName? StringBuilder builder = new StringBuilder(); - // builder.append(propertyValue); - builder.append(propertyValue.substring(0, propertyValue.length() - 1)); - try { - String line; - while (true) { - line = getNonEmptyLine(); - // builder.append("\r\n"); - // builder.append(line); - if (!line.endsWith("\\")) { - builder.append(line); - break; + ArrayList list = new ArrayList(); + int length = propertyValue.length(); + for (int i = 0; i < length; i++) { + char ch = propertyValue.charAt(i); + if (ch == '\\' && i < length - 1) { + char nextCh = propertyValue.charAt(i + 1); + String unescapedString = maybeUnescape(nextCh); + if (unescapedString != null) { + builder.append(unescapedString); + i++; } else { - builder.append(line.substring(0, line.length() - 1)); + builder.append(ch); } + } else if (ch == ';') { + list.add(builder.toString()); + builder = new StringBuilder(); + } else { + builder.append(ch); } - } catch (IOException e) { - throw new VCardException( - "IOException is throw during reading propertyValue" + e); } - // Now, propertyValue may contain "\r\n" - propertyValue = builder.toString(); - } - - if (mBuilder != null) { - // In String#replaceAll() and Pattern class, "\\\\" means single slash. - - final String IMPOSSIBLE_STRING = "\0"; - // First replace two backslashes with impossible strings. - propertyValue = propertyValue.replaceAll("\\\\\\\\", IMPOSSIBLE_STRING); - - // Now, split propertyValue with ; whose previous char is not back slash. - Pattern pattern = Pattern.compile("(? arrayList = new ArrayList(); - for (String str : strArray) { - // Replace impossible strings with original two backslashes - arrayList.add( - unescapeText(str.replaceAll(IMPOSSIBLE_STRING, "\\\\\\\\"))); - } - mBuilder.propertyValues(arrayList); + list.add(builder.toString()); + mBuilder.propertyValues(list); } } /** * vCard 2.1 specifies AGENT allows one vcard entry. It is not encoded at all. + * + * item = ... + * / [groups "."] "AGENT" + * [params] ":" vcard CRLF + * vcard = "BEGIN" [ws] ":" [ws] "VCARD" [ws] 1*CRLF + * items *CRLF "END" [ws] ":" [ws] "VCARD" + * */ - protected void handleAgent(String propertyValue) throws IOException, VCardException { + protected void handleAgent(String propertyValue) throws VCardException { + throw new VCardException("AGENT Property is not supported."); + /* This is insufficient support. Also, AGENT Property is very rare. + Ignore it for now. + TODO: fix this. + String[] strArray = propertyValue.split(":", 2); if (!(strArray.length == 2 || strArray[0].trim().equalsIgnoreCase("BEGIN") && @@ -605,6 +832,7 @@ public class VCardParser_V21 { } parseItems(); readEndVCard(); + */ } /** @@ -615,17 +843,18 @@ public class VCardParser_V21 { } /** - * Convert escaped text into unescaped text. + * Returns unescaped String if the character should be unescaped. Return null otherwise. + * e.g. In vCard 2.1, "\;" should be unescaped into ";" while "\x" should not be. */ - protected String unescapeText(String text) { + protected String maybeUnescape(char ch) { // Original vCard 2.1 specification does not allow transformation // "\:" -> ":", "\," -> ",", and "\\" -> "\", but previous implementation of // this class allowed them, so keep it as is. - // In String#replaceAll(), "\\\\" means single slash. - return text.replaceAll("\\\\;", ";") - .replaceAll("\\\\:", ":") - .replaceAll("\\\\,", ",") - .replaceAll("\\\\\\\\", "\\\\"); + if (ch == '\\' || ch == ';' || ch == ':' || ch == ',') { + return String.valueOf(ch); + } else { + return null; + } } /** @@ -656,12 +885,15 @@ public class VCardParser_V21 { */ public boolean parse(InputStream is, String charset, VBuilder builder) throws IOException, VCardException { + // TODO: make this count error entries instead of just throwing VCardException. + // TODO: If we really need to allow only CRLF as line break, // we will have to develop our own BufferedReader(). - mReader = new BufferedReader(new InputStreamReader(is, charset)); + mReader = new CustomBufferedReader(new InputStreamReader(is, charset)); mBuilder = builder; + long start = System.currentTimeMillis(); if (mBuilder != null) { mBuilder.start(); } @@ -669,9 +901,50 @@ public class VCardParser_V21 { if (mBuilder != null) { mBuilder.end(); } + mTimeTotal += System.currentTimeMillis() - start; + return true; } + public boolean parse(InputStream is, VBuilder builder) throws IOException, VCardException { + return parse(is, DEFAULT_CHARSET, builder); + } + + /** + * Cancel parsing. + * Actual cancel is done after the end of the current one vcard entry parsing. + */ + public void cancel() { + mCanceled = true; + } + + /** + * It is very, very rare case, but there is a case where + * canceled may be already true outside this object. + * @hide + */ + public void parse(InputStream is, String charset, VBuilder builder, boolean canceled) + throws IOException, VCardException { + mCanceled = canceled; + parse(is, charset, builder); + } + + public void showDebugInfo() { + Log.d(LOG_TAG, "total parsing time: " + mTimeTotal + " ms"); + if (mReader instanceof CustomBufferedReader) { + Log.d(LOG_TAG, "total readLine time: " + + ((CustomBufferedReader)mReader).getTotalmillisecond() + " ms"); + } + Log.d(LOG_TAG, "mTimeStartRecord: " + mTimeStartRecord + " ms"); + Log.d(LOG_TAG, "mTimeEndRecord: " + mTimeEndRecord + " ms"); + Log.d(LOG_TAG, "mTimeParseItem1: " + mTimeParseItem1 + " ms"); + Log.d(LOG_TAG, "mTimeParseItem2: " + mTimeParseItem2 + " ms"); + Log.d(LOG_TAG, "mTimeParseItem3: " + mTimeParseItem3 + " ms"); + Log.d(LOG_TAG, "mTimeHandlePropertyValue1: " + mTimeHandlePropertyValue1 + " ms"); + Log.d(LOG_TAG, "mTimeHandlePropertyValue2: " + mTimeHandlePropertyValue2 + " ms"); + Log.d(LOG_TAG, "mTimeHandlePropertyValue3: " + mTimeHandlePropertyValue3 + " ms"); + } + private boolean isLetter(char ch) { if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) { return true; @@ -679,3 +952,24 @@ public class VCardParser_V21 { return false; } } + +class CustomBufferedReader extends BufferedReader { + private long mTime; + + public CustomBufferedReader(Reader in) { + super(in); + } + + @Override + public String readLine() throws IOException { + long start = System.currentTimeMillis(); + String ret = super.readLine(); + long end = System.currentTimeMillis(); + mTime += end - start; + return ret; + } + + public long getTotalmillisecond() { + return mTime; + } +} diff --git a/core/java/android/syncml/pim/vcard/VCardParser_V30.java b/core/java/android/syncml/pim/vcard/VCardParser_V30.java index 901bd49ef006035da9ad149f72b714f1b21091ee..e67525eec3326a2ff15f5f4c125272a38a3ef4f4 100644 --- a/core/java/android/syncml/pim/vcard/VCardParser_V30.java +++ b/core/java/android/syncml/pim/vcard/VCardParser_V30.java @@ -16,8 +16,9 @@ package android.syncml.pim.vcard; +import android.util.Log; + import java.io.IOException; -import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; @@ -26,9 +27,11 @@ import java.util.HashSet; * Please refer to vCard Specification 3.0 (http://tools.ietf.org/html/rfc2426) */ public class VCardParser_V30 extends VCardParser_V21 { + private static final String LOG_TAG = "VCardParser_V30"; + private static final HashSet acceptablePropsWithParam = new HashSet( Arrays.asList( - "LOGO", "PHOTO", "LABEL", "FN", "TITLE", "SOUND", + "BEGIN", "LOGO", "PHOTO", "LABEL", "FN", "TITLE", "SOUND", "VERSION", "TEL", "EMAIL", "TZ", "GEO", "NOTE", "URL", "BDAY", "ROLE", "REV", "UID", "KEY", "MAILER", // 2.1 "NAME", "PROFILE", "SOURCE", "NICKNAME", "CLASS", @@ -51,8 +54,14 @@ public class VCardParser_V30 extends VCardParser_V21 { @Override protected boolean isValidPropertyName(String propertyName) { - return acceptablePropsWithParam.contains(propertyName) || - acceptablePropsWithoutParam.contains(propertyName); + if (!(acceptablePropsWithParam.contains(propertyName) || + acceptablePropsWithoutParam.contains(propertyName) || + propertyName.startsWith("X-")) && + !mWarningValueMap.contains(propertyName)) { + mWarningValueMap.add(propertyName); + Log.w(LOG_TAG, "Property name unsupported by vCard 3.0: " + propertyName); + } + return true; } @Override @@ -100,7 +109,21 @@ public class VCardParser_V30 extends VCardParser_V21 { } } else if (line.charAt(0) == ' ' || line.charAt(0) == '\t') { if (builder != null) { - // TODO: Check whether MIME requires only one whitespace. + // See Section 5.8.1 of RFC 2425 (MIME-DIR document). + // Following is the excerpts from it. + // + // DESCRIPTION:This is a long description that exists on a long line. + // + // Can be represented as: + // + // DESCRIPTION:This is a long description + // that exists on a long line. + // + // It could also be represented as: + // + // DESCRIPTION:This is a long descrip + // tion that exists o + // n a long line. builder.append(line.substring(1)); } else if (mPreviousLine != null) { builder = new StringBuilder(); @@ -113,10 +136,13 @@ public class VCardParser_V30 extends VCardParser_V21 { } else { if (mPreviousLine == null) { mPreviousLine = line; + if (builder != null) { + return builder.toString(); + } } else { String ret = mPreviousLine; mPreviousLine = line; - return ret; + return ret; } } } @@ -130,15 +156,16 @@ public class VCardParser_V30 extends VCardParser_V21 { * [group "."] "END" ":" "VCARD" 1*CRLF */ @Override - protected boolean readBeginVCard() throws IOException, VCardException { + protected boolean readBeginVCard(boolean allowGarbage) throws IOException, VCardException { // TODO: vCard 3.0 supports group. - return super.readBeginVCard(); + return super.readBeginVCard(allowGarbage); } @Override - protected void readEndVCard() throws VCardException { + protected void readEndVCard(boolean useCache, boolean allowGarbage) + throws IOException, VCardException { // TODO: vCard 3.0 supports group. - super.readEndVCard(); + super.readEndVCard(useCache, allowGarbage); } /** @@ -214,23 +241,6 @@ public class VCardParser_V30 extends VCardParser_V21 { throw new VCardException("AGENT in vCard 3.0 is not supported yet."); } - // vCard 3.0 supports "B" as BASE64 encoding. - @Override - protected void handlePropertyValue( - String propertyName, String propertyValue) throws - IOException, VCardException { - if (mEncoding != null && mEncoding.equalsIgnoreCase("B")) { - String result = getBase64(propertyValue); - if (mBuilder != null) { - ArrayList v = new ArrayList(); - v.add(result); - mBuilder.propertyValues(v); - } - } - - super.handlePropertyValue(propertyName, propertyValue); - } - /** * vCard 3.0 does not require two CRLF at the last of BASE64 data. * It only requires that data should be MIME-encoded. @@ -258,28 +268,39 @@ public class VCardParser_V30 extends VCardParser_V21 { return builder.toString(); } - /** - * Return unescapeText(text). - * In vCard 3.0, 8bit text is always encoded. - */ - @Override - protected String maybeUnescapeText(String text) { - return unescapeText(text); - } - /** * ESCAPED-CHAR = "\\" / "\;" / "\," / "\n" / "\N") * ; \\ encodes \, \n or \N encodes newline * ; \; encodes ;, \, encodes , - */ + * + * Note: Apple escape ':' into '\:' while does not escape '\' + */ @Override - protected String unescapeText(String text) { - // In String#replaceAll(), "\\\\" means single slash. - return text.replaceAll("\\\\;", ";") - .replaceAll("\\\\:", ":") - .replaceAll("\\\\,", ",") - .replaceAll("\\\\n", "\r\n") - .replaceAll("\\\\N", "\r\n") - .replaceAll("\\\\\\\\", "\\\\"); + protected String maybeUnescapeText(String text) { + StringBuilder builder = new StringBuilder(); + int length = text.length(); + for (int i = 0; i < length; i++) { + char ch = text.charAt(i); + if (ch == '\\' && i < length - 1) { + char next_ch = text.charAt(++i); + if (next_ch == 'n' || next_ch == 'N') { + builder.append("\r\n"); + } else { + builder.append(next_ch); + } + } else { + builder.append(ch); + } + } + return builder.toString(); + } + + @Override + protected String maybeUnescape(char ch) { + if (ch == 'n' || ch == 'N') { + return "\r\n"; + } else { + return String.valueOf(ch); + } } } diff --git a/core/java/android/syncml/pim/vcard/VCardSourceDetector.java b/core/java/android/syncml/pim/vcard/VCardSourceDetector.java new file mode 100644 index 0000000000000000000000000000000000000000..8c483912335b54d42c44d058b61d9d41acb2697a --- /dev/null +++ b/core/java/android/syncml/pim/vcard/VCardSourceDetector.java @@ -0,0 +1,140 @@ +/* + * 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.syncml.pim.vcard; + +import android.syncml.pim.VBuilder; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * Class which tries to detects the source of the vCard from its properties. + * Currently this implementation is very premature. + * @hide + */ +public class VCardSourceDetector implements VBuilder { + // Should only be used in package. + static final int TYPE_UNKNOWN = 0; + static final int TYPE_APPLE = 1; + static final int TYPE_JAPANESE_MOBILE_PHONE = 2; // Used in Japanese mobile phones. + static final int TYPE_FOMA = 3; // Used in some Japanese FOMA mobile phones. + static final int TYPE_WINDOWS_MOBILE_JP = 4; + // TODO: Excel, etc. + + private static Set APPLE_SIGNS = new HashSet(Arrays.asList( + "X-PHONETIC-FIRST-NAME", "X-PHONETIC-MIDDLE-NAME", "X-PHONETIC-LAST-NAME", + "X-ABADR", "X-ABUID")); + + private static Set JAPANESE_MOBILE_PHONE_SIGNS = new HashSet(Arrays.asList( + "X-GNO", "X-GN", "X-REDUCTION")); + + private static Set WINDOWS_MOBILE_PHONE_SIGNS = new HashSet(Arrays.asList( + "X-MICROSOFT-ASST_TEL", "X-MICROSOFT-ASSISTANT", "X-MICROSOFT-OFFICELOC")); + + // Note: these signes appears before the signs of the other type (e.g. "X-GN"). + // In other words, Japanese FOMA mobile phones are detected as FOMA, not JAPANESE_MOBILE_PHONES. + private static Set FOMA_SIGNS = new HashSet(Arrays.asList( + "X-SD-VERN", "X-SD-FORMAT_VER", "X-SD-CATEGORIES", "X-SD-CLASS", "X-SD-DCREATED", + "X-SD-DESCRIPTION")); + private static String TYPE_FOMA_CHARSET_SIGN = "X-SD-CHAR_CODE"; + + private int mType = TYPE_UNKNOWN; + // Some mobile phones (like FOMA) tells us the charset of the data. + private boolean mNeedParseSpecifiedCharset; + private String mSpecifiedCharset; + + public void start() { + } + + public void end() { + } + + public void startRecord(String type) { + } + + public void startProperty() { + mNeedParseSpecifiedCharset = false; + } + + public void endProperty() { + } + + public void endRecord() { + } + + public void propertyGroup(String group) { + } + + public void propertyName(String name) { + if (name.equalsIgnoreCase(TYPE_FOMA_CHARSET_SIGN)) { + mType = TYPE_FOMA; + mNeedParseSpecifiedCharset = true; + return; + } + if (mType != TYPE_UNKNOWN) { + return; + } + if (WINDOWS_MOBILE_PHONE_SIGNS.contains(name)) { + mType = TYPE_WINDOWS_MOBILE_JP; + } else if (FOMA_SIGNS.contains(name)) { + mType = TYPE_FOMA; + } else if (JAPANESE_MOBILE_PHONE_SIGNS.contains(name)) { + mType = TYPE_JAPANESE_MOBILE_PHONE; + } else if (APPLE_SIGNS.contains(name)) { + mType = TYPE_APPLE; + } + } + + public void propertyParamType(String type) { + } + + public void propertyParamValue(String value) { + } + + public void propertyValues(List values) { + if (mNeedParseSpecifiedCharset && values.size() > 0) { + mSpecifiedCharset = values.get(0); + } + } + + int getType() { + return mType; + } + + /** + * Return charset String guessed from the source's properties. + * This method must be called after parsing target file(s). + * @return Charset String. Null is returned if guessing the source fails. + */ + public String getEstimatedCharset() { + if (mSpecifiedCharset != null) { + return mSpecifiedCharset; + } + switch (mType) { + case TYPE_WINDOWS_MOBILE_JP: + case TYPE_FOMA: + case TYPE_JAPANESE_MOBILE_PHONE: + return "SHIFT_JIS"; + case TYPE_APPLE: + return "UTF-8"; + default: + return null; + } + } +} diff --git a/core/java/android/test/AndroidTestCase.java b/core/java/android/test/AndroidTestCase.java index 9bafa32e42a9b8199be6350feacb653de85a841c..de0587ab7366d3c8284a95baf1ced06e641ba013 100644 --- a/core/java/android/test/AndroidTestCase.java +++ b/core/java/android/test/AndroidTestCase.java @@ -16,12 +16,14 @@ package android.test; +import android.content.ContentValues; import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import junit.framework.TestCase; import java.lang.reflect.Field; -import junit.framework.TestCase; - /** * Extend this if you need to access Resources or other things that depend on Activity Context. */ @@ -52,6 +54,72 @@ public class AndroidTestCase extends TestCase { return mContext; } + /** + * Asserts that launching a given activity is protected by a particular permission by + * attempting to start the activity and validating that a {@link SecurityException} + * is thrown that mentions the permission in its error message. + * + * Note that an instrumentation isn't needed because all we are looking for is a security error + * and we don't need to wait for the activity to launch and get a handle to the activity. + * + * @param packageName The package name of the activity to launch. + * @param className The class of the activity to launch. + * @param permission The name of the permission. + */ + public void assertActivityRequiresPermission( + String packageName, String className, String permission) { + final Intent intent = new Intent(); + intent.setClassName(packageName, className); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + + try { + getContext().startActivity(intent); + fail("expected security exception for " + permission); + } catch (SecurityException expected) { + assertNotNull("security exception's error message.", expected.getMessage()); + assertTrue("error message should contain " + permission + ".", + expected.getMessage().contains(permission)); + } + } + + + /** + * Asserts that reading from the content uri requires a particular permission by querying the + * uri and ensuring a {@link SecurityException} is thrown mentioning the particular permission. + * + * @param uri The uri that requires a permission to query. + * @param permission The permission that should be required. + */ + public void assertReadingContentUriRequiresPermission(Uri uri, String permission) { + try { + getContext().getContentResolver().query(uri, null, null, null, null); + fail("expected SecurityException requiring " + permission); + } catch (SecurityException expected) { + assertNotNull("security exception's error message.", expected.getMessage()); + assertTrue("error message should contain " + permission + ".", + expected.getMessage().contains(permission)); + } + } + + /** + * Asserts that writing to the content uri requires a particular permission by inserting into + * the uri and ensuring a {@link SecurityException} is thrown mentioning the particular + * permission. + * + * @param uri The uri that requires a permission to query. + * @param permission The permission that should be required. + */ + public void assertWritingContentUriRequiresPermission(Uri uri, String permission) { + try { + getContext().getContentResolver().insert(uri, new ContentValues()); + fail("expected SecurityException requiring " + permission); + } catch (SecurityException expected) { + assertNotNull("security exception's error message.", expected.getMessage()); + assertTrue("error message should contain " + permission + ".", + expected.getMessage().contains(permission)); + } + } + /** * This function is called by various TestCase implementations, at tearDown() time, in order * to scrub out any class variables. This protects against memory leaks in the case where a diff --git a/core/java/android/test/InstrumentationTestCase.java b/core/java/android/test/InstrumentationTestCase.java index 470ab0d9a48e67bd54a2ba138b449faffc18f93f..2145d7cc7891c3f1f19060af7ee1278791c6f0dc 100644 --- a/core/java/android/test/InstrumentationTestCase.java +++ b/core/java/android/test/InstrumentationTestCase.java @@ -241,7 +241,13 @@ public class InstrumentationTestCase extends TestCase { try { final Field keyCodeField = KeyEvent.class.getField("KEYCODE_" + key); final int keyCode = keyCodeField.getInt(null); - instrumentation.sendKeyDownUpSync(keyCode); + try { + instrumentation.sendKeyDownUpSync(keyCode); + } catch (SecurityException e) { + // Ignore security exceptions that are now thrown + // when trying to send to another app, to retain + // compatibility with existing tests. + } } catch (NoSuchFieldException e) { Log.w("ActivityTestCase", "Unknown keycode: KEYCODE_" + key); break; @@ -266,7 +272,13 @@ public class InstrumentationTestCase extends TestCase { final Instrumentation instrumentation = getInstrumentation(); for (int i = 0; i < count; i++) { - instrumentation.sendKeyDownUpSync(keys[i]); + try { + instrumentation.sendKeyDownUpSync(keys[i]); + } catch (SecurityException e) { + // Ignore security exceptions that are now thrown + // when trying to send to another app, to retain + // compatibility with existing tests. + } } instrumentation.waitForIdleSync(); @@ -292,7 +304,13 @@ public class InstrumentationTestCase extends TestCase { final int keyCount = keys[i]; final int keyCode = keys[i + 1]; for (int j = 0; j < keyCount; j++) { - instrumentation.sendKeyDownUpSync(keyCode); + try { + instrumentation.sendKeyDownUpSync(keyCode); + } catch (SecurityException e) { + // Ignore security exceptions that are now thrown + // when trying to send to another app, to retain + // compatibility with existing tests. + } } } diff --git a/core/java/android/text/LoginFilter.java b/core/java/android/text/LoginFilter.java index 27c703f1f083adf2fda1520be2726fd9f42d10e0..9045c09fb6600fa8baf167aa9918896d28c54393 100644 --- a/core/java/android/text/LoginFilter.java +++ b/core/java/android/text/LoginFilter.java @@ -49,10 +49,6 @@ public abstract class LoginFilter implements InputFilter { */ public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) { - char[] out = new char[end - start]; // reserve enough space for whole string - int outidx = 0; - boolean changed = false; - onStart(); // Scan through beginning characters in dest, calling onInvalidCharacter() @@ -63,14 +59,26 @@ public abstract class LoginFilter implements InputFilter { } // Scan through changed characters rejecting disallowed chars + SpannableStringBuilder modification = null; + int modoff = 0; + for (int i = start; i < end; i++) { char c = source.charAt(i); if (isAllowed(c)) { - // Character allowed. Add it to the sequence. - out[outidx++] = c; + // Character allowed. + modoff++; } else { - if (mAppendInvalid) out[outidx++] = c; - else changed = true; // we changed the original string + if (mAppendInvalid) { + modoff++; + } else { + if (modification == null) { + modification = new SpannableStringBuilder(source, start, end); + modoff = i - start; + } + + modification.delete(modoff, modoff + 1); + } + onInvalidCharacter(c); } } @@ -84,20 +92,9 @@ public abstract class LoginFilter implements InputFilter { onStop(); - if (!changed) { - return null; - } - - String s = new String(out, 0, outidx); - - if (source instanceof Spanned) { - SpannableString sp = new SpannableString(s); - TextUtils.copySpansFrom((Spanned) source, - start, end, null, sp, 0); - return sp; - } else { - return s; - } + // Either returns null if we made no changes, + // or what we wanted to change it to if there were changes. + return modification; } /** diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java index 5b4c3802b3cebe9e97229736bdb61311b129ce43..53096dddefb592a25938d6adc7b847f679c67c55 100644 --- a/core/java/android/text/TextUtils.java +++ b/core/java/android/text/TextUtils.java @@ -916,6 +916,17 @@ public class TextUtils { sp.setSpan(o, p.readInt(), p.readInt(), p.readInt()); } + /** + * Copies the spans from the region start...end in + * source to the region + * destoff...destoff+end-start in dest. + * Spans in source that begin before start + * or end after end but overlap this range are trimmed + * as if they began at start or ended at end. + * + * @throws IndexOutOfBoundsException if any of the copied spans + * are out of range in dest. + */ public static void copySpansFrom(Spanned source, int start, int end, Class kind, Spannable dest, int destoff) { diff --git a/core/java/android/text/format/DateFormat.java b/core/java/android/text/format/DateFormat.java index 0dc96c369b7802154d917419e50b1428558bd491..3d10f171538c5c5cb292790d0c38e3c8c6e47e21 100644 --- a/core/java/android/text/format/DateFormat.java +++ b/core/java/android/text/format/DateFormat.java @@ -242,7 +242,7 @@ public class DateFormat { /** * Returns a {@link java.text.DateFormat} object that can format the time according - * to the current user preference. + * to the current locale and the user's 12-/24-hour clock preference. * @param context the application context * @return the {@link java.text.DateFormat} object that properly formats the time. */ @@ -260,46 +260,88 @@ public class DateFormat { } /** - * Returns a {@link java.text.DateFormat} object that can format the date according - * to the current user preference. + * Returns a {@link java.text.DateFormat} object that can format the date + * in short form (such as 12/31/1999) according + * to the current locale and the user's date-order preference. * @param context the application context * @return the {@link java.text.DateFormat} object that properly formats the date. */ public static final java.text.DateFormat getDateFormat(Context context) { - String value = getDateFormatString(context); + String value = Settings.System.getString(context.getContentResolver(), + Settings.System.DATE_FORMAT); + + return getDateFormatForSetting(context, value); + } + + /** + * Returns a {@link java.text.DateFormat} object to format the date + * as if the date format setting were set to value, + * including null to use the locale's default format. + * @param context the application context + * @param value the date format setting string to interpret for + * the current locale + * @hide + */ + public static java.text.DateFormat getDateFormatForSetting(Context context, + String value) { + if (value != null) { + int month = value.indexOf('M'); + int day = value.indexOf('d'); + int year = value.indexOf('y'); + + if (month >= 0 && day >= 0 && year >= 0) { + String template = context.getString(R.string.numeric_date_template); + if (year < month) { + if (month < day) { + value = String.format(template, "yyyy", "MM", "dd"); + } else { + value = String.format(template, "yyyy", "dd", "MM"); + } + } else if (month < day) { + if (day < year) { + value = String.format(template, "MM", "dd", "yyyy"); + } else { // unlikely + value = String.format(template, "MM", "yyyy", "dd"); + } + } else { // day < month + if (month < year) { + value = String.format(template, "dd", "MM", "yyyy"); + } else { // unlikely + value = String.format(template, "dd", "yyyy", "MM"); + } + } + + return new java.text.SimpleDateFormat(value); + } + } + + /* + * The setting is not set; use the default. + * We use a resource string here instead of just DateFormat.SHORT + * so that we get a four-digit year instead a two-digit year. + */ + value = context.getString(R.string.numeric_date_format); return new java.text.SimpleDateFormat(value); } /** * Returns a {@link java.text.DateFormat} object that can format the date - * in long form (such as December 31, 1999) based on user preference. + * in long form (such as December 31, 1999) for the current locale. * @param context the application context * @return the {@link java.text.DateFormat} object that formats the date in long form. */ public static final java.text.DateFormat getLongDateFormat(Context context) { - String value = getDateFormatString(context); - if (value.indexOf('M') < value.indexOf('d')) { - value = context.getString(R.string.full_date_month_first); - } else { - value = context.getString(R.string.full_date_day_first); - } - return new java.text.SimpleDateFormat(value); + return java.text.DateFormat.getDateInstance(java.text.DateFormat.LONG); } /** * Returns a {@link java.text.DateFormat} object that can format the date - * in medium form (such as Dec. 31, 1999) based on user preference. + * in medium form (such as Dec. 31, 1999) for the current locale. * @param context the application context * @return the {@link java.text.DateFormat} object that formats the date in long form. */ public static final java.text.DateFormat getMediumDateFormat(Context context) { - String value = getDateFormatString(context); - if (value.indexOf('M') < value.indexOf('d')) { - value = context.getString(R.string.medium_date_month_first); - } else { - value = context.getString(R.string.medium_date_day_first); - } - return new java.text.SimpleDateFormat(value); + return java.text.DateFormat.getDateInstance(java.text.DateFormat.MEDIUM); } /** @@ -338,6 +380,12 @@ public class DateFormat { } private static String getDateFormatString(Context context) { + java.text.DateFormat df; + df = java.text.DateFormat.getDateInstance(java.text.DateFormat.SHORT); + if (df instanceof SimpleDateFormat) { + return ((SimpleDateFormat) df).toPattern(); + } + String value = Settings.System.getString(context.getContentResolver(), Settings.System.DATE_FORMAT); if (value == null || value.length() < 6) { diff --git a/core/java/android/text/format/DateUtils.java b/core/java/android/text/format/DateUtils.java index 8a7cdd92950f115d896204ea19a2efb3b35d822a..1a4eb699b56d76be42025260411a2b7f90cfd075 100644 --- a/core/java/android/text/format/DateUtils.java +++ b/core/java/android/text/format/DateUtils.java @@ -62,15 +62,6 @@ public class DateUtils com.android.internal.R.string.day_of_week_short_friday, com.android.internal.R.string.day_of_week_short_saturday, }; - private static final int[] sDaysShorter = new int[] { - com.android.internal.R.string.day_of_week_shorter_sunday, - com.android.internal.R.string.day_of_week_shorter_monday, - com.android.internal.R.string.day_of_week_shorter_tuesday, - com.android.internal.R.string.day_of_week_shorter_wednesday, - com.android.internal.R.string.day_of_week_shorter_thursday, - com.android.internal.R.string.day_of_week_shorter_friday, - com.android.internal.R.string.day_of_week_shorter_saturday, - }; private static final int[] sDaysShortest = new int[] { com.android.internal.R.string.day_of_week_shortest_sunday, com.android.internal.R.string.day_of_week_shortest_monday, @@ -80,6 +71,20 @@ public class DateUtils com.android.internal.R.string.day_of_week_shortest_friday, com.android.internal.R.string.day_of_week_shortest_saturday, }; + private static final int[] sMonthsStandaloneLong = new int [] { + com.android.internal.R.string.month_long_standalone_january, + com.android.internal.R.string.month_long_standalone_february, + com.android.internal.R.string.month_long_standalone_march, + com.android.internal.R.string.month_long_standalone_april, + com.android.internal.R.string.month_long_standalone_may, + com.android.internal.R.string.month_long_standalone_june, + com.android.internal.R.string.month_long_standalone_july, + com.android.internal.R.string.month_long_standalone_august, + com.android.internal.R.string.month_long_standalone_september, + com.android.internal.R.string.month_long_standalone_october, + com.android.internal.R.string.month_long_standalone_november, + com.android.internal.R.string.month_long_standalone_december, + }; private static final int[] sMonthsLong = new int [] { com.android.internal.R.string.month_long_january, com.android.internal.R.string.month_long_february, @@ -127,7 +132,7 @@ public class DateUtils com.android.internal.R.string.pm, }; private static Configuration sLastConfig; - private static String sStatusTimeFormat; + private static java.text.DateFormat sStatusTimeFormat; private static String sElapsedFormatMMSS; private static String sElapsedFormatHMMSS; @@ -142,6 +147,9 @@ public class DateUtils public static final long HOUR_IN_MILLIS = MINUTE_IN_MILLIS * 60; public static final long DAY_IN_MILLIS = HOUR_IN_MILLIS * 24; public static final long WEEK_IN_MILLIS = DAY_IN_MILLIS * 7; + /** + * This constant is actually the length of 364 days, not of a year! + */ public static final long YEAR_IN_MILLIS = WEEK_IN_MILLIS * 52; // The following FORMAT_* symbols are used for specifying the format of @@ -171,8 +179,14 @@ public class DateUtils // Date and time format strings that are constant and don't need to be // translated. + /** + * This is not actually the preferred 24-hour date format in all locales. + */ public static final String HOUR_MINUTE_24 = "%H:%M"; public static final String MONTH_FORMAT = "%B"; + /** + * This is not actually a useful month name in all locales. + */ public static final String ABBREV_MONTH_FORMAT = "%b"; public static final String NUMERIC_MONTH_FORMAT = "%m"; public static final String MONTH_DAY_FORMAT = "%-d"; @@ -255,18 +269,15 @@ public class DateUtils * For use with the 'abbrev' parameter of {@link #getDayOfWeekString} and {@link #getMonthString}. * @more *

        e.g. "Su" or "Jan" - *

        In some languages, the results returned for LENGTH_SHORT may be the same as - * return for {@link #LENGTH_MEDIUM}. + *

        In most languages, the results returned for LENGTH_SHORT will be the same as + * the results returned for {@link #LENGTH_MEDIUM}. */ public static final int LENGTH_SHORT = 30; /** * Request an even shorter abbreviated version of the name. - * For use with the 'abbrev' parameter of {@link #getDayOfWeekString} and {@link #getMonthString}. - * @more - *

        e.g. "M", "Tu", "Th" or "J" - *

        In some languages, the results returned for LENGTH_SHORTEST may be the same as - * return for {@link #LENGTH_SHORTER}. + * Do not use this. Currently this will always return the same result + * as {@link #LENGTH_SHORT}. */ public static final int LENGTH_SHORTER = 40; @@ -275,8 +286,8 @@ public class DateUtils * For use with the 'abbrev' parameter of {@link #getDayOfWeekString} and {@link #getMonthString}. * @more *

        e.g. "S", "T", "T" or "J" - *

        In some languages, the results returned for LENGTH_SHORTEST may be the same as - * return for {@link #LENGTH_SHORTER}. + *

        In some languages, the results returned for LENGTH_SHORTEST will be the same as + * the results returned for {@link #LENGTH_SHORT}. */ public static final int LENGTH_SHORTEST = 50; @@ -284,9 +295,12 @@ public class DateUtils * Return a string for the day of the week. * @param dayOfWeek One of {@link Calendar#SUNDAY Calendar.SUNDAY}, * {@link Calendar#MONDAY Calendar.MONDAY}, etc. - * @param abbrev One of {@link #LENGTH_LONG}, {@link #LENGTH_SHORT}, {@link #LENGTH_SHORTER} - * or {@link #LENGTH_SHORTEST}. For forward compatibility, anything else - * will return the same as {#LENGTH_MEDIUM}. + * @param abbrev One of {@link #LENGTH_LONG}, {@link #LENGTH_SHORT}, + * {@link #LENGTH_MEDIUM}, or {@link #LENGTH_SHORTEST}. + * Note that in most languages, {@link #LENGTH_SHORT} + * will return the same as {@link #LENGTH_MEDIUM}. + * Undefined lengths will return {@link #LENGTH_MEDIUM} + * but may return something different in the future. * @throws IndexOutOfBoundsException if the dayOfWeek is out of bounds. */ public static String getDayOfWeekString(int dayOfWeek, int abbrev) { @@ -295,7 +309,7 @@ public class DateUtils case LENGTH_LONG: list = sDaysLong; break; case LENGTH_MEDIUM: list = sDaysMedium; break; case LENGTH_SHORT: list = sDaysShort; break; - case LENGTH_SHORTER: list = sDaysShorter; break; + case LENGTH_SHORTER: list = sDaysShort; break; case LENGTH_SHORTEST: list = sDaysShortest; break; default: list = sDaysMedium; break; } @@ -316,13 +330,14 @@ public class DateUtils } /** - * Return a localized string for the day of the week. + * Return a localized string for the month of the year. * @param month One of {@link Calendar#JANUARY Calendar.JANUARY}, * {@link Calendar#FEBRUARY Calendar.FEBRUARY}, etc. - * @param abbrev One of {@link #LENGTH_LONG}, {@link #LENGTH_SHORT}, {@link #LENGTH_SHORTER} - * or {@link #LENGTH_SHORTEST}. For forward compatibility, anything else - * will return the same as {#LENGTH_MEDIUM}. - * @return Localized day of the week. + * @param abbrev One of {@link #LENGTH_LONG}, {@link #LENGTH_MEDIUM}, + * or {@link #LENGTH_SHORTEST}. + * Undefined lengths will return {@link #LENGTH_MEDIUM} + * but may return something different in the future. + * @return Localized month of the year. */ public static String getMonthString(int month, int abbrev) { // Note that here we use sMonthsMedium for MEDIUM, SHORT and SHORTER. @@ -343,6 +358,40 @@ public class DateUtils return r.getString(list[month - Calendar.JANUARY]); } + /** + * Return a localized string for the month of the year, for + * contexts where the month is not formatted together with + * a day of the month. + * + * @param month One of {@link Calendar#JANUARY Calendar.JANUARY}, + * {@link Calendar#FEBRUARY Calendar.FEBRUARY}, etc. + * @param abbrev One of {@link #LENGTH_LONG}, {@link #LENGTH_MEDIUM}, + * or {@link #LENGTH_SHORTEST}. + * Undefined lengths will return {@link #LENGTH_MEDIUM} + * but may return something different in the future. + * @return Localized month of the year. + * @hide Pending API council approval + */ + public static String getStandaloneMonthString(int month, int abbrev) { + // Note that here we use sMonthsMedium for MEDIUM, SHORT and SHORTER. + // This is a shortcut to not spam the translators with too many variations + // of the same string. If we find that in a language the distinction + // is necessary, we can can add more without changing this API. + int[] list; + switch (abbrev) { + case LENGTH_LONG: list = sMonthsStandaloneLong; + break; + case LENGTH_MEDIUM: list = sMonthsMedium; break; + case LENGTH_SHORT: list = sMonthsMedium; break; + case LENGTH_SHORTER: list = sMonthsMedium; break; + case LENGTH_SHORTEST: list = sMonthsShortest; break; + default: list = sMonthsMedium; break; + } + + Resources r = Resources.getSystem(); + return r.getString(list[month - Calendar.JANUARY]); + } + /** * Returns a string describing the elapsed time since startTime. * @param startTime some time in the past. @@ -572,7 +621,7 @@ public class DateUtils Configuration cfg = r.getConfiguration(); if (sLastConfig == null || !sLastConfig.equals(cfg)) { sLastConfig = cfg; - sStatusTimeFormat = r.getString(com.android.internal.R.string.status_bar_time_format); + sStatusTimeFormat = java.text.DateFormat.getTimeInstance(java.text.DateFormat.SHORT); sElapsedFormatMMSS = r.getString(com.android.internal.R.string.elapsed_time_short_format_mm_ss); sElapsedFormatHMMSS = r.getString(com.android.internal.R.string.elapsed_time_short_format_h_mm_ss); } @@ -586,7 +635,7 @@ public class DateUtils */ public static final CharSequence timeString(long millis) { initFormatStrings(); - return DateFormat.format(sStatusTimeFormat, millis); + return sStatusTimeFormat.format(millis); } /** @@ -1066,7 +1115,9 @@ public class DateUtils * *

        * If FORMAT_CAP_AMPM is set and 12-hour time is used, then the "AM" - * and "PM" are capitalized. + * and "PM" are capitalized. You should not use this flag + * because in some locales these terms cannot be capitalized, and in + * many others it doesn't make sense to do so even though it is possible. * *

        * If FORMAT_NO_NOON is set and 12-hour time is used, then "12pm" is @@ -1074,15 +1125,19 @@ public class DateUtils * *

        * If FORMAT_CAP_NOON is set and 12-hour time is used, then "Noon" is - * shown instead of "noon". + * shown instead of "noon". You should probably not use this flag + * because in many locales it will not make sense to capitalize + * the term. * *

        * If FORMAT_NO_MIDNIGHT is set and 12-hour time is used, then "12am" is * shown instead of "midnight". * *

        - * If FORMAT_CAP_NOON is set and 12-hour time is used, then "Midnight" is - * shown instead of "midnight". + * If FORMAT_CAP_MIDNIGHT is set and 12-hour time is used, then "Midnight" + * is shown instead of "midnight". You should probably not use this + * flag because in many locales it will not make sense to capitalize + * the term. * *

        * If FORMAT_12HOUR is set and the time is shown, then the time is @@ -1224,8 +1279,8 @@ public class DateUtils use24Hour = DateFormat.is24HourFormat(context); } if (use24Hour) { - startTimeFormat = HOUR_MINUTE_24; - endTimeFormat = HOUR_MINUTE_24; + startTimeFormat = endTimeFormat = + res.getString(com.android.internal.R.string.hour_minute_24); } else { boolean abbrevTime = (flags & (FORMAT_ABBREV_TIME | FORMAT_ABBREV_ALL)) != 0; boolean capAMPM = (flags & FORMAT_CAP_AMPM) != 0; @@ -1392,7 +1447,8 @@ public class DateUtils if (numericDate) { monthFormat = NUMERIC_MONTH_FORMAT; } else if (abbrevMonth) { - monthFormat = ABBREV_MONTH_FORMAT; + monthFormat = + res.getString(com.android.internal.R.string.short_format_month); } else { monthFormat = MONTH_FORMAT; } diff --git a/core/java/android/text/format/Formatter.java b/core/java/android/text/format/Formatter.java index 1b30aa0a218c3ee72048d5051a6ab7aa63bf9646..367b26ce45d3947d1a7122d42eddde92068ff91e 100644 --- a/core/java/android/text/format/Formatter.java +++ b/core/java/android/text/format/Formatter.java @@ -59,9 +59,15 @@ public final class Formatter { result = result / 1024; } if (result < 100) { - return String.format("%.2f%s", result, context.getText(suffix).toString()); + String value = String.format("%.2f", result); + return context.getResources(). + getString(com.android.internal.R.string.fileSizeSuffix, + value, context.getString(suffix)); } - return String.format("%.0f%s", result, context.getText(suffix).toString()); + String value = String.format("%.0f", result); + return context.getResources(). + getString(com.android.internal.R.string.fileSizeSuffix, + value, context.getString(suffix)); } /** diff --git a/core/java/android/text/format/Time.java b/core/java/android/text/format/Time.java index daa99c250bfa413fab762d2470d3d60ffa2ef8d5..8eae111ee5d354909b4728e1604c6b2b0c8ef9ee 100644 --- a/core/java/android/text/format/Time.java +++ b/core/java/android/text/format/Time.java @@ -135,6 +135,7 @@ public class Time { private static Locale sLocale; private static String[] sShortMonths; private static String[] sLongMonths; + private static String[] sLongStandaloneMonths; private static String[] sShortWeekdays; private static String[] sLongWeekdays; private static String sTimeOnlyFormat; @@ -321,6 +322,20 @@ public class Time { r.getString(com.android.internal.R.string.month_long_november), r.getString(com.android.internal.R.string.month_long_december), }; + sLongStandaloneMonths = new String[] { + r.getString(com.android.internal.R.string.month_long_standalone_january), + r.getString(com.android.internal.R.string.month_long_standalone_february), + r.getString(com.android.internal.R.string.month_long_standalone_march), + r.getString(com.android.internal.R.string.month_long_standalone_april), + r.getString(com.android.internal.R.string.month_long_standalone_may), + r.getString(com.android.internal.R.string.month_long_standalone_june), + r.getString(com.android.internal.R.string.month_long_standalone_july), + r.getString(com.android.internal.R.string.month_long_standalone_august), + r.getString(com.android.internal.R.string.month_long_standalone_september), + r.getString(com.android.internal.R.string.month_long_standalone_october), + r.getString(com.android.internal.R.string.month_long_standalone_november), + r.getString(com.android.internal.R.string.month_long_standalone_december), + }; sShortWeekdays = new String[] { r.getString(com.android.internal.R.string.day_of_week_medium_sunday), r.getString(com.android.internal.R.string.day_of_week_medium_monday), @@ -438,6 +453,7 @@ public class Time { * * @param s the string to parse * @return true if the resulting time value is in UTC time + * @throws android.util.TimeFormatException if s cannot be parsed. */ public boolean parse3339(String s) { if (nativeParse3339(s)) { diff --git a/core/java/android/text/method/DialerKeyListener.java b/core/java/android/text/method/DialerKeyListener.java index b121e608b5b5440ad10b41b2c73b22b54ab5589d..584e83f53e53d47aa2f71004f68317caecae2c5e 100644 --- a/core/java/android/text/method/DialerKeyListener.java +++ b/core/java/android/text/method/DialerKeyListener.java @@ -106,7 +106,7 @@ public class DialerKeyListener extends NumberKeyListener */ public static final char[] CHARACTERS = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '#', '*', - '+', '-', '(', ')', ',', '/', 'N', '.', ' ' + '+', '-', '(', ')', ',', '/', 'N', '.', ' ', ';' }; private static DialerKeyListener sInstance; diff --git a/core/java/android/text/method/Touch.java b/core/java/android/text/method/Touch.java index f2fb9cb68d7faabfef8d34aac2b880c15f280b9a..dfc16f5adf7fbfdb5ba4753156df71f4a2b8b02c 100644 --- a/core/java/android/text/method/Touch.java +++ b/core/java/android/text/method/Touch.java @@ -81,6 +81,12 @@ public class Touch { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: + ds = buffer.getSpans(0, buffer.length(), DragState.class); + + for (int i = 0; i < ds.length; i++) { + buffer.removeSpan(ds[i]); + } + buffer.setSpan(new DragState(event.getX(), event.getY(), widget.getScrollX(), widget.getScrollY()), 0, 0, Spannable.SPAN_MARK_MARK); diff --git a/core/java/android/util/CharsetUtils.java b/core/java/android/util/CharsetUtils.java index 75530296d2d9397d34e0c65226df36f647accb1d..9d91acaa5f87d8da2cd800b239d989d94f01ad17 100644 --- a/core/java/android/util/CharsetUtils.java +++ b/core/java/android/util/CharsetUtils.java @@ -142,20 +142,25 @@ public final class CharsetUtils { /** * Returns whether the given character set name indicates the Shift-JIS - * encoding. + * encoding. Returns false if the name is null. * * @param charsetName the character set name * @return {@code true} if the name corresponds to Shift-JIS or * {@code false} if not */ private static boolean isShiftJis(String charsetName) { - if (charsetName.length() != 9) { - // Bail quickly if the length doesn't match. + // Bail quickly if the length doesn't match. + if (charsetName == null) { + return false; + } + int length = charsetName.length(); + if (length != 4 && length != 9) { return false; } return charsetName.equalsIgnoreCase("shift_jis") - || charsetName.equalsIgnoreCase("shift-jis"); + || charsetName.equalsIgnoreCase("shift-jis") + || charsetName.equalsIgnoreCase("sjis"); } /** diff --git a/core/java/android/util/DisplayMetrics.java b/core/java/android/util/DisplayMetrics.java index e4dd020e1a81812232dba1877d56abd7fea5ec57..4179edbe7f9cfbe48962e3625495e270e22755a1 100644 --- a/core/java/android/util/DisplayMetrics.java +++ b/core/java/android/util/DisplayMetrics.java @@ -16,6 +16,8 @@ package android.util; +import android.content.res.CompatibilityInfo; +import android.content.res.Configuration; import android.os.*; @@ -35,8 +37,7 @@ public class DisplayMetrics { * The device's density. * @hide */ - public static final int DEVICE_DENSITY = SystemProperties.getInt("ro.sf.lcd_density", - DEFAULT_DENSITY); + public static final int DEVICE_DENSITY = getDeviceDensity(); /** * The absolute width of the display in pixels. @@ -101,22 +102,83 @@ public class DisplayMetrics { } /** - * Set the display metrics' density and update parameters depend on it. - * @hide + * Update the display metrics based on the compatibility info and orientation + * NOTE: DO NOT EXPOSE THIS API! It is introducing a circular dependency + * with the higher-level android.res package. + * {@hide} */ - public void updateDensity(float newDensity) { - float ratio = newDensity / density; - density = newDensity; - scaledDensity = density; - widthPixels *= ratio; - heightPixels *= ratio; - xdpi *= ratio; - ydpi *= ratio; + public void updateMetrics(CompatibilityInfo compatibilityInfo, int orientation, + int screenLayout) { + int xOffset = 0; + if (!compatibilityInfo.isConfiguredExpandable()) { + // Note: this assume that configuration is updated before calling + // updateMetrics method. + if (screenLayout == Configuration.SCREENLAYOUT_LARGE) { + // This is a large screen device and the app is not + // compatible with large screens, to diddle it. + + compatibilityInfo.setExpandable(false); + // Figure out the compatibility width and height of the screen. + int defaultWidth; + int defaultHeight; + switch (orientation) { + case Configuration.ORIENTATION_LANDSCAPE: { + defaultWidth = (int)(CompatibilityInfo.DEFAULT_PORTRAIT_HEIGHT * density); + defaultHeight = (int)(CompatibilityInfo.DEFAULT_PORTRAIT_WIDTH * density); + break; + } + case Configuration.ORIENTATION_PORTRAIT: + case Configuration.ORIENTATION_SQUARE: + default: { + defaultWidth = (int)(CompatibilityInfo.DEFAULT_PORTRAIT_WIDTH * density); + defaultHeight = (int)(CompatibilityInfo.DEFAULT_PORTRAIT_HEIGHT * density); + break; + } + case Configuration.ORIENTATION_UNDEFINED: { + // don't change + return; + } + } + + if (defaultWidth < widthPixels) { + // content/window's x offset in original pixels + xOffset = ((widthPixels - defaultWidth) / 2); + widthPixels = defaultWidth; + } + if (defaultHeight < heightPixels) { + heightPixels = defaultHeight; + } + + } else { + // the screen size is same as expected size. make it expandable + compatibilityInfo.setExpandable(true); + } + } + compatibilityInfo.setVisibleRect(xOffset, widthPixels, heightPixels); + if (compatibilityInfo.isScalingRequired()) { + float invertedRatio = compatibilityInfo.applicationInvertedScale; + density *= invertedRatio; + scaledDensity *= invertedRatio; + xdpi *= invertedRatio; + ydpi *= invertedRatio; + widthPixels *= invertedRatio; + heightPixels *= invertedRatio; + } } + @Override public String toString() { return "DisplayMetrics{density=" + density + ", width=" + widthPixels + ", height=" + heightPixels + ", scaledDensity=" + scaledDensity + ", xdpi=" + xdpi + ", ydpi=" + ydpi + "}"; } + + private static int getDeviceDensity() { + // qemu.sf.lcd_density can be used to override ro.sf.lcd_density + // when running in the emulator, allowing for dynamic configurations. + // The reason for this is that ro.sf.lcd_density is write-once and is + // set by the init process when it parses build.prop before anything else. + return SystemProperties.getInt("qemu.sf.lcd_density", + SystemProperties.getInt("ro.sf.lcd_density", DEFAULT_DENSITY)); + } } diff --git a/core/java/android/util/LongSparseArray.java b/core/java/android/util/LongSparseArray.java new file mode 100644 index 0000000000000000000000000000000000000000..d90045f8625c9a7f71e2f4c76359fc2dcd28430d --- /dev/null +++ b/core/java/android/util/LongSparseArray.java @@ -0,0 +1,342 @@ +/* + * 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.util; + +import com.android.internal.util.ArrayUtils; + +/** + * SparseArrays map longs to Objects. Unlike a normal array of Objects, + * there can be gaps in the indices. It is intended to be more efficient + * than using a HashMap to map Longs to Objects. + * + * @hide + */ +public class LongSparseArray { + private static final Object DELETED = new Object(); + private boolean mGarbage = false; + + /** + * Creates a new SparseArray containing no mappings. + */ + public LongSparseArray() { + this(10); + } + + /** + * Creates a new SparseArray containing no mappings that will not + * require any additional memory allocation to store the specified + * number of mappings. + */ + public LongSparseArray(int initialCapacity) { + initialCapacity = ArrayUtils.idealIntArraySize(initialCapacity); + + mKeys = new long[initialCapacity]; + mValues = new Object[initialCapacity]; + mSize = 0; + } + + /** + * Gets the Object mapped from the specified key, or null + * if no such mapping has been made. + */ + public E get(long key) { + return get(key, null); + } + + /** + * Gets the Object mapped from the specified key, or the specified Object + * if no such mapping has been made. + */ + public E get(long key, E valueIfKeyNotFound) { + int i = binarySearch(mKeys, 0, mSize, key); + + if (i < 0 || mValues[i] == DELETED) { + return valueIfKeyNotFound; + } else { + return (E) mValues[i]; + } + } + + /** + * Removes the mapping from the specified key, if there was any. + */ + public void delete(long key) { + int i = binarySearch(mKeys, 0, mSize, key); + + if (i >= 0) { + if (mValues[i] != DELETED) { + mValues[i] = DELETED; + mGarbage = true; + } + } + } + + /** + * Alias for {@link #delete(long)}. + */ + public void remove(long key) { + delete(key); + } + + private void gc() { + // Log.e("SparseArray", "gc start with " + mSize); + + int n = mSize; + int o = 0; + long[] keys = mKeys; + Object[] values = mValues; + + for (int i = 0; i < n; i++) { + Object val = values[i]; + + if (val != DELETED) { + if (i != o) { + keys[o] = keys[i]; + values[o] = val; + } + + o++; + } + } + + mGarbage = false; + mSize = o; + + // Log.e("SparseArray", "gc end with " + mSize); + } + + /** + * Adds a mapping from the specified key to the specified value, + * replacing the previous mapping from the specified key if there + * was one. + */ + public void put(long key, E value) { + int i = binarySearch(mKeys, 0, mSize, key); + + if (i >= 0) { + mValues[i] = value; + } else { + i = ~i; + + if (i < mSize && mValues[i] == DELETED) { + mKeys[i] = key; + mValues[i] = value; + return; + } + + if (mGarbage && mSize >= mKeys.length) { + gc(); + + // Search again because indices may have changed. + i = ~binarySearch(mKeys, 0, mSize, key); + } + + if (mSize >= mKeys.length) { + int n = ArrayUtils.idealIntArraySize(mSize + 1); + + long[] nkeys = new long[n]; + Object[] nvalues = new Object[n]; + + // Log.e("SparseArray", "grow " + mKeys.length + " to " + n); + System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length); + System.arraycopy(mValues, 0, nvalues, 0, mValues.length); + + mKeys = nkeys; + mValues = nvalues; + } + + if (mSize - i != 0) { + // Log.e("SparseArray", "move " + (mSize - i)); + System.arraycopy(mKeys, i, mKeys, i + 1, mSize - i); + System.arraycopy(mValues, i, mValues, i + 1, mSize - i); + } + + mKeys[i] = key; + mValues[i] = value; + mSize++; + } + } + + /** + * Returns the number of key-value mappings that this SparseArray + * currently stores. + */ + public int size() { + if (mGarbage) { + gc(); + } + + return mSize; + } + + /** + * Given an index in the range 0...size()-1, returns + * the key from the indexth key-value mapping that this + * SparseArray stores. + */ + public long keyAt(int index) { + if (mGarbage) { + gc(); + } + + return mKeys[index]; + } + + /** + * Given an index in the range 0...size()-1, returns + * the value from the indexth key-value mapping that this + * SparseArray stores. + */ + public E valueAt(int index) { + if (mGarbage) { + gc(); + } + + return (E) mValues[index]; + } + + /** + * Given an index in the range 0...size()-1, sets a new + * value for the indexth key-value mapping that this + * SparseArray stores. + */ + public void setValueAt(int index, E value) { + if (mGarbage) { + gc(); + } + + mValues[index] = value; + } + + /** + * Returns the index for which {@link #keyAt} would return the + * specified key, or a negative number if the specified + * key is not mapped. + */ + public int indexOfKey(long key) { + if (mGarbage) { + gc(); + } + + return binarySearch(mKeys, 0, mSize, key); + } + + /** + * Returns an index for which {@link #valueAt} would return the + * specified key, or a negative number if no keys map to the + * specified value. + * Beware that this is a linear search, unlike lookups by key, + * and that multiple keys can map to the same value and this will + * find only one of them. + */ + public int indexOfValue(E value) { + if (mGarbage) { + gc(); + } + + for (int i = 0; i < mSize; i++) + if (mValues[i] == value) + return i; + + return -1; + } + + /** + * Removes all key-value mappings from this SparseArray. + */ + public void clear() { + int n = mSize; + Object[] values = mValues; + + for (int i = 0; i < n; i++) { + values[i] = null; + } + + mSize = 0; + mGarbage = false; + } + + /** + * Puts a key/value pair into the array, optimizing for the case where + * the key is greater than all existing keys in the array. + */ + public void append(long key, E value) { + if (mSize != 0 && key <= mKeys[mSize - 1]) { + put(key, value); + return; + } + + if (mGarbage && mSize >= mKeys.length) { + gc(); + } + + int pos = mSize; + if (pos >= mKeys.length) { + int n = ArrayUtils.idealIntArraySize(pos + 1); + + long[] nkeys = new long[n]; + Object[] nvalues = new Object[n]; + + // Log.e("SparseArray", "grow " + mKeys.length + " to " + n); + System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length); + System.arraycopy(mValues, 0, nvalues, 0, mValues.length); + + mKeys = nkeys; + mValues = nvalues; + } + + mKeys[pos] = key; + mValues[pos] = value; + mSize = pos + 1; + } + + private static int binarySearch(long[] a, int start, int len, long key) { + int high = start + len, low = start - 1, guess; + + while (high - low > 1) { + guess = (high + low) / 2; + + if (a[guess] < key) + low = guess; + else + high = guess; + } + + if (high == start + len) + return ~(start + len); + else if (a[high] == key) + return high; + else + return ~high; + } + + private void checkIntegrity() { + for (int i = 1; i < mSize; i++) { + if (mKeys[i] <= mKeys[i - 1]) { + for (int j = 0; j < mSize; j++) { + Log.e("FAIL", j + ": " + mKeys[j] + " -> " + mValues[j]); + } + + throw new RuntimeException(); + } + } + } + + private long[] mKeys; + private Object[] mValues; + private int mSize; +} \ No newline at end of file diff --git a/core/java/android/view/GestureDetector.java b/core/java/android/view/GestureDetector.java index 23f3e3c3ece9df010bcec53a4d2897ac957baf6f..1e558be1ccc7d330f06d70b21f027d7a7fe69b20 100644 --- a/core/java/android/view/GestureDetector.java +++ b/core/java/android/view/GestureDetector.java @@ -198,6 +198,7 @@ public class GestureDetector { private int mTouchSlopSquare; private int mDoubleTapSlopSquare; private int mMinimumFlingVelocity; + private int mMaximumFlingVelocity; private static final int LONGPRESS_TIMEOUT = ViewConfiguration.getLongPressTimeout(); private static final int TAP_TIMEOUT = ViewConfiguration.getTapTimeout(); @@ -361,11 +362,13 @@ public class GestureDetector { doubleTapSlop = ViewConfiguration.getDoubleTapSlop(); //noinspection deprecation mMinimumFlingVelocity = ViewConfiguration.getMinimumFlingVelocity(); + mMaximumFlingVelocity = ViewConfiguration.getMaximumFlingVelocity(); } else { final ViewConfiguration configuration = ViewConfiguration.get(context); touchSlop = configuration.getScaledTouchSlop(); doubleTapSlop = configuration.getScaledDoubleTapSlop(); mMinimumFlingVelocity = configuration.getScaledMinimumFlingVelocity(); + mMaximumFlingVelocity = configuration.getScaledMaximumFlingVelocity(); } mTouchSlopSquare = touchSlop * touchSlop; mDoubleTapSlopSquare = doubleTapSlop * doubleTapSlop; @@ -505,7 +508,7 @@ public class GestureDetector { // A fling must travel the minimum tap distance final VelocityTracker velocityTracker = mVelocityTracker; - velocityTracker.computeCurrentVelocity(1000); + velocityTracker.computeCurrentVelocity(1000, mMaximumFlingVelocity); final float velocityY = velocityTracker.getYVelocity(); final float velocityX = velocityTracker.getXVelocity(); diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java index 86261c4a7edb90dec61fc2d715dc507ffb7a5bf2..a224ed306b0a6dbb41201392ac1f4b678d28f170 100644 --- a/core/java/android/view/MotionEvent.java +++ b/core/java/android/view/MotionEvent.java @@ -59,32 +59,32 @@ public final class MotionEvent implements Parcelable { public static final int ACTION_OUTSIDE = 4; private static final boolean TRACK_RECYCLED_LOCATION = false; - + /** * Flag indicating the motion event intersected the top edge of the screen. */ public static final int EDGE_TOP = 0x00000001; - + /** * Flag indicating the motion event intersected the bottom edge of the screen. */ public static final int EDGE_BOTTOM = 0x00000002; - + /** * Flag indicating the motion event intersected the left edge of the screen. */ public static final int EDGE_LEFT = 0x00000004; - + /** * Flag indicating the motion event intersected the right edge of the screen. */ public static final int EDGE_RIGHT = 0x00000008; - + static private final int MAX_RECYCLED = 10; static private Object gRecyclerLock = new Object(); static private int gRecyclerUsed = 0; static private MotionEvent gRecyclerTop = null; - + private long mDownTime; private long mEventTime; private int mAction; @@ -109,7 +109,7 @@ public final class MotionEvent implements Parcelable { private MotionEvent() { } - + static private MotionEvent obtain() { synchronized (gRecyclerLock) { if (gRecyclerTop == null) { @@ -123,26 +123,26 @@ public final class MotionEvent implements Parcelable { return ev; } } - + /** * Create a new MotionEvent, filling in all of the basic values that * define the motion. - * - * @param downTime The time (in ms) when the user originally pressed down to start + * + * @param downTime The time (in ms) when the user originally pressed down to start * a stream of position events. This must be obtained from {@link SystemClock#uptimeMillis()}. - * @param eventTime The the time (in ms) when this specific event was generated. This + * @param eventTime The the time (in ms) when this specific event was generated. This * must be obtained from {@link SystemClock#uptimeMillis()}. * @param action The kind of action being performed -- one of either * {@link #ACTION_DOWN}, {@link #ACTION_MOVE}, {@link #ACTION_UP}, or * {@link #ACTION_CANCEL}. * @param x The X coordinate of this event. * @param y The Y coordinate of this event. - * @param pressure The current pressure of this event. The pressure generally - * ranges from 0 (no pressure at all) to 1 (normal pressure), however - * values higher than 1 may be generated depending on the calibration of + * @param pressure The current pressure of this event. The pressure generally + * ranges from 0 (no pressure at all) to 1 (normal pressure), however + * values higher than 1 may be generated depending on the calibration of * the input device. * @param size A scaled value of the approximate size of the area being pressed when - * touched with the finger. The actual value in pixels corresponding to the finger + * touched with the finger. The actual value in pixels corresponding to the finger * touch is normalized with a device specific range of values * and scaled to a value between 0 and 1. * @param metaState The state of any meta / modifier keys that were in effect when @@ -174,15 +174,15 @@ public final class MotionEvent implements Parcelable { return ev; } - + /** * Create a new MotionEvent, filling in a subset of the basic motion * values. Those not specified here are: device id (always 0), pressure * and size (always 1), x and y precision (always 1), and edgeFlags (always 0). - * - * @param downTime The time (in ms) when the user originally pressed down to start + * + * @param downTime The time (in ms) when the user originally pressed down to start * a stream of position events. This must be obtained from {@link SystemClock#uptimeMillis()}. - * @param eventTime The the time (in ms) when this specific event was generated. This + * @param eventTime The the time (in ms) when this specific event was generated. This * must be obtained from {@link SystemClock#uptimeMillis()}. * @param action The kind of action being performed -- one of either * {@link #ACTION_DOWN}, {@link #ACTION_MOVE}, {@link #ACTION_UP}, or @@ -212,27 +212,47 @@ public final class MotionEvent implements Parcelable { } /** - * Scales down the cood of this event by the given scale. + * Scales down the coordination of this event by the given scale. * * @hide */ public void scale(float scale) { - if (scale != 1.0f) { - mX *= scale; - mY *= scale; - mRawX *= scale; - mRawY *= scale; - mSize *= scale; - mXPrecision *= scale; - mYPrecision *= scale; - if (mHistory != null) { - float[] history = mHistory; - int length = history.length; - for (int i = 0; i < length; i += 4) { - history[i] *= scale; - history[i + 2] *= scale; - history[i + 3] *= scale; - } + mX *= scale; + mY *= scale; + mRawX *= scale; + mRawY *= scale; + mSize *= scale; + mXPrecision *= scale; + mYPrecision *= scale; + if (mHistory != null) { + float[] history = mHistory; + int length = history.length; + for (int i = 0; i < length; i += 4) { + history[i] *= scale; // X + history[i + 1] *= scale; // Y + // no need to scale pressure ([i+2]) + history[i + 3] *= scale; // Size, TODO: square this? + } + } + } + + /** + * Translate the coordination of the event by given x and y. + * + * @hide + */ + public void translate(float dx, float dy) { + mX += dx; + mY += dy; + mRawX += dx; + mRawY += dx; + if (mHistory != null) { + float[] history = mHistory; + int length = history.length; + for (int i = 0; i < length; i += 4) { + history[i] += dx; // X + history[i + 1] += dy; // Y + // no need to translate pressure (i+2) and size (i+3) } } } @@ -265,7 +285,7 @@ public final class MotionEvent implements Parcelable { } return ev; } - + /** * Recycle the MotionEvent, to be re-used by a later caller. After calling * this function you must not ever touch the event again. @@ -291,7 +311,7 @@ public final class MotionEvent implements Parcelable { } } } - + /** * Return the kind of action being performed -- one of either * {@link #ACTION_DOWN}, {@link #ACTION_MOVE}, {@link #ACTION_UP}, or @@ -302,8 +322,8 @@ public final class MotionEvent implements Parcelable { } /** - * Returns the time (in ms) when the user originally pressed down to start - * a stream of position events. + * Returns the time (in ms) when the user originally pressed down to start + * a stream of position events. */ public final long getDownTime() { return mDownTime; @@ -317,25 +337,25 @@ public final class MotionEvent implements Parcelable { } /** - * Returns the X coordinate of this event. Whole numbers are pixels; the - * value may have a fraction for input devices that are sub-pixel precise. + * Returns the X coordinate of this event. Whole numbers are pixels; the + * value may have a fraction for input devices that are sub-pixel precise. */ public final float getX() { return mX; } /** - * Returns the Y coordinate of this event. Whole numbers are pixels; the - * value may have a fraction for input devices that are sub-pixel precise. + * Returns the Y coordinate of this event. Whole numbers are pixels; the + * value may have a fraction for input devices that are sub-pixel precise. */ public final float getY() { return mY; } /** - * Returns the current pressure of this event. The pressure generally - * ranges from 0 (no pressure at all) to 1 (normal pressure), however - * values higher than 1 may be generated depending on the calibration of + * Returns the current pressure of this event. The pressure generally + * ranges from 0 (no pressure at all) to 1 (normal pressure), however + * values higher than 1 may be generated depending on the calibration of * the input device. */ public final float getPressure() { @@ -344,9 +364,9 @@ public final class MotionEvent implements Parcelable { /** * Returns a scaled value of the approximate size, of the area being pressed when - * touched with the finger. The actual value in pixels corresponding to the finger + * touched with the finger. The actual value in pixels corresponding to the finger * touch is normalized with the device specific range of values - * and scaled to a value between 0 and 1. The value of size can be used to + * and scaled to a value between 0 and 1. The value of size can be used to * determine fat touch events. */ public final float getSize() { @@ -396,7 +416,7 @@ public final class MotionEvent implements Parcelable { public final float getXPrecision() { return mXPrecision; } - + /** * Return the precision of the Y coordinates being reported. You can * multiple this number with {@link #getY} to find the actual hardware @@ -406,89 +426,89 @@ public final class MotionEvent implements Parcelable { public final float getYPrecision() { return mYPrecision; } - + /** * Returns the number of historical points in this event. These are * movements that have occurred between this event and the previous event. * This only applies to ACTION_MOVE events -- all other actions will have * a size of 0. - * + * * @return Returns the number of historical points in the event. */ public final int getHistorySize() { return mNumHistory; } - + /** * Returns the time that a historical movement occurred between this event * and the previous event. Only applies to ACTION_MOVE events. - * + * * @param pos Which historical value to return; must be less than * {@link #getHistorySize} - * + * * @see #getHistorySize * @see #getEventTime */ public final long getHistoricalEventTime(int pos) { return mHistoryTimes[pos]; } - + /** * Returns a historical X coordinate that occurred between this event * and the previous event. Only applies to ACTION_MOVE events. - * + * * @param pos Which historical value to return; must be less than * {@link #getHistorySize} - * + * * @see #getHistorySize * @see #getX */ public final float getHistoricalX(int pos) { return mHistory[pos*4]; } - + /** * Returns a historical Y coordinate that occurred between this event * and the previous event. Only applies to ACTION_MOVE events. - * + * * @param pos Which historical value to return; must be less than * {@link #getHistorySize} - * + * * @see #getHistorySize * @see #getY */ public final float getHistoricalY(int pos) { return mHistory[pos*4 + 1]; } - + /** * Returns a historical pressure coordinate that occurred between this event * and the previous event. Only applies to ACTION_MOVE events. - * + * * @param pos Which historical value to return; must be less than * {@link #getHistorySize} - * + * * @see #getHistorySize * @see #getPressure */ public final float getHistoricalPressure(int pos) { return mHistory[pos*4 + 2]; } - + /** * Returns a historical size coordinate that occurred between this event * and the previous event. Only applies to ACTION_MOVE events. - * + * * @param pos Which historical value to return; must be less than * {@link #getHistorySize} - * + * * @see #getHistorySize * @see #getSize */ public final float getHistoricalSize(int pos) { return mHistory[pos*4 + 3]; } - + /** * Return the id for the device that this event came from. An id of * zero indicates that the event didn't come from a physical device; other @@ -497,12 +517,12 @@ public final class MotionEvent implements Parcelable { public final int getDeviceId() { return mDeviceId; } - + /** * Returns a bitfield indicating which edges, if any, where touched by this - * MotionEvent. For touch events, clients can use this to determine if the - * user's finger was touching the edge of the display. - * + * MotionEvent. For touch events, clients can use this to determine if the + * user's finger was touching the edge of the display. + * * @see #EDGE_LEFT * @see #EDGE_TOP * @see #EDGE_RIGHT @@ -511,12 +531,12 @@ public final class MotionEvent implements Parcelable { public final int getEdgeFlags() { return mEdgeFlags; } - + /** * Sets the bitfield indicating which edges, if any, where touched by this - * MotionEvent. - * + * MotionEvent. + * * @see #getEdgeFlags() */ public final void setEdgeFlags(int flags) { @@ -548,11 +568,11 @@ public final class MotionEvent implements Parcelable { pos[i+1] += deltaY; } } - + /** * Set this event's location. Applies {@link #offsetLocation} with a * delta from the current location to the given new location. - * + * * @param x New absolute X location. * @param y New absolute Y location. */ @@ -563,13 +583,13 @@ public final class MotionEvent implements Parcelable { offsetLocation(deltaX, deltaY); } } - + /** * Add a new movement to the batch of movements in this event. The event's * current location, position and size is updated to the new values. In * the future, the current values in the event will be added to a list of * historic values. - * + * * @param x The new X position. * @param y The new Y position. * @param pressure The new pressure. @@ -599,16 +619,16 @@ public final class MotionEvent implements Parcelable { mHistoryTimes = historyTimes = newHistoryTimes; } } - + historyTimes[N] = mEventTime; - + final int pos = N*4; history[pos] = mX; history[pos+1] = mY; history[pos+2] = mPressure; history[pos+3] = mSize; mNumHistory = N+1; - + mEventTime = eventTime; mX = mRawX = x; mY = mRawY = y; @@ -616,7 +636,7 @@ public final class MotionEvent implements Parcelable { mSize = size; mMetaState |= metaState; } - + @Override public String toString() { return "MotionEvent{" + Integer.toHexString(System.identityHashCode(this)) diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index 3d023f7a9691091583812724ec7eca526b4670f0..45b0f0a7908292bb096d989b4f655cd324f8f93c 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -17,6 +17,8 @@ package android.view; import android.content.Context; +import android.content.res.CompatibilityInfo; +import android.content.res.CompatibilityInfo.Translator; import android.graphics.Canvas; import android.graphics.PixelFormat; import android.graphics.PorterDuff; @@ -100,6 +102,8 @@ public class SurfaceView extends View { static final int KEEP_SCREEN_ON_MSG = 1; static final int GET_NEW_SURFACE_MSG = 2; + int mWindowType = WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA; + boolean mIsCreating = false; final Handler mHandler = new Handler() { @@ -135,28 +139,21 @@ public class SurfaceView extends View { int mFormat = -1; int mType = -1; final Rect mSurfaceFrame = new Rect(); - private final float mAppScale; - private final float mAppScaleInverted; + private Translator mTranslator; public SurfaceView(Context context) { super(context); setWillNotDraw(true); - mAppScale = context.getApplicationScale(); - mAppScaleInverted = 1.0f / mAppScale; } public SurfaceView(Context context, AttributeSet attrs) { super(context, attrs); setWillNotDraw(true); - mAppScale = context.getApplicationScale(); - mAppScaleInverted = 1.0f / mAppScale; } public SurfaceView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); setWillNotDraw(true); - mAppScale = context.getApplicationScale(); - mAppScaleInverted = 1.0f / mAppScale; } /** @@ -259,9 +256,9 @@ public class SurfaceView extends View { public boolean dispatchTouchEvent(MotionEvent event) { // SurfaceView uses pre-scaled size unless fixed size is requested. This hook // scales the event back to the pre-scaled coordinates for such surface. - if (mRequestedWidth < 0 && mAppScale != 1.0f) { + if (mRequestedWidth < 0 && mTranslator != null) { MotionEvent scaledBack = MotionEvent.obtain(event); - scaledBack.scale(mAppScale); + scaledBack.scale(mTranslator.applicationScale); try { return super.dispatchTouchEvent(scaledBack); } finally { @@ -285,20 +282,33 @@ public class SurfaceView extends View { super.dispatchDraw(canvas); } + /** + * Hack to allow special layering of windows. The type is one of the + * types in WindowManager.LayoutParams. This is a hack so: + * @hide + */ + public void setWindowType(int type) { + mWindowType = type; + } + private void updateWindow(boolean force) { if (!mHaveFrame) { return; } + mTranslator = ((ViewRoot)getRootView().getParent()).mTranslator; + + float appScale = mTranslator == null ? 1.0f : mTranslator.applicationScale; int myWidth = mRequestedWidth; if (myWidth <= 0) myWidth = getWidth(); int myHeight = mRequestedHeight; if (myHeight <= 0) myHeight = getHeight(); - // Use original size for surface unless fixed size is requested. - if (mRequestedWidth <= 0) { - myWidth *= mAppScale; - myHeight *= mAppScale; + // Use original size if the app specified the size of the view, + // and let the flinger to scale up. + if (mRequestedWidth <= 0 && mTranslator != null && mTranslator.scalingRequired) { + myWidth *= appScale; + myHeight *= appScale; } getLocationInWindow(mLocation); @@ -316,7 +326,7 @@ public class SurfaceView extends View { + " visible=" + visibleChanged + " left=" + (mLeft != mLocation[0]) + " top=" + (mTop != mLocation[1])); - + try { final boolean visible = mVisible = mRequestedVisible; mLeft = mLocation[0]; @@ -326,23 +336,30 @@ public class SurfaceView extends View { mFormat = mRequestedFormat; mType = mRequestedType; - // Scaling window's layout here beause mLayout is not used elsewhere. - mLayout.x = (int) (mLeft * mAppScale); - mLayout.y = (int) (mTop * mAppScale); - mLayout.width = (int) (getWidth() * mAppScale); - mLayout.height = (int) (getHeight() * mAppScale); + // Scaling/Translate window's layout here because mLayout is not used elsewhere. + + // Places the window relative + mLayout.x = mLeft; + mLayout.y = mTop; + mLayout.width = getWidth(); + mLayout.height = getHeight(); + if (mTranslator != null) { + mTranslator.translateLayoutParamsInAppWindowToScreen(mLayout); + } + mLayout.format = mRequestedFormat; mLayout.flags |=WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS | WindowManager.LayoutParams.FLAG_SCALED | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE + | WindowManager.LayoutParams.FLAG_NO_COMPATIBILITY_SCALING ; mLayout.memoryType = mRequestedType; if (mWindow == null) { mWindow = new MyWindow(this); - mLayout.type = WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA; + mLayout.type = mWindowType; mLayout.gravity = Gravity.LEFT|Gravity.TOP; mSession.add(mWindow, mLayout, mVisible ? VISIBLE : GONE, mContentInsets); @@ -356,15 +373,12 @@ public class SurfaceView extends View { mSurfaceLock.lock(); mDrawingStopped = !visible; + final int relayoutResult = mSession.relayout( mWindow, mLayout, mWidth, mHeight, visible ? VISIBLE : GONE, false, mWinFrame, mContentInsets, mVisibleInsets, mSurface); - mContentInsets.scale(mAppScaleInverted); - mVisibleInsets.scale(mAppScaleInverted); - mWinFrame.scale(mAppScaleInverted); - if (localLOGV) Log.i(TAG, "New surface: " + mSurface + ", vis=" + visible + ", frame=" + mWinFrame); mSurfaceFrame.left = 0; @@ -433,24 +447,14 @@ public class SurfaceView extends View { private static class MyWindow extends IWindow.Stub { private final WeakReference mSurfaceView; - private final float mAppScale; - private final float mAppScaleInverted; public MyWindow(SurfaceView surfaceView) { mSurfaceView = new WeakReference(surfaceView); - mAppScale = surfaceView.getContext().getApplicationScale(); - mAppScaleInverted = 1.0f / mAppScale; } public void resized(int w, int h, Rect coveredInsets, Rect visibleInsets, boolean reportDraw) { SurfaceView surfaceView = mSurfaceView.get(); - float scale = mAppScaleInverted; - w *= scale; - h *= scale; - coveredInsets.scale(scale); - visibleInsets.scale(scale); - if (surfaceView != null) { if (localLOGV) Log.v( "SurfaceView", surfaceView + " got resized: w=" + @@ -613,7 +617,6 @@ public class SurfaceView extends View { Canvas c = null; if (!mDrawingStopped && mWindow != null) { Rect frame = dirty != null ? dirty : mSurfaceFrame; - frame.scale(mAppScale); try { c = mSurface.lockCanvas(frame); } catch (Exception e) { diff --git a/core/java/android/view/VelocityTracker.java b/core/java/android/view/VelocityTracker.java index c708f547642a5c527562b9e4f1de5d2283ff6b18..5d89c46a3db1afd2d5262a04e04bab7f15deb619 100644 --- a/core/java/android/view/VelocityTracker.java +++ b/core/java/android/view/VelocityTracker.java @@ -165,7 +165,17 @@ public final class VelocityTracker implements Poolable { pastTime[i] = 0; } } - + + /** + * Equivalent to invoking {@link #computeCurrentVelocity(int, float)} with a maximum + * velocity of Float.MAX_VALUE. + * + * @see #computeCurrentVelocity(int, float) + */ + public void computeCurrentVelocity(int units) { + computeCurrentVelocity(units, Float.MAX_VALUE); + } + /** * Compute the current velocity based on the points that have been * collected. Only call this when you actually want to retrieve velocity @@ -175,8 +185,11 @@ public final class VelocityTracker implements Poolable { * * @param units The units you would like the velocity in. A value of 1 * provides pixels per millisecond, 1000 provides pixels per second, etc. + * @param maxVelocity The maximum velocity that can be computed by this method. + * This value must be declared in the same unit as the units parameter. This value + * must be positive. */ - public void computeCurrentVelocity(int units) { + public void computeCurrentVelocity(int units, float maxVelocity) { final float[] pastX = mPastX; final float[] pastY = mPastY; final long[] pastTime = mPastTime; @@ -210,8 +223,8 @@ public final class VelocityTracker implements Poolable { if (accumY == 0) accumY = vel; else accumY = (accumY + vel) * .5f; } - mXVelocity = accumX; - mYVelocity = accumY; + mXVelocity = accumX < 0.0f ? Math.max(accumX, -maxVelocity) : Math.min(accumX, maxVelocity); + mYVelocity = accumY < 0.0f ? Math.max(accumY, -maxVelocity) : Math.min(accumY, maxVelocity); if (localLOGV) Log.v(TAG, "Y velocity=" + mYVelocity +" X velocity=" + mXVelocity + " N=" + N); diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 9e709cfd1f475e7968748191b0ed451fc07c3294..ff8868bb0f2fb7aafd85c6db08e14cf4017d8399 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -16,6 +16,9 @@ package android.view; +import com.android.internal.R; +import com.android.internal.view.menu.MenuBuilder; + import android.content.Context; import android.content.res.Resources; import android.content.res.TypedArray; @@ -25,12 +28,12 @@ import android.graphics.LinearGradient; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.PixelFormat; +import android.graphics.Point; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.graphics.Region; import android.graphics.Shader; -import android.graphics.Point; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.os.Handler; @@ -42,47 +45,47 @@ import android.os.RemoteException; import android.os.SystemClock; import android.os.SystemProperties; import android.util.AttributeSet; +import android.util.Config; import android.util.EventLog; import android.util.Log; -import android.util.SparseArray; -import android.util.Poolable; import android.util.Pool; -import android.util.Pools; +import android.util.Poolable; import android.util.PoolableManager; -import android.util.Config; +import android.util.Pools; +import android.util.SparseArray; import android.view.ContextMenu.ContextMenuInfo; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityEventSource; +import android.view.accessibility.AccessibilityManager; import android.view.animation.Animation; +import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputMethodManager; -import android.view.inputmethod.EditorInfo; import android.widget.ScrollBarDrawable; -import com.android.internal.R; -import com.android.internal.view.menu.MenuBuilder; - +import java.lang.ref.SoftReference; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.WeakHashMap; -import java.lang.ref.SoftReference; -import java.lang.reflect.Method; -import java.lang.reflect.InvocationTargetException; /** *

        * 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, which are - * used to create interactive UI components (buttons, text fields, etc.). The + * 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 + *

        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: + * include: *
        Declaring Layout *
        Creating Menus *
        Common Layout Objects @@ -93,7 +96,7 @@ import java.lang.reflect.InvocationTargetException; *
        How Android Draws Views. *

        *
        - * + * * *

        Using Views

        *

        @@ -419,7 +422,7 @@ import java.lang.reflect.InvocationTargetException; *

        * *

        - * Note that the framework will not draw views that are not in the invalid region. + * Note that the framework will not draw views that are not in the invalid region. *

        * *

        @@ -535,25 +538,52 @@ import java.lang.reflect.InvocationTargetException; * take care of redrawing the appropriate views until the animation completes. *

        * + * @attr ref android.R.styleable#View_background + * @attr ref android.R.styleable#View_clickable + * @attr ref android.R.styleable#View_contentDescription + * @attr ref android.R.styleable#View_drawingCacheQuality + * @attr ref android.R.styleable#View_duplicateParentState + * @attr ref android.R.styleable#View_id + * @attr ref android.R.styleable#View_fadingEdge + * @attr ref android.R.styleable#View_fadingEdgeLength * @attr ref android.R.styleable#View_fitsSystemWindows + * @attr ref android.R.styleable#View_isScrollContainer + * @attr ref android.R.styleable#View_focusable + * @attr ref android.R.styleable#View_focusableInTouchMode + * @attr ref android.R.styleable#View_hapticFeedbackEnabled + * @attr ref android.R.styleable#View_keepScreenOn + * @attr ref android.R.styleable#View_longClickable + * @attr ref android.R.styleable#View_minHeight + * @attr ref android.R.styleable#View_minWidth * @attr ref android.R.styleable#View_nextFocusDown * @attr ref android.R.styleable#View_nextFocusLeft * @attr ref android.R.styleable#View_nextFocusRight * @attr ref android.R.styleable#View_nextFocusUp + * @attr ref android.R.styleable#View_onClick + * @attr ref android.R.styleable#View_padding + * @attr ref android.R.styleable#View_paddingBottom + * @attr ref android.R.styleable#View_paddingLeft + * @attr ref android.R.styleable#View_paddingRight + * @attr ref android.R.styleable#View_paddingTop + * @attr ref android.R.styleable#View_saveEnabled * @attr ref android.R.styleable#View_scrollX * @attr ref android.R.styleable#View_scrollY - * @attr ref android.R.styleable#View_scrollbarTrackHorizontal - * @attr ref android.R.styleable#View_scrollbarThumbHorizontal * @attr ref android.R.styleable#View_scrollbarSize + * @attr ref android.R.styleable#View_scrollbarStyle * @attr ref android.R.styleable#View_scrollbars + * @attr ref android.R.styleable#View_scrollbarTrackHorizontal + * @attr ref android.R.styleable#View_scrollbarThumbHorizontal * @attr ref android.R.styleable#View_scrollbarThumbVertical * @attr ref android.R.styleable#View_scrollbarTrackVertical * @attr ref android.R.styleable#View_scrollbarAlwaysDrawHorizontalTrack * @attr ref android.R.styleable#View_scrollbarAlwaysDrawVerticalTrack + * @attr ref android.R.styleable#View_soundEffectsEnabled + * @attr ref android.R.styleable#View_tag + * @attr ref android.R.styleable#View_visibility * * @see android.view.ViewGroup */ -public class View implements Drawable.Callback, KeyEvent.Callback { +public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource { private static final boolean DBG = false; /** @@ -850,6 +880,18 @@ public class View implements Drawable.Callback, KeyEvent.Callback { */ public static final int HAPTIC_FEEDBACK_ENABLED = 0x10000000; + /** + * View flag indicating whether {@link #addFocusables(ArrayList, int, int)} + * should add all focusable Views regardless if they are focusable in touch mode. + */ + public static final int FOCUSABLES_ALL = 0x00000000; + + /** + * View flag indicating whether {@link #addFocusables(ArrayList, int, int)} + * should add only Views focusable in touch mode. + */ + public static final int FOCUSABLES_TOUCH_MODE = 0x00000001; + /** * Use with {@link #focusSearch}. Move focus to the previous selectable * item. @@ -1427,6 +1469,27 @@ public class View implements Drawable.Callback, KeyEvent.Callback { */ static final int DIRTY_MASK = 0x00600000; + /** + * Indicates whether the background is opaque. + * + * @hide + */ + static final int OPAQUE_BACKGROUND = 0x00800000; + + /** + * Indicates whether the scrollbars are opaque. + * + * @hide + */ + static final int OPAQUE_SCROLLBARS = 0x01000000; + + /** + * Indicates whether the view is opaque. + * + * @hide + */ + static final int OPAQUE_MASK = 0x01800000; + /** * The parent this view is attached to. * {@hide} @@ -1449,7 +1512,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { @ViewDebug.FlagToString(mask = LAYOUT_REQUIRED, equals = LAYOUT_REQUIRED, name = "LAYOUT_REQUIRED"), @ViewDebug.FlagToString(mask = DRAWING_CACHE_VALID, equals = DRAWING_CACHE_VALID, - name = "DRAWING_CACHE_VALID", outputIf = false), + name = "DRAWING_CACHE_INVALID", outputIf = false), @ViewDebug.FlagToString(mask = DRAWN, equals = DRAWN, name = "DRAWN", outputIf = true), @ViewDebug.FlagToString(mask = DRAWN, equals = DRAWN, name = "NOT_DRAWN", outputIf = false), @ViewDebug.FlagToString(mask = DIRTY_MASK, equals = DIRTY_OPAQUE, name = "DIRTY_OPAQUE"), @@ -1550,6 +1613,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback { @ViewDebug.ExportedProperty protected int mPaddingBottom; + /** + * Briefly describes the view and is primarily used for accessibility support. + */ + private CharSequence mContentDescription; + /** * Cache the paddingRight set by the user to append to the scrollbar's size. */ @@ -1622,6 +1690,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { private int[] mDrawableState = null; private SoftReference mDrawingCache; + private SoftReference mUnscaledDrawingCache; /** * When this view has focus and the next focus is {@link #FOCUS_LEFT}, @@ -1701,7 +1770,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { public View(Context context) { mContext = context; mResources = context != null ? context.getResources() : null; - mViewFlags = SOUND_EFFECTS_ENABLED|HAPTIC_FEEDBACK_ENABLED; + mViewFlags = SOUND_EFFECTS_ENABLED | HAPTIC_FEEDBACK_ENABLED; ++sInstanceCount; } @@ -1762,7 +1831,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { int viewFlagMasks = 0; boolean setScrollContainer = false; - + int x = 0; int y = 0; @@ -1858,16 +1927,21 @@ public class View implements Drawable.Callback, KeyEvent.Callback { viewFlagMasks |= DRAWING_CACHE_QUALITY_MASK; } break; + case com.android.internal.R.styleable.View_contentDescription: + mContentDescription = a.getString(attr); + break; case com.android.internal.R.styleable.View_soundEffectsEnabled: if (!a.getBoolean(attr, true)) { viewFlagValues &= ~SOUND_EFFECTS_ENABLED; viewFlagMasks |= SOUND_EFFECTS_ENABLED; } + break; case com.android.internal.R.styleable.View_hapticFeedbackEnabled: if (!a.getBoolean(attr, true)) { viewFlagValues &= ~HAPTIC_FEEDBACK_ENABLED; viewFlagMasks |= HAPTIC_FEEDBACK_ENABLED; } + break; case R.styleable.View_scrollbars: final int scrollbars = a.getInt(attr, SCROLLBARS_NONE); if (scrollbars != SCROLLBARS_NONE) { @@ -1922,6 +1996,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback { mMinHeight = a.getDimensionPixelSize(attr, 0); break; case R.styleable.View_onClick: + if (context.isRestricted()) { + throw new IllegalStateException("The android:onClick attribute cannot " + + "be used within a restricted context"); + } + final String handlerName = a.getString(attr); if (handlerName != null) { setOnClickListener(new OnClickListener() { @@ -1990,7 +2069,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback { if (!setScrollContainer && (viewFlagValues&SCROLLBARS_VERTICAL) != 0) { setScrollContainer(true); } - + + computeOpaqueFlags(); + a.recycle(); } @@ -2255,6 +2336,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback { * otherwise is returned. */ public boolean performClick() { + sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED); + if (mOnClickListener != null) { playSoundEffect(SoundEffectConstants.CLICK); mOnClickListener.onClick(this); @@ -2272,6 +2355,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback { * otherwise is returned. */ public boolean performLongClick() { + sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED); + boolean handled = false; if (mOnLongClickListener != null) { handled = mOnLongClickListener.onLongClick(View.this); @@ -2387,7 +2472,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { if (!(parent instanceof View)) { break; } - + child = (View) parent; parent = child.getParent(); } @@ -2479,7 +2564,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { * and previouslyFocusedRect provide insight into where the focus is coming from. * When overriding, be sure to call up through to the super class so that * the standard focus handling will occur. - * + * * @param gainFocus True if the View has focus; false otherwise. * @param direction The direction focus has moved when requestFocus() * is called to give this view focus. Values are @@ -2492,6 +2577,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback { * from (in addition to direction). Will be null otherwise. */ protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) { + if (gainFocus) { + sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED); + } + InputMethodManager imm = InputMethodManager.peekInstance(); if (!gainFocus) { if (isPressed()) { @@ -2506,13 +2595,86 @@ public class View implements Drawable.Callback, KeyEvent.Callback { && mAttachInfo.mHasWindowFocus) { imm.focusIn(this); } - + invalidate(); if (mOnFocusChangeListener != null) { mOnFocusChangeListener.onFocusChange(this, gainFocus); } } + /** + * {@inheritDoc} + */ + public void sendAccessibilityEvent(int eventType) { + if (AccessibilityManager.getInstance(mContext).isEnabled()) { + sendAccessibilityEventUnchecked(AccessibilityEvent.obtain(eventType)); + } + } + + /** + * {@inheritDoc} + */ + public void sendAccessibilityEventUnchecked(AccessibilityEvent event) { + event.setClassName(getClass().getName()); + event.setPackageName(getContext().getPackageName()); + event.setEnabled(isEnabled()); + event.setContentDescription(mContentDescription); + + if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_FOCUSED && mAttachInfo != null) { + ArrayList focusablesTempList = mAttachInfo.mFocusablesTempList; + getRootView().addFocusables(focusablesTempList, View.FOCUS_FORWARD, FOCUSABLES_ALL); + event.setItemCount(focusablesTempList.size()); + event.setCurrentItemIndex(focusablesTempList.indexOf(this)); + focusablesTempList.clear(); + } + + dispatchPopulateAccessibilityEvent(event); + + AccessibilityManager.getInstance(mContext).sendAccessibilityEvent(event); + } + + /** + * Dispatches an {@link AccessibilityEvent} to the {@link View} children + * to be populated. + * + * @param event The event. + * + * @return True if the event population was completed. + */ + public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { + return false; + } + + /** + * Gets the {@link View} description. It briefly describes the view and is + * primarily used for accessibility support. Set this property to enable + * better accessibility support for your application. This is especially + * true for views that do not have textual representation (For example, + * ImageButton). + * + * @return The content descriptiopn. + * + * @attr ref android.R.styleable#View_contentDescription + */ + public CharSequence getContentDescription() { + return mContentDescription; + } + + /** + * Sets the {@link View} description. It briefly describes the view and is + * primarily used for accessibility support. Set this property to enable + * better accessibility support for your application. This is especially + * true for views that do not have textual representation (For example, + * ImageButton). + * + * @param contentDescription The content description. + * + * @attr ref android.R.styleable#View_contentDescription + */ + public void setContentDescription(CharSequence contentDescription) { + mContentDescription = contentDescription; + } + /** * Invoked whenever this view loses focus, either by losing window focus or by losing * focus within its window. This method can be used to clear any state tied to the @@ -2522,7 +2684,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { * Subclasses of View overriding this method should always call super.onFocusLost(). * * @see #onFocusChanged(boolean, int, android.graphics.Rect) - * @see #onWindowFocusChanged(boolean) + * @see #onWindowFocusChanged(boolean) * * @hide pending API council approval */ @@ -3222,11 +3384,37 @@ public class View implements Drawable.Callback, KeyEvent.Callback { * @param direction The direction of the focus */ public void addFocusables(ArrayList views, int direction) { - if (!isFocusable()) return; + addFocusables(views, direction, FOCUSABLES_TOUCH_MODE); + } - if (isInTouchMode() && !isFocusableInTouchMode()) return; + /** + * Adds any focusable views that are descendants of this view (possibly + * including this view if it is focusable itself) to views. This method + * adds all focusable views regardless if we are in touch mode or + * only views focusable in touch mode if we are in touch mode depending on + * the focusable mode paramater. + * + * @param views Focusable views found so far or null if all we are interested is + * the number of focusables. + * @param direction The direction of the focus. + * @param focusableMode The type of focusables to be added. + * + * @see #FOCUSABLES_ALL + * @see #FOCUSABLES_TOUCH_MODE + */ + public void addFocusables(ArrayList views, int direction, int focusableMode) { + if (!isFocusable()) { + return; + } - views.add(this); + if ((focusableMode & FOCUSABLES_TOUCH_MODE) == FOCUSABLES_TOUCH_MODE && + isInTouchMode() && !isFocusableInTouchMode()) { + return; + } + + if (views != null) { + views.add(this); + } } /** @@ -3398,14 +3586,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback { */ public void onStartTemporaryDetach() { } - + /** * Called after {@link #onStartTemporaryDetach} when the container is done * changing the view. */ public void onFinishTemporaryDetach() { } - + /** * capture information of this view for later analysis: developement only * check dynamic switch to make sure we only dump view @@ -3790,25 +3978,25 @@ public class View implements Drawable.Callback, KeyEvent.Callback { * a call on that method would return a non-null InputConnection, and * they are really a first-class editor that the user would normally * start typing on when the go into a window containing your view. - * + * *

        The default implementation always returns false. This does * not mean that its {@link #onCreateInputConnection(EditorInfo)} * will not be called or the user can not otherwise perform edits on your * view; it is just a hint to the system that this is not the primary * purpose of this view. - * + * * @return Returns true if this view is a text editor, else false. */ public boolean onCheckIsTextEditor() { return false; } - + /** * Create a new InputConnection for an InputMethod to interact * with the view. The default implementation returns null, since it doesn't * support input methods. You can override this to implement such support. * This is only needed for views that take focus and text input. - * + * *

        When implementing this, you probably also want to implement * {@link #onCheckIsTextEditor()} to indicate you will return a * non-null InputConnection. @@ -3832,7 +4020,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { public boolean checkInputConnectionProxy(View view) { return false; } - + /** * Show the context menu for this view. It is not safe to hold on to the * menu after returning from this method. @@ -4563,14 +4751,42 @@ public class View implements Drawable.Callback, KeyEvent.Callback { * whether an instance is opaque. Opaque Views are treated in a special way by * the View hierarchy, possibly allowing it to perform optimizations during * invalidate/draw passes. - * + * * @return True if this View is guaranteed to be fully opaque, false otherwise. * * @hide Pending API council approval */ @ViewDebug.ExportedProperty public boolean isOpaque() { - return mBGDrawable != null && mBGDrawable.getOpacity() == PixelFormat.OPAQUE; + return (mPrivateFlags & OPAQUE_MASK) == OPAQUE_MASK; + } + + private void computeOpaqueFlags() { + // Opaque if: + // - Has a background + // - Background is opaque + // - Doesn't have scrollbars or scrollbars are inside overlay + + if (mBGDrawable != null && mBGDrawable.getOpacity() == PixelFormat.OPAQUE) { + mPrivateFlags |= OPAQUE_BACKGROUND; + } else { + mPrivateFlags &= ~OPAQUE_BACKGROUND; + } + + final int flags = mViewFlags; + if (((flags & SCROLLBARS_VERTICAL) == 0 && (flags & SCROLLBARS_HORIZONTAL) == 0) || + (flags & SCROLLBARS_STYLE_MASK) == SCROLLBARS_INSIDE_OVERLAY) { + mPrivateFlags |= OPAQUE_SCROLLBARS; + } else { + mPrivateFlags &= ~OPAQUE_SCROLLBARS; + } + } + + /** + * @hide + */ + protected boolean hasOpaqueScrollbars() { + return (mPrivateFlags & OPAQUE_SCROLLBARS) == OPAQUE_SCROLLBARS; } /** @@ -4897,6 +5113,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { public void setHorizontalScrollBarEnabled(boolean horizontalScrollBarEnabled) { if (isHorizontalScrollBarEnabled() != horizontalScrollBarEnabled) { mViewFlags ^= SCROLLBARS_HORIZONTAL; + computeOpaqueFlags(); recomputePadding(); } } @@ -4926,6 +5143,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { public void setVerticalScrollBarEnabled(boolean verticalScrollBarEnabled) { if (isVerticalScrollBarEnabled() != verticalScrollBarEnabled) { mViewFlags ^= SCROLLBARS_VERTICAL; + computeOpaqueFlags(); recomputePadding(); } } @@ -4954,6 +5172,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { public void setScrollBarStyle(int style) { if (style != (mViewFlags & SCROLLBARS_STYLE_MASK)) { mViewFlags = (mViewFlags & ~SCROLLBARS_STYLE_MASK) | (style & SCROLLBARS_STYLE_MASK); + computeOpaqueFlags(); recomputePadding(); } } @@ -5132,9 +5351,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback { } } } - + /** - * Override this if the vertical scrollbar needs to be hidden in a subclass, like when + * 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 @@ -5569,6 +5788,17 @@ public class View implements Drawable.Callback, KeyEvent.Callback { return (mViewFlags & DRAWING_CACHE_ENABLED) == DRAWING_CACHE_ENABLED; } + /** + *

        Calling this method is equivalent to calling getDrawingCache(false).

        + * + * @return A non-scaled bitmap representing this view or null if cache is disabled. + * + * @see #getDrawingCache(boolean) + */ + public Bitmap getDrawingCache() { + return getDrawingCache(false); + } + /** *

        Returns the bitmap in which this view drawing is cached. The returned bitmap * is null when caching is disabled. If caching is enabled and the cache is not ready, @@ -5576,22 +5806,35 @@ public class View implements Drawable.Callback, KeyEvent.Callback { * draw from the cache when the cache is enabled. To benefit from the cache, you must * request the drawing cache by calling this method and draw it on screen if the * returned bitmap is not null.

        + * + *

        Note about auto scaling in compatibility mode: When auto scaling is not enabled, + * this method will create a bitmap of the same size as this view. Because this bitmap + * will be drawn scaled by the parent ViewGroup, the result on screen might show + * scaling artifacts. To avoid such artifacts, you should call this method by setting + * the auto scaling to true. Doing so, however, will generate a bitmap of a different + * size than the view. This implies that your application must be able to handle this + * size.

        + * + * @param autoScale Indicates whether the generated bitmap should be scaled based on + * the current density of the screen when the application is in compatibility + * mode. * - * @return a bitmap representing this view or null if cache is disabled - * + * @return A bitmap representing this view or null if cache is disabled. + * * @see #setDrawingCacheEnabled(boolean) * @see #isDrawingCacheEnabled() - * @see #buildDrawingCache() + * @see #buildDrawingCache(boolean) * @see #destroyDrawingCache() */ - public Bitmap getDrawingCache() { + public Bitmap getDrawingCache(boolean autoScale) { if ((mViewFlags & WILL_NOT_CACHE_DRAWING) == WILL_NOT_CACHE_DRAWING) { return null; } if ((mViewFlags & DRAWING_CACHE_ENABLED) == DRAWING_CACHE_ENABLED) { - buildDrawingCache(); + buildDrawingCache(autoScale); } - return mDrawingCache == null ? null : mDrawingCache.get(); + return autoScale ? (mDrawingCache == null ? null : mDrawingCache.get()) : + (mUnscaledDrawingCache == null ? null : mUnscaledDrawingCache.get()); } /** @@ -5610,6 +5853,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback { if (bitmap != null) bitmap.recycle(); mDrawingCache = null; } + if (mUnscaledDrawingCache != null) { + final Bitmap bitmap = mUnscaledDrawingCache.get(); + if (bitmap != null) bitmap.recycle(); + mUnscaledDrawingCache = null; + } } /** @@ -5636,19 +5884,37 @@ public class View implements Drawable.Callback, KeyEvent.Callback { return mDrawingCacheBackgroundColor; } + /** + *

        Calling this method is equivalent to calling buildDrawingCache(false).

        + * + * @see #buildDrawingCache(boolean) + */ + public void buildDrawingCache() { + buildDrawingCache(false); + } + /** *

        Forces the drawing cache to be built if the drawing cache is invalid.

        * *

        If you call {@link #buildDrawingCache()} manually without calling * {@link #setDrawingCacheEnabled(boolean) setDrawingCacheEnabled(true)}, you * should cleanup the cache by calling {@link #destroyDrawingCache()} afterwards.

        + * + *

        Note about auto scaling in compatibility mode: When auto scaling is not enabled, + * this method will create a bitmap of the same size as this view. Because this bitmap + * will be drawn scaled by the parent ViewGroup, the result on screen might show + * scaling artifacts. To avoid such artifacts, you should call this method by setting + * the auto scaling to true. Doing so, however, will generate a bitmap of a different + * size than the view. This implies that your application must be able to handle this + * size.

        * * @see #getDrawingCache() * @see #destroyDrawingCache() */ - public void buildDrawingCache() { - if ((mPrivateFlags & DRAWING_CACHE_VALID) == 0 || mDrawingCache == null || - mDrawingCache.get() == null) { + public void buildDrawingCache(boolean autoScale) { + if ((mPrivateFlags & DRAWING_CACHE_VALID) == 0 || (autoScale ? + (mDrawingCache == null || mDrawingCache.get() == null) : + (mUnscaledDrawingCache == null || mUnscaledDrawingCache.get() == null))) { if (ViewDebug.TRACE_HIERARCHY) { ViewDebug.trace(this, ViewDebug.HierarchyTraceType.BUILD_CACHE); @@ -5657,8 +5923,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback { EventLog.writeEvent(60002, hashCode()); } - final int width = mRight - mLeft; - final int height = mBottom - mTop; + int width = mRight - mLeft; + int height = mBottom - mTop; + + final AttachInfo attachInfo = mAttachInfo; + final boolean scalingRequired = attachInfo != null && attachInfo.mScalingRequired; + + if (autoScale && scalingRequired) { + width = (int) ((width * attachInfo.mApplicationScale) + 0.5f); + height = (int) ((height * attachInfo.mApplicationScale) + 0.5f); + } final int drawingCacheBackgroundColor = mDrawingCacheBackgroundColor; final boolean opaque = drawingCacheBackgroundColor != 0 || @@ -5672,7 +5946,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback { } boolean clear = true; - Bitmap bitmap = mDrawingCache == null ? null : mDrawingCache.get(); + Bitmap bitmap = autoScale ? (mDrawingCache == null ? null : mDrawingCache.get()) : + (mUnscaledDrawingCache == null ? null : mUnscaledDrawingCache.get()); if (bitmap == null || bitmap.getWidth() != width || bitmap.getHeight() != height) { @@ -5701,12 +5976,20 @@ public class View implements Drawable.Callback, KeyEvent.Callback { try { bitmap = Bitmap.createBitmap(width, height, quality); - mDrawingCache = new SoftReference(bitmap); + if (autoScale) { + mDrawingCache = new SoftReference(bitmap); + } else { + mUnscaledDrawingCache = new SoftReference(bitmap); + } } catch (OutOfMemoryError e) { // If there is not enough memory to create the bitmap cache, just // ignore the issue as bitmap caches are not required to draw the // view hierarchy - mDrawingCache = null; + if (autoScale) { + mDrawingCache = null; + } else { + mUnscaledDrawingCache = null; + } return; } @@ -5714,7 +5997,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback { } Canvas canvas; - final AttachInfo attachInfo = mAttachInfo; if (attachInfo != null) { canvas = attachInfo.mCanvas; if (canvas == null) { @@ -5737,15 +6019,22 @@ public class View implements Drawable.Callback, KeyEvent.Callback { computeScroll(); final int restoreCount = canvas.save(); + + if (autoScale && scalingRequired) { + final float scale = attachInfo.mApplicationScale; + canvas.scale(scale, scale); + } + canvas.translate(-mScrollX, -mScrollY); - mPrivateFlags = (mPrivateFlags & ~DIRTY_MASK) | DRAWN; + mPrivateFlags |= DRAWN; // Fast path for layouts with no backgrounds if ((mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) { if (ViewDebug.TRACE_HIERARCHY) { ViewDebug.trace(this, ViewDebug.HierarchyTraceType.DRAW); } + mPrivateFlags &= ~DIRTY_MASK; dispatchDraw(canvas); } else { draw(canvas); @@ -5792,7 +6081,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { canvas = new Canvas(bitmap); } - if ((backgroundColor&0xff000000) != 0) { + if ((backgroundColor & 0xff000000) != 0) { bitmap.eraseColor(backgroundColor); } @@ -5800,6 +6089,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback { final int restoreCount = canvas.save(); canvas.translate(-mScrollX, -mScrollY); + // Temporarily remove the dirty mask + int flags = mPrivateFlags; + mPrivateFlags &= ~DIRTY_MASK; + // Fast path for layouts with no backgrounds if ((mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) { dispatchDraw(canvas); @@ -5807,13 +6100,15 @@ public class View implements Drawable.Callback, KeyEvent.Callback { draw(canvas); } + mPrivateFlags = flags; + canvas.restoreToCount(restoreCount); if (attachInfo != null) { // Restore the cached Canvas for our siblings attachInfo.mCanvas = canvas; } - + return bitmap; } @@ -5927,8 +6222,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback { ViewDebug.trace(this, ViewDebug.HierarchyTraceType.DRAW); } - final boolean dirtyOpaque = (mPrivateFlags & DIRTY_MASK) == DIRTY_OPAQUE; - mPrivateFlags = (mPrivateFlags & ~DIRTY_MASK) | DRAWN; + final int privateFlags = mPrivateFlags; + final boolean dirtyOpaque = (privateFlags & DIRTY_MASK) == DIRTY_OPAQUE && + (mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState); + mPrivateFlags = (privateFlags & ~DIRTY_MASK) | DRAWN; /* * Draw traversal performs several drawing steps which must be executed @@ -6306,7 +6603,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { boolean changed = false; if (DBG) { - System.out.println(this + " View.setFrame(" + left + "," + top + "," + Log.d("View", this + " View.setFrame(" + left + "," + top + "," + right + "," + bottom + ")"); } @@ -6709,6 +7006,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback { requestLayout = true; } + computeOpaqueFlags(); + if (requestLayout) { requestLayout(); } @@ -6749,7 +7048,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { mUserPaddingBottom = bottom; final int viewFlags = mViewFlags; - + // Common case is there are no scroll bars. if ((viewFlags & (SCROLLBARS_VERTICAL|SCROLLBARS_HORIZONTAL)) != 0) { // TODO: Deal with RTL languages to adjust left padding instead of right. @@ -6762,7 +7061,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { ? 0 : getHorizontalScrollbarHeight(); } } - + if (mPaddingLeft != left) { changed = true; mPaddingLeft = left; @@ -6899,7 +7198,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { return v; } } - + View parent = this; while (parent.mParent != null && parent.mParent instanceof View) { @@ -6920,8 +7219,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback { getLocationInWindow(location); final AttachInfo info = mAttachInfo; - location[0] += info.mWindowLeft; - location[1] += info.mWindowTop; + if (info != null) { + location[0] += info.mWindowLeft; + location[1] += info.mWindowTop; + } } /** @@ -6947,7 +7248,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { location[1] += view.mTop - view.mScrollY; viewParent = view.mParent; } - + if (viewParent instanceof ViewRoot) { // *cough* final ViewRoot vr = (ViewRoot)viewParent; @@ -7098,7 +7399,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { * @return the Object stored in this view as a tag * * @see #setTag(int, Object) - * @see #getTag() + * @see #getTag() */ public Object getTag(int key) { SparseArray tags = null; @@ -7154,7 +7455,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { + "resource id."); } - setTagInternal(this, key, tag); + setTagInternal(this, key, tag); } private static void setTagInternal(View view, int key, Object tag) { @@ -7189,7 +7490,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { /** * Method that subclasses should implement to check their consistency. The type of * consistency check is indicated by the bit field passed as a parameter. - * + * * @param consistency The type of consistency. See ViewDebug for more information. * * @throws IllegalStateException if the view is in an inconsistent state. @@ -7744,7 +8045,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { /** * BZZZTT!!1! - * + * *

        Provide haptic feedback to the user for this view. * *

        The framework will provide haptic feedback for some built in actions, @@ -7763,7 +8064,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { /** * BZZZTT!!1! - * + * *

        Like {@link #performHapticFeedback(int)}, with additional options. * * @param feedbackConstant One of the constants defined in @@ -8158,7 +8459,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback { * window. */ static class AttachInfo { - interface Callbacks { void playSoundEffect(int effectId); boolean performHapticFeedback(int effectId, boolean always); @@ -8227,10 +8527,20 @@ public class View implements Drawable.Callback, KeyEvent.Callback { * The top view of the hierarchy. */ View mRootView; - + IBinder mPanelParentWindowToken; Surface mSurface; + /** + * Scale factor used by the compatibility mode + */ + float mApplicationScale; + + /** + * Indicates whether the application is in compatibility mode + */ + boolean mScalingRequired; + /** * Left position of this view's window */ @@ -8287,6 +8597,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback { */ long mDrawingTime; + /** + * Indicates whether or not ignoring the DIRTY_MASK flags. + */ + boolean mIgnoreDirtyState; + /** * Indicates whether the view's window is currently in touch mode. */ @@ -8365,7 +8680,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback { * calling up the hierarchy. */ final Rect mTmpInvalRect = new Rect(); - + + /** + * Temporary list for use in collecting focusable descendents of a view. + */ + final ArrayList mFocusablesTempList = new ArrayList(24); + /** * Creates a new set of attachment information with the specified * events handler and thread. @@ -8408,18 +8728,18 @@ public class View implements Drawable.Callback, KeyEvent.Callback { // use use a height of 1, and then wack the matrix each time we // actually use it. shader = new LinearGradient(0, 0, 0, 1, 0xFF000000, 0, Shader.TileMode.CLAMP); - + paint.setShader(shader); paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT)); } - + public void setFadeColor(int color) { if (color != 0 && color != mLastColor) { mLastColor = color; color |= 0xFF000000; - + shader = new LinearGradient(0, 0, 0, 1, color, 0, Shader.TileMode.CLAMP); - + paint.setShader(shader); // Restore the default transfer mode (src_over) paint.setXfermode(null); diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java index 8e1524bd839d817af579bb05b23a6a55871298c9..0e36ec296d9c9692f564fd8ae5fc5bd194b23f16 100644 --- a/core/java/android/view/ViewConfiguration.java +++ b/core/java/android/view/ViewConfiguration.java @@ -106,6 +106,11 @@ public class ViewConfiguration { * Minimum velocity to initiate a fling, as measured in pixels per second */ private static final int MINIMUM_FLING_VELOCITY = 50; + + /** + * Maximum velocity to initiate a fling, as measured in pixels per second + */ + private static final int MAXIMUM_FLING_VELOCITY = 4000; /** * The maximum size of View's drawing cache, expressed in bytes. This size @@ -122,6 +127,7 @@ public class ViewConfiguration { private final int mEdgeSlop; private final int mFadingEdgeLength; private final int mMinimumFlingVelocity; + private final int mMaximumFlingVelocity; private final int mScrollbarSize; private final int mTouchSlop; private final int mDoubleTapSlop; @@ -139,6 +145,7 @@ public class ViewConfiguration { mEdgeSlop = EDGE_SLOP; mFadingEdgeLength = FADING_EDGE_LENGTH; mMinimumFlingVelocity = MINIMUM_FLING_VELOCITY; + mMaximumFlingVelocity = MAXIMUM_FLING_VELOCITY; mScrollbarSize = SCROLL_BAR_SIZE; mTouchSlop = TOUCH_SLOP; mDoubleTapSlop = DOUBLE_TAP_SLOP; @@ -164,6 +171,7 @@ public class ViewConfiguration { mEdgeSlop = (int) (density * EDGE_SLOP + 0.5f); mFadingEdgeLength = (int) (density * FADING_EDGE_LENGTH + 0.5f); mMinimumFlingVelocity = (int) (density * MINIMUM_FLING_VELOCITY + 0.5f); + mMaximumFlingVelocity = (int) (density * MAXIMUM_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); @@ -366,6 +374,23 @@ public class ViewConfiguration { return mMinimumFlingVelocity; } + /** + * @return Maximum velocity to initiate a fling, as measured in pixels per second. + * + * @deprecated Use {@link #getScaledMaximumFlingVelocity()} instead. + */ + @Deprecated + public static int getMaximumFlingVelocity() { + return MAXIMUM_FLING_VELOCITY; + } + + /** + * @return Maximum velocity to initiate a fling, as measured in pixels per second. + */ + public int getScaledMaximumFlingVelocity() { + return mMaximumFlingVelocity; + } + /** * The maximum drawing cache size expressed in bytes. * diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java index 74a248f65834dbf0a6360b8faa604e8bedd44a63..46aea022912be1a7621a151da8cf0742230b0dc5 100644 --- a/core/java/android/view/ViewDebug.java +++ b/core/java/android/view/ViewDebug.java @@ -87,17 +87,17 @@ public class ViewDebug { * check that this value is set to true as not to affect performance. */ public static final boolean TRACE_RECYCLER = false; - + /** * The system property of dynamic switch for capturing view information * when it is set, we dump interested fields and methods for the view on focus - */ + */ static final String SYSTEM_PROPERTY_CAPTURE_VIEW = "debug.captureview"; - + /** * The system property of dynamic switch for capturing event information * when it is set, we log key events, touch/motion and trackball events - */ + */ static final String SYSTEM_PROPERTY_CAPTURE_EVENT = "debug.captureevent"; /** @@ -216,7 +216,7 @@ public class ViewDebug { *

                  *
                  * A specified String is output when the following is true:
        -         * 
        +         *
                  * @return An array of int to String mappings
                  */
                 FlagToString[] flagMapping() default { };
        @@ -228,7 +228,7 @@ public class ViewDebug {
                  *
                  * @return true if the properties of this property should be dumped
                  *
        -         * @see #prefix() 
        +         * @see #prefix()
                  */
                 boolean deepExport() default false;
         
        @@ -313,15 +313,15 @@ public class ViewDebug {
             @Retention(RetentionPolicy.RUNTIME)
             public @interface CapturedViewProperty {
                 /**
        -         * When retrieveReturn is true, we need to retrieve second level methods 
        +         * When retrieveReturn is true, we need to retrieve second level methods
                  * e.g., we need myView.getFirstLevelMethod().getSecondLevelMethod()
        -         * we will set retrieveReturn = true on the annotation of 
        +         * we will set retrieveReturn = true on the annotation of
                  * myView.getFirstLevelMethod()
        -         * @return true if we need the second level methods 
        +         * @return true if we need the second level methods
                  */
        -        boolean retrieveReturn() default false;        
        +        boolean retrieveReturn() default false;
             }
        -        
        +
             private static HashMap, Method[]> mCapturedViewMethodsForClasses = null;
             private static HashMap, Field[]> mCapturedViewFieldsForClasses = null;
         
        @@ -401,7 +401,7 @@ public class ViewDebug {
              */
             public static long getViewRootInstanceCount() {
                 return ViewRoot.getInstanceCount();
        -    }    
        +    }
         
             /**
              * Outputs a trace to the currently opened recycler traces. The trace records the type of
        @@ -624,7 +624,7 @@ public class ViewDebug {
              *
              * This method will return immediately if TRACE_HIERARCHY is false.
              *
        -     * @see #startHierarchyTracing(String, View) 
        +     * @see #startHierarchyTracing(String, View)
              * @see #trace(View, android.view.ViewDebug.HierarchyTraceType)
              */
             public static void stopHierarchyTracing() {
        @@ -671,7 +671,7 @@ public class ViewDebug {
         
                 sHierarhcyRoot = null;
             }
        -    
        +
             static void dispatchCommand(View view, String command, String parameters,
                     OutputStream clientStream) throws IOException {
         
        @@ -1039,10 +1039,10 @@ public class ViewDebug {
         
                 final ArrayList foundMethods = new ArrayList();
                 methods = klass.getDeclaredMethods();
        -        
        +
                 int count = methods.length;
                 for (int i = 0; i < count; i++) {
        -            final Method method = methods[i];            
        +            final Method method = methods[i];
                     if (method.getParameterTypes().length == 0 &&
                             method.isAnnotationPresent(ExportedProperty.class) &&
                             method.getReturnType() != Void.class) {
        @@ -1075,7 +1075,7 @@ public class ViewDebug {
                     klass = klass.getSuperclass();
                 } while (klass != Object.class);
             }
        -    
        +
             private static void exportMethods(Context context, Object view, BufferedWriter out,
                     Class klass, String prefix) throws IOException {
         
        @@ -1235,10 +1235,11 @@ public class ViewDebug {
                 for (int j = 0; j < count; j++) {
                     final FlagToString flagMapping = mapping[j];
                     final boolean ifTrue = flagMapping.outputIf();
        -            final boolean test = (intValue & flagMapping.mask()) == flagMapping.equals();
        +            final int maskResult = intValue & flagMapping.mask();
        +            final boolean test = maskResult == flagMapping.equals();
                     if ((test && ifTrue) || (!test && !ifTrue)) {
                         final String name = flagMapping.name();
        -                final String value = ifTrue ? "true" : "false";
        +                final String value = "0x" + Integer.toHexString(maskResult);
                         writeEntry(out, prefix, name, "", value);
                     }
                 }
        @@ -1259,7 +1260,7 @@ public class ViewDebug {
         
                 for (int j = 0; j < valuesCount; j++) {
                     String name;
        -            String value;
        +            String value = null;
         
                     final int intValue = array[j];
         
        @@ -1275,7 +1276,6 @@ public class ViewDebug {
                         }
                     }
         
        -            value = String.valueOf(intValue);
                     if (hasMapping) {
                         int mappingCount = mapping.length;
                         for (int k = 0; k < mappingCount; k++) {
        @@ -1288,7 +1288,9 @@ public class ViewDebug {
                     }
         
                     if (resolveId) {
        -                value = (String) resolveId(context, intValue);
        +                if (value == null) value = (String) resolveId(context, intValue);
        +            } else {
        +                value = String.valueOf(intValue);
                     }
         
                     writeEntry(out, prefix, name, suffix, value);
        @@ -1396,10 +1398,10 @@ public class ViewDebug {
         
                 final ArrayList foundMethods = new ArrayList();
                 methods = klass.getMethods();
        -        
        +
                 int count = methods.length;
                 for (int i = 0; i < count; i++) {
        -            final Method method = methods[i];            
        +            final Method method = methods[i];
                     if (method.getParameterTypes().length == 0 &&
                             method.isAnnotationPresent(CapturedViewProperty.class) &&
                             method.getReturnType() != Void.class) {
        @@ -1413,14 +1415,14 @@ public class ViewDebug {
         
                 return methods;
             }
        -              
        -    private static String capturedViewExportMethods(Object obj, Class klass, 
        +
        +    private static String capturedViewExportMethods(Object obj, Class klass,
                     String prefix) {
         
                 if (obj == null) {
                     return "null";
                 }
        -        
        +
                 StringBuilder sb = new StringBuilder();
                 final Method[] methods = capturedViewGetPropertyMethods(klass);
         
        @@ -1430,41 +1432,41 @@ public class ViewDebug {
                     try {
                         Object methodValue = method.invoke(obj, (Object[]) null);
                         final Class returnType = method.getReturnType();
        -                
        +
                         CapturedViewProperty property = method.getAnnotation(CapturedViewProperty.class);
                         if (property.retrieveReturn()) {
                             //we are interested in the second level data only
                             sb.append(capturedViewExportMethods(methodValue, returnType, method.getName() + "#"));
        -                } else {                    
        +                } else {
                             sb.append(prefix);
                             sb.append(method.getName());
                             sb.append("()=");
        -                    
        +
                             if (methodValue != null) {
        -                        final String value = methodValue.toString().replace("\n", "\\n");                        
        -                        sb.append(value);                        
        +                        final String value = methodValue.toString().replace("\n", "\\n");
        +                        sb.append(value);
                             } else {
                                 sb.append("null");
                             }
                             sb.append("; ");
                         }
                       } catch (IllegalAccessException e) {
        -                  //Exception IllegalAccess, it is OK here 
        +                  //Exception IllegalAccess, it is OK here
                           //we simply ignore this method
                       } catch (InvocationTargetException e) {
        -                  //Exception InvocationTarget, it is OK here 
        +                  //Exception InvocationTarget, it is OK here
                           //we simply ignore this method
        -              }              
        -        }        
        +              }
        +        }
                 return sb.toString();
             }
         
             private static String capturedViewExportFields(Object obj, Class klass, String prefix) {
        -        
        +
                 if (obj == null) {
                     return "null";
                 }
        -        
        +
                 StringBuilder sb = new StringBuilder();
                 final Field[] fields = capturedViewGetPropertyFields(klass);
         
        @@ -1486,25 +1488,25 @@ public class ViewDebug {
                         }
                         sb.append(' ');
                     } catch (IllegalAccessException e) {
        -                //Exception IllegalAccess, it is OK here 
        +                //Exception IllegalAccess, it is OK here
                         //we simply ignore this field
                     }
                 }
                 return sb.toString();
             }
        -    
        +
             /**
        -     * Dump view info for id based instrument test generation 
        +     * Dump view info for id based instrument test generation
              * (and possibly further data analysis). The results are dumped
        -     * to the log. 
        +     * to the log.
              * @param tag for log
              * @param view for dump
              */
        -    public static void dumpCapturedView(String tag, Object view) {        
        +    public static void dumpCapturedView(String tag, Object view) {
                 Class klass = view.getClass();
                 StringBuilder sb = new StringBuilder(klass.getName() + ": ");
                 sb.append(capturedViewExportFields(view, klass, ""));
        -        sb.append(capturedViewExportMethods(view, klass, ""));        
        -        Log.d(tag, sb.toString());        
        +        sb.append(capturedViewExportMethods(view, klass, ""));
        +        Log.d(tag, sb.toString());
             }
         }
        diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
        index 26fe776ba3506f781c2d593059d41d0f38a6e857..f7b7f029df82e4e89429eb421b3531518f400ae2 100644
        --- a/core/java/android/view/ViewGroup.java
        +++ b/core/java/android/view/ViewGroup.java
        @@ -24,15 +24,16 @@ import android.graphics.Bitmap;
         import android.graphics.Canvas;
         import android.graphics.Paint;
         import android.graphics.Rect;
        -import android.graphics.Region;
         import android.graphics.RectF;
        +import android.graphics.Region;
         import android.os.Parcelable;
         import android.os.SystemClock;
         import android.util.AttributeSet;
        +import android.util.Config;
         import android.util.EventLog;
         import android.util.Log;
         import android.util.SparseArray;
        -import android.util.Config;
        +import android.view.accessibility.AccessibilityEvent;
         import android.view.animation.Animation;
         import android.view.animation.AnimationUtils;
         import android.view.animation.LayoutAnimationController;
        @@ -52,6 +53,15 @@ import java.util.ArrayList;
          * 

        * Also see {@link LayoutParams} for layout attributes. *

        + * + * @attr ref android.R.styleable#ViewGroup_clipChildren + * @attr ref android.R.styleable#ViewGroup_clipToPadding + * @attr ref android.R.styleable#ViewGroup_layoutAnimation + * @attr ref android.R.styleable#ViewGroup_animationCache + * @attr ref android.R.styleable#ViewGroup_persistentDrawingCache + * @attr ref android.R.styleable#ViewGroup_alwaysDrawnWithCache + * @attr ref android.R.styleable#ViewGroup_addStatesFromChildren + * @attr ref android.R.styleable#ViewGroup_descendantFocusability */ public abstract class ViewGroup extends View implements ViewParent, ViewManager { private static final boolean DBG = false; @@ -89,7 +99,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager /** * Internal flags. - * + * * This field should be made private, so it is hidden from the SDK. * {@hide} */ @@ -142,7 +152,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * to get the index of the child to draw for that iteration. */ protected static final int FLAG_USE_CHILD_DRAWING_ORDER = 0x400; - + /** * When set, this ViewGroup supports static transformations on children; this causes * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} to be @@ -151,7 +161,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * Any subclass overriding * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} should * set this flags in {@link #mGroupFlags}. - * + * * {@hide} */ protected static final int FLAG_SUPPORT_STATIC_TRANSFORMATIONS = 0x800; @@ -212,7 +222,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * When set, this ViewGroup should not intercept touch events. */ private static final int FLAG_DISALLOW_INTERCEPT = 0x80000; - + /** * Indicates which types of drawing caches are to be kept in memory. * This field should be made private, so it is hidden from the SDK. @@ -601,6 +611,14 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager */ @Override public void addFocusables(ArrayList views, int direction) { + addFocusables(views, direction, FOCUSABLES_TOUCH_MODE); + } + + /** + * {@inheritDoc} + */ + @Override + public void addFocusables(ArrayList views, int direction, int focusableMode) { final int focusableCount = views.size(); final int descendantFocusability = getDescendantFocusability(); @@ -612,7 +630,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager for (int i = 0; i < count; i++) { final View child = children[i]; if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) { - child.addFocusables(views, direction); + child.addFocusables(views, direction, focusableMode); } } } @@ -625,7 +643,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager descendantFocusability != FOCUS_AFTER_DESCENDANTS || // No focusable descendants (focusableCount == views.size())) { - super.addFocusables(views, direction); + super.addFocusables(views, direction, focusableMode); } } @@ -680,7 +698,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager ViewParent parent = mParent; if (parent != null) parent.recomputeViewAttributes(this); } - + @Override void dispatchCollectViewAttributes(int visibility) { visibility |= mViewFlags&VISIBILITY_MASK; @@ -812,16 +830,16 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } } } - + boolean isUpOrCancel = (action == MotionEvent.ACTION_UP) || - (action == MotionEvent.ACTION_CANCEL); + (action == MotionEvent.ACTION_CANCEL); if (isUpOrCancel) { // Note, we've already copied the previous state to our local // variable, so this takes effect on the next event mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT; } - + // The event wasn't an ACTION_DOWN, dispatch it to our target if // we have one. final View target = mMotionTarget; @@ -868,18 +886,18 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * {@inheritDoc} */ public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) { - + if (disallowIntercept == ((mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0)) { // We're already in this state, assume our ancestors are too return; } - + if (disallowIntercept) { mGroupFlags |= FLAG_DISALLOW_INTERCEPT; } else { mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT; } - + // Pass it up to our parent if (mParent != null) { mParent.requestDisallowInterceptTouchEvent(disallowIntercept); @@ -1020,6 +1038,15 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } } + @Override + public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { + boolean populated = false; + for (int i = 0, count = getChildCount(); i < count; i++) { + populated |= getChildAt(i).dispatchPopulateAccessibilityEvent(event); + } + return populated; + } + /** * {@inheritDoc} */ @@ -1139,7 +1166,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager final View child = children[i]; if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) { child.setDrawingCacheEnabled(true); - child.buildDrawingCache(); + child.buildDrawingCache(true); } } @@ -1181,7 +1208,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager bindLayoutAnimation(child); if (cache) { child.setDrawingCacheEnabled(true); - child.buildDrawingCache(); + child.buildDrawingCache(true); } } } @@ -1274,7 +1301,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager post(end); } } - + /** * Returns the index of the child to draw for this iteration. Override this * if you want to change the drawing order of children. By default, it @@ -1282,14 +1309,14 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager *

        * NOTE: In order for this method to be called, the * {@link #FLAG_USE_CHILD_DRAWING_ORDER} must be set. - * + * * @param i The current iteration. * @return The index of the child to draw this iteration. */ protected int getChildDrawingOrder(int childCount, int i) { return i; } - + private void notifyAnimationListener() { mGroupFlags &= ~FLAG_NOTIFY_ANIMATION_LISTENER; mGroupFlags |= FLAG_ANIMATION_DONE; @@ -1403,9 +1430,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } } - // Clear the flag as early as possible to allow draw() implementations + // Sets the flag as early as possible to allow draw() implementations // to call invalidate() successfully when doing animations - child.mPrivateFlags = (child.mPrivateFlags & ~DIRTY_MASK) | DRAWN; + child.mPrivateFlags |= DRAWN; if (!concatMatrix && canvas.quickReject(cl, ct, cr, cb, Canvas.EdgeType.BW) && (child.mPrivateFlags & DRAW_ANIMATION) == 0) { @@ -1417,10 +1444,12 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager final int sx = child.mScrollX; final int sy = child.mScrollY; + boolean scalingRequired = false; Bitmap cache = null; if ((flags & FLAG_CHILDREN_DRAWN_WITH_CACHE) == FLAG_CHILDREN_DRAWN_WITH_CACHE || (flags & FLAG_ALWAYS_DRAWN_WITH_CACHE) == FLAG_ALWAYS_DRAWN_WITH_CACHE) { - cache = child.getDrawingCache(); + cache = child.getDrawingCache(true); + if (mAttachInfo != null) scalingRequired = mAttachInfo.mScalingRequired; } final boolean hasNoCache = cache == null; @@ -1430,6 +1459,11 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager canvas.translate(cl - sx, ct - sy); } else { canvas.translate(cl, ct); + if (scalingRequired) { + // mAttachInfo cannot be null, otherwise scalingRequired == false + final float scale = 1.0f / mAttachInfo.mApplicationScale; + canvas.scale(scale, scale); + } } float alpha = 1.0f; @@ -1472,7 +1506,11 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager if (hasNoCache) { canvas.clipRect(sx, sy, sx + (cr - cl), sy + (cb - ct)); } else { - canvas.clipRect(0, 0, cr - cl, cb - ct); + if (!scalingRequired) { + canvas.clipRect(0, 0, cr - cl, cb - ct); + } else { + canvas.clipRect(0, 0, cache.getWidth(), cache.getHeight()); + } } } @@ -1482,6 +1520,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager if (ViewDebug.TRACE_HIERARCHY) { ViewDebug.trace(this, ViewDebug.HierarchyTraceType.DRAW); } + child.mPrivateFlags &= ~DIRTY_MASK; child.dispatchDraw(canvas); } else { child.draw(canvas); @@ -1546,7 +1585,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager children[i].setSelected(selected); } } - + @Override protected void dispatchSetPressed(boolean pressed) { final View[] children = mChildren; @@ -1577,7 +1616,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager /** * {@inheritDoc} * - * @see #setStaticTransformationsEnabled(boolean) + * @see #setStaticTransformationsEnabled(boolean) */ protected boolean getChildStaticTransformation(View child, Transformation t) { return false; @@ -1844,10 +1883,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager if (child.hasFocus()) { requestChildFocus(child, child.findFocus()); } - + AttachInfo ai = mAttachInfo; if (ai != null) { - boolean lastKeepOn = ai.mKeepScreenOn; + boolean lastKeepOn = ai.mKeepScreenOn; ai.mKeepScreenOn = false; child.dispatchAttachedToWindow(mAttachInfo, (mViewFlags&VISIBILITY_MASK)); if (ai.mKeepScreenOn) { @@ -2047,7 +2086,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } needGlobalAttributesUpdate(false); - + removeFromArray(index); if (clearChildFocus) { @@ -2080,7 +2119,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } needGlobalAttributesUpdate(false); - + if (notifyListener) { onHierarchyChangeListener.onChildViewRemoved(this, view); } @@ -2128,7 +2167,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager View clearChildFocus = null; needGlobalAttributesUpdate(false); - + for (int i = count - 1; i >= 0; i--) { final View view = children[i]; @@ -2173,7 +2212,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager if (child == mFocused) { child.clearFocus(); } - + if (animate && child.getAnimation() != null) { addDisappearingView(child); } else if (child.mAttachInfo != null) { @@ -2323,7 +2362,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager final boolean drawAnimation = (child.mPrivateFlags & DRAW_ANIMATION) == DRAW_ANIMATION; // Check whether the child that requests the invalidate is fully opaque - final boolean isOpaque = child.isOpaque(); + final boolean isOpaque = child.isOpaque() && !drawAnimation && + child.getAnimation() != null; // Mark the child as dirty, using the appropriate flag // Make sure we do not set both flags at the same time final int opaqueFlag = isOpaque ? DIRTY_OPAQUE : DIRTY; @@ -3135,7 +3175,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } } } - + @Override protected boolean fitSystemWindows(Rect insets) { @@ -3269,7 +3309,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * laid out. See * {@link android.R.styleable#ViewGroup_Layout ViewGroup Layout Attributes} * for a list of all child view attributes that this class supports. - * + * *

        * 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: @@ -3400,7 +3440,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * @param output the String to prepend to the internal representation * @return a String with the following format: output + * "ViewGroup.LayoutParams={ width=WIDTH, height=HEIGHT }" - * + * * @hide */ public String debug(String output) { @@ -3413,7 +3453,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * * @param size the size to convert * @return a String instance representing the supplied size - * + * * @hide */ protected static String sizeToString(int size) { diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java index 5090c56b5edcbba0274e6e784a1d3592633db814..6f6e22477d2575020c0746e53a7b5c82f6824b98 100644 --- a/core/java/android/view/ViewRoot.java +++ b/core/java/android/view/ViewRoot.java @@ -30,14 +30,18 @@ import android.os.Process; import android.os.SystemProperties; import android.util.AndroidRuntimeException; import android.util.Config; +import android.util.DisplayMetrics; import android.util.Log; import android.util.EventLog; import android.util.SparseArray; import android.view.View.MeasureSpec; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityManager; import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputMethodManager; import android.widget.Scroller; import android.content.pm.PackageManager; +import android.content.res.CompatibilityInfo; import android.content.Context; import android.app.ActivityManagerNative; import android.Manifest; @@ -90,18 +94,18 @@ public final class ViewRoot extends Handler implements ViewParent, static final ThreadLocal sRunQueues = new ThreadLocal(); - private static int sDrawTime; + private static int sDrawTime; long mLastTrackballTime = 0; final TrackballAxis mTrackballAxisX = new TrackballAxis(); final TrackballAxis mTrackballAxisY = new TrackballAxis(); final int[] mTmpLocation = new int[2]; - + final InputMethodCallback mInputMethodCallback; final SparseArray mPendingEvents = new SparseArray(); int mPendingEventSeq = 0; - + final Thread mThread; final WindowLeaked mLocation; @@ -123,16 +127,13 @@ public final class ViewRoot extends Handler implements ViewParent, int mHeight; Rect mDirty; // will be a graphics.Region soon boolean mIsAnimating; - // TODO: change these to scalar class. - private float mAppScale; - private float mAppScaleInverted; // = 1.0f / mAppScale - private int[] mWindowLayoutParamsBackup = null; + + CompatibilityInfo.Translator mTranslator; final View.AttachInfo mAttachInfo; final Rect mTempRect; // used in the transaction to not thrash the heap. final Rect mVisRect; // used to retrieve visible rect of focused view. - final Point mVisPoint; // used to retrieve global offset of focused view. boolean mTraversalScheduled; boolean mWillDrawSoon; @@ -168,7 +169,7 @@ public final class ViewRoot extends Handler implements ViewParent, int mScrollY; int mCurScrollY; Scroller mScroller; - + EGL10 mEgl; EGLDisplay mEglDisplay; EGLContext mEglContext; @@ -178,7 +179,7 @@ public final class ViewRoot extends Handler implements ViewParent, boolean mUseGL; boolean mGlWanted; - final ViewConfiguration mViewConfiguration; + final ViewConfiguration mViewConfiguration; /** * see {@link #playSoundEffect(int)} @@ -216,7 +217,6 @@ public final class ViewRoot extends Handler implements ViewParent, mDirty = new Rect(); mTempRect = new Rect(); mVisRect = new Rect(); - mVisPoint = new Point(); mWinFrame = new Rect(); mWindow = new W(this, context); mInputMethodCallback = new InputMethodCallback(this); @@ -384,29 +384,39 @@ public final class ViewRoot extends Handler implements ViewParent, synchronized (this) { if (mView == null) { mView = view; - mAppScale = mView.getContext().getApplicationScale(); - if (mAppScale != 1.0f) { - mWindowLayoutParamsBackup = new int[4]; - } - mAppScaleInverted = 1.0f / mAppScale; mWindowAttributes.copyFrom(attrs); + + CompatibilityInfo compatibilityInfo = + mView.getContext().getResources().getCompatibilityInfo(); + mTranslator = compatibilityInfo.getTranslator(attrs); + boolean restore = false; + if (attrs != null && mTranslator != null) { + restore = true; + attrs.backup(); + mTranslator.translateWindowLayout(attrs); + } + if (DEBUG_LAYOUT) Log.d(TAG, "WindowLayout in setView:" + attrs); + mSoftInputMode = attrs.softInputMode; mWindowAttributesChanged = true; mAttachInfo.mRootView = view; + mAttachInfo.mScalingRequired = + mTranslator == null ? false : mTranslator.scalingRequired; + mAttachInfo.mApplicationScale = + mTranslator == null ? 1.0f : mTranslator.applicationScale; if (panelParentView != null) { mAttachInfo.mPanelParentWindowToken = panelParentView.getApplicationWindowToken(); } mAdded = true; int res; /* = WindowManagerImpl.ADD_OKAY; */ - + // Schedule the first layout -before- adding to the window // manager, to make sure we do the relayout before receiving // any other events from the system. requestLayout(); - try { - res = sWindowSession.add(mWindow, attrs, + res = sWindowSession.add(mWindow, mWindowAttributes, getHostVisibility(), mAttachInfo.mContentInsets); } catch (RemoteException e) { mAdded = false; @@ -414,8 +424,15 @@ public final class ViewRoot extends Handler implements ViewParent, mAttachInfo.mRootView = null; unscheduleTraversals(); throw new RuntimeException("Adding window failed", e); + } finally { + if (restore) { + attrs.restore(); + } + } + + if (mTranslator != null) { + mTranslator.translateRectInScreenToAppWindow(mAttachInfo.mContentInsets); } - mAttachInfo.mContentInsets.scale(mAppScaleInverted); mPendingContentInsets.set(mAttachInfo.mContentInsets); mPendingVisibleInsets.set(0, 0, 0, 0); if (Config.LOGV) Log.v("ViewRoot", "Added window " + mWindow); @@ -526,18 +543,20 @@ public final class ViewRoot extends Handler implements ViewParent, public void invalidateChild(View child, Rect dirty) { checkThread(); - if (LOCAL_LOGV) Log.v(TAG, "Invalidate child: " + dirty); - if (mCurScrollY != 0 || mAppScale != 1.0f) { + if (DEBUG_DRAW) Log.v(TAG, "Invalidate child: " + dirty); + if (mCurScrollY != 0 || mTranslator != null) { mTempRect.set(dirty); + dirty = mTempRect; if (mCurScrollY != 0) { - mTempRect.offset(0, -mCurScrollY); + dirty.offset(0, -mCurScrollY); } - if (mAppScale != 1.0f) { - mTempRect.scale(mAppScale); + if (mTranslator != null) { + mTranslator.translateRectInAppWindowToScreen(dirty); + } + if (mAttachInfo.mScalingRequired) { + dirty.inset(-1, -1); } - dirty = mTempRect; } - // TODO: When doing a union with mDirty != empty, we must cancel all the DIRTY_OPAQUE flags mDirty.union(dirty); if (!mWillDrawSoon) { scheduleTraversals(); @@ -553,7 +572,7 @@ public final class ViewRoot extends Handler implements ViewParent, return null; } - public boolean getChildVisibleRect(View child, Rect r, android.graphics.Point offset) { + public boolean getChildVisibleRect(View child, Rect r, android.graphics.Point offset) { if (child != mView) { throw new RuntimeException("child is not mine, honest!"); } @@ -582,7 +601,7 @@ public final class ViewRoot extends Handler implements ViewParent, int getHostVisibility() { return mAppVisible ? mView.getVisibility() : View.GONE; } - + private void performTraversals() { // cache mView since it is used so much below... final View host = mView; @@ -614,19 +633,22 @@ public final class ViewRoot extends Handler implements ViewParent, boolean viewVisibilityChanged = mViewVisibility != viewVisibility || mNewSurfaceNeeded; + float appScale = mAttachInfo.mApplicationScale; + WindowManager.LayoutParams params = null; if (mWindowAttributesChanged) { mWindowAttributesChanged = false; params = lp; } - + Rect frame = mWinFrame; if (mFirst) { fullRedrawNeeded = true; mLayoutRequested = true; - Display d = new Display(0); - desiredWindowWidth = (int) (d.getWidth() * mAppScaleInverted); - desiredWindowHeight = (int) (d.getHeight() * mAppScaleInverted); + DisplayMetrics packageMetrics = + mView.getContext().getResources().getDisplayMetrics(); + desiredWindowWidth = packageMetrics.widthPixels; + desiredWindowHeight = packageMetrics.heightPixels; // For the very first time, tell the view hierarchy that it // is attached to the window. Note that at this point the surface @@ -641,12 +663,13 @@ public final class ViewRoot extends Handler implements ViewParent, host.dispatchAttachedToWindow(attachInfo, 0); getRunQueue().executeActions(attachInfo.mHandler); //Log.i(TAG, "Screen on initialized: " + attachInfo.mKeepScreenOn); + } else { - desiredWindowWidth = mWinFrame.width(); - desiredWindowHeight = mWinFrame.height(); + desiredWindowWidth = frame.width(); + desiredWindowHeight = frame.height(); if (desiredWindowWidth != mWidth || desiredWindowHeight != mHeight) { if (DEBUG_ORIENTATION) Log.v("ViewRoot", - "View " + host + " resized to: " + mWinFrame); + "View " + host + " resized to: " + frame); fullRedrawNeeded = true; mLayoutRequested = true; windowResizesToFitContent = true; @@ -669,7 +692,7 @@ public final class ViewRoot extends Handler implements ViewParent, } boolean insetsChanged = false; - + if (mLayoutRequested) { if (mFirst) { host.fitSystemWindows(mAttachInfo.mContentInsets); @@ -694,9 +717,10 @@ public final class ViewRoot extends Handler implements ViewParent, || lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) { windowResizesToFitContent = true; - Display d = new Display(0); - desiredWindowWidth = (int) (d.getWidth() * mAppScaleInverted); - desiredWindowHeight = (int) (d.getHeight() * mAppScaleInverted); + DisplayMetrics packageMetrics = + mView.getContext().getResources().getDisplayMetrics(); + desiredWindowWidth = packageMetrics.widthPixels; + desiredWindowHeight = packageMetrics.heightPixels; } } @@ -753,7 +777,7 @@ public final class ViewRoot extends Handler implements ViewParent, } } } - + if (params != null && (host.mPrivateFlags & View.REQUEST_TRANSPARENT_REGIONS) != 0) { if (!PixelFormat.formatHasAlpha(params.format)) { params.format = PixelFormat.TRANSLUCENT; @@ -782,7 +806,7 @@ public final class ViewRoot extends Handler implements ViewParent, // computed insets. insetsPending = computesInternalInsets && (mFirst || viewVisibilityChanged); - + if (mWindowAttributes.memoryType == WindowManager.LayoutParams.MEMORY_TYPE_GPU) { if (params == null) { params = mWindowAttributes; @@ -791,7 +815,6 @@ public final class ViewRoot extends Handler implements ViewParent, } } - final Rect frame = mWinFrame; boolean initialized = false; boolean contentInsetsChanged = false; boolean visibleInsetsChanged; @@ -818,7 +841,7 @@ public final class ViewRoot extends Handler implements ViewParent, + " content=" + mPendingContentInsets.toShortString() + " visible=" + mPendingVisibleInsets.toShortString() + " surface=" + mSurface); - + contentInsetsChanged = !mPendingContentInsets.equals( mAttachInfo.mContentInsets); visibleInsetsChanged = !mPendingVisibleInsets.equals( @@ -846,7 +869,7 @@ public final class ViewRoot extends Handler implements ViewParent, // all at once. newSurface = true; fullRedrawNeeded = true; - + if (mGlWanted && !mUseGL) { initializeGL(); initialized = mGlCanvas != null; @@ -864,7 +887,7 @@ public final class ViewRoot extends Handler implements ViewParent, } catch (RemoteException e) { } if (DEBUG_ORIENTATION) Log.v( - "ViewRoot", "Relayout returned: frame=" + mWinFrame + ", surface=" + mSurface); + "ViewRoot", "Relayout returned: frame=" + frame + ", surface=" + mSurface); attachInfo.mWindowLeft = frame.left; attachInfo.mWindowTop = frame.top; @@ -876,7 +899,7 @@ public final class ViewRoot extends Handler implements ViewParent, mHeight = frame.height(); if (initialized) { - mGlCanvas.setViewport((int) (mWidth * mAppScale), (int) (mHeight * mAppScale)); + mGlCanvas.setViewport((int) (mWidth * appScale), (int) (mHeight * appScale)); } boolean focusChangedDueToTouchMode = ensureTouchModeLocally( @@ -891,7 +914,7 @@ public final class ViewRoot extends Handler implements ViewParent, + " mHeight=" + mHeight + " measuredHeight" + host.mMeasuredHeight + " coveredInsetsChanged=" + contentInsetsChanged); - + // Ask host how big it wants to be host.measure(childWidthMeasureSpec, childHeightMeasureSpec); @@ -939,7 +962,6 @@ public final class ViewRoot extends Handler implements ViewParent, if (Config.DEBUG && ViewDebug.profileLayout) { startTime = SystemClock.elapsedRealtime(); } - host.layout(0, 0, host.mMeasuredWidth, host.mMeasuredHeight); if (Config.DEBUG && ViewDebug.consistencyCheckEnabled) { @@ -966,11 +988,10 @@ public final class ViewRoot extends Handler implements ViewParent, mTmpLocation[1] + host.mBottom - host.mTop); host.gatherTransparentRegion(mTransparentRegion); + if (mTranslator != null) { + mTranslator.translateRegionInWindowToScreen(mTransparentRegion); + } - // TODO: scale the region, like: - // Region uses native methods. We probabl should have ScalableRegion class. - - // Region does not have equals method ? if (!mTransparentRegion.equals(mPreviousTransparentRegion)) { mPreviousTransparentRegion.set(mTransparentRegion); // reconfigure window manager @@ -981,7 +1002,6 @@ public final class ViewRoot extends Handler implements ViewParent, } } - if (DBG) { System.out.println("======================================"); System.out.println("performTraversals -- after setFrame"); @@ -1001,20 +1021,23 @@ public final class ViewRoot extends Handler implements ViewParent, givenContent.left = givenContent.top = givenContent.right = givenContent.bottom = givenVisible.left = givenVisible.top = givenVisible.right = givenVisible.bottom = 0; - insets.contentInsets.scale(mAppScale); - insets.visibleInsets.scale(mAppScale); - attachInfo.mTreeObserver.dispatchOnComputeInternalInsets(insets); + Rect contentInsets = insets.contentInsets; + Rect visibleInsets = insets.visibleInsets; + if (mTranslator != null) { + contentInsets = mTranslator.getTranslatedContentInsets(contentInsets); + visibleInsets = mTranslator.getTranslatedVisbileInsets(visibleInsets); + } if (insetsPending || !mLastGivenInsets.equals(insets)) { mLastGivenInsets.set(insets); try { sWindowSession.setInsets(mWindow, insets.mTouchableInsets, - insets.contentInsets, insets.visibleInsets); + contentInsets, visibleInsets); } catch (RemoteException e) { } } } - + if (mFirst) { // handle first focus request if (DEBUG_INPUT_RESIZE) Log.v(TAG, "First: mView.hasFocus()=" @@ -1052,7 +1075,7 @@ public final class ViewRoot extends Handler implements ViewParent, } } } - + boolean cancelDraw = attachInfo.mTreeObserver.dispatchOnPreDraw(); if (!cancelDraw && !newSurface) { @@ -1140,10 +1163,9 @@ public final class ViewRoot extends Handler implements ViewParent, mAttachInfo.mViewScrollChanged = false; mAttachInfo.mTreeObserver.dispatchOnScrollChanged(); } - + int yoff; - final boolean scrolling = mScroller != null - && mScroller.computeScrollOffset(); + final boolean scrolling = mScroller != null && mScroller.computeScrollOffset(); if (scrolling) { yoff = mScroller.getCurrY(); } else { @@ -1153,26 +1175,28 @@ public final class ViewRoot extends Handler implements ViewParent, mCurScrollY = yoff; fullRedrawNeeded = true; } + float appScale = mAttachInfo.mApplicationScale; + boolean scalingRequired = mAttachInfo.mScalingRequired; Rect dirty = mDirty; if (mUseGL) { if (!dirty.isEmpty()) { Canvas canvas = mGlCanvas; - if (mGL!=null && canvas != null) { + if (mGL != null && canvas != null) { mGL.glDisable(GL_SCISSOR_TEST); mGL.glClearColor(0, 0, 0, 0); mGL.glClear(GL_COLOR_BUFFER_BIT); mGL.glEnable(GL_SCISSOR_TEST); mAttachInfo.mDrawingTime = SystemClock.uptimeMillis(); + mAttachInfo.mIgnoreDirtyState = true; mView.mPrivateFlags |= View.DRAWN; - float scale = mAppScale; int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG); try { canvas.translate(0, -yoff); - if (scale != 1.0f) { - canvas.scale(scale, scale); + if (mTranslator != null) { + mTranslator.translateCanvas(canvas); } mView.draw(canvas); if (Config.DEBUG && ViewDebug.consistencyCheckEnabled) { @@ -1182,6 +1206,8 @@ public final class ViewRoot extends Handler implements ViewParent, canvas.restoreToCount(saveCount); } + mAttachInfo.mIgnoreDirtyState = false; + mEgl.eglSwapBuffers(mEglDisplay, mEglSurface); checkEglErrors(); @@ -1201,20 +1227,33 @@ public final class ViewRoot extends Handler implements ViewParent, return; } - if (fullRedrawNeeded) - dirty.union(0, 0, (int) (mWidth * mAppScale), (int) (mHeight * mAppScale)); + if (fullRedrawNeeded) { + mAttachInfo.mIgnoreDirtyState = true; + dirty.union(0, 0, (int) (mWidth * appScale), (int) (mHeight * appScale)); + } if (DEBUG_ORIENTATION || DEBUG_DRAW) { Log.v("ViewRoot", "Draw " + mView + "/" + mWindowAttributes.getTitle() + ": dirty={" + dirty.left + "," + dirty.top + "," + dirty.right + "," + dirty.bottom + "} surface=" - + surface + " surface.isValid()=" + surface.isValid()); + + surface + " surface.isValid()=" + surface.isValid() + ", appScale:" + + appScale + ", width=" + mWidth + ", height=" + mHeight); } Canvas canvas; try { + int left = dirty.left; + int top = dirty.top; + int right = dirty.right; + int bottom = dirty.bottom; canvas = surface.lockCanvas(dirty); + + if (left != dirty.left || top != dirty.top || right != dirty.right || + bottom != dirty.bottom) { + mAttachInfo.mIgnoreDirtyState = true; + } + // TODO: Do this in native canvas.setDensityScale(mDensity); } catch (Surface.OutOfResourcesException e) { @@ -1242,12 +1281,11 @@ public final class ViewRoot extends Handler implements ViewParent, // need to clear it before drawing so that the child will // properly re-composite its drawing on a transparent // background. This automatically respects the clip/dirty region - if (!canvas.isOpaque()) { - canvas.drawColor(0x00000000, PorterDuff.Mode.CLEAR); - } else if (yoff != 0) { - // If we are applying an offset, we need to clear the area - // where the offset doesn't appear to avoid having garbage - // left in the blank areas. + // or + // If we are applying an offset, we need to clear the area + // where the offset doesn't appear to avoid having garbage + // left in the blank areas. + if (!canvas.isOpaque() || yoff != 0) { canvas.drawColor(0, PorterDuff.Mode.CLEAR); } @@ -1256,27 +1294,27 @@ public final class ViewRoot extends Handler implements ViewParent, mAttachInfo.mDrawingTime = SystemClock.uptimeMillis(); mView.mPrivateFlags |= View.DRAWN; - float scale = mAppScale; - Context cxt = mView.getContext(); if (DEBUG_DRAW) { - Log.i(TAG, "Drawing: package:" + cxt.getPackageName() + ", appScale=" + mAppScale); + Context cxt = mView.getContext(); + Log.i(TAG, "Drawing: package:" + cxt.getPackageName() + + ", metrics=" + mView.getContext().getResources().getDisplayMetrics()); } - int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG); + int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG); try { canvas.translate(0, -yoff); - if (scale != 1.0f) { - // re-scale this - canvas.scale(scale, scale); + if (mTranslator != null) { + mTranslator.translateCanvas(canvas); } mView.draw(canvas); - - if (Config.DEBUG && ViewDebug.consistencyCheckEnabled) { - mView.dispatchConsistencyCheck(ViewDebug.CONSISTENCY_DRAWING); - } } finally { + mAttachInfo.mIgnoreDirtyState = false; canvas.restoreToCount(saveCount); } + if (Config.DEBUG && ViewDebug.consistencyCheckEnabled) { + mView.dispatchConsistencyCheck(ViewDebug.CONSISTENCY_DRAWING); + } + if (Config.DEBUG && ViewDebug.showFps) { int now = (int)SystemClock.elapsedRealtime(); if (sDrawTime != 0) { @@ -1289,7 +1327,7 @@ public final class ViewRoot extends Handler implements ViewParent, EventLog.writeEvent(60000, SystemClock.elapsedRealtime() - startTime); } } - + } finally { surface.unlockCanvasAndPost(canvas); } @@ -1297,7 +1335,7 @@ public final class ViewRoot extends Handler implements ViewParent, if (LOCAL_LOGV) { Log.v("ViewRoot", "Surface " + surface + " unlockCanvasAndPost"); } - + if (scrolling) { mFullRedrawNeeded = true; scheduleTraversals(); @@ -1310,7 +1348,7 @@ public final class ViewRoot extends Handler implements ViewParent, final Rect vi = attachInfo.mVisibleInsets; int scrollY = 0; boolean handled = false; - + if (vi.left > ci.left || vi.top > ci.top || vi.right > ci.right || vi.bottom > ci.bottom) { // We'll assume that we aren't going to change the scroll @@ -1397,7 +1435,7 @@ public final class ViewRoot extends Handler implements ViewParent, } } } - + if (scrollY != mScrollY) { if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Pan scroll changed: old=" + mScrollY + " , new=" + scrollY); @@ -1411,10 +1449,10 @@ public final class ViewRoot extends Handler implements ViewParent, } mScrollY = scrollY; } - + return handled; } - + public void requestChildFocus(View child, View focused) { checkThread(); if (mFocusedView != focused) { @@ -1494,7 +1532,7 @@ public final class ViewRoot extends Handler implements ViewParent, } catch (RemoteException e) { } } - + /** * Return true if child is an ancestor of parent, (or equal to the parent). */ @@ -1568,10 +1606,9 @@ public final class ViewRoot extends Handler implements ViewParent, } else { didFinish = event.getAction() == MotionEvent.ACTION_OUTSIDE; } - if (event != null) { - event.scale(mAppScaleInverted); + if (event != null && mTranslator != null) { + mTranslator.translateEventInScreenToAppWindow(event); } - try { boolean handled; if (mView != null && mAdded && event != null) { @@ -1657,6 +1694,7 @@ public final class ViewRoot extends Handler implements ViewParent, case RESIZED: Rect coveredInsets = ((Rect[])msg.obj)[0]; Rect visibleInsets = ((Rect[])msg.obj)[1]; + if (mWinFrame.width() == msg.arg1 && mWinFrame.height() == msg.arg2 && mPendingContentInsets.equals(coveredInsets) && mPendingVisibleInsets.equals(visibleInsets)) { @@ -1691,16 +1729,17 @@ public final class ViewRoot extends Handler implements ViewParent, if (mGlWanted && !mUseGL) { initializeGL(); if (mGlCanvas != null) { - mGlCanvas.setViewport((int) (mWidth * mAppScale), - (int) (mHeight * mAppScale)); + float appScale = mAttachInfo.mApplicationScale; + mGlCanvas.setViewport( + (int) (mWidth * appScale), (int) (mHeight * appScale)); } } } } - + mLastWasImTarget = WindowManager.LayoutParams .mayUseInputMethod(mWindowAttributes.flags); - + InputMethodManager imm = InputMethodManager.peekInstance(); if (mView != null) { if (hasWindowFocus && imm != null && mLastWasImTarget) { @@ -1708,7 +1747,7 @@ public final class ViewRoot extends Handler implements ViewParent, } mView.dispatchWindowFocusChanged(hasWindowFocus); } - + // Note: must be done after the focus change callbacks, // so all of the view state is set up correctly. if (hasWindowFocus) { @@ -1726,6 +1765,10 @@ public final class ViewRoot extends Handler implements ViewParent, ~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION; mHasHadWindowFocus = true; } + + if (hasWindowFocus && mView != null) { + sendAccessibilityEvents(); + } } } break; case DIE: @@ -1892,9 +1935,6 @@ public final class ViewRoot extends Handler implements ViewParent, } else { didFinish = false; } - if (event != null) { - event.scale(mAppScaleInverted); - } if (DEBUG_TRACKBALL) Log.v(TAG, "Motion event:" + event); @@ -2120,50 +2160,50 @@ public final class ViewRoot extends Handler implements ViewParent, } /** - * log motion events + * log motion events */ private static void captureMotionLog(String subTag, MotionEvent ev) { - //check dynamic switch + //check dynamic switch if (ev == null || SystemProperties.getInt(ViewDebug.SYSTEM_PROPERTY_CAPTURE_EVENT, 0) == 0) { return; - } - - StringBuilder sb = new StringBuilder(subTag + ": "); - sb.append(ev.getDownTime()).append(','); - sb.append(ev.getEventTime()).append(','); - sb.append(ev.getAction()).append(','); - sb.append(ev.getX()).append(','); - sb.append(ev.getY()).append(','); - sb.append(ev.getPressure()).append(','); - sb.append(ev.getSize()).append(','); - sb.append(ev.getMetaState()).append(','); - sb.append(ev.getXPrecision()).append(','); - sb.append(ev.getYPrecision()).append(','); - sb.append(ev.getDeviceId()).append(','); + } + + StringBuilder sb = new StringBuilder(subTag + ": "); + sb.append(ev.getDownTime()).append(','); + sb.append(ev.getEventTime()).append(','); + sb.append(ev.getAction()).append(','); + sb.append(ev.getX()).append(','); + sb.append(ev.getY()).append(','); + sb.append(ev.getPressure()).append(','); + sb.append(ev.getSize()).append(','); + sb.append(ev.getMetaState()).append(','); + sb.append(ev.getXPrecision()).append(','); + sb.append(ev.getYPrecision()).append(','); + sb.append(ev.getDeviceId()).append(','); sb.append(ev.getEdgeFlags()); - Log.d(TAG, sb.toString()); + Log.d(TAG, sb.toString()); } /** - * log motion events + * log motion events */ private static void captureKeyLog(String subTag, KeyEvent ev) { - //check dynamic switch - if (ev == null || + //check dynamic switch + if (ev == null || SystemProperties.getInt(ViewDebug.SYSTEM_PROPERTY_CAPTURE_EVENT, 0) == 0) { return; } - StringBuilder sb = new StringBuilder(subTag + ": "); + StringBuilder sb = new StringBuilder(subTag + ": "); sb.append(ev.getDownTime()).append(','); sb.append(ev.getEventTime()).append(','); sb.append(ev.getAction()).append(','); - sb.append(ev.getKeyCode()).append(','); + sb.append(ev.getKeyCode()).append(','); sb.append(ev.getRepeatCount()).append(','); sb.append(ev.getMetaState()).append(','); sb.append(ev.getDeviceId()).append(','); sb.append(ev.getScanCode()); - Log.d(TAG, sb.toString()); - } + Log.d(TAG, sb.toString()); + } int enqueuePendingEvent(Object event, boolean sendDone) { int seq = mPendingEventSeq+1; @@ -2181,7 +2221,7 @@ public final class ViewRoot extends Handler implements ViewParent, } return event; } - + private void deliverKeyEvent(KeyEvent event, boolean sendDone) { // If mView is null, we just consume the key event because it doesn't // make sense to do anything else with it. @@ -2238,7 +2278,7 @@ public final class ViewRoot extends Handler implements ViewParent, } } } - + private void deliverKeyEventToViewHierarchy(KeyEvent event, boolean sendDone) { try { if (mView != null && mAdded) { @@ -2247,8 +2287,8 @@ public final class ViewRoot extends Handler implements ViewParent, if (checkForLeavingTouchModeAndConsume(event)) { return; - } - + } + if (Config.LOGV) { captureKeyLog("captureDispatchKeyEvent", event); } @@ -2324,24 +2364,31 @@ public final class ViewRoot extends Handler implements ViewParent, private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility, boolean insetsPending) throws RemoteException { + float appScale = mAttachInfo.mApplicationScale; boolean restore = false; - if (params != null && mAppScale != 1.0f) { + if (params != null && mTranslator != null) { restore = true; - params.scale(mAppScale, mWindowLayoutParamsBackup); + params.backup(); + mTranslator.translateWindowLayout(params); + } + if (params != null) { + if (DBG) Log.d(TAG, "WindowLayout in layoutWindow:" + params); } int relayoutResult = sWindowSession.relayout( mWindow, params, - (int) (mView.mMeasuredWidth * mAppScale), - (int) (mView.mMeasuredHeight * mAppScale), + (int) (mView.mMeasuredWidth * appScale), + (int) (mView.mMeasuredHeight * appScale), viewVisibility, insetsPending, mWinFrame, mPendingContentInsets, mPendingVisibleInsets, mSurface); if (restore) { - params.restore(mWindowLayoutParamsBackup); + params.restore(); + } + + if (mTranslator != null) { + mTranslator.translateRectInScreenToAppWinFrame(mWinFrame); + mTranslator.translateRectInScreenToAppWindow(mPendingContentInsets); + mTranslator.translateRectInScreenToAppWindow(mPendingVisibleInsets); } - - mPendingContentInsets.scale(mAppScaleInverted); - mPendingVisibleInsets.scale(mAppScaleInverted); - mWinFrame.scale(mAppScaleInverted); return relayoutResult; } @@ -2448,11 +2495,14 @@ public final class ViewRoot extends Handler implements ViewParent, + " visibleInsets=" + visibleInsets.toShortString() + " reportDraw=" + reportDraw); Message msg = obtainMessage(reportDraw ? RESIZED_REPORT :RESIZED); - - coveredInsets.scale(mAppScaleInverted); - visibleInsets.scale(mAppScaleInverted); - msg.arg1 = (int) (w * mAppScaleInverted); - msg.arg2 = (int) (h * mAppScaleInverted); + if (mTranslator != null) { + mTranslator.translateRectInScreenToAppWindow(coveredInsets); + mTranslator.translateRectInScreenToAppWindow(visibleInsets); + w *= mTranslator.applicationInvertedScale; + h *= mTranslator.applicationInvertedScale; + } + msg.arg1 = w; + msg.arg2 = h; msg.obj = new Rect[] { new Rect(coveredInsets), new Rect(visibleInsets) }; sendMessage(msg); } @@ -2511,6 +2561,21 @@ public final class ViewRoot extends Handler implements ViewParent, sendMessage(msg); } + /** + * The window is getting focus so if there is anything focused/selected + * send an {@link AccessibilityEvent} to announce that. + */ + private void sendAccessibilityEvents() { + if (!AccessibilityManager.getInstance(mView.getContext()).isEnabled()) { + return; + } + mView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); + View focusedView = mView.findFocus(); + if (focusedView != null && focusedView != mView) { + focusedView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED); + } + } + public boolean showContextMenuForChild(View originalView) { return false; } @@ -2540,14 +2605,14 @@ public final class ViewRoot extends Handler implements ViewParent, boolean immediate) { return scrollToRectOrFocus(rectangle, immediate); } - + static class InputMethodCallback extends IInputMethodCallback.Stub { private WeakReference mViewRoot; public InputMethodCallback(ViewRoot viewRoot) { mViewRoot = new WeakReference(viewRoot); } - + public void finishedEvent(int seq, boolean handled) { final ViewRoot viewRoot = mViewRoot.get(); if (viewRoot != null) { @@ -2559,13 +2624,13 @@ public final class ViewRoot extends Handler implements ViewParent, // Stub -- not for use in the client. } } - + static class EventCompletion extends Handler { final IWindow mWindow; final KeyEvent mKeyEvent; final boolean mIsPointer; final MotionEvent mMotionEvent; - + EventCompletion(Looper looper, IWindow window, KeyEvent key, boolean isPointer, MotionEvent motion) { super(looper); @@ -2575,7 +2640,7 @@ public final class ViewRoot extends Handler implements ViewParent, mMotionEvent = motion; sendEmptyMessage(0); } - + @Override public void handleMessage(Message msg) { if (mKeyEvent != null) { @@ -2617,7 +2682,7 @@ public final class ViewRoot extends Handler implements ViewParent, } } } - + static class W extends IWindow.Stub { private final WeakReference mViewRoot; private final Looper mMainLooper; @@ -2739,14 +2804,14 @@ public final class ViewRoot extends Handler implements ViewParent, * The maximum amount of acceleration we will apply. */ static final float MAX_ACCELERATION = 20; - + /** * The maximum amount of time (in milliseconds) between events in order * for us to consider the user to be doing fast trackball movements, * and thus apply an acceleration. */ static final long FAST_MOVE_TIME = 150; - + /** * Scaling factor to the time (in milliseconds) between events to how * much to multiple/divide the current acceleration. When movement @@ -2754,7 +2819,7 @@ public final class ViewRoot extends Handler implements ViewParent, * FAST_MOVE_TIME it divides it. */ static final float ACCEL_MOVE_SCALING_FACTOR = (1.0f/40); - + float position; float absPosition; float acceleration = 1; @@ -2806,7 +2871,7 @@ public final class ViewRoot extends Handler implements ViewParent, } else { normTime = 0; } - + // The number of milliseconds between each movement that is // considered "normal" and will not result in any acceleration // or deceleration, scaled by the offset we have here. @@ -2964,7 +3029,7 @@ public final class ViewRoot extends Handler implements ViewParent, sRunQueues.set(rq); return rq; } - + /** * @hide */ diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java index 428de67ee80ecd5cd62814245c41185820dea1a2..d7457a030468e97faeaf74fdde3a57a1f87784ea 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -24,7 +24,7 @@ import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Bundle; import android.os.IBinder; -import android.util.Log; +import android.view.accessibility.AccessibilityEvent; /** * Abstract base class for a top-level window look and behavior policy. An @@ -153,7 +153,16 @@ public abstract class Window { * @return boolean Return true if this event was consumed. */ public boolean dispatchTrackballEvent(MotionEvent event); - + + /** + * Called to process population of {@link AccessibilityEvent}s. + * + * @param event The event. + * + * @return boolean Return true if event population was completed. + */ + public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event); + /** * Instantiate the view to display in the panel for 'featureId'. * You can return null, in which case the default content (typically @@ -367,8 +376,14 @@ public abstract class Window { String title; if (wp.type == WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA) { title="Media"; + } else if (wp.type == WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY) { + title="MediaOvr"; } else if (wp.type == WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) { title="Panel"; + } else if (wp.type == WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL) { + title="SubPanel"; + } else if (wp.type == WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG) { + title="AtchDlg"; } else { title=Integer.toString(wp.type); } diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index c69c28154c2d7c5ba4ad831b6a8374b94f014a4f..bdb86d703b3b86f1f2af5839325c1725ef689c18 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -18,7 +18,6 @@ package android.view; import android.content.pm.ActivityInfo; import android.graphics.PixelFormat; -import android.graphics.Rect; import android.os.IBinder; import android.os.Parcel; import android.os.Parcelable; @@ -209,6 +208,15 @@ public interface WindowManager extends ViewManager { */ public static final int TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW+3; + /** + * Window type: window for showing overlays on top of media windows. + * These windows are displayed between TYPE_APPLICATION_MEDIA and the + * application window. They should be translucent to be useful. This + * is a big ugly hack so: + * @hide + */ + public static final int TYPE_APPLICATION_MEDIA_OVERLAY = FIRST_SUB_WINDOW+4; + /** * End of types of sub-windows. */ @@ -466,6 +474,21 @@ public interface WindowManager extends ViewManager { */ public static final int FLAG_WATCH_OUTSIDE_TOUCH = 0x00040000; + /** Window flag: special flag to let windows be shown when the screen + * is locked. This will let application windows take precedence over + * key guard or any other lock screens. Can be used with + * {@link #FLAG_KEEP_SCREEN_ON} to turn screen on and display windows + * directly before showing the key guard window + * + * {@hide} */ + public static final int FLAG_SHOW_WHEN_LOCKED = 0x00080000; + + /** Window flag: special flag to let a window ignore the compatibility scaling. + * This is used by SurfaceView to create a window that does not scale the content. + * + * {@hide} */ + public static final int FLAG_NO_COMPATIBILITY_SCALING = 0x00100000; + /** Window flag: a special option intended for system dialogs. When * this flag is set, the window will demand focus unconditionally when * it is created. @@ -787,6 +810,7 @@ public interface WindowManager extends ViewManager { screenOrientation = in.readInt(); } + @SuppressWarnings({"PointlessBitwiseExpression"}) public static final int LAYOUT_CHANGED = 1<<0; public static final int TYPE_CHANGED = 1<<1; public static final int FLAGS_CHANGED = 1<<2; @@ -800,6 +824,9 @@ public interface WindowManager extends ViewManager { public static final int SCREEN_ORIENTATION_CHANGED = 1<<10; public static final int SCREEN_BRIGHTNESS_CHANGED = 1<<11; + // internal buffer to backup/restore parameters under compatibility mode. + private int[] mCompatibilityParamsBackup = null; + public final int copyFrom(LayoutParams o) { int changes = 0; @@ -957,36 +984,45 @@ public interface WindowManager extends ViewManager { /** * Scale the layout params' coordinates and size. - * Returns the original info as a backup so that the caller can - * restore the layout params; - */ - void scale(float scale, int[] backup) { - if (scale != 1.0f) { - backup[0] = x; - backup[1] = y; - x *= scale; - y *= scale; - if (width > 0) { - backup[2] = width; - width *= scale; - } - if (height > 0) { - backup[3] = height; - height *= scale; - } + * @hide + */ + public void scale(float scale) { + x *= scale; + y *= scale; + if (width > 0) { + width *= scale; + } + if (height > 0) { + height *= scale; } } /** - * Restore the layout params' coordinates and size. - */ - void restore(int[] backup) { - x = backup[0]; - y = backup[1]; - if (width > 0) { - width = backup[2]; + * Backup the layout parameters used in compatibility mode. + * @see LayoutParams#restore() + */ + void backup() { + int[] backup = mCompatibilityParamsBackup; + if (backup == null) { + // we backup 4 elements, x, y, width, height + backup = mCompatibilityParamsBackup = new int[4]; } - if (height > 0) { + backup[0] = x; + backup[1] = y; + backup[2] = width; + backup[3] = height; + } + + /** + * Restore the layout params' coordinates, size and gravity + * @see LayoutParams#backup() + */ + void restore() { + int[] backup = mCompatibilityParamsBackup; + if (backup != null) { + x = backup[0]; + y = backup[1]; + width = backup[2]; height = backup[3]; } } diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java index 755d7b8cf289254a521ceb99a92cae3f2e694c51..0973599509b1c710f4b0fdb62d6a88491716370f 100644 --- a/core/java/android/view/WindowManagerImpl.java +++ b/core/java/android/view/WindowManagerImpl.java @@ -173,7 +173,6 @@ public class WindowManagerImpl implements WindowManager { mRoots[index] = root; mParams[index] = wparams; } - // do this last because it fires off messages to start doing things root.setView(view, wparams, panelParentView); } diff --git a/core/java/android/view/accessibility/AccessibilityEvent.aidl b/core/java/android/view/accessibility/AccessibilityEvent.aidl new file mode 100644 index 0000000000000000000000000000000000000000..cee36047503619d62733dad8826e0daf1269972d --- /dev/null +++ b/core/java/android/view/accessibility/AccessibilityEvent.aidl @@ -0,0 +1,19 @@ +/** + * 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.view.accessibility; + +parcelable AccessibilityEvent; diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..c22f991183b02128b429596562f2f0a2af280e00 --- /dev/null +++ b/core/java/android/view/accessibility/AccessibilityEvent.java @@ -0,0 +1,734 @@ +/* + * 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.view.accessibility; + +import android.os.Parcel; +import android.os.Parcelable; +import android.text.TextUtils; + +import java.util.ArrayList; +import java.util.List; + +/** + * This class represents accessibility events that are sent by the system when + * something notable happens in the user interface. For example, when a + * {@link android.widget.Button} is clicked, a {@link android.view.View} is focused, etc. + *

        + * This class represents various semantically different accessibility event + * types. Each event type has associated a set of related properties. In other + * words, each event type is characterized via a subset of the properties exposed + * by this class. For each event type there is a corresponding constant defined + * in this class. Since some event types are semantically close there are mask + * constants that group them together. Follows a specification of the event + * types and their associated properties: + *

        + * VIEW TYPES
        + *

        + * View clicked - represents the event of clicking on a {@link android.view.View} + * like {@link android.widget.Button}, {@link android.widget.CompoundButton}, etc.
        + * Type:{@link #TYPE_VIEW_CLICKED}
        + * Properties: + * {@link #getClassName()}, + * {@link #getPackageName()}, + * {@link #getEventTime()}, + * {@link #getText()}, + * {@link #isChecked()}, + * {@link #isEnabled()}, + * {@link #isPassword()}, + * {@link #getItemCount()}, + * {@link #getCurrentItemIndex()} + *

        + * View long clicked - represents the event of long clicking on a {@link android.view.View} + * like {@link android.widget.Button}, {@link android.widget.CompoundButton}, etc.
        + * Type:{@link #TYPE_VIEW_LONG_CLICKED}
        + * Properties: + * {@link #getClassName()}, + * {@link #getPackageName()}, + * {@link #getEventTime()}, + * {@link #getText()}, + * {@link #isChecked()}, + * {@link #isEnabled()}, + * {@link #isPassword()}, + * {@link #getItemCount()}, + * {@link #getCurrentItemIndex()} + *

        + * View selected - represents the event of selecting an item usually in + * the context of an {@link android.widget.AdapterView}.
        + * Type: {@link #TYPE_VIEW_SELECTED}
        + * Properties: + * {@link #getClassName()}, + * {@link #getPackageName()}, + * {@link #getEventTime()}, + * {@link #getText()}, + * {@link #isChecked()}, + * {@link #isEnabled()}, + * {@link #isPassword()}, + * {@link #getItemCount()}, + * {@link #getCurrentItemIndex()} + *

        + * View focused - represents the event of focusing a + * {@link android.view.View}.
        + * Type: {@link #TYPE_VIEW_FOCUSED}
        + * Properties: + * {@link #getClassName()}, + * {@link #getPackageName()}, + * {@link #getEventTime()}, + * {@link #getText()}, + * {@link #isChecked()}, + * {@link #isEnabled()}, + * {@link #isPassword()}, + * {@link #getItemCount()}, + * {@link #getCurrentItemIndex()} + *

        + * View text changed - represents the event of changing the text of an + * {@link android.widget.EditText}.
        + * Type: {@link #TYPE_VIEW_TEXT_CHANGED}
        + * Properties: + * {@link #getClassName()}, + * {@link #getPackageName()}, + * {@link #getEventTime()}, + * {@link #getText()}, + * {@link #isChecked()}, + * {@link #isEnabled()}, + * {@link #isPassword()}, + * {@link #getItemCount()}, + * {@link #getCurrentItemIndex()}, + * {@link #getFromIndex()}, + * {@link #getAddedCount()}, + * {@link #getRemovedCount()}, + * {@link #getBeforeText()} + *

        + * TRANSITION TYPES
        + *

        + * Window state changed - represents the event of opening/closing a + * {@link android.widget.PopupWindow}, {@link android.view.Menu}, + * {@link android.app.Dialog}, etc.
        + * Type: {@link #TYPE_WINDOW_STATE_CHANGED}
        + * Properties: + * {@link #getClassName()}, + * {@link #getPackageName()}, + * {@link #getEventTime()}, + * {@link #getText()} + *

        + * NOTIFICATION TYPES
        + *

        + * Notification state changed - represents the event showing/hiding + * {@link android.app.Notification}. + * Type: {@link #TYPE_NOTIFICATION_STATE_CHANGED}
        + * Properties: + * {@link #getClassName()}, + * {@link #getPackageName()}, + * {@link #getEventTime()}, + * {@link #getText()} + * {@link #getParcelableData()} + *

        + * Security note + *

        + * Since an event contains the text of its source privacy can be compromised by leaking of + * sensitive information such as passwords. To address this issue any event fired in response + * to manipulation of a PASSWORD field does NOT CONTAIN the text of the password. + * + * @see android.view.accessibility.AccessibilityManager + * @see android.accessibilityservice.AccessibilityService + */ +public final class AccessibilityEvent implements Parcelable { + + /** + * Invalid selection/focus position. + * + * @see #getCurrentItemIndex() + */ + public static final int INVALID_POSITION = -1; + + /** + * Maximum length of the text fields. + * + * @see #getBeforeText() + * @see #getText() + */ + public static final int MAX_TEXT_LENGTH = 500; + + /** + * Represents the event of clicking on a {@link android.view.View} like + * {@link android.widget.Button}, {@link android.widget.CompoundButton}, etc. + */ + public static final int TYPE_VIEW_CLICKED = 0x00000001; + + /** + * Represents the event of long clicking on a {@link android.view.View} like + * {@link android.widget.Button}, {@link android.widget.CompoundButton}, etc. + */ + public static final int TYPE_VIEW_LONG_CLICKED = 0x00000002; + + /** + * Represents the event of selecting an item usually in the context of an + * {@link android.widget.AdapterView}. + */ + public static final int TYPE_VIEW_SELECTED = 0x00000004; + + /** + * Represents the event of focusing a {@link android.view.View}. + */ + public static final int TYPE_VIEW_FOCUSED = 0x00000008; + + /** + * Represents the event of changing the text of an {@link android.widget.EditText}. + */ + public static final int TYPE_VIEW_TEXT_CHANGED = 0x00000010; + + /** + * Represents the event of opening/closing a {@link android.widget.PopupWindow}, + * {@link android.view.Menu}, {@link android.app.Dialog}, etc. + */ + public static final int TYPE_WINDOW_STATE_CHANGED = 0x00000020; + + /** + * Represents the event showing/hiding a {@link android.app.Notification}. + */ + public static final int TYPE_NOTIFICATION_STATE_CHANGED = 0x00000040; + + /** + * Mask for {@link AccessibilityEvent} all types. + * + * @see #TYPE_VIEW_CLICKED + * @see #TYPE_VIEW_LONG_CLICKED + * @see #TYPE_VIEW_SELECTED + * @see #TYPE_VIEW_FOCUSED + * @see #TYPE_VIEW_TEXT_CHANGED + * @see #TYPE_WINDOW_STATE_CHANGED + * @see #TYPE_NOTIFICATION_STATE_CHANGED + */ + public static final int TYPES_ALL_MASK = 0xFFFFFFFF; + + private static final int MAX_POOL_SIZE = 2; + private static final Object mPoolLock = new Object(); + private static AccessibilityEvent sPool; + private static int sPoolSize; + + private static final int CHECKED = 0x00000001; + private static final int ENABLED = 0x00000002; + private static final int PASSWORD = 0x00000004; + private static final int FULL_SCREEN = 0x00000080; + + private AccessibilityEvent mNext; + + private int mEventType; + private int mBooleanProperties; + private int mCurrentItemIndex; + private int mItemCount; + private int mFromIndex; + private int mAddedCount; + private int mRemovedCount; + + private long mEventTime; + + private CharSequence mClassName; + private CharSequence mPackageName; + private CharSequence mContentDescription; + private CharSequence mBeforeText; + + private Parcelable mParcelableData; + + private final List mText = new ArrayList(); + + private boolean mIsInPool; + + /* + * Hide constructor from clients. + */ + private AccessibilityEvent() { + mCurrentItemIndex = INVALID_POSITION; + } + + /** + * Gets if the source is checked. + * + * @return True if the view is checked, false otherwise. + */ + public boolean isChecked() { + return getBooleanProperty(CHECKED); + } + + /** + * Sets if the source is checked. + * + * @param isChecked True if the view is checked, false otherwise. + */ + public void setChecked(boolean isChecked) { + setBooleanProperty(CHECKED, isChecked); + } + + /** + * Gets if the source is enabled. + * + * @return True if the view is enabled, false otherwise. + */ + public boolean isEnabled() { + return getBooleanProperty(ENABLED); + } + + /** + * Sets if the source is enabled. + * + * @param isEnabled True if the view is enabled, false otherwise. + */ + public void setEnabled(boolean isEnabled) { + setBooleanProperty(ENABLED, isEnabled); + } + + /** + * Gets if the source is a password field. + * + * @return True if the view is a password field, false otherwise. + */ + public boolean isPassword() { + return getBooleanProperty(PASSWORD); + } + + /** + * Sets if the source is a password field. + * + * @param isPassword True if the view is a password field, false otherwise. + */ + public void setPassword(boolean isPassword) { + setBooleanProperty(PASSWORD, isPassword); + } + + /** + * Sets if the source is taking the entire screen. + * + * @param isFullScreen True if the source is full screen, false otherwise. + */ + public void setFullScreen(boolean isFullScreen) { + setBooleanProperty(FULL_SCREEN, isFullScreen); + } + + /** + * Gets if the source is taking the entire screen. + * + * @return True if the source is full screen, false otherwise. + */ + public boolean isFullScreen() { + return getBooleanProperty(FULL_SCREEN); + } + + /** + * Gets the event type. + * + * @return The event type. + */ + public int getEventType() { + return mEventType; + } + + /** + * Sets the event type. + * + * @param eventType The event type. + */ + public void setEventType(int eventType) { + mEventType = eventType; + } + + /** + * Gets the number of items that can be visited. + * + * @return The number of items. + */ + public int getItemCount() { + return mItemCount; + } + + /** + * Sets the number of items that can be visited. + * + * @param itemCount The number of items. + */ + public void setItemCount(int itemCount) { + mItemCount = itemCount; + } + + /** + * Gets the index of the source in the list of items the can be visited. + * + * @return The current item index. + */ + public int getCurrentItemIndex() { + return mCurrentItemIndex; + } + + /** + * Sets the index of the source in the list of items that can be visited. + * + * @param currentItemIndex The current item index. + */ + public void setCurrentItemIndex(int currentItemIndex) { + mCurrentItemIndex = currentItemIndex; + } + + /** + * Gets the index of the first character of the changed sequence. + * + * @return The index of the first character. + */ + public int getFromIndex() { + return mFromIndex; + } + + /** + * Sets the index of the first character of the changed sequence. + * + * @param fromIndex The index of the first character. + */ + public void setFromIndex(int fromIndex) { + mFromIndex = fromIndex; + } + + /** + * Gets the number of added characters. + * + * @return The number of added characters. + */ + public int getAddedCount() { + return mAddedCount; + } + + /** + * Sets the number of added characters. + * + * @param addedCount The number of added characters. + */ + public void setAddedCount(int addedCount) { + mAddedCount = addedCount; + } + + /** + * Gets the number of removed characters. + * + * @return The number of removed characters. + */ + public int getRemovedCount() { + return mRemovedCount; + } + + /** + * Sets the number of removed characters. + * + * @param removedCount The number of removed characters. + */ + public void setRemovedCount(int removedCount) { + mRemovedCount = removedCount; + } + + /** + * Gets the time in which this event was sent. + * + * @return The event time. + */ + public long getEventTime() { + return mEventTime; + } + + /** + * Sets the time in which this event was sent. + * + * @param eventTime The event time. + */ + public void setEventTime(long eventTime) { + mEventTime = eventTime; + } + + /** + * Gets the class name of the source. + * + * @return The class name. + */ + public CharSequence getClassName() { + return mClassName; + } + + /** + * Sets the class name of the source. + * + * @param className The lass name. + */ + public void setClassName(CharSequence className) { + mClassName = className; + } + + /** + * Gets the package name of the source. + * + * @return The package name. + */ + public CharSequence getPackageName() { + return mPackageName; + } + + /** + * Sets the package name of the source. + * + * @param packageName The package name. + */ + public void setPackageName(CharSequence packageName) { + mPackageName = packageName; + } + + /** + * Gets the text of the event. The index in the list represents the priority + * of the text. Specifically, the lower the index the higher the priority. + * + * @return The text. + */ + public List getText() { + return mText; + } + + /** + * Sets the text before a change. + * + * @return The text before the change. + */ + public CharSequence getBeforeText() { + return mBeforeText; + } + + /** + * Sets the text before a change. + * + * @param beforeText The text before the change. + */ + public void setBeforeText(CharSequence beforeText) { + mBeforeText = beforeText; + } + + /** + * Gets the description of the source. + * + * @return The description. + */ + public CharSequence getContentDescription() { + return mContentDescription; + } + + /** + * Sets the description of the source. + * + * @param contentDescription The description. + */ + public void setContentDescription(CharSequence contentDescription) { + mContentDescription = contentDescription; + } + + /** + * Gets the {@link Parcelable} data. + * + * @return The parcelable data. + */ + public Parcelable getParcelableData() { + return mParcelableData; + } + + /** + * Sets the {@link Parcelable} data of the event. + * + * @param parcelableData The parcelable data. + */ + public void setParcelableData(Parcelable parcelableData) { + mParcelableData = parcelableData; + } + + /** + * Returns a cached instance if such is available or a new one is + * instantiated with type property set. + * + * @param eventType The event type. + * @return An instance. + */ + public static AccessibilityEvent obtain(int eventType) { + AccessibilityEvent event = AccessibilityEvent.obtain(); + event.setEventType(eventType); + return event; + } + + /** + * Returns a cached instance if such is available or a new one is + * instantiated. + * + * @return An instance. + */ + public static AccessibilityEvent obtain() { + synchronized (mPoolLock) { + if (sPool != null) { + AccessibilityEvent event = sPool; + sPool = sPool.mNext; + sPoolSize--; + event.mNext = null; + event.mIsInPool = false; + return event; + } + return new AccessibilityEvent(); + } + } + + /** + * Return an instance back to be reused. + *

        + * Note: You must not touch the object after calling this function. + */ + public void recycle() { + if (mIsInPool) { + return; + } + + clear(); + synchronized (mPoolLock) { + if (sPoolSize <= MAX_POOL_SIZE) { + mNext = sPool; + sPool = this; + mIsInPool = true; + sPoolSize++; + } + } + } + + /** + * Clears the state of this instance. + */ + private void clear() { + mEventType = 0; + mBooleanProperties = 0; + mCurrentItemIndex = INVALID_POSITION; + mItemCount = 0; + mFromIndex = 0; + mAddedCount = 0; + mRemovedCount = 0; + mEventTime = 0; + mClassName = null; + mPackageName = null; + mContentDescription = null; + mBeforeText = null; + mText.clear(); + } + + /** + * Gets the value of a boolean property. + * + * @param property The property. + * @return The value. + */ + private boolean getBooleanProperty(int property) { + return (mBooleanProperties & property) == property; + } + + /** + * Sets a boolean property. + * + * @param property The property. + * @param value The value. + */ + private void setBooleanProperty(int property, boolean value) { + if (value) { + mBooleanProperties |= property; + } else { + mBooleanProperties &= ~property; + } + } + + /** + * Creates a new instance from a {@link Parcel}. + * + * @param parcel A parcel containing the state of a {@link AccessibilityEvent}. + */ + public void initFromParcel(Parcel parcel) { + mEventType = parcel.readInt(); + mBooleanProperties = parcel.readInt(); + mCurrentItemIndex = parcel.readInt(); + mItemCount = parcel.readInt(); + mFromIndex = parcel.readInt(); + mAddedCount = parcel.readInt(); + mRemovedCount = parcel.readInt(); + mEventTime = parcel.readLong(); + mClassName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel); + mPackageName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel); + mContentDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel); + mBeforeText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel); + mParcelableData = parcel.readParcelable(null); + parcel.readList(mText, null); + } + + public void writeToParcel(Parcel parcel, int flags) { + parcel.writeInt(mEventType); + parcel.writeInt(mBooleanProperties); + parcel.writeInt(mCurrentItemIndex); + parcel.writeInt(mItemCount); + parcel.writeInt(mFromIndex); + parcel.writeInt(mAddedCount); + parcel.writeInt(mRemovedCount); + parcel.writeLong(mEventTime); + TextUtils.writeToParcel(mClassName, parcel, 0); + TextUtils.writeToParcel(mPackageName, parcel, 0); + TextUtils.writeToParcel(mContentDescription, parcel, 0); + TextUtils.writeToParcel(mBeforeText, parcel, 0); + parcel.writeParcelable(mParcelableData, flags); + parcel.writeList(mText); + } + + public int describeContents() { + return 0; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append(super.toString()); + builder.append("; EventType: " + mEventType); + builder.append("; EventTime: " + mEventTime); + builder.append("; ClassName: " + mClassName); + builder.append("; PackageName: " + mPackageName); + builder.append("; Text: " + mText); + builder.append("; ContentDescription: " + mContentDescription); + builder.append("; ItemCount: " + mItemCount); + builder.append("; CurrentItemIndex: " + mCurrentItemIndex); + builder.append("; IsEnabled: " + isEnabled()); + builder.append("; IsPassword: " + isPassword()); + builder.append("; IsChecked: " + isChecked()); + builder.append("; IsFullScreen: " + isFullScreen()); + builder.append("; BeforeText: " + mBeforeText); + builder.append("; FromIndex: " + mFromIndex); + builder.append("; AddedCount: " + mAddedCount); + builder.append("; RemovedCount: " + mRemovedCount); + builder.append("; ParcelableData: " + mParcelableData); + return builder.toString(); + } + + /** + * @see Parcelable.Creator + */ + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { + public AccessibilityEvent createFromParcel(Parcel parcel) { + AccessibilityEvent event = AccessibilityEvent.obtain(); + event.initFromParcel(parcel); + return event; + } + + public AccessibilityEvent[] newArray(int size) { + return new AccessibilityEvent[size]; + } + }; +} diff --git a/core/java/android/view/accessibility/AccessibilityEventSource.java b/core/java/android/view/accessibility/AccessibilityEventSource.java new file mode 100644 index 0000000000000000000000000000000000000000..3d70959b1ef97206bab7d3ce695279e868a1d071 --- /dev/null +++ b/core/java/android/view/accessibility/AccessibilityEventSource.java @@ -0,0 +1,52 @@ +/* + * 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.view.accessibility; + +/** + * This interface is implemented by classes source of {@link AccessibilityEvent}s. + */ +public interface AccessibilityEventSource { + + /** + * Handles the request for sending an {@link AccessibilityEvent} given + * the event type. The method must first check if accessibility is on + * via calling {@link AccessibilityManager#isEnabled()}, obtain + * an {@link AccessibilityEvent} from the event pool through calling + * {@link AccessibilityEvent#obtain(int)}, populate the event, and + * send it for dispatch via calling + * {@link AccessibilityManager#sendAccessibilityEvent(AccessibilityEvent)}. + * + * @see AccessibilityEvent + * @see AccessibilityManager + * + * @param eventType The event type. + */ + public void sendAccessibilityEvent(int eventType); + + /** + * Handles the request for sending an {@link AccessibilityEvent}. The + * method does not guarantee to check if accessibility is on before + * sending the event for dispatch. It is responsibility of the caller + * to do the check via calling {@link AccessibilityManager#isEnabled()}. + * + * @see AccessibilityEvent + * @see AccessibilityManager + * + * @param event The event. + */ + public void sendAccessibilityEventUnchecked(AccessibilityEvent event); +} diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java new file mode 100644 index 0000000000000000000000000000000000000000..01862700097d8a87967edcbae9c82b694479e60e --- /dev/null +++ b/core/java/android/view/accessibility/AccessibilityManager.java @@ -0,0 +1,198 @@ +/* + * 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.view.accessibility; + +import static android.util.Config.LOGV; + +import android.content.Context; +import android.content.pm.ServiceInfo; +import android.os.Binder; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.SystemClock; +import android.util.Log; + +import java.util.Collections; +import java.util.List; + +/** + * System level service that serves as an event dispatch for {@link AccessibilityEvent}s. + * Such events are generated when something notable happens in the user interface, + * for example an {@link android.app.Activity} starts, the focus or selection of a + * {@link android.view.View} changes etc. Parties interested in handling accessibility + * events implement and register an accessibility service which extends + * {@link android.accessibilityservice.AccessibilityService}. + * + * @see AccessibilityEvent + * @see android.accessibilityservice.AccessibilityService + * @see android.content.Context#getSystemService + */ +public final class AccessibilityManager { + private static final String LOG_TAG = "AccessibilityManager"; + + static final Object sInstanceSync = new Object(); + + private static AccessibilityManager sInstance; + + private static final int DO_SET_ENABLED = 10; + + final IAccessibilityManager mService; + + final Handler mHandler; + + boolean mIsEnabled; + + final IAccessibilityManagerClient.Stub mClient = new IAccessibilityManagerClient.Stub() { + public void setEnabled(boolean enabled) { + mHandler.obtainMessage(DO_SET_ENABLED, enabled ? 1 : 0, 0).sendToTarget(); + } + }; + + class MyHandler extends Handler { + + MyHandler(Looper mainLooper) { + super(mainLooper); + } + + @Override + public void handleMessage(Message message) { + switch (message.what) { + case DO_SET_ENABLED : + synchronized (mHandler) { + mIsEnabled = (message.arg1 == 1); + } + return; + default : + Log.w(LOG_TAG, "Unknown message type: " + message.what); + } + } + } + + /** + * Get an AccessibilityManager instance (create one if necessary). + * + * @hide + */ + public static AccessibilityManager getInstance(Context context) { + synchronized (sInstanceSync) { + if (sInstance == null) { + sInstance = new AccessibilityManager(context); + } + } + return sInstance; + } + + /** + * Create an instance. + * + * @param context A {@link Context}. + */ + private AccessibilityManager(Context context) { + mHandler = new MyHandler(context.getMainLooper()); + IBinder iBinder = ServiceManager.getService(Context.ACCESSIBILITY_SERVICE); + mService = IAccessibilityManager.Stub.asInterface(iBinder); + try { + mService.addClient(mClient); + } catch (RemoteException re) { + Log.e(LOG_TAG, "AccessibilityManagerService is dead", re); + } + } + + /** + * Returns if the {@link AccessibilityManager} is enabled. + * + * @return True if this {@link AccessibilityManager} is enabled, false otherwise. + */ + public boolean isEnabled() { + synchronized (mHandler) { + return mIsEnabled; + } + } + + /** + * Sends an {@link AccessibilityEvent}. If this {@link AccessibilityManager} is not + * enabled the call is a NOOP. + * + * @param event The {@link AccessibilityEvent}. + * + * @throws IllegalStateException if a client tries to send an {@link AccessibilityEvent} + * while accessibility is not enabled. + */ + public void sendAccessibilityEvent(AccessibilityEvent event) { + if (!mIsEnabled) { + throw new IllegalStateException("Accessibility off. Did you forget to check that?"); + } + boolean doRecycle = false; + try { + event.setEventTime(SystemClock.uptimeMillis()); + // it is possible that this manager is in the same process as the service but + // client using it is called through Binder from another process. Example: MMS + // app adds a SMS notification and the NotificationManagerService calls this method + long identityToken = Binder.clearCallingIdentity(); + doRecycle = mService.sendAccessibilityEvent(event); + Binder.restoreCallingIdentity(identityToken); + if (LOGV) { + Log.i(LOG_TAG, event + " sent"); + } + } catch (RemoteException re) { + Log.e(LOG_TAG, "Error during sending " + event + " ", re); + } finally { + if (doRecycle) { + event.recycle(); + } + } + } + + /** + * Requests interruption of the accessibility feedback from all accessibility services. + */ + public void interrupt() { + if (!mIsEnabled) { + throw new IllegalStateException("Accessibility off. Did you forget to check that?"); + } + try { + mService.interrupt(); + if (LOGV) { + Log.i(LOG_TAG, "Requested interrupt from all services"); + } + } catch (RemoteException re) { + Log.e(LOG_TAG, "Error while requesting interrupt from all services. ", re); + } + } + + /** + * Returns the {@link ServiceInfo}s of the installed accessibility services. + * + * @return An unmodifiable list with {@link ServiceInfo}s. + */ + public List getAccessibilityServiceList() { + List services = null; + try { + services = mService.getAccessibilityServiceList(); + if (LOGV) { + Log.i(LOG_TAG, "Installed AccessibilityServices " + services); + } + } catch (RemoteException re) { + Log.e(LOG_TAG, "Error while obtaining the installed AccessibilityServices. ", re); + } + return Collections.unmodifiableList(services); + } +} diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl new file mode 100644 index 0000000000000000000000000000000000000000..32788be63670e817be428ebfa36aa7dde038c672 --- /dev/null +++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl @@ -0,0 +1,39 @@ +/* //device/java/android/android/app/INotificationManager.aidl +** +** Copyright 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.view.accessibility; + +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.IAccessibilityManagerClient; +import android.content.pm.ServiceInfo; + +/** + * Interface implemented by the AccessibilityManagerService called by + * the AccessibilityMasngers. + * + * @hide + */ +interface IAccessibilityManager { + + void addClient(IAccessibilityManagerClient client); + + boolean sendAccessibilityEvent(in AccessibilityEvent uiEvent); + + List getAccessibilityServiceList(); + + void interrupt(); +} diff --git a/core/java/android/view/accessibility/IAccessibilityManagerClient.aidl b/core/java/android/view/accessibility/IAccessibilityManagerClient.aidl new file mode 100644 index 0000000000000000000000000000000000000000..1eb60fc61b481f14e91d3f7e4d525af2f066452e --- /dev/null +++ b/core/java/android/view/accessibility/IAccessibilityManagerClient.aidl @@ -0,0 +1,29 @@ +/* + * 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.view.accessibility; + +/** + * Interface a client of the IAccessibilityManager implements to + * receive information about changes in the manager state. + * + * @hide + */ +oneway interface IAccessibilityManagerClient { + + void setEnabled(boolean enabled); + +} diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java index 11de3e2ed433d09a72a9fef0dfd9826c763e2530..739373792047bd2ff2a656ca8daa90e438297f48 100644 --- a/core/java/android/view/inputmethod/BaseInputConnection.java +++ b/core/java/android/view/inputmethod/BaseInputConnection.java @@ -297,6 +297,10 @@ public class BaseInputConnection implements InputConnection { b = tmp; } + if (a <= 0) { + return ""; + } + if (length > a) { length = a; } @@ -336,10 +340,19 @@ public class BaseInputConnection implements InputConnection { } /** - * The default implementation does nothing. + * The default implementation turns this into the enter key. */ public boolean performEditorAction(int actionCode) { - return false; + long eventTime = SystemClock.uptimeMillis(); + sendKeyEvent(new KeyEvent(eventTime, eventTime, + KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER, 0, 0, 0, 0, + KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE + | KeyEvent.FLAG_EDITOR_ACTION)); + sendKeyEvent(new KeyEvent(SystemClock.uptimeMillis(), eventTime, + KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER, 0, 0, 0, 0, + KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE + | KeyEvent.FLAG_EDITOR_ACTION)); + return true; } /** @@ -488,12 +501,12 @@ public class BaseInputConnection implements InputConnection { } 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 (a < 0) a = 0; + if (b < 0) b = 0; + if (b < a) { + int tmp = a; + a = b; + b = tmp; } } diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java index ba3f78cf22ecb7cdbed7bac293d09c08b3efea10..dbd268291aa22a19fc1cb074dfddaa468a1af126 100644 --- a/core/java/android/webkit/BrowserFrame.java +++ b/core/java/android/webkit/BrowserFrame.java @@ -142,6 +142,17 @@ class BrowserFrame extends Handler { mLoadInitFromJava = false; } + /** + * Load a url with "POST" method from the network into the main frame. + * @param url The url to load. + * @param data The data for POST request. + */ + public void postUrl(String url, byte[] data) { + mLoadInitFromJava = true; + nativePostUrl(url, data); + mLoadInitFromJava = false; + } + /** * Load the content as if it was loaded by the provided base URL. The * failUrl is used as the history entry for the load data. If null or @@ -752,6 +763,8 @@ class BrowserFrame extends Handler { */ private native void nativeLoadUrl(String url); + private native void nativePostUrl(String url, byte[] postData); + private native void nativeLoadData(String baseUrl, String data, String mimeType, String encoding, String failUrl); diff --git a/core/java/android/webkit/ByteArrayBuilder.java b/core/java/android/webkit/ByteArrayBuilder.java index 806b458fa8c096c6a328e1ce81fb1cf0b97bb0bf..145411cf7a6a65a9d2d583aeebe953af254e11f4 100644 --- a/core/java/android/webkit/ByteArrayBuilder.java +++ b/core/java/android/webkit/ByteArrayBuilder.java @@ -17,6 +17,7 @@ package android.webkit; import java.util.LinkedList; +import java.util.ListIterator; /** Utility class optimized for accumulating bytes, and then spitting them back out. It does not optimize for returning the result in a @@ -94,6 +95,20 @@ class ByteArrayBuilder { return mChunks.isEmpty(); } + public int size() { + return mChunks.size(); + } + + public int getByteSize() { + int total = 0; + ListIterator it = mChunks.listIterator(0); + while (it.hasNext()) { + Chunk c = it.next(); + total += c.mLength; + } + return total; + } + public synchronized void clear() { Chunk c = getFirstChunk(); while (c != null) { diff --git a/core/java/android/webkit/FrameLoader.java b/core/java/android/webkit/FrameLoader.java index 6f1b160479505aaec852906bd492413316e548df..66ab0213543cc0c043d9f04b4b118359656ace0e 100644 --- a/core/java/android/webkit/FrameLoader.java +++ b/core/java/android/webkit/FrameLoader.java @@ -364,7 +364,7 @@ class FrameLoader { String cookie = CookieManager.getInstance().getCookie( mListener.getWebAddress()); if (cookie != null && cookie.length() > 0) { - mHeaders.put("cookie", cookie); + mHeaders.put("Cookie", cookie); } } } diff --git a/core/java/android/webkit/JWebCoreJavaBridge.java b/core/java/android/webkit/JWebCoreJavaBridge.java index 2a84683cdc86b28e366831035de3d3775d6e07bf..1dbd007886f13ae8b9322c3aed1b0a91d6283417 100644 --- a/core/java/android/webkit/JWebCoreJavaBridge.java +++ b/core/java/android/webkit/JWebCoreJavaBridge.java @@ -18,6 +18,7 @@ package android.webkit; import android.os.Handler; import android.os.Message; +import android.security.CertTool; import android.util.Log; final class JWebCoreJavaBridge extends Handler { @@ -186,6 +187,15 @@ final class JWebCoreJavaBridge extends Handler { mHasInstantTimer = false; } + private String[] getKeyStrengthList() { + return CertTool.getInstance().getSupportedKeyStrenghs(); + } + + private String getSignedPublicKey(int index, String challenge, String url) { + // generateKeyPair expects organizations which we don't have. Ignore url. + return CertTool.getInstance().generateKeyPair(index, challenge, null); + } + private native void nativeConstructor(); private native void nativeFinalize(); private native void sharedTimerFired(); diff --git a/core/java/android/webkit/LoadListener.java b/core/java/android/webkit/LoadListener.java index d583eb18a0435de7ee307e8d348e4bb5d7f05c5a..39360cd72a23ee2b4c02d5d8c8b96c5a899e45f4 100644 --- a/core/java/android/webkit/LoadListener.java +++ b/core/java/android/webkit/LoadListener.java @@ -25,16 +25,16 @@ import android.net.http.HttpAuthHeader; import android.net.http.RequestHandle; import android.net.http.SslCertificate; import android.net.http.SslError; -import android.net.http.SslCertificate; import android.os.Handler; import android.os.Message; +import android.security.CertTool; import android.util.Log; import android.webkit.CacheManager.CacheResult; +import android.widget.Toast; import com.android.internal.R; -import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; @@ -72,6 +72,8 @@ class LoadListener extends Handler implements EventHandler { private static final int HTTP_NOT_FOUND = 404; private static final int HTTP_PROXY_AUTH = 407; + private static final String CERT_MIMETYPE = "application/x-x509-ca-cert"; + private static int sNativeLoaderCount; private final ByteArrayBuilder mDataBuilder = new ByteArrayBuilder(8192); @@ -934,6 +936,12 @@ class LoadListener extends Handler implements EventHandler { // This commits the headers without checking the response status code. private void commitHeaders() { + if (mIsMainPageLoader && CERT_MIMETYPE.equals(mMimeType)) { + // In the case of downloading certificate, we will save it to the + // Keystore in commitLoad. Do not call webcore. + return; + } + // Commit the headers to WebCore int nativeResponse = createNativeResponse(); // The native code deletes the native response object. @@ -974,6 +982,30 @@ class LoadListener extends Handler implements EventHandler { private void commitLoad() { if (mCancelled) return; + if (mIsMainPageLoader && CERT_MIMETYPE.equals(mMimeType)) { + // In the case of downloading certificate, we will save it to the + // Keystore and stop the current loading so that it will not + // generate a new history page + byte[] cert = new byte[mDataBuilder.getByteSize()]; + int position = 0; + ByteArrayBuilder.Chunk c; + while (true) { + c = mDataBuilder.getFirstChunk(); + if (c == null) break; + + if (c.mLength != 0) { + System.arraycopy(c.mArray, 0, cert, position, c.mLength); + position += c.mLength; + } + mDataBuilder.releaseChunk(c); + } + CertTool.getInstance().addCertificate(cert, mContext); + Toast.makeText(mContext, R.string.certificateSaved, + Toast.LENGTH_SHORT).show(); + mBrowserFrame.stopLoading(); + return; + } + // Give the data to WebKit now PerfChecker checker = new PerfChecker(); ByteArrayBuilder.Chunk c; diff --git a/core/java/android/webkit/TextDialog.java b/core/java/android/webkit/TextDialog.java index 9de97c92267b03d7272c02469e210582d585444d..99de56d7db5df348c0d4cbab5c38f06c94df11dc 100644 --- a/core/java/android/webkit/TextDialog.java +++ b/core/java/android/webkit/TextDialog.java @@ -538,7 +538,8 @@ import java.util.ArrayList; * removing the password input type. */ public void setSingleLine(boolean single) { - int inputType = EditorInfo.TYPE_CLASS_TEXT; + int inputType = EditorInfo.TYPE_CLASS_TEXT + | EditorInfo.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT; if (!single) { inputType |= EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE | EditorInfo.TYPE_TEXT_FLAG_CAP_SENTENCES diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java index 105eacdca7544d14735a2b1cc7f4735df56a4b2e..ec671d5dac5232f6cd66e16a919e34c332852b70 100644 --- a/core/java/android/webkit/WebSettings.java +++ b/core/java/android/webkit/WebSettings.java @@ -69,7 +69,24 @@ public class WebSettings { } int value; } - + + /** + * Enum for specifying the WebView's desired density. + * FAR makes 100% looking like in 240dpi + * MEDIUM makes 100% looking like in 160dpi + * CLOSE makes 100% looking like in 120dpi + * @hide Pending API council approval + */ + public enum ZoomDensity { + FAR(150), // 240dpi + MEDIUM(100), // 160dpi + CLOSE(75); // 120dpi + ZoomDensity(int size) { + value = size; + } + int value; + } + /** * Default cache usage pattern Use with {@link #setCacheMode}. */ @@ -105,6 +122,8 @@ public class WebSettings { LOW } + // WebView associated with this WebSettings. + private WebView mWebView; // BrowserFrame used to access the native frame pointer. private BrowserFrame mBrowserFrame; // Flag to prevent multiple SYNC messages at one time. @@ -123,7 +142,7 @@ public class WebSettings { private String mSerifFontFamily = "serif"; private String mCursiveFontFamily = "cursive"; private String mFantasyFontFamily = "fantasy"; - private String mDefaultTextEncoding = "Latin-1"; + private String mDefaultTextEncoding; private String mUserAgent; private boolean mUseDefaultUserAgent; private String mAcceptLanguage; @@ -145,6 +164,7 @@ public class WebSettings { // Don't need to synchronize the get/set methods as they // are basic types, also none of these values are used in // native WebCore code. + private ZoomDensity mDefaultZoom = ZoomDensity.MEDIUM; private RenderPriority mRenderPriority = RenderPriority.NORMAL; private int mOverrideCacheMode = LOAD_DEFAULT; private boolean mSaveFormData = true; @@ -237,9 +257,12 @@ public class WebSettings { * Package constructor to prevent clients from creating a new settings * instance. */ - WebSettings(Context context) { + WebSettings(Context context, WebView webview) { mEventHandler = new EventHandler(); mContext = context; + mWebView = webview; + mDefaultTextEncoding = context.getString(com.android.internal. + R.string.default_text_encoding); if (sLockForLocaleSettings == null) { sLockForLocaleSettings = new Object(); @@ -444,6 +467,31 @@ public class WebSettings { return mTextSize; } + /** + * Set the default zoom density of the page. This should be called from UI + * thread. + * @param zoom A ZoomDensity value + * @see WebSettings.ZoomDensity + * @hide Pending API council approval + */ + public void setDefaultZoom(ZoomDensity zoom) { + if (mDefaultZoom != zoom) { + mDefaultZoom = zoom; + mWebView.updateDefaultZoomDensity(zoom.value); + } + } + + /** + * Get the default zoom density of the page. This should be called from UI + * thread. + * @return A ZoomDensity value + * @see WebSettings.ZoomDensity + * @hide Pending API council approval + */ + public ZoomDensity getDefaultZoom() { + return mDefaultZoom; + } + /** * Enables using light touches to make a selection and activate mouseovers. */ diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 563d819d301f4976e5958cde70bf97bc4afe1d0a..fcf946f7d12a74341cb025d7f822ccdfa3996aa9 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -237,6 +237,7 @@ public class WebView extends AbsoluteLayout * Helper class to get velocity for fling */ VelocityTracker mVelocityTracker; + private int mMaximumFling; /** * Touch mode @@ -395,22 +396,27 @@ public class WebView extends AbsoluteLayout // width which view is considered to be fully zoomed out static final int ZOOM_OUT_WIDTH = 1008; - private static final float DEFAULT_MAX_ZOOM_SCALE = 4.0f; - private static final float DEFAULT_MIN_ZOOM_SCALE = 0.25f; + // default scale limit. Depending on the display density + private static float DEFAULT_MAX_ZOOM_SCALE; + private static float DEFAULT_MIN_ZOOM_SCALE; // scale limit, which can be set through viewport meta tag in the web page - private float mMaxZoomScale = DEFAULT_MAX_ZOOM_SCALE; - private float mMinZoomScale = DEFAULT_MIN_ZOOM_SCALE; + private float mMaxZoomScale; + private float mMinZoomScale; private boolean mMinZoomScaleFixed = false; // initial scale in percent. 0 means using default. private int mInitialScale = 0; + // default scale. Depending on the display density. + static int DEFAULT_SCALE_PERCENT; + private float mDefaultScale; + // 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; + private float mActualScale; + private float mInvActualScale; // if this is non-zero, it is used on drawing rather than mActualScale private float mZoomScale; private float mInvInitialZoomScale; @@ -635,7 +641,7 @@ public class WebView extends AbsoluteLayout mZoomFitPageButton.setOnClickListener( new View.OnClickListener() { public void onClick(View v) { - zoomWithPreview(1f); + zoomWithPreview(mDefaultScale); updateZoomButtonsEnabled(); } }); @@ -658,7 +664,7 @@ public class WebView extends AbsoluteLayout // or out. mZoomButtonsController.setZoomInEnabled(canZoomIn); mZoomButtonsController.setZoomOutEnabled(canZoomOut); - mZoomFitPageButton.setEnabled(mActualScale != 1); + mZoomFitPageButton.setEnabled(mActualScale != mDefaultScale); } mZoomOverviewButton.setVisibility(canZoomScrollOut() ? View.VISIBLE: View.GONE); @@ -671,13 +677,41 @@ public class WebView extends AbsoluteLayout setClickable(true); setLongClickable(true); - final int slop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); + final ViewConfiguration configuration = ViewConfiguration.get(getContext()); + final int slop = configuration.getScaledTouchSlop(); mTouchSlopSquare = slop * slop; mMinLockSnapReverseDistance = slop; + final float density = getContext().getResources().getDisplayMetrics().density; // use one line height, 16 based on our current default font, for how // far we allow a touch be away from the edge of a link - mNavSlop = (int) (16 * getContext().getResources() - .getDisplayMetrics().density); + mNavSlop = (int) (16 * density); + // density adjusted scale factors + DEFAULT_SCALE_PERCENT = (int) (100 * density); + mDefaultScale = density; + mActualScale = density; + mInvActualScale = 1 / density; + DEFAULT_MAX_ZOOM_SCALE = 4.0f * density; + DEFAULT_MIN_ZOOM_SCALE = 0.25f * density; + mMaxZoomScale = DEFAULT_MAX_ZOOM_SCALE; + mMinZoomScale = DEFAULT_MIN_ZOOM_SCALE; + mMaximumFling = configuration.getScaledMaximumFlingVelocity(); + } + + /* package */void updateDefaultZoomDensity(int zoomDensity) { + final float density = getContext().getResources().getDisplayMetrics().density + * 100 / zoomDensity; + if (Math.abs(density - mDefaultScale) > 0.01) { + float scaleFactor = density / mDefaultScale; + // adjust the limits + mNavSlop = (int) (16 * density); + DEFAULT_SCALE_PERCENT = (int) (100 * density); + DEFAULT_MAX_ZOOM_SCALE = 4.0f * density; + DEFAULT_MIN_ZOOM_SCALE = 0.25f * density; + mDefaultScale = density; + mMaxZoomScale *= scaleFactor; + mMinZoomScale *= scaleFactor; + setNewZoomScale(mActualScale * scaleFactor, false); + } } /* package */ boolean onSavePassword(String schemePlusHost, String username, @@ -1117,6 +1151,29 @@ public class WebView extends AbsoluteLayout clearTextEntry(); } + /** + * Load the url with postData using "POST" method into the WebView. If url + * is not a network url, it will be loaded with {link + * {@link #loadUrl(String)} instead. + * + * @param url The url of the resource to load. + * @param postData The data will be passed to "POST" request. + * + * @hide pending API solidification + */ + public void postUrl(String url, byte[] postData) { + if (URLUtil.isNetworkUrl(url)) { + switchOutDrawHistory(); + HashMap arg = new HashMap(); + arg.put("url", url); + arg.put("data", postData); + mWebViewCore.sendMessage(EventHub.POST_URL, arg); + clearTextEntry(); + } else { + loadUrl(url); + } + } + /** * Load the given data into the WebView. This will load the data into * WebView using the data: scheme. Content loaded through this mechanism @@ -4103,7 +4160,7 @@ public class WebView extends AbsoluteLayout int maxX = Math.max(computeHorizontalScrollRange() - getViewWidth(), 0); int maxY = Math.max(computeVerticalScrollRange() - getViewHeight(), 0); - mVelocityTracker.computeCurrentVelocity(1000); + mVelocityTracker.computeCurrentVelocity(1000, mMaximumFling); int vx = (int) mVelocityTracker.getXVelocity(); int vy = (int) mVelocityTracker.getYVelocity(); @@ -4134,9 +4191,9 @@ public class WebView extends AbsoluteLayout private boolean zoomWithPreview(float scale) { float oldScale = mActualScale; - // snap to 100% if it is close - if (scale > 0.95f && scale < 1.05f) { - scale = 1.0f; + // snap to DEFAULT_SCALE if it is close + if (scale > (mDefaultScale - 0.05) && scale < (mDefaultScale + 0.05)) { + scale = mDefaultScale; } setNewZoomScale(scale, false); @@ -4517,9 +4574,11 @@ public class WebView extends AbsoluteLayout break; } case SWITCH_TO_LONGPRESS: { - mTouchMode = TOUCH_DONE_MODE; - performLongClick(); - updateTextEntry(); + if (!mPreventDrag) { + mTouchMode = TOUCH_DONE_MODE; + performLongClick(); + updateTextEntry(); + } break; } case SWITCH_TO_ENTER: @@ -4651,8 +4710,8 @@ public class WebView extends AbsoluteLayout } int initialScale = msg.arg1; int viewportWidth = msg.arg2; - // by default starting a new page with 100% zoom scale. - float scale = 1.0f; + // start a new page with DEFAULT_SCALE zoom scale. + float scale = mDefaultScale; if (mInitialScale > 0) { scale = mInitialScale / 100.0f; } else { diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java index e9df453b78f126bc8964977470bc1083cea9d10e..a5fa41e8ed1872d8b84d82d5ca62f531bd0dd7e5 100644 --- a/core/java/android/webkit/WebViewCore.java +++ b/core/java/android/webkit/WebViewCore.java @@ -97,7 +97,7 @@ final class WebViewCore { private boolean mViewportUserScalable = true; - private int mRestoredScale = 100; + private int mRestoredScale = WebView.DEFAULT_SCALE_PERCENT; private int mRestoredX = 0; private int mRestoredY = 0; @@ -139,7 +139,7 @@ final class WebViewCore { // ready. mEventHub = new EventHub(); // Create a WebSettings object for maintaining all settings - mSettings = new WebSettings(mContext); + mSettings = new WebSettings(mContext, mWebView); // The WebIconDatabase needs to be initialized within the UI thread so // just request the instance here. WebIconDatabase.getInstance(); @@ -544,6 +544,8 @@ final class WebViewCore { "WEBKIT_DRAW", // = 130; "SYNC_SCROLL", // = 131; "REFRESH_PLUGINS", // = 132; + // this will replace REFRESH_PLUGINS in the next release + "POST_URL", // = 142; "SPLIT_PICTURE_SET", // = 133; "CLEAR_CONTENT", // = 134; "SET_FINAL_FOCUS", // = 135; @@ -589,6 +591,8 @@ final class WebViewCore { static final int WEBKIT_DRAW = 130; static final int SYNC_SCROLL = 131; static final int REFRESH_PLUGINS = 132; + // this will replace REFRESH_PLUGINS in the next release + static final int POST_URL = 142; static final int SPLIT_PICTURE_SET = 133; static final int CLEAR_CONTENT = 134; @@ -672,6 +676,13 @@ final class WebViewCore { loadUrl((String) msg.obj); break; + case POST_URL: { + HashMap param = (HashMap) msg.obj; + String url = (String) param.get("url"); + byte[] data = (byte[]) param.get("data"); + mBrowserFrame.postUrl(url, data); + break; + } case LOAD_DATA: HashMap loadParams = (HashMap) msg.obj; String baseUrl = (String) loadParams.get("baseUrl"); @@ -1549,19 +1560,33 @@ final class WebViewCore { // set the viewport settings from WebKit setViewportSettingsFromNative(); + // adjust the default scale to match the density + if (WebView.DEFAULT_SCALE_PERCENT != 100) { + float adjust = (float) WebView.DEFAULT_SCALE_PERCENT / 100.0f; + if (mViewportInitialScale > 0) { + mViewportInitialScale *= adjust; + } + if (mViewportMinimumScale > 0) { + mViewportMinimumScale *= adjust; + } + if (mViewportMaximumScale > 0) { + mViewportMaximumScale *= adjust; + } + } + // infer the values if they are not defined. if (mViewportWidth == 0) { if (mViewportInitialScale == 0) { - mViewportInitialScale = 100; + mViewportInitialScale = WebView.DEFAULT_SCALE_PERCENT; } if (mViewportMinimumScale == 0) { - mViewportMinimumScale = 100; + mViewportMinimumScale = WebView.DEFAULT_SCALE_PERCENT; } } if (mViewportUserScalable == false) { - mViewportInitialScale = 100; - mViewportMinimumScale = 100; - mViewportMaximumScale = 100; + mViewportInitialScale = WebView.DEFAULT_SCALE_PERCENT; + mViewportMinimumScale = WebView.DEFAULT_SCALE_PERCENT; + mViewportMaximumScale = WebView.DEFAULT_SCALE_PERCENT; } if (mViewportMinimumScale > mViewportInitialScale) { if (mViewportInitialScale == 0) { @@ -1575,9 +1600,10 @@ final class WebViewCore { mViewportMaximumScale = mViewportInitialScale; } else if (mViewportInitialScale == 0) { mViewportInitialScale = mViewportMaximumScale; - } + } } - if (mViewportWidth < 0 && mViewportInitialScale == 100) { + if (mViewportWidth < 0 + && mViewportInitialScale == WebView.DEFAULT_SCALE_PERCENT) { mViewportWidth = 0; } diff --git a/core/java/android/webkit/gears/AndroidRadioDataProvider.java b/core/java/android/webkit/gears/AndroidRadioDataProvider.java index 2d431a8d6162eff92da00ae7e5b5f3c7a29fbb69..13840428a8b726f15e69f92e2cd96df51a0f2464 100644 --- a/core/java/android/webkit/gears/AndroidRadioDataProvider.java +++ b/core/java/android/webkit/gears/AndroidRadioDataProvider.java @@ -28,6 +28,7 @@ package android.webkit.gears; import android.content.Context; import android.telephony.CellLocation; import android.telephony.ServiceState; +import android.telephony.SignalStrength; import android.telephony.gsm.GsmCellLocation; import android.telephony.PhoneStateListener; import android.telephony.TelephonyManager; @@ -54,6 +55,7 @@ public final class AndroidRadioDataProvider extends PhoneStateListener { public static final class RadioData { public int cellId = -1; public int locationAreaCode = -1; + // TODO: use new SignalStrength instead of asu public int signalStrength = -1; public int mobileCountryCode = -1; public int mobileNetworkCode = -1; @@ -179,6 +181,7 @@ public final class AndroidRadioDataProvider extends PhoneStateListener { private CellLocation cellLocation = null; /** The last known signal strength */ + // TODO: use new SignalStrength instead of asu private int signalStrength = -1; /** The last known serviceState */ @@ -207,7 +210,7 @@ public final class AndroidRadioDataProvider extends PhoneStateListener { // Register for cell id, signal strength and service state changed // notifications. telephonyManager.listen(this, PhoneStateListener.LISTEN_CELL_LOCATION - | PhoneStateListener.LISTEN_SIGNAL_STRENGTH + | PhoneStateListener.LISTEN_SIGNAL_STRENGTHS | PhoneStateListener.LISTEN_SERVICE_STATE); } @@ -226,8 +229,9 @@ public final class AndroidRadioDataProvider extends PhoneStateListener { } @Override - public void onSignalStrengthChanged(int asu) { - signalStrength = asu; + public void onSignalStrengthsChanged(SignalStrength ss) { + int gsmSignalStrength = ss.getGsmSignalStrength(); + signalStrength = (gsmSignalStrength == 99 ? -1 : gsmSignalStrength); notifyListeners(); } diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index 1ca59b2430c791d6cd363ed340e432588bde4ea9..f9ca8cb7a43b46e5f213c3c2c96bfbb099103609 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -54,7 +54,9 @@ import java.util.ArrayList; import java.util.List; /** - * Common code shared between ListView and GridView + * Base class that can be used to implement virtualized lists of items. A list does + * not have a spatial definition here. For instance, subclases of this class can + * display the content of the list in a grid, in a carousel, as stack, etc. * * @attr ref android.R.styleable#AbsListView_listSelector * @attr ref android.R.styleable#AbsListView_drawSelectorOnTop @@ -86,7 +88,7 @@ public abstract class AbsListView extends AdapterView implements Te public static final int TRANSCRIPT_MODE_NORMAL = 1; /** * The list will automatically scroll to the bottom, no matter what items - * are currently visible. + * are currently visible. * * @see #setTranscriptMode(int) */ @@ -123,7 +125,7 @@ public abstract class AbsListView extends AdapterView implements Te * Indicates the view is in the process of being flung */ static final int TOUCH_MODE_FLING = 4; - + /** * Indicates that the user is currently dragging the fast scroll thumb */ @@ -316,7 +318,7 @@ public abstract class AbsListView extends AdapterView implements Te * bitmap cache after scrolling. */ boolean mScrollingCacheEnabled; - + /** * Whether or not to enable the fast scroll feature on this list */ @@ -389,7 +391,7 @@ public abstract class AbsListView extends AdapterView implements Te * The last CheckForTap runnable we posted, if any */ private Runnable mPendingCheckForTap; - + /** * The last CheckForKeyLongPress runnable we posted, if any */ @@ -427,14 +429,17 @@ public abstract class AbsListView extends AdapterView implements Te */ private FastScroller mFastScroller; - private int mTouchSlop; + private boolean mGlobalLayoutListenerAddedFilter; + private int mTouchSlop; private float mDensityScale; private InputConnection mDefInputConnection; private InputConnectionWrapper mPublicInputConnection; private Runnable mClearScrollingCache; + private int mMinimumVelocity; + private int mMaximumVelocity; /** * Interface definition for a callback to be invoked when the list or grid @@ -529,21 +534,35 @@ public abstract class AbsListView extends AdapterView implements Te int color = a.getColor(R.styleable.AbsListView_cacheColorHint, 0); setCacheColorHint(color); - + boolean enableFastScroll = a.getBoolean(R.styleable.AbsListView_fastScrollEnabled, false); setFastScrollEnabled(enableFastScroll); boolean smoothScrollbar = a.getBoolean(R.styleable.AbsListView_smoothScrollbar, true); setSmoothScrollbarEnabled(smoothScrollbar); - + a.recycle(); } + private void initAbsListView() { + // Setting focusable in touch mode will set the focusable property to true + setFocusableInTouchMode(true); + setWillNotDraw(false); + setAlwaysDrawnWithCacheEnabled(false); + setScrollingCacheEnabled(true); + + final ViewConfiguration configuration = ViewConfiguration.get(mContext); + mTouchSlop = configuration.getScaledTouchSlop(); + mMinimumVelocity = configuration.getScaledMinimumFlingVelocity(); + mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); + mDensityScale = getContext().getResources().getDisplayMetrics().density; + } + /** - * Enables fast scrolling by letting the user quickly scroll through lists by - * dragging the fast scroll thumb. The adapter attached to the list may want + * Enables fast scrolling by letting the user quickly scroll through lists by + * dragging the fast scroll thumb. The adapter attached to the list may want * to implement {@link SectionIndexer} if it wishes to display alphabet preview and - * jump between sections of the list. + * jump between sections of the list. * @see SectionIndexer * @see #isFastScrollEnabled() * @param enabled whether or not to enable fast scrolling @@ -561,7 +580,7 @@ public abstract class AbsListView extends AdapterView implements Te } } } - + /** * Returns the current state of the fast scroll feature. * @see #setFastScrollEnabled(boolean) @@ -571,10 +590,10 @@ 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 + * @hide */ @Override protected boolean isVerticalScrollBarHidden() { @@ -592,11 +611,11 @@ public abstract class AbsListView extends AdapterView implements Te * When smooth scrollbar is disabled, the position and size of the scrollbar thumb * is based solely on the number of items in the adapter and the position of the * visible items inside the adapter. This provides a stable scrollbar as the user - * navigates through a list of items with varying heights. + * navigates through a list of items with varying heights. * * @param enabled Whether or not to enable smooth scrollbar. * - * @see #setSmoothScrollbarEnabled(boolean) + * @see #setSmoothScrollbarEnabled(boolean) * @attr ref android.R.styleable#AbsListView_smoothScrollbar */ public void setSmoothScrollbarEnabled(boolean enabled) { @@ -712,17 +731,6 @@ public abstract class AbsListView extends AdapterView implements Te } } - private void initAbsListView() { - // Setting focusable in touch mode will set the focusable property to true - setFocusableInTouchMode(true); - setWillNotDraw(false); - setAlwaysDrawnWithCacheEnabled(false); - setScrollingCacheEnabled(true); - - mTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop(); - mDensityScale = getContext().getResources().getDisplayMetrics().density; - } - private void useDefaultSelector() { setSelector(getResources().getDrawable( com.android.internal.R.drawable.list_selector_background)); @@ -828,7 +836,7 @@ public abstract class AbsListView extends AdapterView implements Te public Parcelable onSaveInstanceState() { /* * This doesn't really make sense as the place to dismiss the - * popup, but there don't seem to be any other useful hooks + * popups, but there don't seem to be any other useful hooks * that happen early enough to keep from getting complaints * about having leaked the window. */ @@ -908,17 +916,14 @@ public abstract class AbsListView extends AdapterView implements Te } private boolean acceptFilter() { - if (!mTextFilterEnabled || !(getAdapter() instanceof Filterable) || - ((Filterable) getAdapter()).getFilter() == null) { - return false; - } - return true; + return mTextFilterEnabled && getAdapter() instanceof Filterable && + ((Filterable) getAdapter()).getFilter() != null; } /** * Sets the initial value for the text filter. * @param filterText The text to use for the filter. - * + * * @see #setTextFilterEnabled */ public void setFilterText(String filterText) { @@ -944,7 +949,7 @@ public abstract class AbsListView extends AdapterView implements Te } /** - * Returns the list's text filter, if available. + * Returns the list's text filter, if available. * @return the list's text filter or null if filtering isn't enabled */ public CharSequence getTextFilter() { @@ -953,7 +958,7 @@ public abstract class AbsListView extends AdapterView implements Te } return null; } - + @Override protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) { super.onFocusChanged(gainFocus, direction, previouslyFocusedRect); @@ -1096,6 +1101,10 @@ public abstract class AbsListView extends AdapterView implements Te listPadding.bottom = mSelectionBottomPadding + mPaddingBottom; } + /** + * Subclasses should NOT override this method but + * {@link #layoutChildren()} instead. + */ @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); @@ -1111,17 +1120,22 @@ public abstract class AbsListView extends AdapterView implements Te protected boolean setFrame(int left, int top, int right, int bottom) { final boolean changed = super.setFrame(left, top, right, bottom); - // Reposition the popup when the frame has changed. This includes - // translating the widget, not just changing its dimension. The - // filter popup needs to follow the widget. - if (mFiltered && changed && getWindowVisibility() == View.VISIBLE && mPopup != null && - mPopup.isShowing()) { - positionPopup(); + if (changed) { + // Reposition the popup when the frame has changed. This includes + // translating the widget, not just changing its dimension. The + // filter popup needs to follow the widget. + final boolean visible = getWindowVisibility() == View.VISIBLE; + if (mFiltered && visible && mPopup != null && mPopup.isShowing()) { + positionPopup(); + } } return changed; } + /** + * Subclasses must override this method to layout their children. + */ protected void layoutChildren() { } @@ -1324,6 +1338,7 @@ public abstract class AbsListView extends AdapterView implements Te mDataChanged = true; rememberSyncState(); } + if (mFastScroller != null) { mFastScroller.onSizeChanged(w, h, oldw, oldh); } @@ -1494,7 +1509,7 @@ public abstract class AbsListView extends AdapterView implements Te System.arraycopy(state, enabledPos + 1, state, enabledPos, state.length - enabledPos - 1); } - + return state; } @@ -1510,6 +1525,9 @@ public abstract class AbsListView extends AdapterView implements Te final ViewTreeObserver treeObserver = getViewTreeObserver(); if (treeObserver != null) { treeObserver.addOnTouchModeChangeListener(this); + if (mTextFilterEnabled && mPopup != null && !mGlobalLayoutListenerAddedFilter) { + treeObserver.addOnGlobalLayoutListener(this); + } } } @@ -1520,6 +1538,10 @@ public abstract class AbsListView extends AdapterView implements Te final ViewTreeObserver treeObserver = getViewTreeObserver(); if (treeObserver != null) { treeObserver.removeOnTouchModeChangeListener(this); + if (mTextFilterEnabled && mPopup != null) { + treeObserver.removeGlobalOnLayoutListener(this); + mGlobalLayoutListenerAddedFilter = false; + } } } @@ -1586,16 +1608,16 @@ public abstract class AbsListView extends AdapterView implements Te */ private class WindowRunnnable { private int mOriginalAttachCount; - + public void rememberWindowAttachCount() { mOriginalAttachCount = getWindowAttachCount(); } - + public boolean sameWindow() { return hasWindowFocus() && getWindowAttachCount() == mOriginalAttachCount; } } - + private class PerformClick extends WindowRunnnable implements Runnable { View mChild; int mClickMotionPosition; @@ -1622,7 +1644,7 @@ public abstract class AbsListView extends AdapterView implements Te final long longPressId = mAdapter.getItemId(mMotionPosition); boolean handled = false; - if (sameWindow() && !mDataChanged) { + if (sameWindow() && !mDataChanged) { handled = performLongPress(child, longPressPosition, longPressId); } if (handled) { @@ -1636,7 +1658,7 @@ public abstract class AbsListView extends AdapterView implements Te } } } - + private class CheckForKeyLongPress extends WindowRunnnable implements Runnable { public void run() { if (isPressed() && mSelectedPosition >= 0) { @@ -1812,7 +1834,7 @@ public abstract class AbsListView extends AdapterView implements Te mTouchMode = TOUCH_MODE_DONE_WAITING; } } else { - mTouchMode = TOUCH_MODE_DONE_WAITING; + mTouchMode = TOUCH_MODE_DONE_WAITING; } } } @@ -1867,13 +1889,13 @@ public abstract class AbsListView extends AdapterView implements Te @Override public boolean onTouchEvent(MotionEvent ev) { - if (mFastScroller != null) { boolean intercepted = mFastScroller.onTouchEvent(ev); if (intercepted) { return true; - } + } } + final int action = ev.getAction(); final int x = (int) ev.getX(); final int y = (int) ev.getY(); @@ -2041,12 +2063,9 @@ public abstract class AbsListView extends AdapterView implements Te break; case TOUCH_MODE_SCROLL: final VelocityTracker velocityTracker = mVelocityTracker; - velocityTracker.computeCurrentVelocity(1000); - int initialVelocity = (int)velocityTracker.getYVelocity(); - - if ((Math.abs(initialVelocity) > - ViewConfiguration.get(mContext).getScaledMinimumFlingVelocity()) && - (getChildCount() > 0)) { + velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); + final int initialVelocity = (int) velocityTracker.getYVelocity(); + if (Math.abs(initialVelocity) > mMinimumVelocity && (getChildCount() > 0)) { if (mFlingRunnable == null) { mFlingRunnable = new FlingRunnable(); } @@ -2059,10 +2078,10 @@ public abstract class AbsListView extends AdapterView implements Te } setPressed(false); - + // Need to redraw since we probably aren't drawing the selector anymore invalidate(); - + final Handler handler = getHandler(); if (handler != null) { handler.removeCallbacks(mPendingCheckForLongPress); @@ -2106,7 +2125,7 @@ public abstract class AbsListView extends AdapterView implements Te return true; } - + @Override public void draw(Canvas canvas) { super.draw(canvas); @@ -2121,14 +2140,14 @@ public abstract class AbsListView extends AdapterView implements Te int x = (int) ev.getX(); int y = (int) ev.getY(); View v; - + if (mFastScroller != null) { boolean intercepted = mFastScroller.onInterceptTouchEvent(ev); if (intercepted) { return true; } } - + switch (action) { case MotionEvent.ACTION_DOWN: { int motionPosition = findMotionRow(y); @@ -2775,7 +2794,7 @@ public abstract class AbsListView extends AdapterView implements Te /** * Removes the filter window */ - void dismissPopup() { + private void dismissPopup() { if (mPopup != null) { mPopup.dismiss(); } @@ -2978,7 +2997,7 @@ public abstract class AbsListView extends AdapterView implements Te } return null; } - + /** * For filtering we proxy an input connection to an internal text editor, * and this allows the proxying to happen. @@ -2987,7 +3006,7 @@ public abstract class AbsListView extends AdapterView implements Te public boolean checkInputConnectionProxy(View view) { return view == mTextFilter; } - + /** * Creates the window for the text filter and populates it with an EditText field; * @@ -3017,6 +3036,7 @@ public abstract class AbsListView extends AdapterView implements Te p.setBackgroundDrawable(null); mPopup = p; getViewTreeObserver().addOnGlobalLayoutListener(this); + mGlobalLayoutListenerAddedFilter = true; } if (animateEntrance) { mPopup.setAnimationStyle(com.android.internal.R.style.Animation_TypingFilter); @@ -3379,7 +3399,7 @@ public abstract class AbsListView extends AdapterView implements Te mCurrentScrap = scrapViews[0]; mScrapViews = scrapViews; } - + public boolean shouldRecycleViewType(int viewType) { return viewType >= 0; } diff --git a/core/java/android/widget/AdapterView.java b/core/java/android/widget/AdapterView.java index 173e80f54f75ee3051ac8e774ce18b4c9338eea8..7d2fcbc0fa41d5b3cca95020e78555ffc4245f6b 100644 --- a/core/java/android/widget/AdapterView.java +++ b/core/java/android/widget/AdapterView.java @@ -24,11 +24,12 @@ import android.os.SystemClock; import android.util.AttributeSet; import android.util.SparseArray; import android.view.ContextMenu; +import android.view.SoundEffectConstants; import android.view.View; -import android.view.ViewGroup; import android.view.ViewDebug; -import android.view.SoundEffectConstants; +import android.view.ViewGroup; import android.view.ContextMenu.ContextMenuInfo; +import android.view.accessibility.AccessibilityEvent; /** @@ -618,7 +619,9 @@ public abstract class AdapterView extends ViewGroup { } /** - * Sets the currently selected item + * Sets the currently selected item. To support accessibility subclasses that + * override this method must invoke the overriden super method first. + * * @param position Index (starting at 0) of the data item to be selected. */ public abstract void setSelection(int position); @@ -844,6 +847,11 @@ public abstract class AdapterView extends ViewGroup { fireOnSelected(); } } + + // we fire selection events here not in View + if (mSelectedPosition != ListView.INVALID_POSITION && isShown() && !isInTouchMode()) { + sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED); + } } private void fireOnSelected() { @@ -860,6 +868,35 @@ public abstract class AdapterView extends ViewGroup { } } + @Override + public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { + boolean populated = false; + // This is an exceptional case which occurs when a window gets the + // focus and sends a focus event via its focused child to announce + // current focus/selection. AdapterView fires selection but not focus + // events so we change the event type here. + if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_FOCUSED) { + event.setEventType(AccessibilityEvent.TYPE_VIEW_SELECTED); + } + + // we send selection events only from AdapterView to avoid + // generation of such event for each child + View selectedView = getSelectedView(); + if (selectedView != null) { + populated = selectedView.dispatchPopulateAccessibilityEvent(event); + } + + if (!populated) { + if (selectedView != null) { + event.setEnabled(selectedView.isEnabled()); + } + event.setItemCount(getCount()); + event.setCurrentItemIndex(getSelectedItemPosition()); + } + + return populated; + } + @Override protected boolean canAnimate() { return super.canAnimate() && mItemCount > 0; diff --git a/core/java/android/widget/AlphabetIndexer.java b/core/java/android/widget/AlphabetIndexer.java index 4e466a09de7e4290a63f2a4c6611c0d202f1a4ac..f50676ab3914f3e38b0ace682122967ca2744c86 100644 --- a/core/java/android/widget/AlphabetIndexer.java +++ b/core/java/android/widget/AlphabetIndexer.java @@ -248,8 +248,8 @@ public class AlphabetIndexer extends DataSetObserver implements SectionIndexer { public int getSectionForPosition(int position) { int savedCursorPos = mDataCursor.getPosition(); mDataCursor.moveToPosition(position); - mDataCursor.moveToPosition(savedCursorPos); String curName = mDataCursor.getString(mColumnIndex); + mDataCursor.moveToPosition(savedCursorPos); // Linear search, as there are only a few items in the section index // Could speed this up later if it actually gets used. for (int i = 0; i < mAlphabetLength; i++) { diff --git a/core/java/android/widget/AppSecurityPermissions.java b/core/java/android/widget/AppSecurityPermissions.java index 5fa00e7d443a701a56dd8f6e74604142671ea96c..c4b5ef8915586c9ee85c5fc7e6b314a5d886b909 100755 --- a/core/java/android/widget/AppSecurityPermissions.java +++ b/core/java/android/widget/AppSecurityPermissions.java @@ -124,25 +124,25 @@ public class AppSecurityPermissions implements View.OnClickListener { if(pkg == null) { return; } - // Extract shared user permissions if any + // Get requested permissions + if (pkg.requestedPermissions != null) { + ArrayList strList = pkg.requestedPermissions; + int size = strList.size(); + if (size > 0) { + extractPerms(strList.toArray(new String[size]), permSet); + } + } + // Get permissions related to shared user if any if(pkg.mSharedUserId != null) { int sharedUid; try { sharedUid = mPm.getUidForSharedUser(pkg.mSharedUserId); + getAllUsedPermissions(sharedUid, permSet); } catch (NameNotFoundException e) { Log.w(TAG, "Could'nt retrieve shared user id for:"+pkg.packageName); - return; } - getAllUsedPermissions(sharedUid, permSet); - } else { - ArrayList strList = pkg.requestedPermissions; - int size; - if((strList == null) || ((size = strList.size()) == 0)) { - return; - } - // Extract permissions defined in current package - extractPerms(strList.toArray(new String[size]), permSet); } + // Retrieve list of permissions for(PermissionInfo tmpInfo : permSet) { mPermsList.add(tmpInfo); } @@ -176,14 +176,9 @@ public class AppSecurityPermissions implements View.OnClickListener { Log.w(TAG, "Could'nt retrieve permissions for package:"+packageName); return; } - if(pkgInfo == null) { - return; - } - String strList[] = pkgInfo.requestedPermissions; - if(strList == null) { - return; + if ((pkgInfo != null) && (pkgInfo.requestedPermissions != null)) { + extractPerms(pkgInfo.requestedPermissions, permSet); } - extractPerms(strList, permSet); } private void extractPerms(String strList[], Set permSet) { diff --git a/core/java/android/widget/ArrayAdapter.java b/core/java/android/widget/ArrayAdapter.java index c28210df6864a328195c268487b1d8d17556a402..32e55048ed18734c71c95b7f9a4e82283a7bf6c1 100644 --- a/core/java/android/widget/ArrayAdapter.java +++ b/core/java/android/widget/ArrayAdapter.java @@ -348,7 +348,12 @@ public class ArrayAdapter extends BaseAdapter implements Filterable { "ArrayAdapter requires the resource ID to be a TextView", e); } - text.setText(getItem(position).toString()); + T item = getItem(position); + if (item instanceof CharSequence) { + text.setText((CharSequence)item); + } else { + text.setText(item.toString()); + } return view; } diff --git a/core/java/android/widget/AutoCompleteTextView.java b/core/java/android/widget/AutoCompleteTextView.java index a1d16eadc0d8383ed6738d9230ffe58b3357e34a..675aba23ae81274a1c16f791fe352e57f6324505 100644 --- a/core/java/android/widget/AutoCompleteTextView.java +++ b/core/java/android/widget/AutoCompleteTextView.java @@ -80,6 +80,7 @@ import com.android.internal.R; * @attr ref android.R.styleable#AutoCompleteTextView_dropDownSelector * @attr ref android.R.styleable#AutoCompleteTextView_dropDownAnchor * @attr ref android.R.styleable#AutoCompleteTextView_dropDownWidth + * @attr ref android.R.styleable#AutoCompleteTextView_dropDownHeight */ public class AutoCompleteTextView extends EditText implements Filter.FilterListener { static final boolean DEBUG = false; @@ -101,6 +102,7 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe private int mDropDownAnchorId; private View mDropDownAnchorView; // view is retrieved lazily from id once needed private int mDropDownWidth; + private int mDropDownHeight; private Drawable mDropDownListHighlight; @@ -122,10 +124,7 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe 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; + private Runnable mShowDropDownRunnable; private AutoCompleteTextView.PassThroughClickListener mPassThroughClickListener; @@ -170,6 +169,8 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe // (for full screen width) or WRAP_CONTENT (to match the width of the anchored view). mDropDownWidth = a.getLayoutDimension(R.styleable.AutoCompleteTextView_dropDownWidth, ViewGroup.LayoutParams.WRAP_CONTENT); + mDropDownHeight = a.getLayoutDimension(R.styleable.AutoCompleteTextView_dropDownHeight, + ViewGroup.LayoutParams.WRAP_CONTENT); mHintResource = a.getResourceId(R.styleable.AutoCompleteTextView_completionHintView, R.layout.simple_dropdown_hint); @@ -258,6 +259,34 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe public void setDropDownWidth(int width) { mDropDownWidth = width; } + + /** + *

        Returns the current height for the auto-complete drop down list. This can + * be a fixed height, or {@link ViewGroup.LayoutParams#FILL_PARENT} to fill + * the screen, or {@link ViewGroup.LayoutParams#WRAP_CONTENT} to fit the height + * of the drop down's content.

        + * + * @return the height for the drop down list + * + * @attr ref android.R.styleable#AutoCompleteTextView_dropDownHeight + */ + public int getDropDownHeight() { + return mDropDownHeight; + } + + /** + *

        Sets the current height for the auto-complete drop down list. This can + * be a fixed height, or {@link ViewGroup.LayoutParams#FILL_PARENT} to fill + * the screen, or {@link ViewGroup.LayoutParams#WRAP_CONTENT} to fit the height + * of the drop down's content.

        + * + * @param height the height to use + * + * @attr ref android.R.styleable#AutoCompleteTextView_dropDownHeight + */ + public void setDropDownHeight(int height) { + mDropDownHeight = height; + } /** *

        Returns the id for the view that the auto-complete drop down list is anchored to.

        @@ -589,7 +618,7 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe if (isPopupShowing()) { // special case for the back key, we do not even try to send it // to the drop down list but instead, consume it immediately - if (keyCode == KeyEvent.KEYCODE_BACK) { + if (keyCode == KeyEvent.KEYCODE_BACK && !mDropDownAlwaysVisible) { dismissDropDown(); return true; } @@ -637,15 +666,19 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe mDropDownList.getAdapter().getCount() - 1)) { // When the selection is at the top, we block the key // event to prevent focus from moving. - mDropDownList.hideSelector(); - mDropDownList.requestLayout(); + clearListSelection(); mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED); - mPopup.update(); + showDropDown(); return true; + } else { + // WARNING: Please read the comment where mListSelectionHidden + // is declared + mDropDownList.mListSelectionHidden = false; } + consumed = mDropDownList.onKeyDown(keyCode, event); - if (DEBUG) Log.v(TAG, "Key down: code=" + keyCode + " list consumed=" - + consumed); + if (DEBUG) Log.v(TAG, "Key down: code=" + keyCode + " list consumed=" + consumed); + if (consumed) { // If it handled the key event, then the user is // navigating in the list, so we should put it in front. @@ -655,7 +688,7 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe // by ensuring it has focus and getting its window out // of touch mode. mDropDownList.requestFocusFromTouch(); - mPopup.update(); + showDropDown(); switch (keyCode) { // avoid passing the focus from the text view to the @@ -755,7 +788,7 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe } else { // drop down is automatically dismissed when enough characters // are deleted from the text view - dismissDropDown(); + if (!mDropDownAlwaysVisible) dismissDropDown(); if (mFilter != null) { mFilter.filter(null); } @@ -788,9 +821,12 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe * it back. */ public void clearListSelection() { - if (mDropDownList != null) { - mDropDownList.hideSelector(); - mDropDownList.requestLayout(); + final DropDownListView list = mDropDownList; + if (list != null) { + // WARNING: Please read the comment where mListSelectionHidden is declared + list.mListSelectionHidden = true; + list.hideSelector(); + list.requestLayout(); } } @@ -801,6 +837,7 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe */ public void setListSelection(int position) { if (mPopup.isShowing() && (mDropDownList != null)) { + mDropDownList.mListSelectionHidden = false; mDropDownList.setSelection(position); // ListView.setSelection() will call requestLayout() } @@ -893,7 +930,7 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe } } - if (mDropDownDismissedOnCompletion) { + if (mDropDownDismissedOnCompletion && !mDropDownAlwaysVisible) { dismissDropDown(); } } @@ -950,6 +987,8 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe * @param text the selected suggestion in the drop down list */ protected void replaceText(CharSequence text) { + clearComposingText(); + setText(text); // make sure we keep the caret at the end of the text view Editable spannable = getText(); @@ -958,7 +997,8 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe /** {@inheritDoc} */ public void onFilterComplete(int count) { - if (mAttachCount <= 0) return; + // Not attached to window, don't update drop-down + if (getWindowVisibility() == View.GONE) return; /* * This checks enoughToFilter() again because filtering requests @@ -971,7 +1011,7 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe if (hasFocus() && hasWindowFocus()) { showDropDown(); } - } else { + } else if (!mDropDownAlwaysVisible) { dismissDropDown(); } } @@ -980,7 +1020,7 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe public void onWindowFocusChanged(boolean hasWindowFocus) { super.onWindowFocusChanged(hasWindowFocus); performValidation(); - if (!hasWindowFocus) { + if (!hasWindowFocus && !mDropDownAlwaysVisible) { dismissDropDown(); } } @@ -989,7 +1029,7 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) { super.onFocusChanged(focused, direction, previouslyFocusedRect); performValidation(); - if (!focused) { + if (!focused && !mDropDownAlwaysVisible) { dismissDropDown(); } } @@ -997,13 +1037,11 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); - mAttachCount++; } @Override protected void onDetachedFromWindow() { dismissDropDown(); - mAttachCount--; super.onDetachedFromWindow(); } @@ -1043,13 +1081,27 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe return mDropDownAnchorView == null ? this : mDropDownAnchorView; } + /** + * Issues a runnable to show the dropdown as soon as possible. + * + * @hide internal used only by Search Dialog + */ + public void showDropDownAfterLayout() { + post(mShowDropDownRunnable); + } + /** *

        Displays the drop down on screen.

        */ public void showDropDown() { int height = buildDropDown(); + + int widthSpec = 0; + int heightSpec = 0; + + boolean noInputMethod = mPopup.getInputMethodMode() == PopupWindow.INPUT_METHOD_NOT_NEEDED; + if (mPopup.isShowing()) { - int widthSpec; if (mDropDownWidth == ViewGroup.LayoutParams.FILL_PARENT) { // The call to PopupWindow's update method below can accept -1 for any // value you do not want to update. @@ -1059,20 +1111,51 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe } else { widthSpec = mDropDownWidth; } + + if (mDropDownHeight == ViewGroup.LayoutParams.FILL_PARENT) { + // The call to PopupWindow's update method below can accept -1 for any + // value you do not want to update. + heightSpec = noInputMethod ? height : ViewGroup.LayoutParams.FILL_PARENT; + if (noInputMethod) { + mPopup.setWindowLayoutMode( + mDropDownWidth == ViewGroup.LayoutParams.FILL_PARENT ? + ViewGroup.LayoutParams.FILL_PARENT : 0, 0); + } else { + mPopup.setWindowLayoutMode( + mDropDownWidth == ViewGroup.LayoutParams.FILL_PARENT ? + ViewGroup.LayoutParams.FILL_PARENT : 0, + ViewGroup.LayoutParams.FILL_PARENT); + } + } else if (mDropDownHeight == ViewGroup.LayoutParams.WRAP_CONTENT) { + heightSpec = height; + } else { + heightSpec = mDropDownHeight; + } + mPopup.update(getDropDownAnchorView(), mDropDownHorizontalOffset, - mDropDownVerticalOffset, widthSpec, height); + mDropDownVerticalOffset, widthSpec, heightSpec); } else { if (mDropDownWidth == ViewGroup.LayoutParams.FILL_PARENT) { - mPopup.setWindowLayoutMode(ViewGroup.LayoutParams.FILL_PARENT, 0); + widthSpec = ViewGroup.LayoutParams.FILL_PARENT; } else { - mPopup.setWindowLayoutMode(0, 0); if (mDropDownWidth == ViewGroup.LayoutParams.WRAP_CONTENT) { mPopup.setWidth(getDropDownAnchorView().getWidth()); } else { mPopup.setWidth(mDropDownWidth); } } - mPopup.setHeight(height); + + if (mDropDownHeight == ViewGroup.LayoutParams.FILL_PARENT) { + heightSpec = ViewGroup.LayoutParams.FILL_PARENT; + } else { + if (mDropDownHeight == ViewGroup.LayoutParams.WRAP_CONTENT) { + mPopup.setHeight(height); + } else { + mPopup.setHeight(mDropDownHeight); + } + } + + mPopup.setWindowLayoutMode(widthSpec, heightSpec); mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED); // use outside touchable to dismiss drop down when touching outside of it, so @@ -1082,8 +1165,7 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe mPopup.showAsDropDown(getDropDownAnchorView(), mDropDownHorizontalOffset, mDropDownVerticalOffset); mDropDownList.setSelection(ListView.INVALID_POSITION); - mDropDownList.hideSelector(); - mDropDownList.requestFocus(); + clearListSelection(); post(mHideSelector); } } @@ -1119,6 +1201,22 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe mHideSelector = new ListSelectorHider(); + /** + * This Runnable exists for the sole purpose of checking if the view layout has got + * completed and if so call showDropDown to display the drop down. This is used to show + * the drop down as soon as possible after user opens up the search dialog, without + * waiting for the normal UI pipeline to do it's job which is slower than this method. + */ + mShowDropDownRunnable = new Runnable() { + public void run() { + // View layout should be all done before displaying the drop down. + View view = getDropDownAnchorView(); + if (view != null && view.getWindowToken() != null) { + showDropDown(); + } + } + }; + mDropDownList = new DropDownListView(context); mDropDownList.setSelector(mDropDownListHighlight); mDropDownList.setAdapter(mAdapter); @@ -1126,6 +1224,22 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe mDropDownList.setOnItemClickListener(mDropDownItemClickListener); mDropDownList.setFocusable(true); mDropDownList.setFocusableInTouchMode(true); + mDropDownList.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + public void onItemSelected(AdapterView parent, View view, + int position, long id) { + + if (position != -1) { + DropDownListView dropDownList = mDropDownList; + + if (dropDownList != null) { + dropDownList.mListSelectionHidden = false; + } + } + } + + public void onNothingSelected(AdapterView parent) { + } + }); if (mItemSelectedListener != null) { mDropDownList.setOnItemSelectedListener(mItemSelectedListener); @@ -1180,10 +1294,12 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe final int maxHeight = mPopup.getMaxAvailableHeight( getDropDownAnchorView(), mDropDownVerticalOffset, ignoreBottomDecorations); - final int measuredHeight = mDropDownList.measureHeightOfChildren(MeasureSpec.UNSPECIFIED, - 0, ListView.NO_POSITION, maxHeight - otherHeights, 2) + otherHeights; + if (mDropDownAlwaysVisible) { + return maxHeight; + } - return mDropDownAlwaysVisible ? maxHeight : measuredHeight; + return mDropDownList.measureHeightOfChildren(MeasureSpec.UNSPECIFIED, + 0, ListView.NO_POSITION, maxHeight - otherHeights, 2) + otherHeights; } private View getHintView(Context context) { @@ -1249,10 +1365,7 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe private class ListSelectorHider implements Runnable { public void run() { - if (mDropDownList != null) { - mDropDownList.hideSelector(); - mDropDownList.requestLayout(); - } + clearListSelection(); } } @@ -1279,6 +1392,36 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe * passed to the drop down; the list only looks focused.

        */ private static class DropDownListView extends ListView { + /* + * WARNING: This is a workaround for a touch mode issue. + * + * Touch mode is propagated lazily to windows. This causes problems in + * the following scenario: + * - Type something in the AutoCompleteTextView and get some results + * - Move down with the d-pad to select an item in the list + * - Move up with the d-pad until the selection disappears + * - Type more text in the AutoCompleteTextView *using the soft keyboard* + * and get new results; you are now in touch mode + * - The selection comes back on the first item in the list, even though + * the list is supposed to be in touch mode + * + * Using the soft keyboard triggers the touch mode change but that change + * is propagated to our window only after the first list layout, therefore + * after the list attempts to resurrect the selection. + * + * The trick to work around this issue is to pretend the list is in touch + * mode when we know that the selection should not appear, that is when + * we know the user moved the selection away from the list. + * + * This boolean is set to true whenever we explicitely hide the list's + * selection and reset to false whenver we know the user moved the + * selection back to the list. + * + * When this boolean is true, isInTouchMode() returns true, otherwise it + * returns super.isInTouchMode(). + */ + private boolean mListSelectionHidden; + /** *

        Creates a new list view wrapper.

        * @@ -1324,6 +1467,12 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe return mSelectionBottomPadding; } + @Override + public boolean isInTouchMode() { + // WARNING: Please read the comment where mListSelectionHidden is declared + return mListSelectionHidden || super.isInTouchMode(); + } + /** *

        Returns the focus state in the drop down.

        * diff --git a/core/java/android/widget/CheckedTextView.java b/core/java/android/widget/CheckedTextView.java index abcc715c77e58c60d128462510b209b009f0ede2..fd590ed16a8beea9c3726baf9465bc6feb6afb98 100644 --- a/core/java/android/widget/CheckedTextView.java +++ b/core/java/android/widget/CheckedTextView.java @@ -16,14 +16,15 @@ package android.widget; +import com.android.internal.R; + import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.view.Gravity; - -import com.android.internal.R; +import android.view.accessibility.AccessibilityEvent; /** @@ -194,5 +195,13 @@ public class CheckedTextView extends TextView implements Checkable { invalidate(); } } - + + @Override + public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { + boolean populated = super.dispatchPopulateAccessibilityEvent(event); + if (!populated) { + event.setChecked(mChecked); + } + return populated; + } } diff --git a/core/java/android/widget/CompoundButton.java b/core/java/android/widget/CompoundButton.java index d4482dc5988e10061df10336b234c4412cf80fc8..98b0976adb8dc6774a9266944decc8a2c2f755d1 100644 --- a/core/java/android/widget/CompoundButton.java +++ b/core/java/android/widget/CompoundButton.java @@ -26,7 +26,7 @@ import android.os.Parcel; import android.os.Parcelable; import android.util.AttributeSet; import android.view.Gravity; - +import android.view.accessibility.AccessibilityEvent; /** *

        @@ -124,6 +124,7 @@ public abstract class CompoundButton extends Button implements Checkable { if (mOnCheckedChangeWidgetListener != null) { mOnCheckedChangeWidgetListener.onCheckedChanged(this, mChecked); } + mBroadcasting = false; } } @@ -204,6 +205,25 @@ public abstract class CompoundButton extends Button implements Checkable { refreshDrawableState(); } + @Override + public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { + boolean populated = super.dispatchPopulateAccessibilityEvent(event); + + if (!populated) { + int resourceId = 0; + if (mChecked) { + resourceId = R.string.accessibility_compound_button_selected; + } else { + resourceId = R.string.accessibility_compound_button_unselected; + } + String state = getResources().getString(resourceId); + event.getText().add(state); + event.setChecked(mChecked); + } + + return populated; + } + @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); diff --git a/core/java/android/widget/ExpandableListView.java b/core/java/android/widget/ExpandableListView.java index 0fc8f495d8c40c3e1a5840ea377c704072c1e8fc..536062168e6168895406e794c4e674540b7e4bd7 100644 --- a/core/java/android/widget/ExpandableListView.java +++ b/core/java/android/widget/ExpandableListView.java @@ -1083,6 +1083,11 @@ public class ExpandableListView extends ListView { @Override public void onRestoreInstanceState(Parcelable state) { + if (!(state instanceof SavedState)) { + super.onRestoreInstanceState(state); + return; + } + SavedState ss = (SavedState) state; super.onRestoreInstanceState(ss.getSuperState()); diff --git a/core/java/android/widget/FastScroller.java b/core/java/android/widget/FastScroller.java index 33684779502534de432d3377deb91870b3a35878..cd965fc62e6266e9ac24638b63870a103ba4b9ff 100644 --- a/core/java/android/widget/FastScroller.java +++ b/core/java/android/widget/FastScroller.java @@ -134,7 +134,7 @@ class FastScroller { mScrollCompleted = true; - getSections(); + getSectionsFromIndexer(); mOverlayPos = new RectF(); mScrollFade = new ScrollFade(); @@ -250,7 +250,18 @@ class FastScroller { } } - private void getSections() { + SectionIndexer getSectionIndexer() { + return mSectionIndexer; + } + + Object[] getSections() { + if (mListAdapter == null && mList != null) { + getSectionsFromIndexer(); + } + return mSections; + } + + private void getSectionsFromIndexer() { Adapter adapter = mList.getAdapter(); mSectionIndexer = null; if (adapter instanceof HeaderViewListAdapter) { @@ -391,8 +402,7 @@ class FastScroller { boolean onInterceptTouchEvent(MotionEvent ev) { if (mState > STATE_NONE && ev.getAction() == MotionEvent.ACTION_DOWN) { - if (ev.getX() > mList.getWidth() - mThumbW && ev.getY() >= mThumbY && - ev.getY() <= mThumbY + mThumbH) { + if (isPointInside(ev.getX(), ev.getY())) { setState(STATE_DRAGGING); return true; } @@ -404,20 +414,20 @@ class FastScroller { if (mState == STATE_NONE) { return false; } - if (me.getAction() == MotionEvent.ACTION_DOWN) { - if (me.getX() > mList.getWidth() - mThumbW - && me.getY() >= mThumbY - && me.getY() <= mThumbY + mThumbH) { - + + final int action = me.getAction(); + + if (action == MotionEvent.ACTION_DOWN) { + if (isPointInside(me.getX(), me.getY())) { setState(STATE_DRAGGING); if (mListAdapter == null && mList != null) { - getSections(); + getSectionsFromIndexer(); } cancelFling(); return true; } - } else if (me.getAction() == MotionEvent.ACTION_UP) { + } else if (action == MotionEvent.ACTION_UP) { if (mState == STATE_DRAGGING) { setState(STATE_VISIBLE); final Handler handler = mHandler; @@ -425,7 +435,7 @@ class FastScroller { handler.postDelayed(mScrollFade, 1000); return true; } - } else if (me.getAction() == MotionEvent.ACTION_MOVE) { + } else if (action == MotionEvent.ACTION_MOVE) { if (mState == STATE_DRAGGING) { final int viewHeight = mList.getHeight(); // Jitter @@ -448,7 +458,11 @@ class FastScroller { } return false; } - + + boolean isPointInside(float x, float y) { + return x > mList.getWidth() - mThumbW && y >= mThumbY && y <= mThumbY + mThumbH; + } + public class ScrollFade implements Runnable { long mStartTime; diff --git a/core/java/android/widget/FrameLayout.java b/core/java/android/widget/FrameLayout.java index 80fbf9eddecfde9288fbabb1fff4825712d86c3d..3afd5d42f8574488fedc490a644bb4b2c65baca2 100644 --- a/core/java/android/widget/FrameLayout.java +++ b/core/java/android/widget/FrameLayout.java @@ -353,25 +353,24 @@ public class FrameLayout extends ViewGroup { if (mForeground != null) { final Drawable foreground = mForeground; + if (mForegroundBoundsChanged) { mForegroundBoundsChanged = false; - if (foreground != null) { - final Rect selfBounds = mSelfBounds; - final Rect overlayBounds = mOverlayBounds; - - final int w = mRight-mLeft; - final int h = mBottom-mTop; - - if (mForegroundInPadding) { - selfBounds.set(0, 0, w, h); - } else { - selfBounds.set(mPaddingLeft, mPaddingTop, w - mPaddingRight, h - mPaddingBottom); - } + final Rect selfBounds = mSelfBounds; + final Rect overlayBounds = mOverlayBounds; - Gravity.apply(mForegroundGravity, foreground.getIntrinsicWidth(), - foreground.getIntrinsicHeight(), selfBounds, overlayBounds); - foreground.setBounds(overlayBounds); + final int w = mRight-mLeft; + final int h = mBottom-mTop; + + if (mForegroundInPadding) { + selfBounds.set(0, 0, w, h); + } else { + selfBounds.set(mPaddingLeft, mPaddingTop, w - mPaddingRight, h - mPaddingBottom); } + + Gravity.apply(mForegroundGravity, foreground.getIntrinsicWidth(), + foreground.getIntrinsicHeight(), selfBounds, overlayBounds); + foreground.setBounds(overlayBounds); } foreground.draw(canvas); diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java index 02fc7c6b8816068cb9a890b27b862f9f07a29869..f86b37cf8b3adf41f5a62810c0300bc05231153e 100644 --- a/core/java/android/widget/HorizontalScrollView.java +++ b/core/java/android/widget/HorizontalScrollView.java @@ -114,6 +114,8 @@ public class HorizontalScrollView extends FrameLayout { private boolean mSmoothScrollingEnabled = true; private int mTouchSlop; + private int mMinimumVelocity; + private int mMaximumVelocity; public HorizontalScrollView(Context context) { this(context, null); @@ -179,7 +181,10 @@ public class HorizontalScrollView extends FrameLayout { setFocusable(true); setDescendantFocusability(FOCUS_AFTER_DESCENDANTS); setWillNotDraw(false); - mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); + final ViewConfiguration configuration = ViewConfiguration.get(mContext); + mTouchSlop = configuration.getScaledTouchSlop(); + mMinimumVelocity = configuration.getScaledMinimumFlingVelocity(); + mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); } @Override @@ -477,12 +482,10 @@ public class HorizontalScrollView extends FrameLayout { break; case MotionEvent.ACTION_UP: final VelocityTracker velocityTracker = mVelocityTracker; - velocityTracker.computeCurrentVelocity(1000); + velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); int initialVelocity = (int) velocityTracker.getXVelocity(); - if ((Math.abs(initialVelocity) > - ViewConfiguration.get(mContext).getScaledMinimumFlingVelocity()) && - getChildCount() > 0) { + if ((Math.abs(initialVelocity) > mMinimumVelocity) && getChildCount() > 0) { fling(-initialVelocity); } diff --git a/core/java/android/widget/ImageButton.java b/core/java/android/widget/ImageButton.java index 4c1cbf6a2f91f73027d4609f9b6d2f6ca25d19f4..d417e40bf29350a192dcbf3da608ee5c8a9651e3 100644 --- a/core/java/android/widget/ImageButton.java +++ b/core/java/android/widget/ImageButton.java @@ -27,9 +27,35 @@ import java.util.Map; /** *

        - * An image button displays an image that can be pressed, or clicked, by the - * user. - *

        + * Displays a button with an image (instead of text) that can be pressed + * or clicked by the user. By default, an ImageButton looks like a regular + * {@link android.widget.Button}, with the standard button background + * that changes color during different button states. The image on the surface + * of the button is defined either by the {@code android:src} attribute in the + * {@code <ImageButton>} XML element or by the + * {@link #setImageResource(int)} method.

        + * + *

        To remove the standard button background image, define your own + * background image or set the background color to be transparent.

        + *

        To indicate the different button states (focused, selected, etc.), you can + * define a different image for each state. E.g., a blue image by default, an + * orange one for when focused, and a yellow one for when pressed. An easy way to + * do this is with an XML drawable "selector." For example:

        + *
        + * <?xml version="1.0" encoding="utf-8"?>
        + * <selector xmlns:android="http://schemas.android.com/apk/res/android">
        + *     <item android:drawable="@drawable/button_normal" /> <!-- default -->
        + *     <item android:state_pressed="true"
        + *           android:drawable="@drawable/button_pressed" /> <!-- pressed -->
        + *     <item android:state_focused="true"
        + *           android:drawable="@drawable/button_focused" /> <!-- focused -->
        + * </selector>
        + * + *

        Save the XML file in your project {@code res/drawable/} folder and then + * reference it as a drawable for the source of your ImageButton (in the + * {@code android:src} attribute). Android will automatically change the image + * based on the state of the button and the corresponding images + * defined in the XML.

        * *

        XML attributes

        *

        diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java index 480b0b8a135a23bab1cd1dda6329a8b9f82e437c..27967742a135dbe2264de400d77a57c17a942a9e 100644 --- a/core/java/android/widget/ImageView.java +++ b/core/java/android/widget/ImageView.java @@ -32,6 +32,8 @@ import android.net.Uri; import android.util.AttributeSet; import android.util.Log; import android.view.View; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityManager; import android.widget.RemoteViews.RemoteView; @@ -848,7 +850,7 @@ public class ImageView extends View { public int getBaseline() { return mBaselineAligned ? getMeasuredHeight() : -1; } - + /** * Set a tinting option for the image. * @@ -878,7 +880,7 @@ public class ImageView extends View { invalidate(); } } - + public void setAlpha(int alpha) { alpha &= 0xFF; // keep it legal if (mAlpha != alpha) { diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java index 5472d6859c0abdf2ae82b8ddd90788ad55b670c4..f8a6f89a98809f05f43a57f1774d7f8f8bf3ca0b 100644 --- a/core/java/android/widget/ListView.java +++ b/core/java/android/widget/ListView.java @@ -21,6 +21,7 @@ import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Rect; import android.graphics.PixelFormat; +import android.graphics.Paint; import android.graphics.drawable.Drawable; import android.graphics.drawable.ColorDrawable; import android.os.Parcel; @@ -35,6 +36,7 @@ import android.view.ViewDebug; import android.view.ViewGroup; import android.view.ViewParent; import android.view.SoundEffectConstants; +import android.view.accessibility.AccessibilityEvent; import com.google.android.collect.Lists; import com.android.internal.R; @@ -132,6 +134,7 @@ public class ListView extends AbsListView { // used for temporary calculations. private final Rect mTempRect = new Rect(); + private Paint mDividerPaint; // the single allocated result per list view; kinda cheesey but avoids // allocating these thingies too often. @@ -171,6 +174,8 @@ public class ListView extends AbsListView { setDividerHeight(dividerHeight); } + setChoiceMode(a.getInt(R.styleable.ListView_choiceMode, CHOICE_MODE_NONE)); + mHeaderDividersEnabled = a.getBoolean(R.styleable.ListView_headerDividersEnabled, true); mFooterDividersEnabled = a.getBoolean(R.styleable.ListView_footerDividersEnabled, true); @@ -1845,6 +1850,39 @@ public class ListView extends AbsListView { } } + @Override + public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { + boolean populated = super.dispatchPopulateAccessibilityEvent(event); + + // If the item count is less than 15 then subtract disabled items from the count and + // position. Otherwise ignore disabled items. + if (!populated) { + int itemCount = 0; + int currentItemIndex = getSelectedItemPosition(); + + ListAdapter adapter = getAdapter(); + if (adapter != null) { + final int count = adapter.getCount(); + if (count < 15) { + for (int i = 0; i < count; i++) { + if (adapter.isEnabled(i)) { + itemCount++; + } else if (i <= currentItemIndex) { + currentItemIndex--; + } + } + } else { + itemCount = count; + } + } + + event.setItemCount(itemCount); + event.setCurrentItemIndex(currentItemIndex); + } + + return populated; + } + /** * setSelectionAfterHeaderView set the selection to be the first list item * after the header views. @@ -2786,12 +2824,20 @@ public class ListView extends AbsListView { */ @Override public boolean isOpaque() { - return (mCachingStarted && mIsCacheColorOpaque && mDividerIsOpaque) || super.isOpaque(); + return (mCachingStarted && mIsCacheColorOpaque && mDividerIsOpaque && + hasOpaqueScrollbars()) || super.isOpaque(); } @Override public void setCacheColorHint(int color) { - mIsCacheColorOpaque = (color >>> 24) == 0xFF; + final boolean opaque = (color >>> 24) == 0xFF; + mIsCacheColorOpaque = opaque; + if (opaque) { + if (mDividerPaint == null) { + mDividerPaint = new Paint(); + } + mDividerPaint.setColor(color); + } super.setCacheColorHint(color); } @@ -2814,6 +2860,17 @@ public class ListView extends AbsListView { final int first = mFirstPosition; final boolean areAllItemsSelectable = mAreAllItemsSelectable; final ListAdapter adapter = mAdapter; + // If the list is opaque *and* the background is not, we want to + // fill a rect where the dividers would be for non-selectable items + // If the list is opaque and the background is also opaque, we don't + // need to draw anything since the background will do it for us + final boolean fillForMissingDividers = isOpaque() && !super.isOpaque(); + + if (fillForMissingDividers && mDividerPaint == null && mIsCacheColorOpaque) { + mDividerPaint = new Paint(); + mDividerPaint.setColor(getCacheColorHint()); + } + final Paint paint = mDividerPaint; if (!mStackFromBottom) { int bottom; @@ -2825,12 +2882,18 @@ public class ListView extends AbsListView { View child = getChildAt(i); bottom = child.getBottom(); // Don't draw dividers next to items that are not enabled - if (bottom < listBottom && (areAllItemsSelectable || - (adapter.isEnabled(first + i) && (i == count - 1 || - adapter.isEnabled(first + i + 1))))) { - bounds.top = bottom; - bounds.bottom = bottom + dividerHeight; - drawDivider(canvas, bounds, i); + if (bottom < listBottom) { + if ((areAllItemsSelectable || + (adapter.isEnabled(first + i) && (i == count - 1 || + adapter.isEnabled(first + i + 1))))) { + bounds.top = bottom; + bounds.bottom = bottom + dividerHeight; + drawDivider(canvas, bounds, i); + } else if (fillForMissingDividers) { + bounds.top = bottom; + bounds.bottom = bottom + dividerHeight; + canvas.drawRect(bounds, paint); + } } } } @@ -2844,16 +2907,22 @@ public class ListView extends AbsListView { View child = getChildAt(i); top = child.getTop(); // Don't draw dividers next to items that are not enabled - if (top > listTop && (areAllItemsSelectable || - (adapter.isEnabled(first + i) && (i == count - 1 || - adapter.isEnabled(first + i + 1))))) { - bounds.top = top - dividerHeight; - bounds.bottom = top; - // Give the method the child ABOVE the divider, so we - // subtract one from our child - // position. Give -1 when there is no child above the - // divider. - drawDivider(canvas, bounds, i - 1); + if (top > listTop) { + if ((areAllItemsSelectable || + (adapter.isEnabled(first + i) && (i == count - 1 || + adapter.isEnabled(first + i + 1))))) { + bounds.top = top - dividerHeight; + bounds.bottom = top; + // Give the method the child ABOVE the divider, so we + // subtract one from our child + // position. Give -1 when there is no child above the + // divider. + drawDivider(canvas, bounds, i - 1); + } else if (fillForMissingDividers) { + bounds.top = top - dividerHeight; + bounds.bottom = top; + canvas.drawRect(bounds, paint); + } } } } @@ -3195,9 +3264,13 @@ public class ListView extends AbsListView { if (mChoiceMode == CHOICE_MODE_MULTIPLE) { mCheckStates.put(position, value); } else { - boolean oldValue = mCheckStates.get(position, false); + // Clear the old value: if something was selected and value == false + // then it is unselected mCheckStates.clear(); - if (!oldValue) { + // If value == true, select the appropriate position + // this may end up selecting the value we just cleared but this way + // we don't have to first to a get(position) + if (value) { mCheckStates.put(position, true); } } diff --git a/core/java/android/widget/MultiAutoCompleteTextView.java b/core/java/android/widget/MultiAutoCompleteTextView.java index 05abc2661eb2be3c6cbc923abb5a05dbdb79ca42..ae8027784bc38730a340482520dfd5edc36d3593 100644 --- a/core/java/android/widget/MultiAutoCompleteTextView.java +++ b/core/java/android/widget/MultiAutoCompleteTextView.java @@ -195,6 +195,8 @@ public class MultiAutoCompleteTextView extends AutoCompleteTextView { */ @Override protected void replaceText(CharSequence text) { + clearComposingText(); + int end = getSelectionEnd(); int start = mTokenizer.findTokenStart(getText(), end); diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java index 78c7bd8e54c4bc0ec3c1b2664f935b08a23a2287..0c2cd55d2ee37ccfee0ab4e14c5dd73dbebaf208 100644 --- a/core/java/android/widget/PopupWindow.java +++ b/core/java/android/widget/PopupWindow.java @@ -18,6 +18,8 @@ package android.widget; import com.android.internal.R; +import android.content.Context; +import android.content.res.TypedArray; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; @@ -33,8 +35,6 @@ import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.graphics.drawable.StateListDrawable; import android.os.IBinder; -import android.content.Context; -import android.content.res.TypedArray; import android.util.AttributeSet; import java.lang.ref.WeakReference; @@ -49,7 +49,7 @@ import java.lang.ref.WeakReference; */ public class PopupWindow { /** - * Mode for {@link #setInputMethodMode(int): the requirements for the + * Mode for {@link #setInputMethodMode(int)}: the requirements for the * input method should be based on the focusability of the popup. That is * if it is focusable than it needs to work with the input method, else * it doesn't. @@ -57,16 +57,15 @@ public class PopupWindow { public static final int INPUT_METHOD_FROM_FOCUSABLE = 0; /** - * Mode for {@link #setInputMethodMode(int): this popup always needs to + * Mode for {@link #setInputMethodMode(int)}: this popup always needs to * work with an input method, regardless of whether it is focusable. This * means that it will always be displayed so that the user can also operate * the input method while it is shown. */ - public static final int INPUT_METHOD_NEEDED = 1; /** - * Mode for {@link #setInputMethodMode(int): this popup never needs to + * Mode for {@link #setInputMethodMode(int)}: this popup never needs to * work with an input method, regardless of whether it is focusable. This * means that it will always be displayed to use as much space on the * screen as needed, regardless of whether this covers the input method. @@ -823,6 +822,7 @@ public class PopupWindow { p.flags = computeFlags(p.flags); p.type = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL; p.token = token; + p.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; p.setTitle("PopupWindow:" + Integer.toHexString(hashCode())); return p; @@ -990,7 +990,7 @@ public class PopupWindow { int bottomEdge = displayFrame.bottom; if (ignoreBottomDecorations) { - bottomEdge = WindowManagerImpl.getDefault().getDefaultDisplay().getHeight(); + bottomEdge = anchor.getContext().getResources().getDisplayMetrics().heightPixels; } final int distanceToBottom = bottomEdge - (anchorPos[1] + anchor.getHeight()) - yOffset; final int distanceToTop = anchorPos[1] - displayFrame.top + yOffset; @@ -1017,6 +1017,7 @@ public class PopupWindow { unregisterForScrollChanged(); mWindowManager.removeView(mPopupView); + if (mPopupView != mContentView && mPopupView instanceof ViewGroup) { ((ViewGroup) mPopupView).removeView(mContentView); } @@ -1071,6 +1072,20 @@ public class PopupWindow { mWindowManager.updateViewLayout(mPopupView, p); } } + + /** + *

        Updates the dimension of the popup window. Calling this function + * also updates the window with the current popup state as described + * for {@link #update()}.

        + * + * @param width the new width + * @param height the new height + */ + public void update(int width, int height) { + WindowManager.LayoutParams p = (WindowManager.LayoutParams) + mPopupView.getLayoutParams(); + update(p.x, p.y, width, height, false); + } /** *

        Updates the position and the dimension of the popup window. Width and @@ -1115,8 +1130,7 @@ public class PopupWindow { return; } - WindowManager.LayoutParams p = (WindowManager.LayoutParams) - mPopupView.getLayoutParams(); + WindowManager.LayoutParams p = (WindowManager.LayoutParams) mPopupView.getLayoutParams(); boolean update = force; @@ -1203,8 +1217,7 @@ public class PopupWindow { registerForScrollChanged(anchor, xoff, yoff); } - WindowManager.LayoutParams p = (WindowManager.LayoutParams) - mPopupView.getLayoutParams(); + WindowManager.LayoutParams p = (WindowManager.LayoutParams) mPopupView.getLayoutParams(); if (updateDimension) { if (width == -1) { @@ -1316,7 +1329,16 @@ public class PopupWindow { return super.onTouchEvent(event); } } - + + @Override + public void sendAccessibilityEvent(int eventType) { + // clinets are interested in the content not the container, make it event source + if (mContentView != null) { + mContentView.sendAccessibilityEvent(eventType); + } else { + super.sendAccessibilityEvent(eventType); + } + } } } diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java index 441414a20de4ebda7ab179b9fd7895ce07ecf674..2c9e71e02f0823d37b010980ad1eee4429a099c8 100644 --- a/core/java/android/widget/ProgressBar.java +++ b/core/java/android/widget/ProgressBar.java @@ -30,6 +30,7 @@ import android.graphics.drawable.Drawable; import android.graphics.drawable.LayerDrawable; import android.graphics.drawable.ShapeDrawable; import android.graphics.drawable.StateListDrawable; +import android.graphics.drawable.Animatable; import android.graphics.drawable.shapes.RoundRectShape; import android.graphics.drawable.shapes.Shape; import android.util.AttributeSet; @@ -683,7 +684,7 @@ public class ProgressBar extends View { return; } - if (mIndeterminateDrawable instanceof AnimationDrawable) { + if (mIndeterminateDrawable instanceof Animatable) { mShouldStartAnimationDrawable = true; mAnimation = null; } else { @@ -708,8 +709,8 @@ public class ProgressBar extends View { void stopAnimation() { mAnimation = null; mTransformation = null; - if (mIndeterminateDrawable instanceof AnimationDrawable) { - ((AnimationDrawable) mIndeterminateDrawable).stop(); + if (mIndeterminateDrawable instanceof Animatable) { + ((Animatable) mIndeterminateDrawable).stop(); mShouldStartAnimationDrawable = false; } } @@ -818,8 +819,8 @@ public class ProgressBar extends View { } d.draw(canvas); canvas.restore(); - if (mShouldStartAnimationDrawable && d instanceof AnimationDrawable) { - ((AnimationDrawable) d).start(); + if (mShouldStartAnimationDrawable && d instanceof Animatable) { + ((Animatable) d).start(); mShouldStartAnimationDrawable = false; } } diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java index edbb3db255abb06759fa278280509168e54d8932..e62dda58ba423bf1f2b04b5141b9da2bac3c01e8 100644 --- a/core/java/android/widget/RelativeLayout.java +++ b/core/java/android/widget/RelativeLayout.java @@ -16,40 +16,59 @@ package android.widget; +import com.android.internal.R; + import android.content.Context; import android.content.res.TypedArray; +import android.content.res.Resources; +import android.graphics.Rect; import android.util.AttributeSet; -import android.view.View; -import android.view.ViewGroup; +import android.util.SparseArray; +import android.util.Poolable; +import android.util.Pool; +import android.util.Pools; +import android.util.PoolableManager; +import static android.util.Log.d; import android.view.Gravity; +import android.view.View; import android.view.ViewDebug; +import android.view.ViewGroup; +import android.view.accessibility.AccessibilityEvent; import android.widget.RemoteViews.RemoteView; -import android.graphics.Rect; -import com.android.internal.R; +import java.util.Comparator; +import java.util.SortedSet; +import java.util.TreeSet; +import java.util.LinkedList; +import java.util.HashSet; +import java.util.ArrayList; /** * A Layout where the positions of the children can be described in relation to each other or to the * parent. For the sake of efficiency, the relations between views are evaluated in one pass, so if * view Y is dependent on the position of view X, make sure the view X comes first in the layout. - * + * *

        * Note that you cannot have a circular dependency between the size of the RelativeLayout and the * position of its children. For example, you cannot have a RelativeLayout whose height is set to * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT WRAP_CONTENT} and a child set to * {@link #ALIGN_PARENT_BOTTOM}. *

        - * + * *

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

        - * + * * @attr ref android.R.styleable#RelativeLayout_gravity * @attr ref android.R.styleable#RelativeLayout_ignoreGravity */ @RemoteView public class RelativeLayout extends ViewGroup { + private static final String LOG_TAG = "RelativeLayout"; + + private static final boolean DEBUG_GRAPH = false; + public static final int TRUE = -1; /** @@ -137,6 +156,13 @@ public class RelativeLayout extends ViewGroup { private final Rect mSelfBounds = new Rect(); private int mIgnoreGravity; + private SortedSet mTopToBottomLeftToRightSet = null; + + private boolean mDirtyHierarchy; + private View[] mSortedHorizontalChildren = new View[0]; + private View[] mSortedVerticalChildren = new View[0]; + private final DependencyGraph mGraph = new DependencyGraph(); + public RelativeLayout(Context context) { super(context); } @@ -224,8 +250,55 @@ public class RelativeLayout extends ViewGroup { return mBaselineView != null ? mBaselineView.getBaseline() : super.getBaseline(); } + @Override + public void requestLayout() { + super.requestLayout(); + mDirtyHierarchy = true; + } + + private void sortChildren() { + int count = getChildCount(); + if (mSortedVerticalChildren.length != count) mSortedVerticalChildren = new View[count]; + if (mSortedHorizontalChildren.length != count) mSortedHorizontalChildren = new View[count]; + + final DependencyGraph graph = mGraph; + graph.clear(); + + for (int i = 0; i < count; i++) { + final View child = getChildAt(i); + graph.add(child); + } + + if (DEBUG_GRAPH) { + d(LOG_TAG, "=== Sorted vertical children"); + graph.log(getResources(), ABOVE, BELOW, ALIGN_BASELINE, ALIGN_TOP, ALIGN_BOTTOM); + d(LOG_TAG, "=== Sorted horizontal children"); + graph.log(getResources(), LEFT_OF, RIGHT_OF, ALIGN_LEFT, ALIGN_RIGHT); + } + + graph.getSortedViews(mSortedVerticalChildren, ABOVE, BELOW, ALIGN_BASELINE, + ALIGN_TOP, ALIGN_BOTTOM); + graph.getSortedViews(mSortedHorizontalChildren, LEFT_OF, RIGHT_OF, ALIGN_LEFT, ALIGN_RIGHT); + + if (DEBUG_GRAPH) { + d(LOG_TAG, "=== Ordered list of vertical children"); + for (View view : mSortedVerticalChildren) { + DependencyGraph.printViewId(getResources(), view); + } + d(LOG_TAG, "=== Ordered list of horizontal children"); + for (View view : mSortedHorizontalChildren) { + DependencyGraph.printViewId(getResources(), view); + } + } + } + @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + if (mDirtyHierarchy) { + mDirtyHierarchy = false; + sortChildren(); + } + int myWidth = -1; int myHeight = -1; @@ -254,7 +327,6 @@ public class RelativeLayout extends ViewGroup { height = myHeight; } - int len = this.getChildCount(); mHasBaselineAlignedChild = false; View ignore = null; @@ -268,22 +340,50 @@ public class RelativeLayout extends ViewGroup { int right = Integer.MIN_VALUE; int bottom = Integer.MIN_VALUE; + boolean offsetHorizontalAxis = false; + boolean offsetVerticalAxis = false; + if ((horizontalGravity || verticalGravity) && mIgnoreGravity != View.NO_ID) { ignore = findViewById(mIgnoreGravity); } - for (int i = 0; i < len; i++) { - View child = getChildAt(i); + final boolean isWrapContentWidth = widthMode != MeasureSpec.EXACTLY; + final boolean isWrapContentHeight = heightMode != MeasureSpec.EXACTLY; + + View[] views = mSortedHorizontalChildren; + int count = views.length; + for (int i = 0; i < count; i++) { + View child = views[i]; + if (child.getVisibility() != GONE) { + LayoutParams params = (LayoutParams) child.getLayoutParams(); + + applyHorizontalSizeRules(params, myWidth); + measureChildHorizontal(child, params, myWidth, myHeight); + if (positionChildHorizontal(child, params, myWidth, isWrapContentWidth)) { + offsetHorizontalAxis = true; + } + } + } + + views = mSortedVerticalChildren; + count = views.length; + + for (int i = 0; i < count; i++) { + View child = views[i]; if (child.getVisibility() != GONE) { LayoutParams params = (LayoutParams) child.getLayoutParams(); - applySizeRules(params, myWidth, myHeight); + + applyVerticalSizeRules(params, myHeight); measureChild(child, params, myWidth, myHeight); - positionChild(child, params, myWidth, myHeight); + if (positionChildVertical(child, params, myHeight, isWrapContentHeight)) { + offsetVerticalAxis = true; + } - if (widthMode != MeasureSpec.EXACTLY) { + if (isWrapContentWidth) { width = Math.max(width, params.mRight); } - if (heightMode != MeasureSpec.EXACTLY) { + + if (isWrapContentHeight) { height = Math.max(height, params.mBottom); } @@ -300,15 +400,15 @@ public class RelativeLayout extends ViewGroup { } if (mHasBaselineAlignedChild) { - for (int i = 0; i < len; i++) { + for (int i = 0; i < count; i++) { View child = getChildAt(i); if (child.getVisibility() != GONE) { LayoutParams params = (LayoutParams) child.getLayoutParams(); alignBaseline(child, params); if (child != ignore || verticalGravity) { - left = Math.min(left, params.mLeft - params.leftMargin); - top = Math.min(top, params.mTop - params.topMargin); + left = Math.min(left, params.mLeft - params.leftMargin); + top = Math.min(top, params.mTop - params.topMargin); } if (child != ignore || horizontalGravity) { @@ -319,8 +419,8 @@ public class RelativeLayout extends ViewGroup { } } - if (widthMode != MeasureSpec.EXACTLY) { - // Width already has left padding in it since it was calculated by looking at + if (isWrapContentWidth) { + // Width already has left padding in it since it was calculated by looking at // the right of each child view width += mPaddingRight; @@ -330,9 +430,23 @@ public class RelativeLayout extends ViewGroup { width = Math.max(width, getSuggestedMinimumWidth()); width = resolveSize(width, widthMeasureSpec); + + if (offsetHorizontalAxis) { + for (int i = 0; i < count; i++) { + View child = getChildAt(i); + if (child.getVisibility() != GONE) { + LayoutParams params = (LayoutParams) child.getLayoutParams(); + final int[] rules = params.getRules(); + if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_HORIZONTAL] != 0) { + centerHorizontal(child, params, width); + } + } + } + } } - if (heightMode != MeasureSpec.EXACTLY) { - // Height already has top padding in it since it was calculated by looking at + + if (isWrapContentHeight) { + // Height already has top padding in it since it was calculated by looking at // the bottom of each child view height += mPaddingBottom; @@ -342,6 +456,19 @@ public class RelativeLayout extends ViewGroup { height = Math.max(height, getSuggestedMinimumHeight()); height = resolveSize(height, heightMeasureSpec); + + if (offsetVerticalAxis) { + for (int i = 0; i < count; i++) { + View child = getChildAt(i); + if (child.getVisibility() != GONE) { + LayoutParams params = (LayoutParams) child.getLayoutParams(); + final int[] rules = params.getRules(); + if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_VERTICAL] != 0) { + centerVertical(child, params, height); + } + } + } + } } if (horizontalGravity || verticalGravity) { @@ -355,7 +482,7 @@ public class RelativeLayout extends ViewGroup { final int horizontalOffset = contentBounds.left - left; final int verticalOffset = contentBounds.top - top; if (horizontalOffset != 0 || verticalOffset != 0) { - for (int i = 0; i < len; i++) { + for (int i = 0; i < count; i++) { View child = getChildAt(i); if (child.getVisibility() != GONE && child != ignore) { LayoutParams params = (LayoutParams) child.getLayoutParams(); @@ -409,9 +536,7 @@ public class RelativeLayout extends ViewGroup { * @param myWidth Width of the the RelativeLayout * @param myHeight Height of the RelativeLayout */ - private void measureChild(View child, LayoutParams params, int myWidth, - int myHeight) { - + private void measureChild(View child, LayoutParams params, int myWidth, int myHeight) { int childWidthMeasureSpec = getChildMeasureSpec(params.mLeft, params.mRight, params.width, params.leftMargin, params.rightMargin, @@ -425,6 +550,21 @@ public class RelativeLayout extends ViewGroup { child.measure(childWidthMeasureSpec, childHeightMeasureSpec); } + private void measureChildHorizontal(View child, LayoutParams params, int myWidth, int myHeight) { + int childWidthMeasureSpec = getChildMeasureSpec(params.mLeft, + params.mRight, params.width, + params.leftMargin, params.rightMargin, + mPaddingLeft, mPaddingRight, + myWidth); + int childHeightMeasureSpec; + if (params.width == LayoutParams.FILL_PARENT) { + childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(myHeight, MeasureSpec.EXACTLY); + } else { + childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(myHeight, MeasureSpec.AT_MOST); + } + child.measure(childWidthMeasureSpec, childHeightMeasureSpec); + } + /** * Get a measure spec that accounts for all of the constraints on this view. * This includes size contstraints imposed by the RelativeLayout as well as @@ -504,19 +644,9 @@ public class RelativeLayout extends ViewGroup { return MeasureSpec.makeMeasureSpec(childSpecSize, childSpecMode); } - /** - * After the child has been measured, assign it a position. Some views may - * already have final values for l,t,r,b. Others may have one or both edges - * unfixed (i.e. set to -1) in each dimension. These will get positioned - * based on which edge is fixed, the view's desired dimension, and whether - * or not it is centered. - * - * @param child Child to position - * @param params LayoutParams associated with child - * @param myWidth Width of the the RelativeLayout - * @param myHeight Height of the RelativeLayout - */ - private void positionChild(View child, LayoutParams params, int myWidth, int myHeight) { + private boolean positionChildHorizontal(View child, LayoutParams params, int myWidth, + boolean wrapContent) { + int[] rules = params.getRules(); if (params.mLeft < 0 && params.mRight >= 0) { @@ -527,13 +657,26 @@ public class RelativeLayout extends ViewGroup { params.mRight = params.mLeft + child.getMeasuredWidth(); } else if (params.mLeft < 0 && params.mRight < 0) { // Both left and right vary - if (0 != rules[CENTER_IN_PARENT] || 0 != rules[CENTER_HORIZONTAL]) { - centerHorizontal(child, params, myWidth); + if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_HORIZONTAL] != 0) { + if (!wrapContent) { + centerHorizontal(child, params, myWidth); + } else { + params.mLeft = mPaddingLeft + params.leftMargin; + params.mRight = params.mLeft + child.getMeasuredWidth(); + } + return true; } else { params.mLeft = mPaddingLeft + params.leftMargin; params.mRight = params.mLeft + child.getMeasuredWidth(); } } + return false; + } + + private boolean positionChildVertical(View child, LayoutParams params, int myHeight, + boolean wrapContent) { + + int[] rules = params.getRules(); if (params.mTop < 0 && params.mBottom >= 0) { // Bottom is fixed, but top varies @@ -543,26 +686,23 @@ public class RelativeLayout extends ViewGroup { params.mBottom = params.mTop + child.getMeasuredHeight(); } else if (params.mTop < 0 && params.mBottom < 0) { // Both top and bottom vary - if (0 != rules[CENTER_IN_PARENT] || 0 != rules[CENTER_VERTICAL]) { - centerVertical(child, params, myHeight); + if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_VERTICAL] != 0) { + if (!wrapContent) { + centerVertical(child, params, myHeight); + } else { + params.mTop = mPaddingTop + params.topMargin; + params.mBottom = params.mTop + child.getMeasuredHeight(); + } + return true; } else { params.mTop = mPaddingTop + params.topMargin; params.mBottom = params.mTop + child.getMeasuredHeight(); } } + return false; } - /** - * Set l,t,r,b values in the LayoutParams for one view based on its layout rules. - * Big assumption #1: All antecedents of this view have been sized & positioned - * Big assumption #2: The dimensions of the parent view (the RelativeLayout) - * are already known if they are needed. - * - * @param childParams LayoutParams for the view being positioned - * @param myWidth Width of the the RelativeLayout - * @param myHeight Height of the RelativeLayout - */ - private void applySizeRules(LayoutParams childParams, int myWidth, int myHeight) { + private void applyHorizontalSizeRules(LayoutParams childParams, int myWidth) { int[] rules = childParams.getRules(); RelativeLayout.LayoutParams anchorParams; @@ -622,6 +762,11 @@ public class RelativeLayout extends ViewGroup { // FIXME uh oh... } } + } + + private void applyVerticalSizeRules(LayoutParams childParams, int myHeight) { + int[] rules = childParams.getRules(); + RelativeLayout.LayoutParams anchorParams; childParams.mTop = -1; childParams.mBottom = -1; @@ -684,18 +829,16 @@ public class RelativeLayout extends ViewGroup { private View getRelatedView(int[] rules, int relation) { int id = rules[relation]; if (id != 0) { - View v = findViewById(id); - if (v == null) { - return null; - } + DependencyGraph.Node node = mGraph.mKeyNodes.get(id); + if (node == null) return null; + View v = node.view; // Find the first non-GONE view up the chain while (v.getVisibility() == View.GONE) { rules = ((LayoutParams) v.getLayoutParams()).getRules(); - v = v.findViewById(rules[relation]); - if (v == null) { - return null; - } + node = mGraph.mKeyNodes.get((rules[relation])); + if (node == null) return null; + v = node.view; } return v; @@ -782,6 +925,57 @@ public class RelativeLayout extends ViewGroup { return new LayoutParams(p); } + @Override + public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { + if (mTopToBottomLeftToRightSet == null) { + mTopToBottomLeftToRightSet = new TreeSet(new TopToBottomLeftToRightComparator()); + } + + // sort children top-to-bottom and left-to-right + for (int i = 0, count = getChildCount(); i < count; i++) { + mTopToBottomLeftToRightSet.add(getChildAt(i)); + } + + for (View view : mTopToBottomLeftToRightSet) { + if (view.dispatchPopulateAccessibilityEvent(event)) { + mTopToBottomLeftToRightSet.clear(); + return true; + } + } + + mTopToBottomLeftToRightSet.clear(); + return false; + } + + /** + * Compares two views in left-to-right and top-to-bottom fashion. + */ + private class TopToBottomLeftToRightComparator implements Comparator { + public int compare(View first, View second) { + // top - bottom + int topDifference = first.getTop() - second.getTop(); + if (topDifference != 0) { + return topDifference; + } + // left - right + int leftDifference = first.getLeft() - second.getLeft(); + if (leftDifference != 0) { + return leftDifference; + } + // break tie by height + int heightDiference = first.getHeight() - second.getHeight(); + if (heightDiference != 0) { + return heightDiference; + } + // break tie by width + int widthDiference = first.getWidth() - second.getWidth(); + if (widthDiference != 0) { + return widthDiference; + } + return 0; + } + } + /** * Per-child layout information associated with RelativeLayout. * @@ -823,7 +1017,7 @@ public class RelativeLayout extends ViewGroup { @ViewDebug.IntToString(from = RIGHT_OF, to = "rightOf") }, mapping = { @ViewDebug.IntToString(from = TRUE, to = "true"), - @ViewDebug.IntToString(from = 0, to = "FALSE/NO_ID") + @ViewDebug.IntToString(from = 0, to = "false/NO_ID") }) private int[] mRules = new int[VERB_COUNT]; @@ -975,4 +1169,284 @@ public class RelativeLayout extends ViewGroup { return mRules; } } + + private static class DependencyGraph { + /** + * List of all views in the graph. + */ + private ArrayList mNodes = new ArrayList(); + + /** + * List of nodes in the graph. Each node is identified by its + * view id (see View#getId()). + */ + private SparseArray mKeyNodes = new SparseArray(); + + /** + * Temporary data structure used to build the list of roots + * for this graph. + */ + private LinkedList mRoots = new LinkedList(); + + /** + * Clears the graph. + */ + void clear() { + final ArrayList nodes = mNodes; + final int count = nodes.size(); + + for (int i = 0; i < count; i++) { + nodes.get(i).release(); + } + nodes.clear(); + + mKeyNodes.clear(); + mRoots.clear(); + } + + /** + * Adds a view to the graph. + * + * @param view The view to be added as a node to the graph. + */ + void add(View view) { + final int id = view.getId(); + final Node node = Node.acquire(view); + + if (id != View.NO_ID) { + mKeyNodes.put(id, node); + } + + mNodes.add(node); + } + + /** + * Builds a sorted list of views. The sorting order depends on the dependencies + * between the view. For instance, if view C needs view A to be processed first + * and view A needs view B to be processed first, the dependency graph + * is: B -> A -> C. The sorted array will contain views B, A and C in this order. + * + * @param sorted The sorted list of views. The length of this array must + * be equal to getChildCount(). + * @param rules The list of rules to take into account. + */ + void getSortedViews(View[] sorted, int... rules) { + final LinkedList roots = findRoots(rules); + int index = 0; + + while (roots.size() > 0) { + final Node node = roots.removeFirst(); + final View view = node.view; + final int key = view.getId(); + + sorted[index++] = view; + + final HashSet dependents = node.dependents; + for (Node dependent : dependents) { + final SparseArray dependencies = dependent.dependencies; + + dependencies.remove(key); + if (dependencies.size() == 0) { + roots.add(dependent); + } + } + } + + if (index < sorted.length) { + throw new IllegalStateException("Circular dependencies cannot exist" + + " in RelativeLayout"); + } + } + + /** + * Finds the roots of the graph. A root is a node with no dependency and + * with [0..n] dependents. + * + * @param rulesFilter The list of rules to consider when building the + * dependencies + * + * @return A list of node, each being a root of the graph + */ + private LinkedList findRoots(int[] rulesFilter) { + final SparseArray keyNodes = mKeyNodes; + final ArrayList nodes = mNodes; + final int count = nodes.size(); + + // Find roots can be invoked several times, so make sure to clear + // all dependents and dependencies before running the algorithm + for (int i = 0; i < count; i++) { + final Node node = nodes.get(i); + node.dependents.clear(); + node.dependencies.clear(); + } + + // Builds up the dependents and dependencies for each node of the graph + for (int i = 0; i < count; i++) { + final Node node = nodes.get(i); + + final LayoutParams layoutParams = (LayoutParams) node.view.getLayoutParams(); + final int[] rules = layoutParams.mRules; + final int rulesCount = rulesFilter.length; + + // Look only the the rules passed in parameter, this way we build only the + // dependencies for a specific set of rules + for (int j = 0; j < rulesCount; j++) { + final int rule = rules[rulesFilter[j]]; + if (rule > 0) { + // The node this node depends on + final Node dependency = keyNodes.get(rule); + if (dependency == node) { + throw new IllegalStateException("A view cannot have a dependency" + + " on itself"); + } + if (dependency == null) { + continue; + } + // Add the current node as a dependent + dependency.dependents.add(node); + // Add a dependency to the current node + node.dependencies.put(rule, dependency); + } + } + } + + final LinkedList roots = mRoots; + roots.clear(); + + // Finds all the roots in the graph: all nodes with no dependencies + for (int i = 0; i < count; i++) { + final Node node = nodes.get(i); + if (node.dependencies.size() == 0) roots.add(node); + } + + return roots; + } + + /** + * Prints the dependency graph for the specified rules. + * + * @param resources The context's resources to print the ids. + * @param rules The list of rules to take into account. + */ + void log(Resources resources, int... rules) { + final LinkedList roots = findRoots(rules); + for (Node node : roots) { + printNode(resources, node); + } + } + + static void printViewId(Resources resources, View view) { + if (view.getId() != View.NO_ID) { + d(LOG_TAG, resources.getResourceEntryName(view.getId())); + } else { + d(LOG_TAG, "NO_ID"); + } + } + + private static void appendViewId(Resources resources, Node node, StringBuilder buffer) { + if (node.view.getId() != View.NO_ID) { + buffer.append(resources.getResourceEntryName(node.view.getId())); + } else { + buffer.append("NO_ID"); + } + } + + private static void printNode(Resources resources, Node node) { + if (node.dependents.size() == 0) { + printViewId(resources, node.view); + } else { + for (Node dependent : node.dependents) { + StringBuilder buffer = new StringBuilder(); + appendViewId(resources, node, buffer); + printdependents(resources, dependent, buffer); + } + } + } + + private static void printdependents(Resources resources, Node node, StringBuilder buffer) { + buffer.append(" -> "); + appendViewId(resources, node, buffer); + + if (node.dependents.size() == 0) { + d(LOG_TAG, buffer.toString()); + } else { + for (Node dependent : node.dependents) { + StringBuilder subBuffer = new StringBuilder(buffer); + printdependents(resources, dependent, subBuffer); + } + } + } + + /** + * A node in the dependency graph. A node is a view, its list of dependencies + * and its list of dependents. + * + * A node with no dependent is considered a root of the graph. + */ + static class Node implements Poolable { + /** + * The view representing this node in the layout. + */ + View view; + + /** + * The list of dependents for this node; a dependent is a node + * that needs this node to be processed first. + */ + final HashSet dependents = new HashSet(); + + /** + * The list of dependencies for this node. + */ + final SparseArray dependencies = new SparseArray(); + + /* + * START POOL IMPLEMENTATION + */ + // The pool is static, so all nodes instances are shared across + // activities, that's why we give it a rather high limit + private static final int POOL_LIMIT = 100; + private static final Pool sPool = Pools.synchronizedPool( + Pools.finitePool(new PoolableManager() { + public Node newInstance() { + return new Node(); + } + + public void onAcquired(Node element) { + } + + public void onReleased(Node element) { + } + }, POOL_LIMIT) + ); + + private Node mNext; + + public void setNextPoolable(Node element) { + mNext = element; + } + + public Node getNextPoolable() { + return mNext; + } + + static Node acquire(View view) { + final Node node = sPool.acquire(); + node.view = view; + + return node; + } + + void release() { + view = null; + dependents.clear(); + dependencies.clear(); + + sPool.release(this); + } + /* + * END POOL IMPLEMENTATION + */ + } + } } diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index 7936f6535d9f0ee3bb1cab8ce369dbcfe61984b3..2dac6521121397569fefe884f39905e806ec1675 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -20,10 +20,8 @@ import android.app.PendingIntent; import android.app.PendingIntent.CanceledException; 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; import android.os.Parcel; @@ -36,15 +34,12 @@ import android.view.View; import android.view.ViewGroup; import android.view.LayoutInflater.Filter; import android.view.View.OnClickListener; -import android.view.animation.Animation; -import android.view.animation.AnimationUtils; import java.lang.Class; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; @@ -69,13 +64,7 @@ public class RemoteViews implements Parcelable, Filter { * The resource ID of the layout file. (Added to the parcel) */ private int mLayoutId; - - /** - * The Context object used to inflate the layout file. Also may - * be used by actions if they need access to the senders resources. - */ - private Context mContext; - + /** * An array of actions to perform on the view tree once it has been * inflated @@ -85,7 +74,7 @@ public class RemoteViews implements Parcelable, Filter { /** * This annotation indicates that a subclass of View is alllowed to be used - * with the {@link android.widget.RemoteViews} mechanism. + * with the {@link RemoteViews} mechanism. */ @Target({ ElementType.TYPE }) @Retention(RetentionPolicy.RUNTIME) @@ -116,7 +105,7 @@ public class RemoteViews implements Parcelable, Filter { public int describeContents() { return 0; } - }; + } /** * Equivalent to calling @@ -232,15 +221,17 @@ public class RemoteViews implements Parcelable, Filter { 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); + if (targetDrawable != null) { + // 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); + } } } @@ -289,6 +280,7 @@ public class RemoteViews implements Parcelable, Filter { this.viewId = in.readInt(); this.methodName = in.readString(); this.type = in.readInt(); + //noinspection ConstantIfStatement if (false) { Log.d("RemoteViews", "read viewId=0x" + Integer.toHexString(this.viewId) + " methodName=" + this.methodName + " type=" + this.type); @@ -340,31 +332,32 @@ public class RemoteViews implements Parcelable, Filter { out.writeInt(this.viewId); out.writeString(this.methodName); out.writeInt(this.type); + //noinspection ConstantIfStatement if (false) { Log.d("RemoteViews", "write viewId=0x" + Integer.toHexString(this.viewId) + " methodName=" + this.methodName + " type=" + this.type); } switch (this.type) { case BOOLEAN: - out.writeInt(((Boolean)this.value).booleanValue() ? 1 : 0); + out.writeInt((Boolean) this.value ? 1 : 0); break; case BYTE: - out.writeByte(((Byte)this.value).byteValue()); + out.writeByte((Byte) this.value); break; case SHORT: - out.writeInt(((Short)this.value).shortValue()); + out.writeInt((Short) this.value); break; case INT: - out.writeInt(((Integer)this.value).intValue()); + out.writeInt((Integer) this.value); break; case LONG: - out.writeLong(((Long)this.value).longValue()); + out.writeLong((Long) this.value); break; case FLOAT: - out.writeFloat(((Float)this.value).floatValue()); + out.writeFloat((Float) this.value); break; case DOUBLE: - out.writeDouble(((Double)this.value).doubleValue()); + out.writeDouble((Double) this.value); break; case CHAR: out.writeInt((int)((Character)this.value).charValue()); @@ -430,7 +423,7 @@ public class RemoteViews implements Parcelable, Filter { } Class klass = view.getClass(); - Method method = null; + Method method; try { method = klass.getMethod(this.methodName, getParameterType()); } @@ -446,6 +439,7 @@ public class RemoteViews implements Parcelable, Filter { } try { + //noinspection ConstantIfStatement if (false) { Log.d("RemoteViews", "view: " + klass.getName() + " calling method: " + this.methodName + "(" + param.getName() + ") with " @@ -816,13 +810,12 @@ public class RemoteViews implements Parcelable, Filter { * @return The inflated view hierarchy */ public View apply(Context context, ViewGroup parent) { - View result = null; + View result; Context c = prepareContext(context); - Resources r = c.getResources(); - LayoutInflater inflater = (LayoutInflater) c - .getSystemService(Context.LAYOUT_INFLATER_SERVICE); + LayoutInflater inflater = (LayoutInflater) + c.getSystemService(Context.LAYOUT_INFLATER_SERVICE); inflater = inflater.cloneInContext(c); inflater.setFilter(this); @@ -858,12 +851,12 @@ public class RemoteViews implements Parcelable, Filter { } private Context prepareContext(Context context) { - Context c = null; + Context c; String packageName = mPackage; if (packageName != null) { try { - c = context.createPackageContext(packageName, 0); + c = context.createPackageContext(packageName, Context.CONTEXT_RESTRICTED); } catch (NameNotFoundException e) { Log.e(LOG_TAG, "Package name " + packageName + " not found"); c = context; @@ -872,8 +865,6 @@ public class RemoteViews implements Parcelable, Filter { c = context; } - mContext = c; - return c; } diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java index c9b3751764eb2f9407c5cbceda5398973734c278..90e1242396f3ff0e7414316a79d028c911afb6dd 100644 --- a/core/java/android/widget/ScrollView.java +++ b/core/java/android/widget/ScrollView.java @@ -115,6 +115,8 @@ public class ScrollView extends FrameLayout { private boolean mSmoothScrollingEnabled = true; private int mTouchSlop; + private int mMinimumVelocity; + private int mMaximumVelocity; public ScrollView(Context context) { this(context, null); @@ -180,7 +182,10 @@ public class ScrollView extends FrameLayout { setFocusable(true); setDescendantFocusability(FOCUS_AFTER_DESCENDANTS); setWillNotDraw(false); - mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); + final ViewConfiguration configuration = ViewConfiguration.get(mContext); + mTouchSlop = configuration.getScaledTouchSlop(); + mMinimumVelocity = configuration.getScaledMinimumFlingVelocity(); + mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); } @Override @@ -478,12 +483,10 @@ public class ScrollView extends FrameLayout { break; case MotionEvent.ACTION_UP: final VelocityTracker velocityTracker = mVelocityTracker; - velocityTracker.computeCurrentVelocity(1000); + velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); int initialVelocity = (int) velocityTracker.getYVelocity(); - if ((Math.abs(initialVelocity) > - ViewConfiguration.get(mContext).getScaledMinimumFlingVelocity()) && - getChildCount() > 0) { + if ((Math.abs(initialVelocity) > mMinimumVelocity) && getChildCount() > 0) { fling(-initialVelocity); } diff --git a/core/java/android/widget/SlidingDrawer.java b/core/java/android/widget/SlidingDrawer.java index 92561edc1cd119dff6afaaa9188c16abc359541a..f70674430135070714ac47fd5bfbc9810f3591ce 100644 --- a/core/java/android/widget/SlidingDrawer.java +++ b/core/java/android/widget/SlidingDrawer.java @@ -16,21 +16,22 @@ package android.widget; -import android.view.ViewGroup; -import android.view.View; -import android.view.MotionEvent; -import android.view.VelocityTracker; -import android.view.SoundEffectConstants; +import android.R; import android.content.Context; import android.content.res.TypedArray; -import android.util.AttributeSet; +import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Rect; -import android.graphics.Bitmap; -import android.os.SystemClock; import android.os.Handler; import android.os.Message; -import android.R; +import android.os.SystemClock; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.SoundEffectConstants; +import android.view.VelocityTracker; +import android.view.View; +import android.view.ViewGroup; +import android.view.accessibility.AccessibilityEvent; /** * SlidingDrawer hides content out of the screen and allows the user to drag a handle @@ -746,6 +747,8 @@ public class SlidingDrawer extends ViewGroup { openDrawer(); invalidate(); requestLayout(); + + sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); } /** @@ -777,6 +780,7 @@ public class SlidingDrawer extends ViewGroup { scrollListener.onScrollStarted(); } animateClose(mVertical ? mHandle.getTop() : mHandle.getLeft()); + if (scrollListener != null) { scrollListener.onScrollEnded(); } @@ -798,6 +802,9 @@ public class SlidingDrawer extends ViewGroup { scrollListener.onScrollStarted(); } animateOpen(mVertical ? mHandle.getTop() : mHandle.getLeft()); + + sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); + if (scrollListener != null) { scrollListener.onScrollEnded(); } @@ -827,6 +834,7 @@ public class SlidingDrawer extends ViewGroup { } mExpanded = true; + if (mOnDrawerOpenListener != null) { mOnDrawerOpenListener.onDrawerOpened(); } diff --git a/core/java/android/widget/TabHost.java b/core/java/android/widget/TabHost.java index dc2c70de92e4b05e7afcc971796c9ed3dfe5beb6..103d44db52a0cf3d38fc85e659261bbdec47f57b 100644 --- a/core/java/android/widget/TabHost.java +++ b/core/java/android/widget/TabHost.java @@ -87,8 +87,9 @@ public class TabHost extends FrameLayout implements ViewTreeObserver.OnTouchMode /** - *

        Call setup() before adding tabs if loading TabHost using findViewById(). However: You do - * not need to call setup() after getTabHost() in {@link android.app.TabActivity TabActivity}. + *

        Call setup() before adding tabs if loading TabHost using findViewById(). + * However: You do not need to call setup() after getTabHost() + * in {@link android.app.TabActivity TabActivity}. * Example:

        mTabHost = (TabHost)findViewById(R.id.tabhost);
         mTabHost.setup();
        @@ -176,7 +177,7 @@ mTabHost.addTab(TAB_TAG_1, "Hello, world!", "Tab 1");
                     // leaving touch mode.. if nothing has focus, let's give it to
                     // the indicator of the current tab
                     if (!mCurrentView.hasFocus() || mCurrentView.isFocused()) {
        -                mTabWidget.getChildAt(mCurrentTab).requestFocus();
        +                mTabWidget.getChildTabViewAt(mCurrentTab).requestFocus();
                     }
                 }
             }
        @@ -196,6 +197,12 @@ mTabHost.addTab(TAB_TAG_1, "Hello, world!", "Tab 1");
                 }
                 View tabIndicator = tabSpec.mIndicatorStrategy.createIndicatorView();
                 tabIndicator.setOnKeyListener(mTabKeyListener);
        +
        +        // If this is a custom view, then do not draw the bottom strips for
        +        // the tab indicators.
        +        if (tabSpec.mIndicatorStrategy instanceof ViewIndicatorStrategy) {
        +            mTabWidget.setDrawBottomStrips(false);
        +        }
                 mTabWidget.addView(tabIndicator);
                 mTabSpecs.add(tabSpec);
         
        @@ -234,7 +241,7 @@ mTabHost.addTab(TAB_TAG_1, "Hello, world!", "Tab 1");
         
             public View getCurrentTabView() {
                 if (mCurrentTab >= 0 && mCurrentTab < mTabSpecs.size()) {
        -            return mTabWidget.getChildAt(mCurrentTab);
        +            return mTabWidget.getChildTabViewAt(mCurrentTab);
                 }
                 return null;
             }
        @@ -272,7 +279,7 @@ mTabHost.addTab(TAB_TAG_1, "Hello, world!", "Tab 1");
                         && (mCurrentView.isRootNamespace())
                         && (mCurrentView.hasFocus())
                         && (mCurrentView.findFocus().focusSearch(View.FOCUS_UP) == null)) {
        -            mTabWidget.getChildAt(mCurrentTab).requestFocus();
        +            mTabWidget.getChildTabViewAt(mCurrentTab).requestFocus();
                     playSoundEffect(SoundEffectConstants.NAVIGATION_UP);
                     return true;
                 }
        @@ -363,14 +370,14 @@ mTabHost.addTab(TAB_TAG_1, "Hello, world!", "Tab 1");
                  * 
                  * @param tag
                  *            Which tab was selected.
        -         * @return The view to distplay the contents of the selected tab.
        +         * @return The view to display the contents of the selected tab.
                  */
                 View createTabContent(String tag);
             }
         
         
             /**
        -     * A tab has a tab indictor, content, and a tag that is used to keep
        +     * A tab has a tab indicator, content, and a tag that is used to keep
              * track of it.  This builder helps choose among these options.
              *
              * For the tab indicator, your choices are:
        @@ -409,6 +416,14 @@ mTabHost.addTab(TAB_TAG_1, "Hello, world!", "Tab 1");
                     return this;
                 }
         
        +        /**
        +         * Specify a view as the tab indicator.
        +         */
        +        public TabSpec setIndicator(View view) {
        +            mIndicatorStrategy = new ViewIndicatorStrategy(view);
        +            return this;
        +        }
        +
                 /**
                  * Specify the id of the view that should be used as the content
                  * of the tab.
        @@ -436,7 +451,7 @@ mTabHost.addTab(TAB_TAG_1, "Hello, world!", "Tab 1");
                 }
         
         
        -        String getTag() {
        +        public String getTag() {
                     return mTag;
                 }
             }
        @@ -524,6 +539,22 @@ mTabHost.addTab(TAB_TAG_1, "Hello, world!", "Tab 1");
                 }
             }
         
        +    /**
        +     * How to create a tab indicator by specifying a view.
        +     */
        +    private class ViewIndicatorStrategy implements IndicatorStrategy {
        +
        +        private final View mView;
        +
        +        private ViewIndicatorStrategy(View view) {
        +            mView = view;
        +        }
        +
        +        public View createIndicatorView() {
        +            return mView;
        +        }
        +    }
        +
             /**
              * How to create the tab content via a view id.
              */
        @@ -607,7 +638,7 @@ mTabHost.addTab(TAB_TAG_1, "Hello, world!", "Tab 1");
                     }
                     mLaunchedView = wd;
                     
        -            // XXX Set FOCUS_AFTER_DESCENDANTS on embedded activies for now so they can get
        +            // XXX Set FOCUS_AFTER_DESCENDANTS on embedded activities for now so they can get
                     // focus if none of their children have it. They need focus to be able to
                     // display menu items.
                     //
        diff --git a/core/java/android/widget/TabWidget.java b/core/java/android/widget/TabWidget.java
        index 20cddcb3bfb22d0dfcab3450d8a72bc129293691..a26bfa23eff81aab54ec3af56b8f959bb5f15897 100644
        --- a/core/java/android/widget/TabWidget.java
        +++ b/core/java/android/widget/TabWidget.java
        @@ -49,6 +49,8 @@ public class TabWidget extends LinearLayout implements OnFocusChangeListener {
             private Drawable mBottomLeftStrip;
             private Drawable mBottomRightStrip;
             private boolean mStripMoved;
        +    private Drawable mDividerDrawable;
        +    private boolean mDrawBottomStrips = true;
         
             public TabWidget(Context context) {
                 this(context, null);
        @@ -87,9 +89,68 @@ public class TabWidget extends LinearLayout implements OnFocusChangeListener {
                 setOnFocusChangeListener(this);
             }
         
        +    /**
        +     * Returns the tab indicator view at the given index.
        +     *
        +     * @param index the zero-based index of the tab indicator view to return
        +     * @return the tab indicator view at the given index
        +     */
        +    public View getChildTabViewAt(int index) {
        +        // If we are using dividers, then instead of tab views at 0, 1, 2, ...
        +        // we have tab views at 0, 2, 4, ...
        +        if (mDividerDrawable != null) {
        +            index *= 2;
        +        }
        +        return getChildAt(index);
        +    }
        +
        +    /**
        +     * Returns the number of tab indicator views.
        +     * @return the number of tab indicator views.
        +     */
        +    public int getTabCount() {
        +        int children = getChildCount();
        +
        +        // If we have dividers, then we will always have an odd number of
        +        // children: 1, 3, 5, ... and we want to convert that sequence to
        +        // this: 1, 2, 3, ...
        +        if (mDividerDrawable != null) {
        +            children = (children + 1) / 2;
        +        }
        +        return children;
        +    }
        +
        +    /**
        +     * Sets the drawable to use as a divider between the tab indicators.
        +     * @param drawable the divider drawable
        +     */
        +    public void setDividerDrawable(Drawable drawable) {
        +        mDividerDrawable = drawable;
        +    }
        +
        +    /**
        +     * Sets the drawable to use as a divider between the tab indicators.
        +     * @param resId the resource identifier of the drawable to use as a
        +     * divider.
        +     */
        +    public void setDividerDrawable(int resId) {
        +        mDividerDrawable = mContext.getResources().getDrawable(resId);
        +    }
        +
        +    /**
        +     * Controls whether the bottom strips on the tab indicators are drawn or
        +     * not.  The default is to draw them.  If the user specifies a custom
        +     * view for the tab indicators, then the TabHost class calls this method
        +     * to disable drawing of the bottom strips.
        +     * @param drawBottomStrips true if the bottom strips should be drawn.
        +     */
        +    void setDrawBottomStrips(boolean drawBottomStrips) {
        +        mDrawBottomStrips = drawBottomStrips;
        +    }
        +
             @Override
             public void childDrawableStateChanged(View child) {
        -        if (child == getChildAt(mSelectedTab)) {
        +        if (child == getChildTabViewAt(mSelectedTab)) {
                     // To make sure that the bottom strip is redrawn
                     invalidate();
                 }
        @@ -100,7 +161,14 @@ public class TabWidget extends LinearLayout implements OnFocusChangeListener {
             public void dispatchDraw(Canvas canvas) {
                 super.dispatchDraw(canvas);
         
        -        View selectedChild = getChildAt(mSelectedTab);
        +        // If the user specified a custom view for the tab indicators, then
        +        // do not draw the bottom strips.
        +        if (!mDrawBottomStrips) {
        +            // Skip drawing the bottom strips.
        +            return;
        +        }
        +
        +        View selectedChild = getChildTabViewAt(mSelectedTab);
                 
                 mBottomLeftStrip.setState(selectedChild.getDrawableState());
                 mBottomRightStrip.setState(selectedChild.getDrawableState());
        @@ -157,13 +225,13 @@ public class TabWidget extends LinearLayout implements OnFocusChangeListener {
              *  @see #focusCurrentTab
              */
             public void setCurrentTab(int index) {
        -        if (index < 0 || index >= getChildCount()) {
        +        if (index < 0 || index >= getTabCount()) {
                     return;
                 }
         
        -        getChildAt(mSelectedTab).setSelected(false);
        +        getChildTabViewAt(mSelectedTab).setSelected(false);
                 mSelectedTab = index;
        -        getChildAt(mSelectedTab).setSelected(true);
        +        getChildTabViewAt(mSelectedTab).setSelected(true);
                 mStripMoved = true;
             }
             
        @@ -189,17 +257,17 @@ public class TabWidget extends LinearLayout implements OnFocusChangeListener {
                 
                 // change the focus if applicable.
                 if (oldTab != index) {
        -            getChildAt(index).requestFocus();
        +            getChildTabViewAt(index).requestFocus();
                 }
             }
             
             @Override
             public void setEnabled(boolean enabled) {
                 super.setEnabled(enabled);
        -        int count = getChildCount();
        +        int count = getTabCount();
                 
        -        for (int i=0; i 0) {
        +            View divider = new View(mContext);
        +            final LinearLayout.LayoutParams lp = new LayoutParams(
        +                    mDividerDrawable.getIntrinsicWidth(),
        +                    mDividerDrawable.getIntrinsicHeight());
        +            lp.setMargins(0, 0, 0, 0);
        +            divider.setLayoutParams(lp);
        +            divider.setBackgroundDrawable(mDividerDrawable);
        +            super.addView(divider);
        +        }
                 super.addView(child);
         
                 // TODO: detect this via geometry with a tabwidget listener rather
                 // than potentially interfere with the view's listener
        -        child.setOnClickListener(new TabClickListener(getChildCount() - 1));
        +        child.setOnClickListener(new TabClickListener(getTabCount() - 1));
                 child.setOnFocusChangeListener(this);
             }
         
        -
        -
        -
             /**
              * Provides a way for {@link TabHost} to be notified that the user clicked on a tab indicator.
              */
        @@ -238,14 +315,15 @@ public class TabWidget extends LinearLayout implements OnFocusChangeListener {
         
             public void onFocusChange(View v, boolean hasFocus) {
                 if (v == this && hasFocus) {
        -            getChildAt(mSelectedTab).requestFocus();
        +            getChildTabViewAt(mSelectedTab).requestFocus();
                     return;
                 }
                 
                 if (hasFocus) {
                     int i = 0;
        -            while (i < getChildCount()) {
        -                if (getChildAt(i) == v) {
        +            int numTabs = getTabCount();
        +            while (i < numTabs) {
        +                if (getChildTabViewAt(i) == v) {
                             setCurrentTab(i);
                             mSelectionChangedListener.onTabSelectionChanged(i, false);
                             break;
        diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
        index adfc74fbfae26a098b24f825bfdf5a503d10353d..d8ed4f0e4e86c352facb86b0561a55e204e95be1 100644
        --- a/core/java/android/widget/TextView.java
        +++ b/core/java/android/widget/TextView.java
        @@ -16,6 +16,11 @@
         
         package android.widget;
         
        +import com.android.internal.util.FastMath;
        +import com.android.internal.widget.EditableInputConnection;
        +
        +import org.xmlpull.v1.XmlPullParserException;
        +
         import android.content.Context;
         import android.content.Intent;
         import android.content.res.ColorStateList;
        @@ -31,17 +36,17 @@ import android.graphics.Typeface;
         import android.graphics.drawable.Drawable;
         import android.os.Bundle;
         import android.os.Handler;
        +import android.os.Message;
         import android.os.Parcel;
         import android.os.Parcelable;
         import android.os.ResultReceiver;
         import android.os.SystemClock;
        -import android.os.Message;
         import android.text.BoringLayout;
        +import android.text.ClipboardManager;
         import android.text.DynamicLayout;
         import android.text.Editable;
         import android.text.GetChars;
         import android.text.GraphicsOperations;
        -import android.text.ClipboardManager;
         import android.text.InputFilter;
         import android.text.InputType;
         import android.text.Layout;
        @@ -49,9 +54,9 @@ import android.text.ParcelableSpan;
         import android.text.Selection;
         import android.text.SpanWatcher;
         import android.text.Spannable;
        +import android.text.SpannableString;
         import android.text.Spanned;
         import android.text.SpannedString;
        -import android.text.SpannableString;
         import android.text.StaticLayout;
         import android.text.TextPaint;
         import android.text.TextUtils;
        @@ -64,19 +69,18 @@ import android.text.method.KeyListener;
         import android.text.method.LinkMovementMethod;
         import android.text.method.MetaKeyKeyListener;
         import android.text.method.MovementMethod;
        -import android.text.method.TimeKeyListener;
        -
         import android.text.method.PasswordTransformationMethod;
         import android.text.method.SingleLineTransformationMethod;
         import android.text.method.TextKeyListener;
        +import android.text.method.TimeKeyListener;
         import android.text.method.TransformationMethod;
         import android.text.style.ParagraphStyle;
         import android.text.style.URLSpan;
         import android.text.style.UpdateAppearance;
         import android.text.util.Linkify;
         import android.util.AttributeSet;
        -import android.util.Log;
         import android.util.FloatMath;
        +import android.util.Log;
         import android.util.TypedValue;
         import android.view.ContextMenu;
         import android.view.Gravity;
        @@ -89,25 +93,22 @@ import android.view.ViewDebug;
         import android.view.ViewRoot;
         import android.view.ViewTreeObserver;
         import android.view.ViewGroup.LayoutParams;
        +import android.view.accessibility.AccessibilityEvent;
        +import android.view.accessibility.AccessibilityManager;
         import android.view.animation.AnimationUtils;
         import android.view.inputmethod.BaseInputConnection;
         import android.view.inputmethod.CompletionInfo;
        +import android.view.inputmethod.EditorInfo;
         import android.view.inputmethod.ExtractedText;
         import android.view.inputmethod.ExtractedTextRequest;
         import android.view.inputmethod.InputConnection;
         import android.view.inputmethod.InputMethodManager;
        -import android.view.inputmethod.EditorInfo;
         import android.widget.RemoteViews.RemoteView;
         
         import java.io.IOException;
         import java.lang.ref.WeakReference;
         import java.util.ArrayList;
         
        -import com.android.internal.util.FastMath;
        -import com.android.internal.widget.EditableInputConnection;
        -
        -import org.xmlpull.v1.XmlPullParserException;
        -
         /**
          * Displays text to the user and optionally allows them to edit it.  A TextView
          * is a complete text editor, however the basic class is configured to not
        @@ -126,6 +127,8 @@ import org.xmlpull.v1.XmlPullParserException;
          * @attr ref android.R.styleable#TextView_textColor
          * @attr ref android.R.styleable#TextView_textColorHighlight
          * @attr ref android.R.styleable#TextView_textColorHint
        + * @attr ref android.R.styleable#TextView_textAppearance
        + * @attr ref android.R.styleable#TextView_textColorLink
          * @attr ref android.R.styleable#TextView_textSize
          * @attr ref android.R.styleable#TextView_textScaleX
          * @attr ref android.R.styleable#TextView_typeface
        @@ -163,13 +166,22 @@ import org.xmlpull.v1.XmlPullParserException;
          * @attr ref android.R.styleable#TextView_capitalize
          * @attr ref android.R.styleable#TextView_autoText
          * @attr ref android.R.styleable#TextView_editable
        + * @attr ref android.R.styleable#TextView_freezesText
        + * @attr ref android.R.styleable#TextView_ellipsize
          * @attr ref android.R.styleable#TextView_drawableTop
          * @attr ref android.R.styleable#TextView_drawableBottom
          * @attr ref android.R.styleable#TextView_drawableRight
          * @attr ref android.R.styleable#TextView_drawableLeft
        + * @attr ref android.R.styleable#TextView_drawablePadding
          * @attr ref android.R.styleable#TextView_lineSpacingExtra
          * @attr ref android.R.styleable#TextView_lineSpacingMultiplier
          * @attr ref android.R.styleable#TextView_marqueeRepeatLimit
        + * @attr ref android.R.styleable#TextView_inputType
        + * @attr ref android.R.styleable#TextView_imeOptions
        + * @attr ref android.R.styleable#TextView_privateImeOptions
        + * @attr ref android.R.styleable#TextView_imeActionLabel
        + * @attr ref android.R.styleable#TextView_imeActionId
        + * @attr ref android.R.styleable#TextView_editorExtras
          */
         @RemoteView
         public class TextView extends View implements ViewTreeObserver.OnPreDrawListener {
        @@ -404,6 +416,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                 boolean singleLine = false;
                 int maxlength = -1;
                 CharSequence text = "";
        +        CharSequence hint = null;
                 int shadowcolor = 0;
                 float dx = 0, dy = 0, r = 0;
                 boolean password = false;
        @@ -531,7 +544,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                         break;
         
                     case com.android.internal.R.styleable.TextView_hint:
        -                setHint(a.getText(attr));
        +                hint = a.getText(attr);
                         break;
         
                     case com.android.internal.R.styleable.TextView_text:
        @@ -861,6 +874,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                 }
         
                 setText(text, bufferType);
        +        if (hint != null) setHint(hint);
         
                 /*
                  * Views are not normally focusable unless specified to be.
        @@ -1328,9 +1342,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                         } else {
                             // We need to retain the last set padding, so just clear
                             // out all of the fields in the existing structure.
        +                    if (dr.mDrawableLeft != null) dr.mDrawableLeft.setCallback(null);
                             dr.mDrawableLeft = null;
        +                    if (dr.mDrawableTop != null) dr.mDrawableTop.setCallback(null);
                             dr.mDrawableTop = null;
        +                    if (dr.mDrawableRight != null) dr.mDrawableRight.setCallback(null);
                             dr.mDrawableRight = null;
        +                    if (dr.mDrawableBottom != null) dr.mDrawableBottom.setCallback(null);
                             dr.mDrawableBottom = null;
                             dr.mDrawableSizeLeft = dr.mDrawableHeightLeft = 0;
                             dr.mDrawableSizeRight = dr.mDrawableHeightRight = 0;
        @@ -1343,19 +1361,32 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                         mDrawables = dr = new Drawables();
                     }
         
        +            if (dr.mDrawableLeft != left && dr.mDrawableLeft != null) {
        +                dr.mDrawableLeft.setCallback(null);
        +            }
                     dr.mDrawableLeft = left;
        +            if (dr.mDrawableTop != left && dr.mDrawableTop != null) {
        +                dr.mDrawableTop.setCallback(null);
        +            }
                     dr.mDrawableTop = top;
        +            if (dr.mDrawableRight != left && dr.mDrawableRight != null) {
        +                dr.mDrawableRight.setCallback(null);
        +            }
                     dr.mDrawableRight = right;
        +            if (dr.mDrawableBottom != left && dr.mDrawableBottom != null) {
        +                dr.mDrawableBottom.setCallback(null);
        +            }
                     dr.mDrawableBottom = bottom;
         
                     final Rect compoundRect = dr.mCompoundRect;
        -            int[] state = null;
        +            int[] state;
         
                     state = getDrawableState();
         
                     if (left != null) {
                         left.setState(state);
                         left.copyBounds(compoundRect);
        +                left.setCallback(this);
                         dr.mDrawableSizeLeft = compoundRect.width();
                         dr.mDrawableHeightLeft = compoundRect.height();
                     } else {
        @@ -1365,6 +1396,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                     if (right != null) {
                         right.setState(state);
                         right.copyBounds(compoundRect);
        +                right.setCallback(this);
                         dr.mDrawableSizeRight = compoundRect.width();
                         dr.mDrawableHeightRight = compoundRect.height();
                     } else {
        @@ -1374,6 +1406,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                     if (top != null) {
                         top.setState(state);
                         top.copyBounds(compoundRect);
        +                top.setCallback(this);
                         dr.mDrawableSizeTop = compoundRect.height();
                         dr.mDrawableWidthTop = compoundRect.width();
                     } else {
        @@ -1383,6 +1416,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                     if (bottom != null) {
                         bottom.setState(state);
                         bottom.copyBounds(compoundRect);
        +                bottom.setCallback(this);
                         dr.mDrawableSizeBottom = compoundRect.height();
                         dr.mDrawableWidthBottom = compoundRect.width();
                     } else {
        @@ -2785,8 +2819,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                     checkForRelayout();
                 }
         
        -        if (mText.length() == 0)
        +        if (mText.length() == 0) {
                     invalidate();
        +        }
             }
         
             /**
        @@ -3646,12 +3681,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
         
             @Override
             protected boolean isPaddingOffsetRequired() {
        -        return mShadowRadius != 0;
        +        return mShadowRadius != 0 || mDrawables != null;
             }
         
             @Override
             protected int getLeftPaddingOffset() {
        -        return (int) Math.min(0, mShadowDx - mShadowRadius);
        +        return getCompoundPaddingLeft() - mPaddingLeft +
        +                (int) Math.min(0, mShadowDx - mShadowRadius);
             }
         
             @Override
        @@ -3666,7 +3702,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
         
             @Override
             protected int getRightPaddingOffset() {
        -        return (int) Math.max(0, mShadowDx + mShadowRadius);
        +        return -(getCompoundPaddingRight() - mPaddingRight) +
        +                (int) Math.max(0, mShadowDx + mShadowRadius);
             }
         
             @Override
        @@ -3679,6 +3716,54 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                 return verified;
             }
         
        +    @Override
        +    public void invalidateDrawable(Drawable drawable) {
        +        if (verifyDrawable(drawable)) {
        +            final Rect dirty = drawable.getBounds();
        +            int scrollX = mScrollX;
        +            int scrollY = mScrollY;
        +
        +            // IMPORTANT: The coordinates below are based on the coordinates computed
        +            // for each compound drawable in onDraw(). Make sure to update each section
        +            // accordingly.
        +            final TextView.Drawables drawables = mDrawables;
        +            if (drawables != null) {
        +                if (drawable == drawables.mDrawableLeft) {
        +                    final int compoundPaddingTop = getCompoundPaddingTop();
        +                    final int compoundPaddingBottom = getCompoundPaddingBottom();
        +                    final int vspace = mBottom - mTop - compoundPaddingBottom - compoundPaddingTop;
        +
        +                    scrollX += mPaddingLeft;
        +                    scrollY += compoundPaddingTop + (vspace - drawables.mDrawableHeightLeft) / 2;
        +                } else if (drawable == drawables.mDrawableRight) {
        +                    final int compoundPaddingTop = getCompoundPaddingTop();
        +                    final int compoundPaddingBottom = getCompoundPaddingBottom();
        +                    final int vspace = mBottom - mTop - compoundPaddingBottom - compoundPaddingTop;
        +
        +                    scrollX += (mRight - mLeft - mPaddingRight - drawables.mDrawableSizeRight);
        +                    scrollY += compoundPaddingTop + (vspace - drawables.mDrawableHeightRight) / 2;
        +                } else if (drawable == drawables.mDrawableTop) {
        +                    final int compoundPaddingLeft = getCompoundPaddingLeft();
        +                    final int compoundPaddingRight = getCompoundPaddingRight();
        +                    final int hspace = mRight - mLeft - compoundPaddingRight - compoundPaddingLeft;
        +
        +                    scrollX += compoundPaddingLeft + (hspace - drawables.mDrawableWidthTop) / 2;
        +                    scrollY += mPaddingTop;
        +                } else if (drawable == drawables.mDrawableBottom) {
        +                    final int compoundPaddingLeft = getCompoundPaddingLeft();
        +                    final int compoundPaddingRight = getCompoundPaddingRight();
        +                    final int hspace = mRight - mLeft - compoundPaddingRight - compoundPaddingLeft;
        +
        +                    scrollX += compoundPaddingLeft + (hspace - drawables.mDrawableWidthBottom) / 2;
        +                    scrollY += (mBottom - mTop - mPaddingBottom - drawables.mDrawableSizeBottom);
        +                }
        +            }
        +
        +            invalidate(dirty.left + scrollX, dirty.top + scrollY,
        +                    dirty.right + scrollX, dirty.bottom + scrollY);
        +        }
        +    }
        +
             @Override
             protected void onDraw(Canvas canvas) {
                 restartMarqueeIfNeeded();
        @@ -3707,6 +3792,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                     int vspace = bottom - top - compoundPaddingBottom - compoundPaddingTop;
                     int hspace = right - left - compoundPaddingRight - compoundPaddingLeft;
         
        +            // IMPORTANT: The coordinates computed are also used in invalidateDrawable()
        +            // Make sure to update invalidateDrawable() when changing this code.
                     if (dr.mDrawableLeft != null) {
                         canvas.save();
                         canvas.translate(scrollX + mPaddingLeft,
        @@ -3716,6 +3803,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                         canvas.restore();
                     }
         
        +            // IMPORTANT: The coordinates computed are also used in invalidateDrawable()
        +            // Make sure to update invalidateDrawable() when changing this code.
                     if (dr.mDrawableRight != null) {
                         canvas.save();
                         canvas.translate(scrollX + right - left - mPaddingRight - dr.mDrawableSizeRight,
        @@ -3724,6 +3813,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                         canvas.restore();
                     }
         
        +            // IMPORTANT: The coordinates computed are also used in invalidateDrawable()
        +            // Make sure to update invalidateDrawable() when changing this code.
                     if (dr.mDrawableTop != null) {
                         canvas.save();
                         canvas.translate(scrollX + compoundPaddingLeft + (hspace - dr.mDrawableWidthTop) / 2,
        @@ -3732,6 +3823,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                         canvas.restore();
                     }
         
        +            // IMPORTANT: The coordinates computed are also used in invalidateDrawable()
        +            // Make sure to update invalidateDrawable() when changing this code.
                     if (dr.mDrawableBottom != null) {
                         canvas.save();
                         canvas.translate(scrollX + compoundPaddingLeft +
        @@ -4714,10 +4807,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                         alignment = Layout.Alignment.ALIGN_NORMAL;
                 }
         
        +        boolean shouldEllipsize = mEllipsize != null && mInput == null;
        +
                 if (mText instanceof Spannable) {
                     mLayout = new DynamicLayout(mText, mTransformed, mTextPaint, w,
                             alignment, mSpacingMult,
        -                    mSpacingAdd, mIncludePad, mEllipsize,
        +                    mSpacingAdd, mIncludePad, mInput == null ? mEllipsize : null,
                             ellipsisWidth);
                 } else {
                     if (boring == UNKNOWN_BORING) {
        @@ -4744,7 +4839,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                             // Log.e("aaa", "Boring: " + mTransformed);
         
                             mSavedLayout = (BoringLayout) mLayout;
        -                } else if (mEllipsize != null && boring.width <= w) {
        +                } else if (shouldEllipsize && boring.width <= w) {
                             if (mSavedLayout != null) {
                                 mLayout = mSavedLayout.
                                         replaceOrMake(mTransformed, mTextPaint,
        @@ -4757,7 +4852,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                                         boring, mIncludePad, mEllipsize,
                                         ellipsisWidth);
                             }
        -                } else if (mEllipsize != null) {
        +                } else if (shouldEllipsize) {
                             mLayout = new StaticLayout(mTransformed,
                                         0, mTransformed.length(),
                                         mTextPaint, w, alignment, mSpacingMult,
        @@ -4769,7 +4864,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                                     mIncludePad);
                             // Log.e("aaa", "Boring but wide: " + mTransformed);
                         }
        -            } else if (mEllipsize != null) {
        +            } else if (shouldEllipsize) {
                         mLayout = new StaticLayout(mTransformed,
                                     0, mTransformed.length(),
                                     mTextPaint, w, alignment, mSpacingMult,
        @@ -4782,9 +4877,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                     }
                 }
         
        +        shouldEllipsize = mEllipsize != null;
                 mHintLayout = null;
         
                 if (mHint != null) {
        +            if (shouldEllipsize) hintWidth = w;
        +
                     if (hintBoring == UNKNOWN_BORING) {
                         hintBoring = BoringLayout.isBoring(mHint, mTextPaint,
                                                            mHintBoring);
        @@ -4794,24 +4892,50 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                     }
         
                     if (hintBoring != null) {
        -                if (hintBoring.width <= hintWidth) {
        +                if (hintBoring.width <= hintWidth &&
        +                    (!shouldEllipsize || hintBoring.width <= ellipsisWidth)) {
                             if (mSavedHintLayout != null) {
                                 mHintLayout = mSavedHintLayout.
                                         replaceOrMake(mHint, mTextPaint,
        -                                hintWidth, alignment, mSpacingMult,
        -                                mSpacingAdd, hintBoring, mIncludePad);
        +                                hintWidth, alignment, mSpacingMult, mSpacingAdd,
        +                                hintBoring, mIncludePad);
                             } else {
                                 mHintLayout = BoringLayout.make(mHint, mTextPaint,
        -                                hintWidth, alignment, mSpacingMult,
        -                                mSpacingAdd, hintBoring, mIncludePad);
        +                                hintWidth, alignment, mSpacingMult, mSpacingAdd,
        +                                hintBoring, mIncludePad);
                             }
         
                             mSavedHintLayout = (BoringLayout) mHintLayout;
        +                } else if (shouldEllipsize && hintBoring.width <= hintWidth) {
        +                    if (mSavedHintLayout != null) {
        +                        mHintLayout = mSavedHintLayout.
        +                                replaceOrMake(mHint, mTextPaint,
        +                                hintWidth, alignment, mSpacingMult, mSpacingAdd,
        +                                hintBoring, mIncludePad, mEllipsize,
        +                                ellipsisWidth);
        +                    } else {
        +                        mHintLayout = BoringLayout.make(mHint, mTextPaint,
        +                                hintWidth, alignment, mSpacingMult, mSpacingAdd,
        +                                hintBoring, mIncludePad, mEllipsize,
        +                                ellipsisWidth);
        +                    }
        +                } else if (shouldEllipsize) {
        +                    mHintLayout = new StaticLayout(mHint,
        +                                0, mHint.length(),
        +                                mTextPaint, hintWidth, alignment, mSpacingMult,
        +                                mSpacingAdd, mIncludePad, mEllipsize,
        +                                ellipsisWidth);
                         } else {
                             mHintLayout = new StaticLayout(mHint, mTextPaint,
                                     hintWidth, alignment, mSpacingMult, mSpacingAdd,
                                     mIncludePad);
                         }
        +            } else if (shouldEllipsize) {
        +                mHintLayout = new StaticLayout(mHint,
        +                            0, mHint.length(),
        +                            mTextPaint, hintWidth, alignment, mSpacingMult,
        +                            mSpacingAdd, mIncludePad, mEllipsize,
        +                            ellipsisWidth);
                     } else {
                         mHintLayout = new StaticLayout(mHint, mTextPaint,
                                 hintWidth, alignment, mSpacingMult, mSpacingAdd,
        @@ -4895,8 +5019,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                 }
             }
         
        -    private static final BoringLayout.Metrics UNKNOWN_BORING =
        -                                                new BoringLayout.Metrics();
        +    private static final BoringLayout.Metrics UNKNOWN_BORING = new BoringLayout.Metrics();
         
             @Override
             protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        @@ -4923,8 +5046,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                     }
         
                     if (des < 0) {
        -                boring = BoringLayout.isBoring(mTransformed, mTextPaint,
        -                                               mBoring);
        +                boring = BoringLayout.isBoring(mTransformed, mTextPaint, mBoring);
                         if (boring != null) {
                             mBoring = boring;
                         }
        @@ -4934,8 +5056,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
         
                     if (boring == null || boring == UNKNOWN_BORING) {
                         if (des < 0) {
        -                    des = (int) FloatMath.ceil(Layout.
        -                                    getDesiredWidth(mTransformed, mTextPaint));
        +                    des = (int) FloatMath.ceil(Layout.getDesiredWidth(mTransformed, mTextPaint));
                         }
         
                         width = des;
        @@ -4953,13 +5074,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                         int hintDes = -1;
                         int hintWidth;
         
        -                if (mHintLayout != null) {
        +                if (mHintLayout != null && mEllipsize == null) {
                             hintDes = desired(mHintLayout);
                         }
         
                         if (hintDes < 0) {
        -                    hintBoring = BoringLayout.isBoring(mHint, mTextPaint,
        -                                                       mHintBoring);
        +                    hintBoring = BoringLayout.isBoring(mHint, mTextPaint, mHintBoring);
                             if (hintBoring != null) {
                                 mHintBoring = hintBoring;
                             }
        @@ -4967,8 +5087,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
         
                         if (hintBoring == null || hintBoring == UNKNOWN_BORING) {
                             if (hintDes < 0) {
        -                        hintDes = (int) FloatMath.ceil(Layout.
        -                                        getDesiredWidth(mHint, mTextPaint));
        +                        hintDes = (int) FloatMath.ceil(
        +                                Layout.getDesiredWidth(mHint, mTextPaint));
                             }
         
                             hintWidth = hintDes;
        @@ -5014,20 +5134,18 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
         
                 if (mLayout == null) {
                     makeNewLayout(want, hintWant, boring, hintBoring,
        -                          width - getCompoundPaddingLeft() - getCompoundPaddingRight(),
        -                          false);
        +                          width - getCompoundPaddingLeft() - getCompoundPaddingRight(), false);
                 } else if ((mLayout.getWidth() != want) || (hintWidth != hintWant) ||
                            (mLayout.getEllipsizedWidth() !=
                                 width - getCompoundPaddingLeft() - getCompoundPaddingRight())) {
                     if (mHint == null && mEllipsize == null &&
                             want > mLayout.getWidth() &&
                             (mLayout instanceof BoringLayout ||
        -                        (fromexisting && des >= 0 && des <= want))) {
        +                            (fromexisting && des >= 0 && des <= want))) {
                         mLayout.increaseWidthTo(want);
                     } else {
                         makeNewLayout(want, hintWant, boring, hintBoring,
        -                              width - getCompoundPaddingLeft() - getCompoundPaddingRight(),
        -                              false);
        +                              width - getCompoundPaddingLeft() - getCompoundPaddingRight(), false);
                     }
                 } else {
                     // Width has not changed.
        @@ -5048,11 +5166,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                     }
                 }
         
        -        int unpaddedHeight = height - getCompoundPaddingTop() -
        -                                getCompoundPaddingBottom();
        +        int unpaddedHeight = height - getCompoundPaddingTop() - getCompoundPaddingBottom();
                 if (mMaxMode == LINES && mLayout.getLineCount() > mMaximum) {
        -            unpaddedHeight = Math.min(unpaddedHeight,
        -                                      mLayout.getLineTop(mMaximum));
        +            unpaddedHeight = Math.min(unpaddedHeight, mLayout.getLineTop(mMaximum));
                 }
         
                 /*
        @@ -5071,8 +5187,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
             }
         
             private int getDesiredHeight() {
        -        return Math.max(getDesiredHeight(mLayout, true),
        -                        getDesiredHeight(mHintLayout, false));
        +        return Math.max(
        +                getDesiredHeight(mLayout, true),
        +                getDesiredHeight(mHintLayout, mEllipsize != null));
             }
         
             private int getDesiredHeight(Layout layout, boolean cap) {
        @@ -5715,6 +5832,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
             }
         
             private void startMarquee() {
        +        // Do not ellipsize EditText
        +        if (mInput != null) return;
        +
                 if (compressText(getWidth() - getCompoundPaddingLeft() - getCompoundPaddingRight())) {
                     return;
                 }
        @@ -6129,10 +6249,18 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
         
             private class ChangeWatcher
             implements TextWatcher, SpanWatcher {
        +
        +        private CharSequence mBeforeText;
        +
                 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);
        +
        +            if (AccessibilityManager.getInstance(mContext).isEnabled()) {
        +                mBeforeText = buffer.toString();
        +            }
        +
                     TextView.this.sendBeforeTextChanged(buffer, start, before, after);
                 }
         
        @@ -6141,6 +6269,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                     if (DEBUG_EXTRACT) Log.v(TAG, "onTextChanged start=" + start
                             + " before=" + before + " after=" + after + ": " + buffer);
                     TextView.this.handleTextChanged(buffer, start, before, after);
        +
        +            if (AccessibilityManager.getInstance(mContext).isEnabled() &&
        +                    (isFocused() || isSelected() &&
        +                    isShown())) {
        +                sendAccessibilityEventTypeViewTextChanged(mBeforeText, start, before, after);
        +                mBeforeText = null;
        +            }
                 }
         
                 public void afterTextChanged(Editable buffer) {
        @@ -6336,6 +6471,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                 
                 protected void onReceiveResult(int resultCode, Bundle resultData) {
                     if (resultCode != InputMethodManager.RESULT_SHOWN) {
        +                final int len = mText.length();
        +                if (mNewStart > len) {
        +                    mNewStart = len;
        +                }
        +                if (mNewEnd > len) {
        +                    mNewEnd = len;
        +                }
                         Selection.setSelection((Spannable)mText, mNewStart, mNewEnd);
                     }
                 }
        @@ -6525,9 +6667,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                     } else if (getLineCount() == 1) {
                         switch (mGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
                             case Gravity.LEFT:
        -                        return (mLayout.getLineRight(0) - mScrollX - (mRight - mLeft) -
        -                                getCompoundPaddingLeft() - getCompoundPaddingRight()) /
        -                                getHorizontalFadingEdgeLength();
        +                        final int textWidth = (mRight - mLeft) - getCompoundPaddingLeft() -
        +                                getCompoundPaddingRight();
        +                        final float lineWidth = mLayout.getLineWidth(0);
        +                        return (lineWidth - textWidth) / getHorizontalFadingEdgeLength();
                             case Gravity.RIGHT:
                                 return 0.0f;
                             case Gravity.CENTER_HORIZONTAL:
        @@ -6775,6 +6918,40 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                 return TextUtils.substring(mTransformed, start, end);
             }
         
        +    @Override
        +    public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
        +        boolean isPassword =
        +            (mInputType & (EditorInfo.TYPE_MASK_CLASS | EditorInfo.TYPE_MASK_VARIATION)) ==
        +            (EditorInfo.TYPE_CLASS_TEXT | EditorInfo.TYPE_TEXT_VARIATION_PASSWORD);
        +
        +        if (!isPassword) {
        +            CharSequence text = getText();
        +            if (TextUtils.isEmpty(text)) {
        +                text = getHint();
        +            }
        +            if (!TextUtils.isEmpty(text)) {
        +                if (text.length() > AccessibilityEvent.MAX_TEXT_LENGTH) {
        +                    text = text.subSequence(0, AccessibilityEvent.MAX_TEXT_LENGTH + 1);
        +                }
        +                event.getText().add(text);
        +            }
        +        } else {
        +            event.setPassword(isPassword);
        +        }
        +        return false;
        +    }
        +
        +    void sendAccessibilityEventTypeViewTextChanged(CharSequence beforeText,
        +            int fromIndex, int removedCount, int addedCount) {
        +        AccessibilityEvent event =
        +            AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED);
        +        event.setFromIndex(fromIndex);
        +        event.setRemovedCount(removedCount);
        +        event.setAddedCount(addedCount);
        +        event.setBeforeText(beforeText);
        +        sendAccessibilityEventUnchecked(event);
        +    }
        +
             @Override
             protected void onCreateContextMenu(ContextMenu menu) {
                 super.onCreateContextMenu(menu);
        diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java
        index ff747878fba0ddf7b9a477a11f0fdd5a3821bb62..670692f552dfbcd414b71c8a420abce639edd747 100644
        --- a/core/java/android/widget/Toast.java
        +++ b/core/java/android/widget/Toast.java
        @@ -21,8 +21,8 @@ import android.app.ITransientNotification;
         import android.content.Context;
         import android.content.res.Resources;
         import android.graphics.PixelFormat;
        -import android.os.RemoteException;
         import android.os.Handler;
        +import android.os.RemoteException;
         import android.os.ServiceManager;
         import android.util.Log;
         import android.view.Gravity;
        @@ -278,7 +278,7 @@ public class Toast {
                 }
                 tv.setText(s);
             }
        -    
        +
             // =======================================================================================
             // All the gunk below is the interaction with the Notification Service, which handles
             // the proper ordering of these system-wide.
        @@ -373,6 +373,7 @@ public class Toast {
                                     TAG, "REMOVE! " + mView + " in " + this);
                             mWM.removeView(mView);
                         }
        +
                         mView = null;
                     }
                 }
        diff --git a/core/java/android/widget/VideoView.java b/core/java/android/widget/VideoView.java
        index 6d3a2d3602c08c98543eb3f759bcdfc0e6e26eb3..20dd8a6bed91ae5bde6bc3ac3f635f1ceef55cca 100644
        --- a/core/java/android/widget/VideoView.java
        +++ b/core/java/android/widget/VideoView.java
        @@ -55,6 +55,7 @@ public class VideoView extends SurfaceView implements MediaPlayerControl {
             private SurfaceHolder mSurfaceHolder = null;
             private MediaPlayer mMediaPlayer = null;
             private boolean     mIsPrepared;
        +    private boolean     mIsPlaybackCompleted;
             private int         mVideoWidth;
             private int         mVideoHeight;
             private int         mSurfaceWidth;
        @@ -260,7 +261,7 @@ public class VideoView extends SurfaceView implements MediaPlayerControl {
                                 mSeekWhenPrepared = 0;
                             }
                             if (mStartWhenPrepared) {
        -                        mMediaPlayer.start();
        +                        start();
                                 mStartWhenPrepared = false;
                                 if (mMediaController != null) {
                                     mMediaController.show();
        @@ -281,7 +282,7 @@ public class VideoView extends SurfaceView implements MediaPlayerControl {
                             mSeekWhenPrepared = 0;
                         }
                         if (mStartWhenPrepared) {
        -                    mMediaPlayer.start();
        +                    start();
                             mStartWhenPrepared = false;
                         }
                     }
        @@ -291,6 +292,7 @@ public class VideoView extends SurfaceView implements MediaPlayerControl {
             private MediaPlayer.OnCompletionListener mCompletionListener =
                 new MediaPlayer.OnCompletionListener() {
                 public void onCompletion(MediaPlayer mp) {
        +            mIsPlaybackCompleted = true;
                     if (mMediaController != null) {
                         mMediaController.hide();
                     }
        @@ -405,7 +407,9 @@ public class VideoView extends SurfaceView implements MediaPlayerControl {
                             mMediaPlayer.seekTo(mSeekWhenPrepared);
                             mSeekWhenPrepared = 0;
                         }
        -                mMediaPlayer.start();
        +                if (!mIsPlaybackCompleted) {
        +                    start();
        +                } 
                         if (mMediaController != null) {
                             mMediaController.show();
                         }
        @@ -490,6 +494,7 @@ public class VideoView extends SurfaceView implements MediaPlayerControl {
             }
             
             public void start() {
        +        mIsPlaybackCompleted = false;
                 if (mMediaPlayer != null && mIsPrepared) {
                         mMediaPlayer.start();
                         mStartWhenPrepared = false;
        diff --git a/core/java/android/widget/ViewSwitcher.java b/core/java/android/widget/ViewSwitcher.java
        index f4f23a8f655b25fdc16579cac2e877c51e6dbdc5..0dcaf954279eccde2b1d4e47a3e94ac94e614de9 100644
        --- a/core/java/android/widget/ViewSwitcher.java
        +++ b/core/java/android/widget/ViewSwitcher.java
        @@ -16,8 +16,6 @@
         
         package android.widget;
         
        -import java.util.Map;
        -
         import android.content.Context;
         import android.util.AttributeSet;
         import android.view.View;
        diff --git a/core/java/android/widget/ZoomButtonsController.java b/core/java/android/widget/ZoomButtonsController.java
        index d9fb78b81d3850b3f1b90db631de8cfcddc562b0..bae4dad5c01fc5948e8a8224c5c4f76389789666 100644
        --- a/core/java/android/widget/ZoomButtonsController.java
        +++ b/core/java/android/widget/ZoomButtonsController.java
        @@ -81,27 +81,27 @@ public class ZoomButtonsController implements View.OnTouchListener {
             private static final int ZOOM_CONTROLS_TOUCH_PADDING = 20;
             private int mTouchPaddingScaledSq;
         
        -    private Context mContext;
        -    private WindowManager mWindowManager;
        +    private final Context mContext;
        +    private final WindowManager mWindowManager;
             private boolean mAutoDismissControls = true;
         
             /**
              * The view that is being zoomed by this zoom controller.
              */
        -    private View mOwnerView;
        +    private final View mOwnerView;
         
             /**
              * The location of the owner view on the screen. This is recalculated
              * each time the zoom controller is shown.
              */
        -    private int[] mOwnerViewRawLocation = new int[2];
        +    private final int[] mOwnerViewRawLocation = new int[2];
         
             /**
              * The container that is added as a window.
              */
        -    private FrameLayout mContainer;
        +    private final FrameLayout mContainer;
             private LayoutParams mContainerLayoutParams;
        -    private int[] mContainerRawLocation = new int[2];
        +    private final int[] mContainerRawLocation = new int[2];
         
             private ZoomControls mControls;
         
        @@ -113,7 +113,7 @@ public class ZoomButtonsController implements View.OnTouchListener {
             /**
              * The {@link #mTouchTargetView}'s location in window, set on touch down.
              */
        -    private int[] mTouchTargetWindowLocation = new int[2];
        +    private final int[] mTouchTargetWindowLocation = new int[2];
         
             /**
              * If the zoom controller is dismissed but the user is still in a touch
        @@ -128,8 +128,8 @@ public class ZoomButtonsController implements View.OnTouchListener {
             /** Whether the container has been added to the window manager. */
             private boolean mIsVisible;
         
        -    private Rect mTempRect = new Rect();
        -    private int[] mTempIntArray = new int[2];
        +    private final Rect mTempRect = new Rect();
        +    private final int[] mTempIntArray = new int[2];
         
             private OnZoomListener mCallback;
         
        @@ -141,13 +141,13 @@ public class ZoomButtonsController implements View.OnTouchListener {
              */
             private Runnable mPostedVisibleInitializer;
         
        -    private IntentFilter mConfigurationChangedFilter =
        +    private final IntentFilter mConfigurationChangedFilter =
                     new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED);
         
             /**
              * Needed to reposition the zoom controls after configuration changes.
              */
        -    private BroadcastReceiver mConfigurationChangedReceiver = new BroadcastReceiver() {
        +    private final BroadcastReceiver mConfigurationChangedReceiver = new BroadcastReceiver() {
                 @Override
                 public void onReceive(Context context, Intent intent) {
                     if (!mIsVisible) return;
        @@ -167,7 +167,7 @@ public class ZoomButtonsController implements View.OnTouchListener {
              */
             private static final int MSG_POST_SET_VISIBLE = 4;
         
        -    private Handler mHandler = new Handler() {
        +    private final Handler mHandler = new Handler() {
                 @Override
                 public void handleMessage(Message msg) {
                     switch (msg.what) {
        @@ -444,6 +444,9 @@ public class ZoomButtonsController implements View.OnTouchListener {
             }
         
             private void refreshPositioningVariables() {
        +        // if the mOwnerView is detached from window then skip.
        +        if (mOwnerView.getWindowToken() == null) return;
        +
                 // Position the zoom controls on the bottom of the owner view.
                 int ownerHeight = mOwnerView.getHeight();
                 int ownerWidth = mOwnerView.getWidth();
        diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
        index e1ff2a52062ff25d80505b69b3cec41c193b1715..4bac5933d7ed72e0b4ee887b06a16f7c74e38c5c 100644
        --- a/core/java/com/android/internal/app/IBatteryStats.aidl
        +++ b/core/java/com/android/internal/app/IBatteryStats.aidl
        @@ -18,6 +18,8 @@ package com.android.internal.app;
         
         import com.android.internal.os.BatteryStatsImpl;
         
        +import android.telephony.SignalStrength;
        +
         interface IBatteryStats {
             byte[] getStatistics();
             void noteStartWakelock(int uid, String name, int type);
        @@ -33,8 +35,9 @@ interface IBatteryStats {
             void noteUserActivity(int uid, int event);
             void notePhoneOn();
             void notePhoneOff();
        -    void notePhoneSignalStrength(int asu);
        +    void notePhoneSignalStrength(in SignalStrength signalStrength);
             void notePhoneDataConnectionState(int dataType, boolean hasData);
        +    void noteAirplaneMode(boolean isAirplaneMode);
             void noteWifiOn(int uid);
             void noteWifiOff(int uid);
             void noteWifiRunning();
        diff --git a/core/java/com/android/internal/backup/IBackupTransport.aidl b/core/java/com/android/internal/backup/IBackupTransport.aidl
        index ce39768137e28eab28b7eaa9b8bab5e3c9850ff0..af06965ae12331cd94b4173e23fd693ede47cbe2 100644
        --- a/core/java/com/android/internal/backup/IBackupTransport.aidl
        +++ b/core/java/com/android/internal/backup/IBackupTransport.aidl
        @@ -16,6 +16,8 @@
         
         package com.android.internal.backup;
         
        +import android.backup.RestoreSet;
        +import android.content.pm.PackageInfo;
         import android.os.ParcelFileDescriptor;
         
         /** {@hide} */
        @@ -25,7 +27,7 @@ interface IBackupTransport {
             1. set up the connection to the destination
                 - set up encryption
                 - for Google cloud, log in using the user's gaia credential or whatever
        -        - for sd, spin off the backup transport and establish communication with it
        +        - for adb, just set up the all-in-one destination file
             2. send each app's backup transaction
                 - parse the data file for key/value pointers etc
                 - send key/blobsize set to the Google cloud, get back quota ok/rejected response
        @@ -36,34 +38,112 @@ interface IBackupTransport {
                 - sd target streams raw data into encryption envelope then to sd?
             3. shut down connection to destination
                 - cloud: tear down connection etc
        -        - sd: close the file and shut down the writer proxy
        +        - adb: close the file
         */
             /**
        -     * Establish a connection to the back-end data repository, if necessary.  If the transport
        -     * needs to initialize state that is not tied to individual applications' backup operations,
        -     * this is where it should be done.
        +     * Ask the transport where, on local device storage, to keep backup state blobs.
        +     * This is per-transport so that mock transports used for testing can coexist with
        +     * "live" backup services without interfering with the live bookkeeping.  The
        +     * returned string should be a name that is expected to be unambiguous among all
        +     * available backup transports; the name of the class implementing the transport
        +     * is a good choice.
              *
        -     * @return Zero on success; a nonzero error code on failure.
        +     * @return A unique name, suitable for use as a file or directory name, that the
        +     *         Backup Manager could use to disambiguate state files associated with
        +     *         different backup transports.
              */
        -    int startSession();
        +    String transportDirName();
         
             /**
        -     * Send one application's data to the backup destination.
        +     * Verify that this is a suitable time for a backup pass.  This should return zero
        +     * if a backup is reasonable right now, some positive value otherwise.  This method
        +     * will be called outside of the {@link #startSession}/{@link #endSession} pair.
              *
        -     * @param packageName The identity of the application whose data is being backed up.
        +     * 

        If this is not a suitable time for a backup, the transport should return a + * backoff delay, in milliseconds, after which the Backup Manager should try again. + * + * @return Zero if this is a suitable time for a backup pass, or a positive time delay + * in milliseconds to suggest deferring the backup pass for a while. + */ + long requestBackupTime(); + + /** + * Send one application's data to the backup destination. The transport may send + * the data immediately, or may buffer it. After this is called, {@link #finishBackup} + * must be called to ensure the data is sent and recorded successfully. + * + * @param packageInfo The identity of the application whose data is being backed up. + * This specifically includes the signature list for the package. * @param data The data stream that resulted from invoking the application's - * BackupService.doBackup() method. This may be a pipe rather than a - * file on persistent media, so it may not be seekable. - * @return Zero on success; a nonzero error code on failure. + * BackupService.doBackup() method. This may be a pipe rather than a file on + * persistent media, so it may not be seekable. + * @return false if errors occurred (the backup should be aborted and rescheduled), + * true if everything is OK so far (but {@link #finishBackup} must be called). + */ + boolean performBackup(in PackageInfo packageInfo, in ParcelFileDescriptor inFd); + + /** + * Erase the give application's data from the backup destination. This clears + * out the given package's data from the current backup set, making it as though + * the app had never yet been backed up. After this is called, {@link finishBackup} + * must be called to ensure that the operation is recorded successfully. + * + * @return false if errors occurred (the backup should be aborted and rescheduled), + * true if everything is OK so far (but {@link #finishBackup} must be called). + */ + boolean clearBackupData(in PackageInfo packageInfo); + + /** + * Finish sending application data to the backup destination. This must be + * called after {@link #performBackup} or {@link clearBackupData} to ensure that + * all data is sent. Only when this method returns true can a backup be assumed + * to have succeeded. + * + * @return false if errors occurred (the backup should be aborted and rescheduled), + * true if everything is OK. */ - int performBackup(String packageName, in ParcelFileDescriptor data); + boolean finishBackup(); /** - * Terminate the backup session, closing files, freeing memory, and cleaning up whatever - * other state the transport required. + * Get the set of backups currently available over this transport. * - * @return Zero on success; a nonzero error code on failure. Even on failure, the session - * is torn down and must be restarted if another backup is attempted. + * @return Descriptions of the set of restore images available for this device, + * or null if an error occurred (the attempt should be rescheduled). + **/ + RestoreSet[] getAvailableRestoreSets(); + + /** + * Start restoring application data from backup. After calling this function, + * alternate calls to {@link #nextRestorePackage} and {@link #nextRestoreData} + * to walk through the actual application data. + * + * @param token A backup token as returned by {@link #getAvailableRestoreSets}. + * @param packages List of applications to restore (if data is available). + * Application data will be restored in the order given. + * @return false if errors occurred (the restore should be aborted and rescheduled), + * true if everything is OK so far (go ahead and call {@link #nextRestorePackage}). + */ + boolean startRestore(long token, in PackageInfo[] packages); + + /** + * Get the package name of the next application with data in the backup store. + * @return The name of one of the packages supplied to {@link #startRestore}, + * or "" (the empty string) if no more backup data is available, + * or null if an error occurred (the restore should be aborted and rescheduled). + */ + String nextRestorePackage(); + + /** + * Get the data for the application returned by {@link #nextRestorePackage}. + * @param data An open, writable file into which the backup data should be stored. + * @return false if errors occurred (the restore should be aborted and rescheduled), + * true if everything is OK so far (go ahead and call {@link #nextRestorePackage}). + */ + boolean getRestoreData(in ParcelFileDescriptor outFd); + + /** + * End a restore session (aborting any in-process data transfer as necessary), + * freeing any resources and connections used during the restore process. */ - int endSession(); + void finishRestore(); } diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/core/java/com/android/internal/backup/LocalTransport.java new file mode 100644 index 0000000000000000000000000000000000000000..2facce2ed190188ddba5527f167754eb1fad7f8c --- /dev/null +++ b/core/java/com/android/internal/backup/LocalTransport.java @@ -0,0 +1,200 @@ +package com.android.internal.backup; + +import android.backup.BackupDataInput; +import android.backup.BackupDataOutput; +import android.backup.RestoreSet; +import android.content.Context; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.os.Environment; +import android.os.ParcelFileDescriptor; +import android.os.RemoteException; +import android.util.Log; + +import org.bouncycastle.util.encoders.Base64; + +import java.io.File; +import java.io.FileFilter; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.ArrayList; + +/** + * Backup transport for stashing stuff into a known location on disk, and + * later restoring from there. For testing only. + */ + +public class LocalTransport extends IBackupTransport.Stub { + private static final String TAG = "LocalTransport"; + private static final boolean DEBUG = true; + + private static final String TRANSPORT_DIR_NAME + = "com.android.internal.backup.LocalTransport"; + + private Context mContext; + private PackageManager mPackageManager; + private File mDataDir = new File(Environment.getDownloadCacheDirectory(), "backup"); + private PackageInfo[] mRestorePackages = null; + private int mRestorePackage = -1; // Index into mRestorePackages + + + public LocalTransport(Context context) { + if (DEBUG) Log.v(TAG, "Transport constructed"); + mContext = context; + mPackageManager = context.getPackageManager(); + } + + + public String transportDirName() throws RemoteException { + return TRANSPORT_DIR_NAME; + } + + public long requestBackupTime() throws RemoteException { + // any time is a good time for local backup + return 0; + } + + public boolean performBackup(PackageInfo packageInfo, ParcelFileDescriptor data) + throws RemoteException { + if (DEBUG) Log.v(TAG, "performBackup() pkg=" + packageInfo.packageName); + + File packageDir = new File(mDataDir, packageInfo.packageName); + packageDir.mkdirs(); + + // Each 'record' in the restore set is kept in its own file, named by + // the record key. Wind through the data file, extracting individual + // record operations and building a set of all the updates to apply + // in this update. + BackupDataInput changeSet = new BackupDataInput(data.getFileDescriptor()); + try { + int bufSize = 512; + byte[] buf = new byte[bufSize]; + while (changeSet.readNextHeader()) { + String key = changeSet.getKey(); + String base64Key = new String(Base64.encode(key.getBytes())); + File entityFile = new File(packageDir, base64Key); + + int dataSize = changeSet.getDataSize(); + + if (DEBUG) Log.v(TAG, "Got change set key=" + key + " size=" + dataSize + + " key64=" + base64Key); + + if (dataSize >= 0) { + FileOutputStream entity = new FileOutputStream(entityFile); + + if (dataSize > bufSize) { + bufSize = dataSize; + buf = new byte[bufSize]; + } + changeSet.readEntityData(buf, 0, dataSize); + if (DEBUG) Log.v(TAG, " data size " + dataSize); + + try { + entity.write(buf, 0, dataSize); + } catch (IOException e) { + Log.e(TAG, "Unable to update key file " + entityFile.getAbsolutePath()); + return false; + } finally { + entity.close(); + } + } else { + entityFile.delete(); + } + } + return true; + } catch (IOException e) { + // oops, something went wrong. abort the operation and return error. + Log.v(TAG, "Exception reading backup input:", e); + return false; + } + } + + public boolean clearBackupData(PackageInfo packageInfo) { + if (DEBUG) Log.v(TAG, "clearBackupData() pkg=" + packageInfo.packageName); + + File packageDir = new File(mDataDir, packageInfo.packageName); + for (File f : packageDir.listFiles()) { + f.delete(); + } + packageDir.delete(); + return true; + } + + public boolean finishBackup() throws RemoteException { + if (DEBUG) Log.v(TAG, "finishBackup()"); + return true; + } + + // Restore handling + public RestoreSet[] getAvailableRestoreSets() throws android.os.RemoteException { + // one hardcoded restore set + RestoreSet set = new RestoreSet("Local disk image", "flash", 0); + RestoreSet[] array = { set }; + return array; + } + + public boolean startRestore(long token, PackageInfo[] packages) { + if (DEBUG) Log.v(TAG, "start restore " + token); + mRestorePackages = packages; + mRestorePackage = -1; + return true; + } + + public String nextRestorePackage() { + if (mRestorePackages == null) throw new IllegalStateException("startRestore not called"); + while (++mRestorePackage < mRestorePackages.length) { + String name = mRestorePackages[mRestorePackage].packageName; + if (new File(mDataDir, name).isDirectory()) { + if (DEBUG) Log.v(TAG, " nextRestorePackage() = " + name); + return name; + } + } + + if (DEBUG) Log.v(TAG, " no more packages to restore"); + return ""; + } + + public boolean getRestoreData(ParcelFileDescriptor outFd) { + if (mRestorePackages == null) throw new IllegalStateException("startRestore not called"); + if (mRestorePackage < 0) throw new IllegalStateException("nextRestorePackage not called"); + File packageDir = new File(mDataDir, mRestorePackages[mRestorePackage].packageName); + + // The restore set is the concatenation of the individual record blobs, + // each of which is a file in the package's directory + File[] blobs = packageDir.listFiles(); + if (blobs == null) { + Log.e(TAG, "Error listing directory: " + packageDir); + return false; // nextRestorePackage() ensures the dir exists, so this is an error + } + + // We expect at least some data if the directory exists in the first place + if (DEBUG) Log.v(TAG, " getRestoreData() found " + blobs.length + " key files"); + BackupDataOutput out = new BackupDataOutput(outFd.getFileDescriptor()); + try { + for (File f : blobs) { + FileInputStream in = new FileInputStream(f); + try { + int size = (int) f.length(); + byte[] buf = new byte[size]; + in.read(buf); + String key = new String(Base64.decode(f.getName())); + if (DEBUG) Log.v(TAG, " ... key=" + key + " size=" + size); + out.writeEntityHeader(key, size); + out.writeEntityData(buf, size); + } finally { + in.close(); + } + } + return true; + } catch (IOException e) { + Log.e(TAG, "Unable to read backup records", e); + return false; + } + } + + public void finishRestore() { + if (DEBUG) Log.v(TAG, "finishRestore()"); + } +} diff --git a/core/java/com/android/internal/backup/SystemBackupAgent.java b/core/java/com/android/internal/backup/SystemBackupAgent.java new file mode 100644 index 0000000000000000000000000000000000000000..6b396d777a664647c1f7cade961935f325b368e2 --- /dev/null +++ b/core/java/com/android/internal/backup/SystemBackupAgent.java @@ -0,0 +1,35 @@ +/* + * 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.backup; + +import android.backup.AbsoluteFileBackupHelper; +import android.backup.BackupHelperAgent; + +/** + * Backup agent for various system-managed data + */ +public class SystemBackupAgent extends BackupHelperAgent { + // the set of files that we back up whole, as absolute paths + String[] mFiles = { + /* WallpaperService.WALLPAPER_FILE */ + "/data/data/com.android.settings/files/wallpaper", + }; + + public void onCreate() { + addHelper("system_files", new AbsoluteFileBackupHelper(this, mFiles)); + } +} diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index e8356a2c641518106cbf10c67c224ebd33bd0023..a03802dff0128af5c9b8e48c1644933db5a69ae2 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -23,23 +23,24 @@ import android.os.ParcelFormatException; import android.os.Parcelable; import android.os.Process; import android.os.SystemClock; +import android.telephony.SignalStrength; import android.telephony.TelephonyManager; import android.util.Log; import android.util.PrintWriterPrinter; import android.util.Printer; import android.util.SparseArray; +import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; +import java.io.FileReader; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.HashMap; -import java.util.HashSet; import java.util.Iterator; import java.util.Map; -import java.util.Set; /** * All information we are collecting about things that can happen that impact @@ -54,7 +55,7 @@ public final class BatteryStatsImpl extends BatteryStats { private static final int MAGIC = 0xBA757475; // 'BATSTATS' // Current on-disk Parcel version - private static final int VERSION = 34; + private static final int VERSION = 39; private final File mFile; private final File mBackupFile; @@ -95,7 +96,7 @@ public final class BatteryStatsImpl extends BatteryStats { boolean mScreenOn; StopwatchTimer mScreenOnTimer; - + int mScreenBrightnessBin = -1; final StopwatchTimer[] mScreenBrightnessTimer = new StopwatchTimer[NUM_SCREEN_BRIGHTNESS_BINS]; @@ -104,6 +105,12 @@ public final class BatteryStatsImpl extends BatteryStats { boolean mPhoneOn; StopwatchTimer mPhoneOnTimer; + boolean mAudioOn; + StopwatchTimer mAudioOnTimer; + + boolean mVideoOn; + StopwatchTimer mVideoOnTimer; + int mPhoneSignalStrengthBin = -1; final StopwatchTimer[] mPhoneSignalStrengthsTimer = new StopwatchTimer[NUM_SIGNAL_STRENGTH_BINS]; @@ -132,18 +139,27 @@ public final class BatteryStatsImpl extends BatteryStats { long mTrackBatteryUptimeStart; long mTrackBatteryPastRealtime; long mTrackBatteryRealtimeStart; - + long mUnpluggedBatteryUptime; long mUnpluggedBatteryRealtime; - + /* * These keep track of battery levels (1-100) at the last plug event and the last unplug event. */ int mDischargeStartLevel; int mDischargeCurrentLevel; - + long mLastWriteTime = 0; // Milliseconds - + + // Mobile data transferred while on battery + private long[] mMobileDataTx = new long[4]; + private long[] mMobileDataRx = new long[4]; + private long[] mTotalDataTx = new long[4]; + private long[] mTotalDataRx = new long[4]; + + private long mRadioDataUptime; + private long mRadioDataStart; + /* * Holds a SamplingTimer associated with each kernel wakelock name being tracked. */ @@ -175,6 +191,8 @@ public final class BatteryStatsImpl extends BatteryStats { private final Map mProcWakelockFileStats = new HashMap(); + private HashMap mUidCache = new HashMap(); + // For debugging public BatteryStatsImpl() { mFile = mBackupFile = null; @@ -319,6 +337,13 @@ public final class BatteryStatsImpl extends BatteryStats { */ long mUnpluggedTime; + /** + * Constructs from a parcel. + * @param type + * @param unpluggables + * @param powerType + * @param in + */ Timer(int type, ArrayList unpluggables, Parcel in) { mType = type; @@ -631,7 +656,6 @@ public final class BatteryStatsImpl extends BatteryStats { * was actually held for an interesting duration. */ long mAcquireTime; - StopwatchTimer(int type, ArrayList timerPool, ArrayList unpluggables, Parcel in) { @@ -692,6 +716,10 @@ public final class BatteryStatsImpl extends BatteryStats { } } + boolean isRunningLocked() { + return mNesting > 0; + } + void stopRunningLocked(BatteryStatsImpl stats) { // Ignore attempt to stop a timer that isn't running if (mNesting == 0) { @@ -882,7 +910,40 @@ public final class BatteryStatsImpl extends BatteryStats { } return kwlt; } - + + private void doDataPlug(long[] dataTransfer, long currentBytes) { + dataTransfer[STATS_LAST] = dataTransfer[STATS_UNPLUGGED]; + dataTransfer[STATS_UNPLUGGED] = -1; + } + + private void doDataUnplug(long[] dataTransfer, long currentBytes) { + dataTransfer[STATS_UNPLUGGED] = currentBytes; + } + + private long getCurrentRadioDataUptimeMs() { + try { + File awakeTimeFile = new File("/sys/devices/virtual/net/rmnet0/awake_time_ms"); + if (!awakeTimeFile.exists()) return 0; + BufferedReader br = new BufferedReader(new FileReader(awakeTimeFile)); + String line = br.readLine(); + br.close(); + return Long.parseLong(line); + } catch (NumberFormatException nfe) { + // Nothing + } catch (IOException ioe) { + // Nothing + } + return 0; + } + + public long getRadioDataUptimeMs() { + if (mRadioDataStart == -1) { + return mRadioDataUptime; + } else { + return getCurrentRadioDataUptimeMs() - mRadioDataStart; + } + } + public void doUnplug(long batteryUptime, long batteryRealtime) { for (int iu = mUidStats.size() - 1; iu >= 0; iu--) { Uid u = mUidStats.valueAt(iu); @@ -894,8 +955,16 @@ public final class BatteryStatsImpl extends BatteryStats { for (int i = mUnpluggables.size() - 1; i >= 0; i--) { mUnpluggables.get(i).unplug(batteryUptime, batteryRealtime); } + // Track total mobile data + doDataUnplug(mMobileDataRx, NetStat.getMobileRxBytes()); + doDataUnplug(mMobileDataTx, NetStat.getMobileTxBytes()); + doDataUnplug(mTotalDataRx, NetStat.getTotalRxBytes()); + doDataUnplug(mTotalDataTx, NetStat.getTotalTxBytes()); + // Track radio awake time + mRadioDataStart = getCurrentRadioDataUptimeMs(); + mRadioDataUptime = 0; } - + public void doPlug(long batteryUptime, long batteryRealtime) { for (int iu = mUidStats.size() - 1; iu >= 0; iu--) { Uid u = mUidStats.valueAt(iu); @@ -911,16 +980,23 @@ public final class BatteryStatsImpl extends BatteryStats { for (int i = mUnpluggables.size() - 1; i >= 0; i--) { mUnpluggables.get(i).plug(batteryUptime, batteryRealtime); } + doDataPlug(mMobileDataRx, NetStat.getMobileRxBytes()); + doDataPlug(mMobileDataTx, NetStat.getMobileTxBytes()); + doDataPlug(mTotalDataRx, NetStat.getTotalRxBytes()); + doDataPlug(mTotalDataTx, NetStat.getTotalTxBytes()); + // Track radio awake time + mRadioDataUptime = getRadioDataUptimeMs(); + mRadioDataStart = -1; } - + public void noteStartGps(int uid) { - mUidStats.get(uid).noteStartGps(); + getUidStatsLocked(uid).noteStartGps(); } public void noteStopGps(int uid) { - mUidStats.get(uid).noteStopGps(); + getUidStatsLocked(uid).noteStopGps(); } - + public void noteScreenOnLocked() { if (!mScreenOn) { mScreenOn = true; @@ -962,10 +1038,7 @@ public final class BatteryStatsImpl extends BatteryStats { } public void noteUserActivityLocked(int uid, int event) { - Uid u = mUidStats.get(uid); - if (u != null) { - u.noteUserActivityLocked(event); - } + getUidStatsLocked(uid).noteUserActivityLocked(event); } public void notePhoneOnLocked() { @@ -981,15 +1054,43 @@ public final class BatteryStatsImpl extends BatteryStats { mPhoneOnTimer.stopRunningLocked(this); } } - - public void notePhoneSignalStrengthLocked(int asu) { + + public void noteAirplaneModeLocked(boolean isAirplaneMode) { + final int bin = mPhoneSignalStrengthBin; + if (bin >= 0) { + if (!isAirplaneMode) { + if (!mPhoneSignalStrengthsTimer[bin].isRunningLocked()) { + mPhoneSignalStrengthsTimer[bin].startRunningLocked(this); + } + } else { + for (int i = 0; i < NUM_SIGNAL_STRENGTH_BINS; i++) { + while (mPhoneSignalStrengthsTimer[i].isRunningLocked()) { + mPhoneSignalStrengthsTimer[i].stopRunningLocked(this); + } + } + } + } + } + + public void notePhoneSignalStrengthLocked(SignalStrength signalStrength) { // Bin the strength. int bin; - if (asu < 0 || asu >= 99) bin = SIGNAL_STRENGTH_NONE_OR_UNKNOWN; - else if (asu >= 16) bin = SIGNAL_STRENGTH_GREAT; - else if (asu >= 8) bin = SIGNAL_STRENGTH_GOOD; - else if (asu >= 4) bin = SIGNAL_STRENGTH_MODERATE; - else bin = SIGNAL_STRENGTH_POOR; + + if (!signalStrength.isGsm()) { + int dBm = signalStrength.getCdmaDbm(); + if (dBm >= -75) bin = SIGNAL_STRENGTH_NONE_OR_UNKNOWN; + else if (dBm >= -85) bin = SIGNAL_STRENGTH_GREAT; + else if (dBm >= -95) bin = SIGNAL_STRENGTH_GOOD; + else if (dBm >= -100) bin = SIGNAL_STRENGTH_MODERATE; + else bin = SIGNAL_STRENGTH_POOR; + } else { + int asu = signalStrength.getGsmSignalStrength(); + if (asu < 0 || asu >= 99) bin = SIGNAL_STRENGTH_NONE_OR_UNKNOWN; + else if (asu >= 16) bin = SIGNAL_STRENGTH_GREAT; + else if (asu >= 8) bin = SIGNAL_STRENGTH_GOOD; + else if (asu >= 4) bin = SIGNAL_STRENGTH_MODERATE; + else bin = SIGNAL_STRENGTH_POOR; + } if (mPhoneSignalStrengthBin != bin) { if (mPhoneSignalStrengthBin >= 0) { mPhoneSignalStrengthsTimer[mPhoneSignalStrengthBin].stopRunningLocked(this); @@ -1017,6 +1118,7 @@ public final class BatteryStatsImpl extends BatteryStats { break; } } + if (DEBUG) Log.i(TAG, "Phone Data Connection -> " + dataType + " = " + hasData); if (mPhoneDataConnectionType != bin) { if (mPhoneDataConnectionType >= 0) { mPhoneDataConnectionsTimer[mPhoneDataConnectionType].stopRunningLocked(this); @@ -1033,16 +1135,10 @@ public final class BatteryStatsImpl extends BatteryStats { } if (mWifiOnUid != uid) { if (mWifiOnUid >= 0) { - Uid u = mUidStats.get(mWifiOnUid); - if (u != null) { - u.noteWifiTurnedOffLocked(); - } + getUidStatsLocked(mWifiOnUid).noteWifiTurnedOffLocked(); } mWifiOnUid = uid; - Uid u = mUidStats.get(uid); - if (u != null) { - u.noteWifiTurnedOnLocked(); - } + getUidStatsLocked(uid).noteWifiTurnedOnLocked(); } } @@ -1052,14 +1148,43 @@ public final class BatteryStatsImpl extends BatteryStats { mWifiOnTimer.stopRunningLocked(this); } if (mWifiOnUid >= 0) { - Uid u = mUidStats.get(mWifiOnUid); - if (u != null) { - u.noteWifiTurnedOffLocked(); - } + getUidStatsLocked(mWifiOnUid).noteWifiTurnedOffLocked(); mWifiOnUid = -1; } } + + public void noteAudioOnLocked(int uid) { + if (!mAudioOn) { + mAudioOn = true; + mAudioOnTimer.startRunningLocked(this); + } + getUidStatsLocked(uid).noteAudioTurnedOnLocked(); + } + public void noteAudioOffLocked(int uid) { + if (mAudioOn) { + mAudioOn = false; + mAudioOnTimer.stopRunningLocked(this); + } + getUidStatsLocked(uid).noteAudioTurnedOffLocked(); + } + + public void noteVideoOnLocked(int uid) { + if (!mVideoOn) { + mVideoOn = true; + mVideoOnTimer.startRunningLocked(this); + } + getUidStatsLocked(uid).noteVideoTurnedOnLocked(); + } + + public void noteVideoOffLocked(int uid) { + if (mVideoOn) { + mVideoOn = false; + mVideoOnTimer.stopRunningLocked(this); + } + getUidStatsLocked(uid).noteVideoTurnedOffLocked(); + } + public void noteWifiRunningLocked() { if (!mWifiRunning) { mWifiRunning = true; @@ -1089,45 +1214,27 @@ public final class BatteryStatsImpl extends BatteryStats { } public void noteFullWifiLockAcquiredLocked(int uid) { - Uid u = mUidStats.get(uid); - if (u != null) { - u.noteFullWifiLockAcquiredLocked(); - } + getUidStatsLocked(uid).noteFullWifiLockAcquiredLocked(); } public void noteFullWifiLockReleasedLocked(int uid) { - Uid u = mUidStats.get(uid); - if (u != null) { - u.noteFullWifiLockReleasedLocked(); - } + getUidStatsLocked(uid).noteFullWifiLockReleasedLocked(); } public void noteScanWifiLockAcquiredLocked(int uid) { - Uid u = mUidStats.get(uid); - if (u != null) { - u.noteScanWifiLockAcquiredLocked(); - } + getUidStatsLocked(uid).noteScanWifiLockAcquiredLocked(); } public void noteScanWifiLockReleasedLocked(int uid) { - Uid u = mUidStats.get(uid); - if (u != null) { - u.noteScanWifiLockReleasedLocked(); - } + getUidStatsLocked(uid).noteScanWifiLockReleasedLocked(); } public void noteWifiMulticastEnabledLocked(int uid) { - Uid u = mUidStats.get(uid); - if (u != null) { - u.noteWifiMulticastEnabledLocked(); - } + getUidStatsLocked(uid).noteWifiMulticastEnabledLocked(); } public void noteWifiMulticastDisabledLocked(int uid) { - Uid u = mUidStats.get(uid); - if (u != null) { - u.noteWifiMulticastDisabledLocked(); - } + getUidStatsLocked(uid).noteWifiMulticastDisabledLocked(); } @Override public long getScreenOnTime(long batteryRealtime, int which) { @@ -1139,7 +1246,7 @@ public final class BatteryStatsImpl extends BatteryStats { return mScreenBrightnessTimer[brightnessBin].getTotalTimeLocked( batteryRealtime, which); } - + @Override public int getInputEventCount(int which) { return mInputEventCounter.getCountLocked(which); } @@ -1147,7 +1254,7 @@ public final class BatteryStatsImpl extends BatteryStats { @Override public long getPhoneOnTime(long batteryRealtime, int which) { return mPhoneOnTimer.getTotalTimeLocked(batteryRealtime, which); } - + @Override public long getPhoneSignalStrengthTime(int strengthBin, long batteryRealtime, int which) { return mPhoneSignalStrengthsTimer[strengthBin].getTotalTimeLocked( @@ -1214,9 +1321,15 @@ public final class BatteryStatsImpl extends BatteryStats { boolean mScanWifiLockOut; StopwatchTimer mScanWifiLockTimer; - + boolean mWifiMulticastEnabled; StopwatchTimer mWifiMulticastTimer; + + boolean mAudioTurnedOn; + StopwatchTimer mAudioTurnedOnTimer; + + boolean mVideoTurnedOn; + StopwatchTimer mVideoTurnedOnTimer; Counter[] mUserActivityCounters; @@ -1247,6 +1360,8 @@ public final class BatteryStatsImpl extends BatteryStats { mScanWifiLockTimer = new StopwatchTimer(SCAN_WIFI_LOCK, null, mUnpluggables); mWifiMulticastTimer = new StopwatchTimer(WIFI_MULTICAST_ENABLED, null, mUnpluggables); + mAudioTurnedOnTimer = new StopwatchTimer(AUDIO_TURNED_ON, null, mUnpluggables); + mVideoTurnedOnTimer = new StopwatchTimer(VIDEO_TURNED_ON, null, mUnpluggables); } @Override @@ -1268,11 +1383,13 @@ public final class BatteryStatsImpl extends BatteryStats { public Map getPackageStats() { return mPackageStats; } - + + @Override public int getUid() { return mUid; } - + + @Override public long getTcpBytesReceived(int which) { if (which == STATS_LAST) { return mLoadedTcpBytesReceived; @@ -1291,7 +1408,8 @@ public final class BatteryStatsImpl extends BatteryStats { return mCurrentTcpBytesReceived + (mStartedTcpBytesReceived >= 0 ? (NetStat.getUidRxBytes(mUid) - mStartedTcpBytesReceived) : 0); } - + + @Override public long getTcpBytesSent(int which) { if (which == STATS_LAST) { return mLoadedTcpBytesSent; @@ -1330,6 +1448,38 @@ public final class BatteryStatsImpl extends BatteryStats { } } + @Override + public void noteVideoTurnedOnLocked() { + if (!mVideoTurnedOn) { + mVideoTurnedOn = true; + mVideoTurnedOnTimer.startRunningLocked(BatteryStatsImpl.this); + } + } + + @Override + public void noteVideoTurnedOffLocked() { + if (mVideoTurnedOn) { + mVideoTurnedOn = false; + mVideoTurnedOnTimer.stopRunningLocked(BatteryStatsImpl.this); + } + } + + @Override + public void noteAudioTurnedOnLocked() { + if (!mAudioTurnedOn) { + mAudioTurnedOn = true; + mAudioTurnedOnTimer.startRunningLocked(BatteryStatsImpl.this); + } + } + + @Override + public void noteAudioTurnedOffLocked() { + if (mAudioTurnedOn) { + mAudioTurnedOn = false; + mAudioTurnedOnTimer.stopRunningLocked(BatteryStatsImpl.this); + } + } + @Override public void noteFullWifiLockReleasedLocked() { if (mFullWifiLockOut) { @@ -1374,7 +1524,17 @@ public final class BatteryStatsImpl extends BatteryStats { public long getWifiTurnedOnTime(long batteryRealtime, int which) { return mWifiTurnedOnTimer.getTotalTimeLocked(batteryRealtime, which); } - + + @Override + public long getAudioTurnedOnTime(long batteryRealtime, int which) { + return mAudioTurnedOnTimer.getTotalTimeLocked(batteryRealtime, which); + } + + @Override + public long getVideoTurnedOnTime(long batteryRealtime, int which) { + return mVideoTurnedOnTimer.getTotalTimeLocked(batteryRealtime, which); + } + @Override public long getFullWifiLockTime(long batteryRealtime, int which) { return mFullWifiLockTimer.getTotalTimeLocked(batteryRealtime, which); @@ -1425,7 +1585,7 @@ public final class BatteryStatsImpl extends BatteryStats { return mCurrentTcpBytesSent + (mStartedTcpBytesSent >= 0 ? (NetStat.getUidTxBytes(mUid) - mStartedTcpBytesSent) : 0); } - + void writeToParcelLocked(Parcel out, long batteryRealtime) { out.writeInt(mWakelockStats.size()); for (Map.Entry wakelockEntry : mWakelockStats.entrySet()) { @@ -1463,6 +1623,8 @@ public final class BatteryStatsImpl extends BatteryStats { out.writeLong(mTcpBytesSentAtLastUnplug); mWifiTurnedOnTimer.writeToParcel(out, batteryRealtime); mFullWifiLockTimer.writeToParcel(out, batteryRealtime); + mAudioTurnedOnTimer.writeToParcel(out, batteryRealtime); + mVideoTurnedOnTimer.writeToParcel(out, batteryRealtime); mScanWifiLockTimer.writeToParcel(out, batteryRealtime); mWifiMulticastTimer.writeToParcel(out, batteryRealtime); if (mUserActivityCounters == null) { @@ -1522,6 +1684,10 @@ public final class BatteryStatsImpl extends BatteryStats { mWifiTurnedOnTimer = new StopwatchTimer(WIFI_TURNED_ON, null, mUnpluggables, in); mFullWifiLockOut = false; mFullWifiLockTimer = new StopwatchTimer(FULL_WIFI_LOCK, null, mUnpluggables, in); + mAudioTurnedOn = false; + mAudioTurnedOnTimer = new StopwatchTimer(AUDIO_TURNED_ON, null, mUnpluggables, in); + mVideoTurnedOn = false; + mVideoTurnedOnTimer = new StopwatchTimer(VIDEO_TURNED_ON, null, mUnpluggables, in); mScanWifiLockOut = false; mScanWifiLockTimer = new StopwatchTimer(SCAN_WIFI_LOCK, null, mUnpluggables, in); mWifiMulticastEnabled = false; @@ -1632,7 +1798,8 @@ public final class BatteryStatsImpl extends BatteryStats { public Timer getSensorTime() { return mTimer; } - + + @Override public int getHandle() { return mHandle; } @@ -1657,6 +1824,11 @@ public final class BatteryStatsImpl extends BatteryStats { */ int mStarts; + /** + * Amount of time the process was running in the foreground. + */ + long mForegroundTime; + /** * The amount of user time loaded from a previous save. */ @@ -1672,6 +1844,11 @@ public final class BatteryStatsImpl extends BatteryStats { */ int mLoadedStarts; + /** + * The amount of foreground time loaded from a previous save. + */ + long mLoadedForegroundTime; + /** * The amount of user time loaded from the previous run. */ @@ -1687,6 +1864,11 @@ public final class BatteryStatsImpl extends BatteryStats { */ int mLastStarts; + /** + * The amount of foreground time loaded from the previous run + */ + long mLastForegroundTime; + /** * The amount of user time when last unplugged. */ @@ -1702,6 +1884,11 @@ public final class BatteryStatsImpl extends BatteryStats { */ int mUnpluggedStarts; + /** + * The amount of foreground time since unplugged. + */ + long mUnpluggedForegroundTime; + Proc() { mUnpluggables.add(this); } @@ -1710,6 +1897,7 @@ public final class BatteryStatsImpl extends BatteryStats { mUnpluggedUserTime = mUserTime; mUnpluggedSystemTime = mSystemTime; mUnpluggedStarts = mStarts; + mUnpluggedForegroundTime = mForegroundTime; } public void plug(long batteryUptime, long batteryRealtime) { @@ -1721,30 +1909,38 @@ public final class BatteryStatsImpl extends BatteryStats { out.writeLong(mUserTime); out.writeLong(mSystemTime); + out.writeLong(mForegroundTime); out.writeInt(mStarts); out.writeLong(mLoadedUserTime); out.writeLong(mLoadedSystemTime); + out.writeLong(mLoadedForegroundTime); out.writeInt(mLoadedStarts); out.writeLong(mLastUserTime); out.writeLong(mLastSystemTime); + out.writeLong(mLastForegroundTime); out.writeInt(mLastStarts); out.writeLong(mUnpluggedUserTime); out.writeLong(mUnpluggedSystemTime); + out.writeLong(mUnpluggedForegroundTime); out.writeInt(mUnpluggedStarts); } void readFromParcelLocked(Parcel in) { mUserTime = in.readLong(); mSystemTime = in.readLong(); + mForegroundTime = in.readLong(); mStarts = in.readInt(); mLoadedUserTime = in.readLong(); mLoadedSystemTime = in.readLong(); + mLoadedForegroundTime = in.readLong(); mLoadedStarts = in.readInt(); mLastUserTime = in.readLong(); mLastSystemTime = in.readLong(); + mLastForegroundTime = in.readLong(); mLastStarts = in.readInt(); mUnpluggedUserTime = in.readLong(); mUnpluggedSystemTime = in.readLong(); + mUnpluggedForegroundTime = in.readLong(); mUnpluggedStarts = in.readInt(); } @@ -1757,6 +1953,10 @@ public final class BatteryStatsImpl extends BatteryStats { mSystemTime += stime; } + public void addForegroundTimeLocked(long ttime) { + mForegroundTime += ttime; + } + public void incStartsLocked() { mStarts++; } @@ -1793,6 +1993,22 @@ public final class BatteryStatsImpl extends BatteryStats { return val; } + @Override + public long getForegroundTime(int which) { + long val; + if (which == STATS_LAST) { + val = mLastForegroundTime; + } else { + val = mForegroundTime; + if (which == STATS_CURRENT) { + val -= mLoadedForegroundTime; + } else if (which == STATS_UNPLUGGED) { + val -= mUnpluggedForegroundTime; + } + } + return val; + } + @Override public int getStarts(int which) { int val; @@ -2315,7 +2531,7 @@ public final class BatteryStatsImpl extends BatteryStats { StopwatchTimer t = getSensorTimerLocked(Sensor.GPS, false); if (t != null) { t.stopRunningLocked(BatteryStatsImpl.this); - } + } } public BatteryStatsImpl getBatteryStats() { @@ -2526,7 +2742,44 @@ public final class BatteryStatsImpl extends BatteryStats { public long getBatteryRealtime(long curTime) { return getBatteryRealtimeLocked(curTime); } - + + private long getTcpBytes(long current, long[] dataBytes, int which) { + if (which == STATS_LAST) { + return dataBytes[STATS_LAST]; + } else { + if (which == STATS_UNPLUGGED) { + if (dataBytes[STATS_UNPLUGGED] < 0) { + return dataBytes[STATS_LAST]; + } else { + return current - dataBytes[STATS_UNPLUGGED]; + } + } else if (which == STATS_TOTAL) { + return (current - dataBytes[STATS_CURRENT]) + dataBytes[STATS_TOTAL]; + } + return current - dataBytes[STATS_CURRENT]; + } + } + + /** Only STATS_UNPLUGGED works properly */ + public long getMobileTcpBytesSent(int which) { + return getTcpBytes(NetStat.getMobileTxBytes(), mMobileDataTx, which); + } + + /** Only STATS_UNPLUGGED works properly */ + public long getMobileTcpBytesReceived(int which) { + return getTcpBytes(NetStat.getMobileRxBytes(), mMobileDataRx, which); + } + + /** Only STATS_UNPLUGGED works properly */ + public long getTotalTcpBytesSent(int which) { + return getTcpBytes(NetStat.getTotalTxBytes(), mTotalDataTx, which); + } + + /** Only STATS_UNPLUGGED works properly */ + public long getTotalTcpBytesReceived(int which) { + return getTcpBytes(NetStat.getTotalRxBytes(), mTotalDataRx, which); + } + @Override public int getDischargeStartLevel() { synchronized(this) { @@ -2567,7 +2820,7 @@ public final class BatteryStatsImpl extends BatteryStats { public void removeUidStatsLocked(int uid) { mUidStats.remove(uid); } - + /** * Retrieve the statistics object for a particular process, creating * if needed. @@ -2577,6 +2830,24 @@ public final class BatteryStatsImpl extends BatteryStats { return u.getProcessStatsLocked(name); } + /** + * Retrieve the statistics object for a particular process, given + * the name of the process. + * @param name process name + * @return the statistics object for the process + */ + public Uid.Proc getProcessStatsLocked(String name, int pid) { + int uid; + if (mUidCache.containsKey(name)) { + uid = mUidCache.get(name); + } else { + uid = Process.getUidForPid(pid); + mUidCache.put(name, uid); + } + Uid u = getUidStatsLocked(uid); + return u.getProcessStatsLocked(name); + } + /** * Retrieve the statistics object for a particular process, creating * if needed. @@ -2752,6 +3023,10 @@ public final class BatteryStatsImpl extends BatteryStats { u.mWifiTurnedOnTimer.readSummaryFromParcelLocked(in); u.mFullWifiLockOut = false; u.mFullWifiLockTimer.readSummaryFromParcelLocked(in); + u.mAudioTurnedOn = false; + u.mAudioTurnedOnTimer.readSummaryFromParcelLocked(in); + u.mVideoTurnedOn = false; + u.mVideoTurnedOnTimer.readSummaryFromParcelLocked(in); u.mScanWifiLockOut = false; u.mScanWifiLockTimer.readSummaryFromParcelLocked(in); u.mWifiMulticastEnabled = false; @@ -2888,6 +3163,8 @@ public final class BatteryStatsImpl extends BatteryStats { u.mWifiTurnedOnTimer.writeSummaryFromParcelLocked(out, NOWREAL); u.mFullWifiLockTimer.writeSummaryFromParcelLocked(out, NOWREAL); + u.mAudioTurnedOnTimer.writeSummaryFromParcelLocked(out, NOWREAL); + u.mVideoTurnedOnTimer.writeSummaryFromParcelLocked(out, NOWREAL); u.mScanWifiLockTimer.writeSummaryFromParcelLocked(out, NOWREAL); u.mWifiMulticastTimer.writeSummaryFromParcelLocked(out, NOWREAL); @@ -3046,11 +3323,24 @@ public final class BatteryStatsImpl extends BatteryStats { mDischargeCurrentLevel = in.readInt(); mLastWriteTime = in.readLong(); + mMobileDataRx[STATS_LAST] = in.readLong(); + mMobileDataRx[STATS_UNPLUGGED] = -1; + mMobileDataTx[STATS_LAST] = in.readLong(); + mMobileDataTx[STATS_UNPLUGGED] = -1; + mTotalDataRx[STATS_LAST] = in.readLong(); + mTotalDataRx[STATS_UNPLUGGED] = -1; + mTotalDataTx[STATS_LAST] = in.readLong(); + mTotalDataTx[STATS_UNPLUGGED] = -1; + + mRadioDataUptime = in.readLong(); + mRadioDataStart = -1; + mKernelWakelockStats.clear(); int NKW = in.readInt(); for (int ikw = 0; ikw < NKW; ikw++) { if (in.readInt() != 0) { String wakelockName = in.readString(); + in.readInt(); // Extra 0/1 written by Timer.writeTimerToParcel SamplingTimer kwlt = new SamplingTimer(mUnpluggables, mOnBattery, in); mKernelWakelockStats.put(wakelockName, kwlt); } @@ -3119,6 +3409,14 @@ public final class BatteryStatsImpl extends BatteryStats { out.writeInt(mDischargeCurrentLevel); out.writeLong(mLastWriteTime); + out.writeLong(getMobileTcpBytesReceived(STATS_UNPLUGGED)); + out.writeLong(getMobileTcpBytesSent(STATS_UNPLUGGED)); + out.writeLong(getTotalTcpBytesReceived(STATS_UNPLUGGED)); + out.writeLong(getTotalTcpBytesSent(STATS_UNPLUGGED)); + + // Write radio uptime for data + out.writeLong(getRadioDataUptimeMs()); + out.writeInt(mKernelWakelockStats.size()); for (Map.Entry ent : mKernelWakelockStats.entrySet()) { SamplingTimer kwlt = ent.getValue(); diff --git a/core/java/com/android/internal/os/PowerProfile.java b/core/java/com/android/internal/os/PowerProfile.java new file mode 100644 index 0000000000000000000000000000000000000000..4a8d8b182f90320b4bdf4a20ada7cdaa9cb7c99b --- /dev/null +++ b/core/java/com/android/internal/os/PowerProfile.java @@ -0,0 +1,238 @@ +/* + * 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.os; + + +import android.content.Context; +import android.content.res.XmlResourceParser; + +import com.android.internal.util.XmlUtils; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; + +/** + * Reports power consumption values for various device activities. Reads values from an XML file. + * Customize the XML file for different devices. + * [hidden] + */ +public class PowerProfile { + + /** + * No power consumption, or accounted for elsewhere. + */ + public static final String POWER_NONE = "none"; + + /** + * Power consumption when CPU is in power collapse mode. + */ + public static final String POWER_CPU_IDLE = "cpu.idle"; + + /** + * Power consumption when CPU is running at normal speed. + */ + public static final String POWER_CPU_NORMAL = "cpu.normal"; + + /** + * Power consumption when CPU is running at full speed. + */ + public static final String POWER_CPU_FULL = "cpu.full"; + + /** + * Power consumption when WiFi driver is scanning for networks. + */ + public static final String POWER_WIFI_SCAN = "wifi.scan"; + + /** + * Power consumption when WiFi driver is on. + */ + public static final String POWER_WIFI_ON = "wifi.on"; + + /** + * Power consumption when WiFi driver is transmitting/receiving. + */ + public static final String POWER_WIFI_ACTIVE = "wifi.active"; + + /** + * Power consumption when GPS is on. + */ + public static final String POWER_GPS_ON = "gps.on"; + + /** + * Power consumption when Bluetooth driver is on. + */ + public static final String POWER_BLUETOOTH_ON = "bluetooth.on"; + + /** + * Power consumption when Bluetooth driver is transmitting/receiving. + */ + public static final String POWER_BLUETOOTH_ACTIVE = "bluetooth.active"; + + /** + * Power consumption when screen is on, not including the backlight power. + */ + public static final String POWER_SCREEN_ON = "screen.on"; + + /** + * Power consumption when cell radio is on but not on a call. + */ + public static final String POWER_RADIO_ON = "radio.on"; + + /** + * Power consumption when talking on the phone. + */ + public static final String POWER_RADIO_ACTIVE = "radio.active"; + + /** + * Power consumption at full backlight brightness. If the backlight is at + * 50% brightness, then this should be multiplied by 0.5 + */ + public static final String POWER_SCREEN_FULL = "screen.full"; + + /** + * Power consumed by the audio hardware when playing back audio content. This is in addition + * to the CPU power, probably due to a DSP and / or amplifier. + */ + public static final String POWER_AUDIO = "dsp.audio"; + + /** + * Power consumed by any media hardware when playing back video content. This is in addition + * to the CPU power, probably due to a DSP. + */ + public static final String POWER_VIDEO = "dsp.video"; + + static final HashMap sPowerMap = new HashMap(); + + private static final String TAG_DEVICE = "device"; + private static final String TAG_ITEM = "item"; + private static final String TAG_ARRAY = "array"; + private static final String TAG_ARRAYITEM = "value"; + private static final String ATTR_NAME = "name"; + + public PowerProfile(Context context) { + // Read the XML file for the given profile (normally only one per + // device) + if (sPowerMap.size() == 0) { + readPowerValuesFromXml(context); + } + } + + private void readPowerValuesFromXml(Context context) { + int id = com.android.internal.R.xml.power_profile; + XmlResourceParser parser = context.getResources().getXml(id); + boolean parsingArray = false; + ArrayList array = new ArrayList(); + String arrayName = null; + + try { + XmlUtils.beginDocument(parser, TAG_DEVICE); + + while (true) { + XmlUtils.nextElement(parser); + + String element = parser.getName(); + if (element == null) break; + + if (parsingArray && !element.equals(TAG_ARRAYITEM)) { + // Finish array + sPowerMap.put(arrayName, array.toArray(new Double[array.size()])); + parsingArray = false; + } + if (element.equals(TAG_ARRAY)) { + parsingArray = true; + array.clear(); + arrayName = parser.getAttributeValue(null, ATTR_NAME); + } else if (element.equals(TAG_ITEM) || element.equals(TAG_ARRAYITEM)) { + String name = null; + if (!parsingArray) name = parser.getAttributeValue(null, ATTR_NAME); + if (parser.next() == XmlPullParser.TEXT) { + String power = parser.getText(); + double value = 0; + try { + value = Double.valueOf(power); + } catch (NumberFormatException nfe) { + } + if (element.equals(TAG_ITEM)) { + sPowerMap.put(name, value); + } else if (parsingArray) { + array.add(value); + } + } + } + } + if (parsingArray) { + sPowerMap.put(arrayName, array.toArray(new Double[array.size()])); + } + } catch (XmlPullParserException e) { + throw new RuntimeException(e); + } catch (IOException e) { + throw new RuntimeException(e); + } finally { + parser.close(); + } + } + + /** + * Returns the average current in mA consumed by the subsystem + * @param type the subsystem type + * @return the average current in milliAmps. + */ + public double getAveragePower(String type) { + if (sPowerMap.containsKey(type)) { + Object data = sPowerMap.get(type); + if (data instanceof Double[]) { + return ((Double[])data)[0]; + } else { + return (Double) sPowerMap.get(type); + } + } else { + return 0; + } + } + + /** + * Returns the average current in mA consumed by the subsystem for the given level. + * @param type the subsystem type + * @param level the level of power at which the subsystem is running. For instance, the + * signal strength of the cell network between 0 and 4 (if there are 4 bars max.). + * If there is no data for multiple levels, the level is ignored. + * @return the average current in milliAmps. + */ + public double getAveragePower(String type, int level) { + if (sPowerMap.containsKey(type)) { + Object data = sPowerMap.get(type); + if (data instanceof Double[]) { + final Double[] values = (Double[]) data; + if (values.length > level && level >= 0) { + return values[level]; + } else if (level < 0) { + return 0; + } else { + return values[values.length - 1]; + } + } else { + return (Double) data; + } + } else { + return 0; + } + } +} diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index ac8b5893416845213ebd4b076322e4bee2e69181..f67a235b0736abc6262192945034ded82daeda00 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -467,7 +467,7 @@ public class ZygoteInit { "--setuid=1000", "--setgid=1000", "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,3001,3002,3003", - "--capabilities=121715744,121715744", + "--capabilities=130104352,130104352", "--runtime-init", "--nice-name=system_server", "com.android.server.SystemServer", diff --git a/core/java/com/android/internal/util/BitwiseInputStream.java b/core/java/com/android/internal/util/BitwiseInputStream.java index 4757919f23f59686f56b4f0f96959933537df07c..86f74f30251815f1b60bf0fdeb7bcf65772636aa 100644 --- a/core/java/com/android/internal/util/BitwiseInputStream.java +++ b/core/java/com/android/internal/util/BitwiseInputStream.java @@ -65,30 +65,31 @@ public class BitwiseInputStream { /** * Read some data and increment the current position. * - * @param bits the amount of data to read (gte 0, lte 8) + * The 8-bit limit on access to bitwise streams is intentional to + * avoid endianness issues. * + * @param bits the amount of data to read (gte 0, lte 8) * @return byte of read data (possibly partially filled, from lsb) */ - public byte read(int bits) throws AccessException { + public int read(int bits) throws AccessException { int index = mPos >>> 3; int offset = 16 - (mPos & 0x07) - bits; // &7==%8 if ((bits < 0) || (bits > 8) || ((mPos + bits) > mEnd)) { throw new AccessException("illegal read " + "(pos " + mPos + ", end " + mEnd + ", bits " + bits + ")"); } - int data = (mBuf[index] & 0x00FF) << 8; - if (offset < 8) data |= (mBuf[index + 1] & 0xFF); + int data = (mBuf[index] & 0xFF) << 8; + if (offset < 8) data |= mBuf[index + 1] & 0xFF; data >>>= offset; data &= (-1 >>> (32 - bits)); mPos += bits; - return (byte)data; + return data; } /** * Read data in bulk into a byte array and increment the current position. * * @param bits the amount of data to read - * * @return newly allocated byte array of read data */ public byte[] readByteArray(int bits) throws AccessException { diff --git a/core/java/com/android/internal/util/BitwiseOutputStream.java b/core/java/com/android/internal/util/BitwiseOutputStream.java index 1b974ce4c9ee6f798272435d4dc6538cf2d95259..70c0be81a56ae66384b240489138f312ffa1e1d5 100644 --- a/core/java/com/android/internal/util/BitwiseOutputStream.java +++ b/core/java/com/android/internal/util/BitwiseOutputStream.java @@ -82,6 +82,9 @@ public class BitwiseOutputStream { /** * Write some data and increment the current position. * + * The 8-bit limit on access to bitwise streams is intentional to + * avoid endianness issues. + * * @param bits the amount of data to write (gte 0, lte 8) * @param data to write, will be masked to expose only bits param from lsb */ @@ -95,8 +98,8 @@ public class BitwiseOutputStream { int offset = 16 - (mPos & 0x07) - bits; // &7==%8 data <<= offset; mPos += bits; - mBuf[index] |= (data >>> 8); - if (offset < 8) mBuf[index + 1] |= (data & 0x00FF); + mBuf[index] |= data >>> 8; + if (offset < 8) mBuf[index + 1] |= data & 0xFF; } /** diff --git a/core/java/com/google/android/net/GoogleHttpClient.java b/core/java/com/google/android/net/GoogleHttpClient.java index 871c925478e4f3931adb0bab304e720e2530ba75..922f5bef8a4933c92e0aa414e408593198a18eae 100644 --- a/core/java/com/google/android/net/GoogleHttpClient.java +++ b/core/java/com/google/android/net/GoogleHttpClient.java @@ -37,6 +37,10 @@ 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.conn.scheme.LayeredSocketFactory; +import org.apache.http.conn.scheme.Scheme; +import org.apache.http.conn.scheme.SchemeRegistry; +import org.apache.http.conn.scheme.SocketFactory; import org.apache.http.impl.client.EntityEnclosingRequestWrapper; import org.apache.http.impl.client.RequestWrapper; import org.apache.http.params.HttpParams; @@ -44,6 +48,8 @@ import org.apache.http.protocol.HttpContext; import org.apache.harmony.xnet.provider.jsse.SSLClientSessionCache; import java.io.IOException; +import java.net.InetAddress; +import java.net.Socket; import java.net.URI; import java.net.URISyntaxException; @@ -66,25 +72,22 @@ public class GoogleHttpClient implements HttpClient { private final AndroidHttpClient mClient; private final ContentResolver mResolver; - private final String mUserAgent; + private final String mAppName, mUserAgent; + private final ThreadLocal mConnectionAllocated = new ThreadLocal(); /** - * Create an HTTP client. Normally one client is shared throughout an app. - * @param resolver to use for accessing URL rewriting rules. - * @param userAgent to report in your HTTP requests. - * @deprecated Use {@link #GoogleHttpClient(android.content.ContentResolver, String, boolean)} + * Create an HTTP client without SSL session persistence. + * @deprecated Use {@link #GoogleHttpClient(android.content.Context, String, boolean)} */ public GoogleHttpClient(ContentResolver resolver, String userAgent) { mClient = AndroidHttpClient.newInstance(userAgent); mResolver = resolver; - mUserAgent = userAgent; + mUserAgent = mAppName = userAgent; } /** - * GoogleHttpClient(Context, String, boolean) - without SSL session - * persistence. - * - * @deprecated use Context instead of ContentResolver. + * Create an HTTP client without SSL session persistence. + * @deprecated Use {@link #GoogleHttpClient(android.content.Context, String, boolean)} */ public GoogleHttpClient(ContentResolver resolver, String appAndVersion, boolean gzipCapable) { @@ -111,21 +114,72 @@ public class GoogleHttpClient implements HttpClient { * headers. Needed because Google servers require gzip in the User-Agent * in order to return gzip'd content. */ - public GoogleHttpClient(Context context, String appAndVersion, - boolean gzipCapable) { - this(context.getContentResolver(), SSLClientSessionCacheFactory.getCache(context), + public GoogleHttpClient(Context context, String appAndVersion, boolean gzipCapable) { + this(context.getContentResolver(), + SSLClientSessionCacheFactory.getCache(context), appAndVersion, gzipCapable); } - private GoogleHttpClient(ContentResolver resolver, SSLClientSessionCache cache, + private GoogleHttpClient(ContentResolver resolver, + SSLClientSessionCache cache, String appAndVersion, boolean gzipCapable) { String userAgent = appAndVersion + " (" + Build.DEVICE + " " + Build.ID + ")"; if (gzipCapable) { userAgent = userAgent + "; gzip"; } + mClient = AndroidHttpClient.newInstance(userAgent, cache); mResolver = resolver; + mAppName = appAndVersion; mUserAgent = userAgent; + + // Wrap all the socket factories with the appropriate wrapper. (Apache + // HTTP, curse its black and stupid heart, inspects the SocketFactory to + // see if it's a LayeredSocketFactory, so we need two wrapper classes.) + SchemeRegistry registry = getConnectionManager().getSchemeRegistry(); + for (String name : registry.getSchemeNames()) { + Scheme scheme = registry.unregister(name); + SocketFactory sf = scheme.getSocketFactory(); + if (sf instanceof LayeredSocketFactory) { + sf = new WrappedLayeredSocketFactory((LayeredSocketFactory) sf); + } else { + sf = new WrappedSocketFactory(sf); + } + registry.register(new Scheme(name, sf, scheme.getDefaultPort())); + } + } + + /** + * Delegating wrapper for SocketFactory records when sockets are connected. + * We use this to know whether a connection was created vs reused, to + * gather per-app statistics about connection reuse rates. + * (Note, we record only *connection*, not *creation* of sockets -- + * what we care about is the network overhead of an actual TCP connect.) + */ + private class WrappedSocketFactory implements SocketFactory { + private SocketFactory mDelegate; + private WrappedSocketFactory(SocketFactory delegate) { mDelegate = delegate; } + public final Socket createSocket() throws IOException { return mDelegate.createSocket(); } + public final boolean isSecure(Socket s) { return mDelegate.isSecure(s); } + + public final Socket connectSocket( + Socket s, String h, int p, + InetAddress la, int lp, HttpParams params) throws IOException { + mConnectionAllocated.set(Boolean.TRUE); + return mDelegate.connectSocket(s, h, p, la, lp, params); + } + } + + /** Like WrappedSocketFactory, but for the LayeredSocketFactory subclass. */ + private class WrappedLayeredSocketFactory + extends WrappedSocketFactory implements LayeredSocketFactory { + private LayeredSocketFactory mDelegate; + private WrappedLayeredSocketFactory(LayeredSocketFactory sf) { super(sf); mDelegate = sf; } + + public final Socket createSocket(Socket s, String host, int port, boolean autoClose) + throws IOException { + return mDelegate.createSocket(s, host, port, autoClose); + } } /** @@ -140,24 +194,21 @@ public class GoogleHttpClient implements HttpClient { public HttpResponse executeWithoutRewriting( HttpUriRequest request, HttpContext context) throws IOException { - String code = "Error"; + int code = -1; long start = SystemClock.elapsedRealtime(); try { 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. + mConnectionAllocated.set(null); - // see if we're logging network stats. - boolean logNetworkStats = NetworkStatsEntity.shouldLogNetworkStats(); + if (NetworkStatsEntity.shouldLogNetworkStats()) { + // 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. - 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 @@ -165,30 +216,39 @@ public class GoogleHttpClient implements HttpClient { long now = SystemClock.elapsedRealtime(); long elapsed = now - start; NetworkStatsEntity entity = new NetworkStatsEntity(origEntity, - mUserAgent, uid, startTx, startRx, + mAppName, 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()); } + code = response.getStatusLine().getStatusCode(); return response; - } catch (IOException e) { - code = "IOException"; - throw e; } finally { // Record some statistics to the checkin service about the outcome. // Note that this is only describing execute(), not body download. + // We assume the database writes are much faster than network I/O, + // and not worth running in a background thread or anything. try { long elapsed = SystemClock.elapsedRealtime() - start; ContentValues values = new ContentValues(); - values.put(Checkin.Stats.TAG, - Checkin.Stats.Tag.HTTP_STATUS + ":" + - mUserAgent + ":" + code); values.put(Checkin.Stats.COUNT, 1); values.put(Checkin.Stats.SUM, elapsed / 1000.0); + + values.put(Checkin.Stats.TAG, Checkin.Stats.Tag.HTTP_REQUEST + ":" + mAppName); + mResolver.insert(Checkin.Stats.CONTENT_URI, values); + + // No sockets and no exceptions means we successfully reused a connection + if (mConnectionAllocated.get() == null && code >= 0) { + values.put(Checkin.Stats.TAG, Checkin.Stats.Tag.HTTP_REUSED + ":" + mAppName); + mResolver.insert(Checkin.Stats.CONTENT_URI, values); + } + + String status = code < 0 ? "IOException" : Integer.toString(code); + values.put(Checkin.Stats.TAG, + Checkin.Stats.Tag.HTTP_STATUS + ":" + mAppName + ":" + status); mResolver.insert(Checkin.Stats.CONTENT_URI, values); } catch (Exception e) { Log.e(TAG, "Error recording stats", e); diff --git a/core/java/com/google/android/util/GoogleWebContentHelper.java b/core/java/com/google/android/util/GoogleWebContentHelper.java index 291142008934ff09c8f21f664c12759fc1962068..8291e2964dc1d6d0c34d39f9b2344b834d1afd4a 100644 --- a/core/java/com/google/android/util/GoogleWebContentHelper.java +++ b/core/java/com/google/android/util/GoogleWebContentHelper.java @@ -130,7 +130,18 @@ public class GoogleWebContentHelper { mWebView.loadUrl(mSecureUrl); return this; } - + + /** + * Loads data into the webview and also provides a failback url + * @return This {@link GoogleWebContentHelper} so methods can be chained. + */ + public GoogleWebContentHelper loadDataWithFailUrl(String base, String data, + String mimeType, String encoding, String failUrl) { + ensureViews(); + mWebView.loadDataWithBaseURL(base, data, mimeType, encoding, failUrl); + return this; + } + /** * Helper to handle the back key. Returns true if the back key was handled, * otherwise returns false. diff --git a/core/jni/Android.mk b/core/jni/Android.mk index 4839b6f1d991e9cfaefe6e63871c9f7c6c5718cc..888cb1163997762939f730cbf4c6fa7de9fdb3bc 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -19,6 +19,7 @@ LOCAL_SRC_FILES:= \ ActivityManager.cpp \ AndroidRuntime.cpp \ CursorWindow.cpp \ + Time.cpp \ com_google_android_gles_jni_EGLImpl.cpp \ com_google_android_gles_jni_GLImpl.cpp.arm \ android_opengl_GLES10.cpp \ @@ -117,7 +118,10 @@ LOCAL_SRC_FILES:= \ android_location_GpsLocationProvider.cpp \ com_android_internal_os_ZygoteInit.cpp \ com_android_internal_graphics_NativeUtils.cpp \ - android_backup_FileBackupHelper.cpp + android_backup_BackupDataInput.cpp \ + android_backup_BackupDataOutput.cpp \ + android_backup_FileBackupHelperBase.cpp \ + android_backup_BackupHelperDispatcher.cpp LOCAL_C_INCLUDES += \ $(JNI_H_INCLUDE) \ @@ -164,8 +168,7 @@ LOCAL_SHARED_LIBRARIES := \ libicui18n \ libicudata \ libmedia \ - libwpa_client \ - libemoji + libwpa_client ifeq ($(BOARD_HAVE_BLUETOOTH),true) LOCAL_C_INCLUDES += \ diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index aa6450d153c23c854f5e9ae55e631d55b24bf37f..c8153012299543c1a1792c1ac33ce5d2dac84eba 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -155,7 +155,10 @@ extern int register_android_ddm_DdmHandleNativeHeap(JNIEnv *env); extern int register_com_android_internal_os_ZygoteInit(JNIEnv* env); extern int register_android_util_Base64(JNIEnv* env); extern int register_android_location_GpsLocationProvider(JNIEnv* env); -extern int register_android_backup_FileBackupHelper(JNIEnv *env); +extern int register_android_backup_BackupDataInput(JNIEnv *env); +extern int register_android_backup_BackupDataOutput(JNIEnv *env); +extern int register_android_backup_FileBackupHelperBase(JNIEnv *env); +extern int register_android_backup_BackupHelperDispatcher(JNIEnv *env); static AndroidRuntime* gCurRuntime = NULL; @@ -1126,7 +1129,10 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_ddm_DdmHandleNativeHeap), REG_JNI(register_android_util_Base64), REG_JNI(register_android_location_GpsLocationProvider), - REG_JNI(register_android_backup_FileBackupHelper), + REG_JNI(register_android_backup_BackupDataInput), + REG_JNI(register_android_backup_BackupDataOutput), + REG_JNI(register_android_backup_FileBackupHelperBase), + REG_JNI(register_android_backup_BackupHelperDispatcher), }; /* diff --git a/libs/ui/Time.cpp b/core/jni/Time.cpp similarity index 99% rename from libs/ui/Time.cpp rename to core/jni/Time.cpp index b5539135facde402b4c19f715bd2047ddb80afc8..f3037f383039077e449fd79ad0c55ca0863263fd 100644 --- a/libs/ui/Time.cpp +++ b/core/jni/Time.cpp @@ -1,4 +1,4 @@ -#include +#include "TimeUtils.h" #include #include diff --git a/include/utils/TimeUtils.h b/core/jni/TimeUtils.h similarity index 100% rename from include/utils/TimeUtils.h rename to core/jni/TimeUtils.h diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp index 65f44d5042576a55c0d4bd05ec839a4b9a177ddd..af8ecf5cccc34219aba5a925d4dc233977033ab1 100644 --- a/core/jni/android/graphics/Bitmap.cpp +++ b/core/jni/android/graphics/Bitmap.cpp @@ -28,7 +28,7 @@ typedef void (*FromColorProc)(void* dst, const SkColor src[], int width, static void FromColor_D32(void* dst, const SkColor src[], int width, int, int) { SkPMColor* d = (SkPMColor*)dst; - + for (int i = 0; i < width; i++) { *d++ = SkPreMultiplyColor(*src++); } @@ -37,7 +37,7 @@ static void FromColor_D32(void* dst, const SkColor src[], int width, static void FromColor_D565(void* dst, const SkColor src[], int width, int x, int y) { uint16_t* d = (uint16_t*)dst; - + DITHER_565_SCAN(y); for (int stop = x + width; x < stop; x++) { SkColor c = *src++; @@ -49,7 +49,7 @@ static void FromColor_D565(void* dst, const SkColor src[], int width, static void FromColor_D4444(void* dst, const SkColor src[], int width, int x, int y) { SkPMColor16* d = (SkPMColor16*)dst; - + DITHER_4444_SCAN(y); for (int stop = x + width; x < stop; x++) { SkPMColor c = SkPreMultiplyColor(*src++); @@ -80,14 +80,14 @@ bool GraphicsJNI::SetPixels(JNIEnv* env, jintArray srcColors, SkAutoLockPixels alp(dstBitmap); void* dst = dstBitmap.getPixels(); FromColorProc proc = ChooseFromColorProc(dstBitmap.config()); - + if (NULL == dst || NULL == proc) { return false; } - + const jint* array = env->GetIntArrayElements(srcColors, NULL); const SkColor* src = (const SkColor*)array + srcOffset; - + // reset to to actual choice from caller dst = dstBitmap.getAddr(x, y); // now copy/convert each scanline @@ -96,7 +96,7 @@ bool GraphicsJNI::SetPixels(JNIEnv* env, jintArray srcColors, src += srcStride; dst = (char*)dst + dstBitmap.rowBytes(); } - + env->ReleaseIntArrayElements(srcColors, const_cast(array), JNI_ABORT); return true; @@ -212,7 +212,7 @@ static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors, doThrowIAE(env, "width and height must be > 0"); return NULL; } - + if (NULL != jColors) { size_t n = env->GetArrayLength(jColors); if (n < SkAbs32(stride) * (size_t)height) { @@ -222,7 +222,7 @@ static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors, } SkBitmap bitmap; - + bitmap.setConfig(config, width, height); if (!GraphicsJNI::setJavaPixelRef(env, &bitmap, NULL)) { return NULL; @@ -232,7 +232,7 @@ static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors, GraphicsJNI::SetPixels(env, jColors, offset, stride, 0, 0, width, height, bitmap); } - + return GraphicsJNI::createBitmap(env, new SkBitmap(bitmap), isMutable, NULL); } @@ -245,7 +245,7 @@ static jobject Bitmap_copy(JNIEnv* env, jobject, const SkBitmap* src, if (!src->copyTo(&result, dstConfig, &allocator)) { return NULL; } - + return GraphicsJNI::createBitmap(env, new SkBitmap(result), isMutable, NULL); } @@ -324,15 +324,15 @@ static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) { SkDebugf("-------- unparcel parcel is NULL\n"); return NULL; } - + android::Parcel* p = android::parcelForJavaObject(env, parcel); - + const bool isMutable = p->readInt32() != 0; const SkBitmap::Config config = (SkBitmap::Config)p->readInt32(); const int width = p->readInt32(); const int height = p->readInt32(); const int rowBytes = p->readInt32(); - + if (SkBitmap::kARGB_8888_Config != config && SkBitmap::kRGB_565_Config != config && SkBitmap::kARGB_4444_Config != config && @@ -355,7 +355,7 @@ static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) { ctable = new SkColorTable(src, count); } } - + if (!GraphicsJNI::setJavaPixelRef(env, bitmap, ctable)) { ctable->safeUnref(); delete bitmap; @@ -368,7 +368,7 @@ static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) { bitmap->lockPixels(); memcpy(bitmap->getPixels(), p->readInplace(size), size); bitmap->unlockPixels(); - + return GraphicsJNI::createBitmap(env, bitmap, isMutable, NULL); } @@ -381,7 +381,7 @@ static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject, } android::Parcel* p = android::parcelForJavaObject(env, parcel); - + p->writeInt32(isMutable); p->writeInt32(bitmap->config()); p->writeInt32(bitmap->width()); @@ -413,7 +413,7 @@ static jobject Bitmap_extractAlpha(JNIEnv* env, jobject clazz, jintArray offsetXY) { SkIPoint offset; SkBitmap* dst = new SkBitmap; - + src->extractAlpha(dst, paint, &offset); if (offsetXY != 0 && env->GetArrayLength(offsetXY) >= 2) { int* array = env->GetIntArrayElements(offsetXY, NULL); @@ -421,7 +421,7 @@ static jobject Bitmap_extractAlpha(JNIEnv* env, jobject clazz, array[1] = offset.fY; env->ReleaseIntArrayElements(offsetXY, array, 0); } - + return GraphicsJNI::createBitmap(env, dst, true, NULL); } @@ -439,7 +439,7 @@ static int Bitmap_getPixel(JNIEnv* env, jobject, const SkBitmap* bitmap, if (NULL == src) { return 0; } - + SkColor dst[1]; proc(dst, src, 1, bitmap->getColorTable()); return dst[0]; @@ -449,7 +449,7 @@ static void Bitmap_getPixels(JNIEnv* env, jobject, const SkBitmap* bitmap, jintArray pixelArray, int offset, int stride, int x, int y, int width, int height) { SkAutoLockPixels alp(*bitmap); - + ToColorProc proc = ChooseToColorProc(*bitmap); if (NULL == proc) { return; @@ -498,7 +498,7 @@ static void Bitmap_copyPixelsToBuffer(JNIEnv* env, jobject, const SkBitmap* bitmap, jobject jbuffer) { SkAutoLockPixels alp(*bitmap); const void* src = bitmap->getPixels(); - + if (NULL != src) { android::AutoBufferPointer abp(env, jbuffer, JNI_TRUE); @@ -511,7 +511,7 @@ static void Bitmap_copyPixelsFromBuffer(JNIEnv* env, jobject, const SkBitmap* bitmap, jobject jbuffer) { SkAutoLockPixels alp(*bitmap); void* dst = bitmap->getPixels(); - + if (NULL != dst) { android::AutoBufferPointer abp(env, jbuffer, JNI_FALSE); // the java side has already checked that buffer is large enough @@ -519,6 +519,11 @@ static void Bitmap_copyPixelsFromBuffer(JNIEnv* env, jobject, } } +static void Bitmap_prepareToDraw(JNIEnv* env, jobject, SkBitmap* bitmap) { + bitmap->lockPixels(); + bitmap->unlockPixels(); +} + /////////////////////////////////////////////////////////////////////////////// #include @@ -552,7 +557,8 @@ static JNINativeMethod gBitmapMethods[] = { { "nativeCopyPixelsToBuffer", "(ILjava/nio/Buffer;)V", (void*)Bitmap_copyPixelsToBuffer }, { "nativeCopyPixelsFromBuffer", "(ILjava/nio/Buffer;)V", - (void*)Bitmap_copyPixelsFromBuffer } + (void*)Bitmap_copyPixelsFromBuffer }, + { "nativePrepareToDraw", "(I)V", (void*)Bitmap_prepareToDraw } }; #define kClassPathName "android/graphics/Bitmap" diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp index 1fd15d687baf2b677a3666eaa2dabfe70742bf3c..137707fa93bf5a74b8d293199b4ebf6786361b51 100644 --- a/core/jni/android/graphics/BitmapFactory.cpp +++ b/core/jni/android/graphics/BitmapFactory.cpp @@ -311,7 +311,7 @@ static SkPixelRef* installPixelRef(SkBitmap* bitmap, SkStream* stream, int sampleSize) { SkPixelRef* pr; // only use ashmem for large images, since mmaps come at a price - if (bitmap->getSize() >= 32 * 65536) { + if (bitmap->getSize() >= 32 * 1024) { pr = new SkImageRef_ashmem(stream, bitmap->config(), sampleSize); } else { pr = new SkImageRef_GlobalPool(stream, bitmap->config(), sampleSize); @@ -520,7 +520,10 @@ static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz, */ AutoFDSeek as(descriptor); - return doDecode(env, stream, padding, bitmapFactoryOptions, true); + /* Allow purgeable iff we own the FD, i.e., in the puregeable and + shareable case. + */ + return doDecode(env, stream, padding, bitmapFactoryOptions, weOwnTheFD); } /* make a deep copy of the asset, and return it as a stream, or NULL if there diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp index 76e6f028d7b6260a16dcdc10551c830539577cc6..d1fe83edce664c13d38103b6fefdbb1133aad662 100644 --- a/core/jni/android/graphics/Paint.cpp +++ b/core/jni/android/graphics/Paint.cpp @@ -69,6 +69,8 @@ public: static void reset(JNIEnv* env, jobject clazz, SkPaint* obj) { obj->reset(); + // utf16 is required for java + obj->setTextEncoding(SkPaint::kUTF16_TextEncoding); } static void assign(JNIEnv* env, jobject clazz, SkPaint* dst, const SkPaint* src) { diff --git a/core/jni/android_backup_BackupDataInput.cpp b/core/jni/android_backup_BackupDataInput.cpp new file mode 100644 index 0000000000000000000000000000000000000000..cf8a8e8a5efa44b6819bf610927a7d7f899b333a --- /dev/null +++ b/core/jni/android_backup_BackupDataInput.cpp @@ -0,0 +1,164 @@ +/* + * 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 "FileBackupHelper_native" +#include + +#include "JNIHelp.h" +#include + +#include + +namespace android +{ + +// java.io.FileDescriptor +static jfieldID s_descriptorField = 0; + +// android.backup.BackupDataInput$EntityHeader +static jfieldID s_keyField = 0; +static jfieldID s_dataSizeField = 0; + +static int +ctor_native(JNIEnv* env, jobject clazz, jobject fileDescriptor) +{ + int err; + + int fd = env->GetIntField(fileDescriptor, s_descriptorField); + if (fd == -1) { + return NULL; + } + + return (int)new BackupDataReader(fd); +} + +static void +dtor_native(JNIEnv* env, jobject clazz, int r) +{ + delete (BackupDataReader*)r; +} + +static jint +readNextHeader_native(JNIEnv* env, jobject clazz, int r, jobject entity) +{ + int err; + bool done; + BackupDataReader* reader = (BackupDataReader*)r; + + int type = 0; + + err = reader->ReadNextHeader(&done, &type); + if (done) { + return 1; + } + + if (err != 0) { + return err < 0 ? err : -1; + } + + switch (type) { + case BACKUP_HEADER_ENTITY_V1: + { + String8 key; + size_t dataSize; + err = reader->ReadEntityHeader(&key, &dataSize); + if (err != 0) { + return err < 0 ? err : -1; + } + // TODO: Set the fields in the entity object + jstring keyStr = env->NewStringUTF(key.string()); + env->SetObjectField(entity, s_keyField, keyStr); + env->SetIntField(entity, s_dataSizeField, dataSize); + return 0; + } + default: + LOGD("Unknown header type: 0x%08x\n", type); + return -1; + } + + // done + return 1; +} + +static jint +readEntityData_native(JNIEnv* env, jobject clazz, int r, jbyteArray data, int offset, int size) +{ + int err; + BackupDataReader* reader = (BackupDataReader*)r; + + if (env->GetArrayLength(data) < (size+offset)) { + // size mismatch + return -1; + } + + jbyte* dataBytes = env->GetByteArrayElements(data, NULL); + if (dataBytes == NULL) { + return -2; + } + + err = reader->ReadEntityData(dataBytes+offset, size); + + env->ReleaseByteArrayElements(data, dataBytes, 0); + + return err; +} + +static jint +skipEntityData_native(JNIEnv* env, jobject clazz, int r) +{ + int err; + BackupDataReader* reader = (BackupDataReader*)r; + + err = reader->SkipEntityData(); + + return err; +} + +static const JNINativeMethod g_methods[] = { + { "ctor", "(Ljava/io/FileDescriptor;)I", (void*)ctor_native }, + { "dtor", "(I)V", (void*)dtor_native }, + { "readNextHeader_native", "(ILandroid/backup/BackupDataInput$EntityHeader;)I", + (void*)readNextHeader_native }, + { "readEntityData_native", "(I[BII)I", (void*)readEntityData_native }, + { "skipEntityData_native", "(I)I", (void*)skipEntityData_native }, +}; + +int register_android_backup_BackupDataInput(JNIEnv* env) +{ + //LOGD("register_android_backup_BackupDataInput"); + + jclass clazz; + + clazz = env->FindClass("java/io/FileDescriptor"); + LOG_FATAL_IF(clazz == NULL, "Unable to find class java.io.FileDescriptor"); + s_descriptorField = env->GetFieldID(clazz, "descriptor", "I"); + LOG_FATAL_IF(s_descriptorField == NULL, + "Unable to find descriptor field in java.io.FileDescriptor"); + + clazz = env->FindClass("android/backup/BackupDataInput$EntityHeader"); + LOG_FATAL_IF(clazz == NULL, "Unable to find class android.backup.BackupDataInput.EntityHeader"); + s_keyField = env->GetFieldID(clazz, "key", "Ljava/lang/String;"); + LOG_FATAL_IF(s_keyField == NULL, + "Unable to find key field in android.backup.BackupDataInput.EntityHeader"); + s_dataSizeField = env->GetFieldID(clazz, "dataSize", "I"); + LOG_FATAL_IF(s_dataSizeField == NULL, + "Unable to find dataSize field in android.backup.BackupDataInput.EntityHeader"); + + return AndroidRuntime::registerNativeMethods(env, "android/backup/BackupDataInput", + g_methods, NELEM(g_methods)); +} + +} diff --git a/core/jni/android_backup_BackupDataOutput.cpp b/core/jni/android_backup_BackupDataOutput.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d02590e62e692b94d604371e1042d0b27eb94ba6 --- /dev/null +++ b/core/jni/android_backup_BackupDataOutput.cpp @@ -0,0 +1,128 @@ +/* + * 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 "FileBackupHelper_native" +#include + +#include "JNIHelp.h" +#include + +#include + +namespace android +{ + +static jfieldID s_descriptorField = 0; + +static int +ctor_native(JNIEnv* env, jobject clazz, jobject fileDescriptor) +{ + int err; + + int fd = env->GetIntField(fileDescriptor, s_descriptorField); + if (fd == -1) { + return NULL; + } + + return (int)new BackupDataWriter(fd); +} + +static void +dtor_native(JNIEnv* env, jobject clazz, int w) +{ + delete (BackupDataWriter*)w; +} + +static jint +writeEntityHeader_native(JNIEnv* env, jobject clazz, int w, jstring key, int dataSize) +{ + int err; + BackupDataWriter* writer = (BackupDataWriter*)w; + + const char* keyUTF = env->GetStringUTFChars(key, NULL); + if (keyUTF == NULL) { + return -1; + } + + err = writer->WriteEntityHeader(String8(keyUTF), dataSize); + + env->ReleaseStringUTFChars(key, keyUTF); + + return err; +} + +static jint +writeEntityData_native(JNIEnv* env, jobject clazz, int w, jbyteArray data, int size) +{ + int err; + BackupDataWriter* writer = (BackupDataWriter*)w; + + if (env->GetArrayLength(data) > size) { + // size mismatch + return -1; + } + + jbyte* dataBytes = env->GetByteArrayElements(data, NULL); + if (dataBytes == NULL) { + return -1; + } + + err = writer->WriteEntityData(dataBytes, size); + + env->ReleaseByteArrayElements(data, dataBytes, JNI_ABORT); + + return err; +} + +static void +setKeyPrefix_native(JNIEnv* env, jobject clazz, int w, jstring keyPrefixObj) +{ + int err; + BackupDataWriter* writer = (BackupDataWriter*)w; + + const char* keyPrefixUTF = env->GetStringUTFChars(keyPrefixObj, NULL); + String8 keyPrefix(keyPrefixUTF ? keyPrefixUTF : ""); + + writer->SetKeyPrefix(keyPrefix); + + env->ReleaseStringUTFChars(keyPrefixObj, keyPrefixUTF); +} + +static const JNINativeMethod g_methods[] = { + { "ctor", "(Ljava/io/FileDescriptor;)I", (void*)ctor_native }, + { "dtor", "(I)V", (void*)dtor_native }, + { "writeEntityHeader_native", "(ILjava/lang/String;I)I", (void*)writeEntityHeader_native }, + { "writeEntityData_native", "(I[BI)I", (void*)writeEntityData_native }, + { "setKeyPrefix_native", "(ILjava/lang/String;)V", (void*)setKeyPrefix_native }, +}; + +int register_android_backup_BackupDataOutput(JNIEnv* env) +{ + //LOGD("register_android_backup_BackupDataOutput"); + + jclass clazz; + + clazz = env->FindClass("java/io/FileDescriptor"); + LOG_FATAL_IF(clazz == NULL, "Unable to find class java.io.FileDescriptor"); + s_descriptorField = env->GetFieldID(clazz, "descriptor", "I"); + LOG_FATAL_IF(s_descriptorField == NULL, + "Unable to find descriptor field in java.io.FileDescriptor"); + + return AndroidRuntime::registerNativeMethods(env, "android/backup/BackupDataOutput", + g_methods, NELEM(g_methods)); +} + +} diff --git a/core/jni/android_backup_BackupHelperDispatcher.cpp b/core/jni/android_backup_BackupHelperDispatcher.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2e3f0b969127b87d9c1fce71103c00508dd77110 --- /dev/null +++ b/core/jni/android_backup_BackupHelperDispatcher.cpp @@ -0,0 +1,259 @@ +/* + * 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 "BackupHelperDispatcher_native" +#include + +#include "JNIHelp.h" +#include + +#include +#include +#include + + +#define VERSION_1_HEADER 0x01706c48 // 'Hlp'1 little endian + +namespace android +{ + +struct chunk_header_v1 { + int headerSize; + int version; + int dataSize; // corresponds to Header.chunkSize + int nameLength; // not including the NULL terminator, which is not written to the file +}; + +// java.io.FileDescriptor +static jfieldID s_descriptorField = 0; +static jfieldID s_chunkSizeField = 0; +static jfieldID s_keyPrefixField = 0; + +static int +readHeader_native(JNIEnv* env, jobject clazz, jobject headerObj, jobject fdObj) +{ + chunk_header_v1 flattenedHeader; + int fd; + ssize_t amt; + String8 keyPrefix; + char* buf; + + fd = env->GetIntField(fdObj, s_descriptorField); + + amt = read(fd, &flattenedHeader.headerSize, sizeof(flattenedHeader.headerSize)); + if (amt != sizeof(flattenedHeader.headerSize)) { + return -1; + } + + int remainingHeader = flattenedHeader.headerSize - sizeof(flattenedHeader.headerSize); + + if (flattenedHeader.headerSize < (int)sizeof(chunk_header_v1)) { + LOGW("Skipping unknown header: %d bytes", flattenedHeader.headerSize); + if (remainingHeader > 0) { + lseek(fd, remainingHeader, SEEK_CUR); + // >0 means skip this chunk + return 1; + } + } + + amt = read(fd, &flattenedHeader.version, + sizeof(chunk_header_v1)-sizeof(flattenedHeader.headerSize)); + if (amt <= 0) { + LOGW("Failed reading chunk header"); + return -1; + } + remainingHeader -= sizeof(chunk_header_v1)-sizeof(flattenedHeader.headerSize); + + if (flattenedHeader.version != VERSION_1_HEADER) { + LOGW("Skipping unknown header version: 0x%08x, %d bytes", flattenedHeader.version, + flattenedHeader.headerSize); + if (remainingHeader > 0) { + lseek(fd, remainingHeader, SEEK_CUR); + // >0 means skip this chunk + return 1; + } + } + +#if 0 + LOGD("chunk header:"); + LOGD(" headerSize=%d", flattenedHeader.headerSize); + LOGD(" version=0x%08x", flattenedHeader.version); + LOGD(" dataSize=%d", flattenedHeader.dataSize); + LOGD(" nameLength=%d", flattenedHeader.nameLength); +#endif + + if (flattenedHeader.dataSize < 0 || flattenedHeader.nameLength < 0 || + remainingHeader < flattenedHeader.nameLength) { + LOGW("Malformed V1 header remainingHeader=%d dataSize=%d nameLength=%d", remainingHeader, + flattenedHeader.dataSize, flattenedHeader.nameLength); + return -1; + } + + buf = keyPrefix.lockBuffer(flattenedHeader.nameLength); + if (buf == NULL) { + LOGW("unable to allocate %d bytes", flattenedHeader.nameLength); + return -1; + } + + amt = read(fd, buf, flattenedHeader.nameLength); + buf[flattenedHeader.nameLength] = 0; + + keyPrefix.unlockBuffer(flattenedHeader.nameLength); + + remainingHeader -= flattenedHeader.nameLength; + + if (remainingHeader > 0) { + lseek(fd, remainingHeader, SEEK_CUR); + } + + env->SetIntField(headerObj, s_chunkSizeField, flattenedHeader.dataSize); + env->SetObjectField(headerObj, s_keyPrefixField, env->NewStringUTF(keyPrefix.string())); + + return 0; +} + +static int +skipChunk_native(JNIEnv* env, jobject clazz, jobject fdObj, jint bytesToSkip) +{ + int fd; + + fd = env->GetIntField(fdObj, s_descriptorField); + + lseek(fd, bytesToSkip, SEEK_CUR); + + return 0; +} + +static int +padding_len(int len) +{ + len = len % 4; + return len == 0 ? len : 4 - len; +} + +static int +allocateHeader_native(JNIEnv* env, jobject clazz, jobject headerObj, jobject fdObj) +{ + int pos; + jstring nameObj; + int nameLength; + int namePadding; + int headerSize; + int fd; + + fd = env->GetIntField(fdObj, s_descriptorField); + + nameObj = (jstring)env->GetObjectField(headerObj, s_keyPrefixField); + + nameLength = env->GetStringUTFLength(nameObj); + namePadding = padding_len(nameLength); + + headerSize = sizeof(chunk_header_v1) + nameLength + namePadding; + + pos = lseek(fd, 0, SEEK_CUR); + + lseek(fd, headerSize, SEEK_CUR); + + return pos; +} + +static int +writeHeader_native(JNIEnv* env, jobject clazz, jobject headerObj, jobject fdObj, jint pos) +{ + int err; + chunk_header_v1 header; + int fd; + int namePadding; + int prevPos; + jstring nameObj; + const char* buf; + + fd = env->GetIntField(fdObj, s_descriptorField); + prevPos = lseek(fd, 0, SEEK_CUR); + + nameObj = (jstring)env->GetObjectField(headerObj, s_keyPrefixField); + header.nameLength = env->GetStringUTFLength(nameObj); + namePadding = padding_len(header.nameLength); + + header.headerSize = sizeof(chunk_header_v1) + header.nameLength + namePadding; + header.version = VERSION_1_HEADER; + header.dataSize = prevPos - (pos + header.headerSize); + + lseek(fd, pos, SEEK_SET); + err = write(fd, &header, sizeof(chunk_header_v1)); + if (err != sizeof(chunk_header_v1)) { + return errno; + } + + buf = env->GetStringUTFChars(nameObj, NULL); + err = write(fd, buf, header.nameLength); + env->ReleaseStringUTFChars(nameObj, buf); + if (err != header.nameLength) { + return errno; + } + + if (namePadding != 0) { + int zero = 0; + err = write(fd, &zero, namePadding); + if (err != namePadding) { + return errno; + } + } + + lseek(fd, prevPos, SEEK_SET); + return 0; +} + +static const JNINativeMethod g_methods[] = { + { "readHeader_native", + "(Landroid/backup/BackupHelperDispatcher$Header;Ljava/io/FileDescriptor;)I", + (void*)readHeader_native }, + { "skipChunk_native", + "(Ljava/io/FileDescriptor;I)I", + (void*)skipChunk_native }, + { "allocateHeader_native", + "(Landroid/backup/BackupHelperDispatcher$Header;Ljava/io/FileDescriptor;)I", + (void*)allocateHeader_native }, + { "writeHeader_native", + "(Landroid/backup/BackupHelperDispatcher$Header;Ljava/io/FileDescriptor;I)I", + (void*)writeHeader_native }, +}; + +int register_android_backup_BackupHelperDispatcher(JNIEnv* env) +{ + jclass clazz; + + clazz = env->FindClass("java/io/FileDescriptor"); + LOG_FATAL_IF(clazz == NULL, "Unable to find class java.io.FileDescriptor"); + s_descriptorField = env->GetFieldID(clazz, "descriptor", "I"); + LOG_FATAL_IF(s_descriptorField == NULL, + "Unable to find descriptor field in java.io.FileDescriptor"); + + clazz = env->FindClass("android/backup/BackupHelperDispatcher$Header"); + LOG_FATAL_IF(clazz == NULL, + "Unable to find class android.backup.BackupHelperDispatcher.Header"); + s_chunkSizeField = env->GetFieldID(clazz, "chunkSize", "I"); + LOG_FATAL_IF(s_chunkSizeField == NULL, + "Unable to find chunkSize field in android.backup.BackupHelperDispatcher.Header"); + s_keyPrefixField = env->GetFieldID(clazz, "keyPrefix", "Ljava/lang/String;"); + LOG_FATAL_IF(s_keyPrefixField == NULL, + "Unable to find keyPrefix field in android.backup.BackupHelperDispatcher.Header"); + + return AndroidRuntime::registerNativeMethods(env, "android/backup/BackupHelperDispatcher", + g_methods, NELEM(g_methods)); +} + +} diff --git a/core/jni/android_backup_FileBackupHelper.cpp b/core/jni/android_backup_FileBackupHelper.cpp deleted file mode 100644 index c6de3a52f69b0a3308c337fb48bf7ce330dfbe37..0000000000000000000000000000000000000000 --- a/core/jni/android_backup_FileBackupHelper.cpp +++ /dev/null @@ -1,84 +0,0 @@ -/* - * 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 "FileBackupHelper_native" -#include - -#include "JNIHelp.h" -#include - -#include - -namespace android -{ - -static jfieldID s_descriptorField = 0; - -static int -performBackup_native(JNIEnv* env, jobject clazz, jstring basePath, jobject oldState, jobject data, - jobject newState, jobjectArray files) -{ - int err; - - // all parameters have already been checked against null - LOGD("oldState=%p newState=%p data=%p\n", oldState, newState, data); - int oldStateFD = oldState != NULL ? env->GetIntField(oldState, s_descriptorField) : -1; - int newStateFD = env->GetIntField(newState, s_descriptorField); - int dataFD = env->GetIntField(data, s_descriptorField); - - char const* basePathUTF = env->GetStringUTFChars(basePath, NULL); - LOGD("basePathUTF=\"%s\"\n", basePathUTF); - const int fileCount = env->GetArrayLength(files); - char const** filesUTF = (char const**)malloc(sizeof(char*)*fileCount); - for (int i=0; iGetStringUTFChars((jstring)env->GetObjectArrayElement(files, i), NULL); - } - - err = back_up_files(oldStateFD, dataFD, newStateFD, basePathUTF, filesUTF, fileCount); - - for (int i=0; iReleaseStringUTFChars((jstring)env->GetObjectArrayElement(files, i), filesUTF[i]); - } - free(filesUTF); - env->ReleaseStringUTFChars(basePath, basePathUTF); - - return err; -} - -static const JNINativeMethod g_methods[] = { - { "performBackup_native", - "(Ljava/lang/String;Ljava/io/FileDescriptor;Ljava/io/FileDescriptor;" - "Ljava/io/FileDescriptor;[Ljava/lang/String;)I", - (void*)performBackup_native }, -}; - -int register_android_backup_FileBackupHelper(JNIEnv* env) -{ - LOGD("register_android_backup_FileBackupHelper"); - - jclass clazz; - - clazz = env->FindClass("java/io/FileDescriptor"); - LOG_FATAL_IF(clazz == NULL, "Unable to find class java.io.FileDescriptor"); - s_descriptorField = env->GetFieldID(clazz, "descriptor", "I"); - LOG_FATAL_IF(s_descriptorField == NULL, - "Unable to find descriptor field in java.io.FileDescriptor"); - - return AndroidRuntime::registerNativeMethods(env, "android/backup/FileBackupHelper", - g_methods, NELEM(g_methods)); -} - -} diff --git a/core/jni/android_backup_FileBackupHelperBase.cpp b/core/jni/android_backup_FileBackupHelperBase.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8225a36b4164813f2ff71dbf08b53e7c267723a4 --- /dev/null +++ b/core/jni/android_backup_FileBackupHelperBase.cpp @@ -0,0 +1,136 @@ +/* + * 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 "FileBackupHelper_native" +#include + +#include "JNIHelp.h" +#include + +#include + +namespace android +{ + +// java.io.FileDescriptor +static jfieldID s_descriptorField = 0; + +static int +ctor(JNIEnv* env, jobject clazz) +{ + return (int)new RestoreHelperBase(); +} + +static void +dtor(JNIEnv* env, jobject clazz, jint ptr) +{ + delete (RestoreHelperBase*)ptr; +} + +static int +performBackup_native(JNIEnv* env, jobject clazz, jobject oldState, int data, + jobject newState, jobjectArray files, jobjectArray keys) +{ + int err; + + // all parameters have already been checked against null + int oldStateFD = oldState != NULL ? env->GetIntField(oldState, s_descriptorField) : -1; + int newStateFD = env->GetIntField(newState, s_descriptorField); + BackupDataWriter* dataStream = (BackupDataWriter*)data; + + const int fileCount = env->GetArrayLength(files); + char const** filesUTF = (char const**)malloc(sizeof(char*)*fileCount); + for (int i=0; iGetStringUTFChars((jstring)env->GetObjectArrayElement(files, i), NULL); + } + + const int keyCount = env->GetArrayLength(keys); + char const** keysUTF = (char const**)malloc(sizeof(char*)*keyCount); + for (int i=0; iGetStringUTFChars((jstring)env->GetObjectArrayElement(keys, i), NULL); + } + + err = back_up_files(oldStateFD, dataStream, newStateFD, filesUTF, keysUTF, fileCount); + + for (int i=0; iReleaseStringUTFChars((jstring)env->GetObjectArrayElement(files, i), filesUTF[i]); + } + free(filesUTF); + + for (int i=0; iReleaseStringUTFChars((jstring)env->GetObjectArrayElement(keys, i), keysUTF[i]); + } + free(keysUTF); + + return err; +} + + +static int +writeFile_native(JNIEnv* env, jobject clazz, jint ptr, jstring filenameObj, int backupReaderPtr) +{ + int err; + RestoreHelperBase* restore = (RestoreHelperBase*)ptr; + BackupDataReader* reader = (BackupDataReader*)backupReaderPtr; + char const* filename; + + filename = env->GetStringUTFChars(filenameObj, NULL); + + err = restore->WriteFile(String8(filename), reader); + + env->ReleaseStringUTFChars(filenameObj, filename); + + return err; +} + +static int +writeSnapshot_native(JNIEnv* env, jobject clazz, jint ptr, jobject fileDescriptor) +{ + int err; + + RestoreHelperBase* restore = (RestoreHelperBase*)ptr; + int fd = env->GetIntField(fileDescriptor, s_descriptorField); + + err = restore->WriteSnapshot(fd); + + return err; +} + +static const JNINativeMethod g_methods[] = { + { "ctor", "()I", (void*)ctor }, + { "dtor", "(I)V", (void*)dtor }, + { "performBackup_native", + "(Ljava/io/FileDescriptor;ILjava/io/FileDescriptor;[Ljava/lang/String;[Ljava/lang/String;)I", + (void*)performBackup_native }, + { "writeFile_native", "(ILjava/lang/String;I)I", (void*)writeFile_native }, + { "writeSnapshot_native", "(ILjava/io/FileDescriptor;)I", (void*)writeSnapshot_native }, +}; + +int register_android_backup_FileBackupHelperBase(JNIEnv* env) +{ + jclass clazz; + + clazz = env->FindClass("java/io/FileDescriptor"); + LOG_FATAL_IF(clazz == NULL, "Unable to find class java.io.FileDescriptor"); + s_descriptorField = env->GetFieldID(clazz, "descriptor", "I"); + LOG_FATAL_IF(s_descriptorField == NULL, + "Unable to find descriptor field in java.io.FileDescriptor"); + + return AndroidRuntime::registerNativeMethods(env, "android/backup/FileBackupHelperBase", + g_methods, NELEM(g_methods)); +} + +} diff --git a/core/jni/android_bluetooth_Database.cpp b/core/jni/android_bluetooth_Database.cpp index 136c9a328d5593683a9df45c17685eb5497a9226..73b8efd3e3f91a65a3a62ad9f61e5f2d5437c13f 100644 --- a/core/jni/android_bluetooth_Database.cpp +++ b/core/jni/android_bluetooth_Database.cpp @@ -53,6 +53,7 @@ static void initializeNativeDataNative(JNIEnv* env, jobject object) { LOGE("Could not get onto the system bus!"); dbus_error_free(&err); } + dbus_connection_set_exit_on_disconnect(conn, FALSE); } #endif } diff --git a/core/jni/android_emoji_EmojiFactory.cpp b/core/jni/android_emoji_EmojiFactory.cpp index 59f63a83cac541b3c3afe86bf1825dfc35cbbd62..7d6e24fa256740a5fb5a3ca99ab25a624094e162 100644 --- a/core/jni/android_emoji_EmojiFactory.cpp +++ b/core/jni/android_emoji_EmojiFactory.cpp @@ -1,7 +1,7 @@ #include "SkTypes.h" #include "SkImageDecoder.h" -#define LOG_TAG "DoCoMoEmojiFactory_jni" +#define LOG_TAG "EmojiFactory_jni" #include #include @@ -13,15 +13,11 @@ namespace android { -// Note: This class is originally developed so that libandroid_runtime does -// not have to depend on libemoji which is optional library. However, we -// cannot use this class, since current (2009-02-16) bionic libc does not allow -// dlopen()-ing inside dlopen(), while not only this class but also libemoji -// uses dlopen(). class EmojiFactoryCaller { public: - EmojiFactoryCaller(); + EmojiFactoryCaller() {} virtual ~EmojiFactoryCaller(); + bool Init(); EmojiFactory *TryCallGetImplementation(const char* name); EmojiFactory *TryCallGetAvailableImplementation(); private: @@ -30,35 +26,45 @@ class EmojiFactoryCaller { EmojiFactory *(*m_get_available_implementation)(); }; -EmojiFactoryCaller::EmojiFactoryCaller() { +bool EmojiFactoryCaller::Init() { + const char* error_msg; m_handle = dlopen("libemoji.so", RTLD_LAZY | RTLD_LOCAL); - const char* error_str = dlerror(); - if (error_str) { - LOGI("Failed to load libemoji.so: %s", error_str); - return; + + if (m_handle == NULL) { + error_msg = "Failed to load libemoji.so"; + goto FAIL; } m_get_implementation = reinterpret_cast( dlsym(m_handle, "GetImplementation")); - error_str = dlerror(); - if (error_str) { - LOGE("Failed to get symbol of GetImplementation: %s", error_str); - dlclose(m_handle); - m_handle = NULL; - return; + if (m_get_implementation == NULL) { + error_msg = "Failed to get symbol of GetImplementation"; + goto FAIL; } m_get_available_implementation = reinterpret_cast( dlsym(m_handle,"GetAvailableImplementation")); - error_str = dlerror(); - if (error_str) { - LOGE("Failed to get symbol of GetAvailableImplementation: %s", error_str); + if (m_get_available_implementation == NULL) { + error_msg = "Failed to get symbol of GetAvailableImplementation"; + goto FAIL; + } + + return true; + +FAIL: + const char* error_str = dlerror(); + if (error_str == NULL) { + error_str = "unknown reason"; + } + + LOGE("%s: %s", error_msg, error_str); + if (m_handle != NULL) { dlclose(m_handle); m_handle = NULL; - return; } + return false; } EmojiFactoryCaller::~EmojiFactoryCaller() { @@ -82,10 +88,9 @@ EmojiFactory *EmojiFactoryCaller::TryCallGetAvailableImplementation() { return m_get_available_implementation(); } -// Note: bionic libc's dlopen() does not allow recursive dlopen(). So currently -// we cannot use EmojiFactoryCaller here. -// static EmojiFactoryCaller* gCaller; -// static pthread_once_t g_once = PTHREAD_ONCE_INIT; +static EmojiFactoryCaller* gCaller; +static pthread_once_t g_once = PTHREAD_ONCE_INIT; +static bool lib_emoji_factory_is_ready; static jclass gString_class; @@ -95,9 +100,10 @@ static jmethodID gBitmap_constructorMethodID; static jclass gEmojiFactory_class; static jmethodID gEmojiFactory_constructorMethodID; -// static void InitializeCaller() { -// gCaller = new EmojiFactoryCaller(); -// } +static void InitializeCaller() { + gCaller = new EmojiFactoryCaller(); + lib_emoji_factory_is_ready = gCaller->Init(); +} static jobject create_java_EmojiFactory( JNIEnv* env, EmojiFactory* factory, jstring name) { @@ -116,19 +122,23 @@ static jobject create_java_EmojiFactory( static jobject android_emoji_EmojiFactory_newInstance( JNIEnv* env, jobject clazz, jstring name) { - // pthread_once(&g_once, InitializeCaller); - if (NULL == name) { return NULL; } + pthread_once(&g_once, InitializeCaller); + if (!lib_emoji_factory_is_ready) { + return NULL; + } const jchar* jchars = env->GetStringChars(name, NULL); jsize len = env->GetStringLength(name); String8 str(String16(jchars, len)); - // EmojiFactory *factory = gCaller->TryCallGetImplementation(str.string()); - EmojiFactory *factory = EmojiFactory::GetImplementation(str.string()); - + EmojiFactory *factory = gCaller->TryCallGetImplementation(str.string()); + // EmojiFactory *factory = EmojiFactory::GetImplementation(str.string()); + if (NULL == factory) { + return NULL; + } env->ReleaseStringChars(name, jchars); return create_java_EmojiFactory(env, factory, name); @@ -136,10 +146,13 @@ static jobject android_emoji_EmojiFactory_newInstance( static jobject android_emoji_EmojiFactory_newAvailableInstance( JNIEnv* env, jobject clazz) { - // pthread_once(&g_once, InitializeCaller); + pthread_once(&g_once, InitializeCaller); + if (!lib_emoji_factory_is_ready) { + return NULL; + } - // EmojiFactory *factory = gCaller->TryCallGetAvailableImplementation(); - EmojiFactory *factory = EmojiFactory::GetAvailableImplementation(); + EmojiFactory *factory = gCaller->TryCallGetAvailableImplementation(); + // EmojiFactory *factory = EmojiFactory::GetAvailableImplementation(); if (NULL == factory) { return NULL; } diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp index c10799316b81d8d41089f1c33971f3a6a166c737..77a8a7286c633a744885e55c7d7659b22b6bb720 100644 --- a/core/jni/android_hardware_Camera.cpp +++ b/core/jni/android_hardware_Camera.cpp @@ -38,12 +38,6 @@ enum CallbackMessageID { kErrorCallback = 5 }; -enum CameraError { - kCameraErrorUnknown = 1, - kCameraErrorMediaServer = 100 -}; - - struct fields_t { jfieldID context; jfieldID surface; @@ -53,19 +47,33 @@ struct fields_t { static fields_t fields; static Mutex sLock; -struct camera_context_t { +// provides persistent context for calls from native code to Java +class JNICameraContext: public CameraListener +{ +public: + JNICameraContext(JNIEnv* env, jobject weak_this, jclass clazz, const sp& camera); + ~JNICameraContext() { release(); } + virtual void notify(int32_t msgType, int32_t ext1, int32_t ext2); + virtual void postData(int32_t msgType, const sp& dataPtr); + sp getCamera() { Mutex::Autolock _l(mLock); return mCamera; } + void release(); + +private: + void copyAndPost(JNIEnv* env, const sp& dataPtr, int msgType); + jobject mCameraJObjectWeak; // weak reference to java object jclass mCameraJClass; // strong reference to java class sp mCamera; // strong reference to native object + Mutex mLock; }; -sp get_native_camera(JNIEnv *env, jobject thiz, camera_context_t** pContext) +sp get_native_camera(JNIEnv *env, jobject thiz, JNICameraContext** pContext) { sp camera; Mutex::Autolock _l(sLock); - camera_context_t* context = reinterpret_cast(env->GetIntField(thiz, fields.context)); + JNICameraContext* context = reinterpret_cast(env->GetIntField(thiz, fields.context)); if (context != NULL) { - camera = context->mCamera; + camera = context->getCamera(); } LOGV("get_native_camera: context=%p, camera=%p", context, camera.get()); if (camera == 0) { @@ -76,30 +84,108 @@ sp get_native_camera(JNIEnv *env, jobject thiz, camera_context_t** pCont return camera; } -static void err_callback(status_t err, void *cookie) +JNICameraContext::JNICameraContext(JNIEnv* env, jobject weak_this, jclass clazz, const sp& camera) { - camera_context_t* context = reinterpret_cast(cookie); - if ((context == NULL) || (context->mCamera == 0)) return; + mCameraJObjectWeak = env->NewGlobalRef(weak_this); + mCameraJClass = (jclass)env->NewGlobalRef(clazz); + mCamera = camera; +} - LOGV("err_callback: context=%p, camera=%p", context, context->mCamera.get()); +void JNICameraContext::release() +{ + LOGV("release"); + Mutex::Autolock _l(mLock); + JNIEnv *env = AndroidRuntime::getJNIEnv(); - int error; - switch (err) { - case DEAD_OBJECT: - error = kCameraErrorMediaServer; - break; - default: - error = kCameraErrorUnknown; - break; + if (mCameraJObjectWeak != NULL) { + env->DeleteGlobalRef(mCameraJObjectWeak); + mCameraJObjectWeak = NULL; + } + if (mCameraJClass != NULL) { + env->DeleteGlobalRef(mCameraJClass); + mCameraJClass = NULL; + } + mCamera.clear(); +} + +void JNICameraContext::notify(int32_t msgType, int32_t ext1, int32_t ext2) +{ + LOGV("notify"); + + // VM pointer will be NULL if object is released + Mutex::Autolock _l(mLock); + if (mCameraJObjectWeak == NULL) { + LOGW("callback on dead camera object"); + return; + } + JNIEnv *env = AndroidRuntime::getJNIEnv(); + env->CallStaticVoidMethod(mCameraJClass, fields.post_event, + mCameraJObjectWeak, msgType, ext1, ext2); +} + +void JNICameraContext::copyAndPost(JNIEnv* env, const sp& dataPtr, int msgType) +{ + jbyteArray obj = NULL; + + // allocate Java byte array and copy data + if (dataPtr != NULL) { + ssize_t offset; + size_t size; + sp heap = dataPtr->getMemory(&offset, &size); + LOGV("postData: off=%d, size=%d", offset, size); + uint8_t *heapBase = (uint8_t*)heap->base(); + + if (heapBase != NULL) { + uint8_t *data = heapBase + offset; + obj = env->NewByteArray(size); + if (obj == NULL) { + LOGE("Couldn't allocate byte array for JPEG data"); + env->ExceptionClear(); + } else { + jbyte *bytes = env->GetByteArrayElements(obj, NULL); + memcpy(bytes, data, size); + env->ReleaseByteArrayElements(obj, bytes, 0); + + } + } else { + LOGE("image heap is NULL"); + } + } + + // post image data to Java + env->CallStaticVoidMethod(mCameraJClass, fields.post_event, + mCameraJObjectWeak, msgType, 0, 0, obj); + if (obj) { + env->DeleteLocalRef(obj); } +} +void JNICameraContext::postData(int32_t msgType, const sp& dataPtr) +{ + // VM pointer will be NULL if object is released + Mutex::Autolock _l(mLock); JNIEnv *env = AndroidRuntime::getJNIEnv(); - if (env == NULL) { - LOGE("err_callback on dead VM"); + if (mCameraJObjectWeak == NULL) { + LOGW("callback on dead camera object"); return; } - env->CallStaticVoidMethod(context->mCameraJClass, fields.post_event, - context->mCameraJObjectWeak, kErrorCallback, error, 0, NULL); + + // return data based on callback type + switch(msgType) { + case CAMERA_MSG_VIDEO_FRAME: + // should never happen + break; + // don't return raw data to Java + case CAMERA_MSG_RAW_IMAGE: + LOGV("rawCallback"); + env->CallStaticVoidMethod(mCameraJClass, fields.post_event, + mCameraJObjectWeak, msgType, 0, 0, NULL); + break; + default: + LOGV("dataCallback(%d, %p)", msgType, dataPtr.get()); + copyAndPost(env, dataPtr, msgType); + break; + } } // connect to camera service @@ -115,33 +201,24 @@ static void android_hardware_Camera_native_setup(JNIEnv *env, jobject thiz, jobj // make sure camera hardware is alive if (camera->getStatus() != NO_ERROR) { - jniThrowException(env, "java/io/IOException", "Camera initialization failed"); + jniThrowException(env, "java/lang/RuntimeException", "Camera initialization failed"); return; } jclass clazz = env->GetObjectClass(thiz); if (clazz == NULL) { - LOGE("Can't find android/hardware/Camera"); - // XXX no idea what to throw here, can this even happen? - jniThrowException(env, "java/lang/Exception", NULL); + jniThrowException(env, "java/lang/RuntimeException", "Can't find android/hardware/Camera"); return; } // We use a weak reference so the Camera object can be garbage collected. // The reference is only used as a proxy for callbacks. - camera_context_t* context = new camera_context_t; - context->mCameraJObjectWeak = env->NewGlobalRef(weak_this); - context->mCameraJClass = (jclass)env->NewGlobalRef(clazz); - context->mCamera = camera; + sp context = new JNICameraContext(env, weak_this, clazz, camera); + context->incStrong(thiz); + camera->setListener(context); // save context in opaque field - env->SetIntField(thiz, fields.context, (int)context); - - LOGV("native_setup: mCameraJObjectWeak=%x, camera_obj=%x, context=%p", - (int)context->mCameraJObjectWeak, (int)thiz, context); - - // set error callback - camera->setErrorCallback(err_callback, context); + env->SetIntField(thiz, fields.context, (int)context.get()); } // disconnect from camera service @@ -150,11 +227,11 @@ static void android_hardware_Camera_native_setup(JNIEnv *env, jobject thiz, jobj // finalizer is invoked later. static void android_hardware_Camera_release(JNIEnv *env, jobject thiz) { - camera_context_t* context = NULL; + JNICameraContext* context = NULL; sp camera; { Mutex::Autolock _l(sLock); - context = reinterpret_cast(env->GetIntField(thiz, fields.context)); + context = reinterpret_cast(env->GetIntField(thiz, fields.context)); // Make sure we do not attempt to callback on a deleted Java object. env->SetIntField(thiz, fields.context, 0); @@ -162,21 +239,18 @@ static void android_hardware_Camera_release(JNIEnv *env, jobject thiz) // clean up if release has not been called before if (context != NULL) { - camera = context->mCamera; - context->mCamera.clear(); + camera = context->getCamera(); + context->release(); LOGV("native_release: context=%p camera=%p", context, camera.get()); // clear callbacks if (camera != NULL) { - camera->setPreviewCallback(NULL, NULL, FRAME_CALLBACK_FLAG_NOOP); - camera->setErrorCallback(NULL, NULL); + camera->setPreviewCallbackFlags(FRAME_CALLBACK_FLAG_NOOP); camera->disconnect(); - env->DeleteGlobalRef(context->mCameraJObjectWeak); - env->DeleteGlobalRef(context->mCameraJClass); } // remove context to prevent further Java access - delete context; + context->decStrong(thiz); } } @@ -186,54 +260,15 @@ static void android_hardware_Camera_setPreviewDisplay(JNIEnv *env, jobject thiz, sp camera = get_native_camera(env, thiz, NULL); if (camera == 0) return; - sp surface = reinterpret_cast(env->GetIntField(jSurface, fields.surface)); + sp surface = NULL; + if (jSurface != NULL) { + surface = reinterpret_cast(env->GetIntField(jSurface, fields.surface)); + } if (camera->setPreviewDisplay(surface) != NO_ERROR) { jniThrowException(env, "java/io/IOException", "setPreviewDisplay failed"); } } -static void preview_callback(const sp& mem, void *cookie) -{ - LOGV("preview_callback"); - JNIEnv *env = AndroidRuntime::getJNIEnv(); - if (env == NULL) { - LOGE("preview_callback on dead VM"); - return; - } - camera_context_t* context = reinterpret_cast(cookie); - if ((context == NULL) || (context->mCamera == 0)) { - LOGW("context or camera is NULL in preview_callback"); - return; - } - LOGV("native_release: context=%p camera=%p", context, context->mCamera.get()); - - int arg1 = 0, arg2 = 0; - jobject obj = NULL; - - ssize_t offset; - size_t size; - sp heap = mem->getMemory(&offset, &size); - - uint8_t *data = ((uint8_t *)heap->base()) + offset; - - jbyteArray array = env->NewByteArray(size); - if (array == NULL) { - LOGE("Couldn't allocate byte array for YUV data"); - env->ExceptionClear(); - return; - } - - jbyte *bytes = env->GetByteArrayElements(array, NULL); - memcpy(bytes, data, size); - env->ReleaseByteArrayElements(array, bytes, 0); - - obj = array; - - env->CallStaticVoidMethod(context->mCameraJClass, fields.post_event, - context->mCameraJObjectWeak, kPreviewCallback, arg1, arg2, obj); - env->DeleteLocalRef(array); -} - static void android_hardware_Camera_startPreview(JNIEnv *env, jobject thiz) { LOGV("startPreview"); @@ -241,7 +276,7 @@ static void android_hardware_Camera_startPreview(JNIEnv *env, jobject thiz) if (camera == 0) return; if (camera->startPreview() != NO_ERROR) { - jniThrowException(env, "java/io/IOException", "startPreview failed"); + jniThrowException(env, "java/lang/RuntimeException", "startPreview failed"); return; } } @@ -269,7 +304,7 @@ static void android_hardware_Camera_setHasPreviewCallback(JNIEnv *env, jobject t // Important: Only install preview_callback if the Java code has called // setPreviewCallback() with a non-null value, otherwise we'd pay to memcpy // each preview frame for nothing. - camera_context_t* context; + JNICameraContext* context; sp camera = get_native_camera(env, thiz, &context); if (camera == 0) return; @@ -279,130 +314,32 @@ static void android_hardware_Camera_setHasPreviewCallback(JNIEnv *env, jobject t } else { callback_flag = FRAME_CALLBACK_FLAG_NOOP; } - camera->setPreviewCallback(installed ? preview_callback : NULL, context, callback_flag); -} - -static void autofocus_callback_impl(bool success, void *cookie) -{ - LOGV("autoFocusCallback"); - camera_context_t* context = reinterpret_cast(cookie); - - JNIEnv *env = AndroidRuntime::getJNIEnv(); - if (env == NULL) { - LOGE("autofocus_callback on dead VM"); - return; - } - env->CallStaticVoidMethod(context->mCameraJClass, fields.post_event, - context->mCameraJObjectWeak, kAutoFocusCallback, success, 0, NULL); + camera->setPreviewCallbackFlags(callback_flag); } static void android_hardware_Camera_autoFocus(JNIEnv *env, jobject thiz) { LOGV("autoFocus"); - camera_context_t* context; + JNICameraContext* context; sp c = get_native_camera(env, thiz, &context); if (c == 0) return; - c->setAutoFocusCallback(autofocus_callback_impl, context); if (c->autoFocus() != NO_ERROR) { - jniThrowException(env, "java/io/IOException", "autoFocus failed"); - } -} - -static void jpeg_callback(const sp& mem, void *cookie) -{ - LOGV("jpegCallback"); - camera_context_t* context = reinterpret_cast(cookie); - - JNIEnv *env = AndroidRuntime::getJNIEnv(); - if (env == NULL) { - LOGE("jpeg`_callback on dead VM"); - return; - } - int arg1 = 0, arg2 = 0; - jobject obj = NULL; - - if (mem == NULL) { - env->CallStaticVoidMethod(context->mCameraJClass, fields.post_event, - context->mCameraJObjectWeak, kJpegCallback, arg1, arg2, NULL); - return; + jniThrowException(env, "java/lang/RuntimeException", "autoFocus failed"); } - ssize_t offset; - size_t size; - sp heap = mem->getMemory(&offset, &size); - LOGV("jpeg_callback: mem off=%d, size=%d", offset, size); - - uint8_t *heap_base = (uint8_t *)heap->base(); - if (heap_base == NULL) { - LOGE("YUV heap is NULL"); - return; - } - - uint8_t *data = heap_base + offset; - - jbyteArray array = env->NewByteArray(size); - if (array == NULL) { - LOGE("Couldn't allocate byte array for JPEG data"); - env->ExceptionClear(); - return; - } - - jbyte *bytes = env->GetByteArrayElements(array, NULL); - memcpy(bytes, data, size); - env->ReleaseByteArrayElements(array, bytes, 0); - - obj = array; - - env->CallStaticVoidMethod(context->mCameraJClass, fields.post_event, - context->mCameraJObjectWeak, kJpegCallback, arg1, arg2, obj); - env->DeleteLocalRef(array); -} - -static void shutter_callback_impl(void *cookie) -{ - LOGV("shutterCallback"); - camera_context_t* context = reinterpret_cast(cookie); - - JNIEnv *env = AndroidRuntime::getJNIEnv(); - if (env == NULL) { - LOGE("shutter_callback on dead VM"); - return; - } - env->CallStaticVoidMethod(context->mCameraJClass, fields.post_event, - context->mCameraJObjectWeak, kShutterCallback, 0, 0, NULL); -} - -static void raw_callback(const sp& mem __attribute__((unused)), - void *cookie) -{ - LOGV("rawCallback"); - camera_context_t* context = reinterpret_cast(cookie); - - JNIEnv *env = AndroidRuntime::getJNIEnv(); - if (env == NULL) { - LOGE("raw_callback on dead VM"); - return; - } - env->CallStaticVoidMethod(context->mCameraJClass, fields.post_event, - context->mCameraJObjectWeak, kRawCallback, 0, 0, NULL); } static void android_hardware_Camera_takePicture(JNIEnv *env, jobject thiz) { LOGV("takePicture"); - camera_context_t* context; + JNICameraContext* context; sp camera = get_native_camera(env, thiz, &context); if (camera == 0) return; - camera->setShutterCallback(shutter_callback_impl, context); - camera->setRawCallback(raw_callback, context); - camera->setJpegCallback(jpeg_callback, context); if (camera->takePicture() != NO_ERROR) { - jniThrowException(env, "java/io/IOException", "takePicture failed"); + jniThrowException(env, "java/lang/RuntimeException", "takePicture failed"); return; } - - return; } static void android_hardware_Camera_setParameters(JNIEnv *env, jobject thiz, jstring params) @@ -418,7 +355,7 @@ static void android_hardware_Camera_setParameters(JNIEnv *env, jobject thiz, jst env->ReleaseStringCritical(params, str); } if (camera->setParameters(params8) != NO_ERROR) { - jniThrowException(env, "java/lang/IllegalArgumentException", "setParameters failed"); + jniThrowException(env, "java/lang/RuntimeException", "setParameters failed"); return; } } diff --git a/core/jni/android_hardware_SensorManager.cpp b/core/jni/android_hardware_SensorManager.cpp index 75aa458035bdb0fa33a8fd62855014d3029ad2ac..3e27978ec02e8e2fc9a27a765286269c9badfc1a 100644 --- a/core/jni/android_hardware_SensorManager.cpp +++ b/core/jni/android_hardware_SensorManager.cpp @@ -14,9 +14,13 @@ * limitations under the License. */ -#define LOG_TAG "Sensors" +#define LOG_TAG "SensorManager" + +#define LOG_NDEBUG 0 +#include "utils/Log.h" #include +#include #include "jni.h" #include "JNIHelp.h" @@ -106,12 +110,33 @@ sensors_data_uninit(JNIEnv *env, jclass clazz) } static jint -sensors_data_open(JNIEnv *env, jclass clazz, jobject fdo) +sensors_data_open(JNIEnv *env, jclass clazz, jobjectArray fdArray, jintArray intArray) { jclass FileDescriptor = env->FindClass("java/io/FileDescriptor"); - jfieldID offset = env->GetFieldID(FileDescriptor, "descriptor", "I"); - int fd = env->GetIntField(fdo, offset); - return sSensorDevice->data_open(sSensorDevice, fd); // doesn't take ownership of fd + jfieldID fieldOffset = env->GetFieldID(FileDescriptor, "descriptor", "I"); + int numFds = (fdArray ? env->GetArrayLength(fdArray) : 0); + int numInts = (intArray ? env->GetArrayLength(intArray) : 0); + native_handle_t* handle = native_handle_create(numFds, numInts); + int offset = 0; + + for (int i = 0; i < numFds; i++) { + jobject fdo = env->GetObjectArrayElement(fdArray, i); + if (fdo) { + handle->data[offset++] = env->GetIntField(fdo, fieldOffset); + } else { + handle->data[offset++] = -1; + } + } + if (numInts > 0) { + jint* ints = env->GetIntArrayElements(intArray, 0); + for (int i = 0; i < numInts; i++) { + handle->data[offset++] = ints[i]; + } + env->ReleaseIntArrayElements(intArray, ints, 0); + } + + // doesn't take ownership of the native handle + return sSensorDevice->data_open(sSensorDevice, handle); } static jint @@ -157,7 +182,7 @@ static JNINativeMethod gMethods[] = { (void*)sensors_module_get_next_sensor }, {"sensors_data_init", "()I", (void*)sensors_data_init }, {"sensors_data_uninit", "()I", (void*)sensors_data_uninit }, - {"sensors_data_open", "(Ljava/io/FileDescriptor;)I", (void*)sensors_data_open }, + {"sensors_data_open", "([Ljava/io/FileDescriptor;[I)I", (void*)sensors_data_open }, {"sensors_data_close", "()I", (void*)sensors_data_close }, {"sensors_data_poll", "([F[I[J)I", (void*)sensors_data_poll }, }; diff --git a/core/jni/android_location_GpsLocationProvider.cpp b/core/jni/android_location_GpsLocationProvider.cpp old mode 100644 new mode 100755 index 004b0e3ea07ebf68499755b0177f97dd80661a33..bf0bd65eddbc21580be1645dd6c13ca282a0cda2 --- a/core/jni/android_location_GpsLocationProvider.cpp +++ b/core/jni/android_location_GpsLocationProvider.cpp @@ -176,7 +176,7 @@ static jboolean android_location_GpsLocationProvider_start(JNIEnv* env, jobject { int result = sGpsInterface->set_position_mode(positionMode, (singleFix ? 0 : fixFrequency)); if (result) { - return result; + return false; } return (sGpsInterface->start() == 0); @@ -270,6 +270,12 @@ static void android_location_GpsLocationProvider_inject_time(JNIEnv* env, jobjec sGpsInterface->inject_time(time, timeReference, uncertainty); } +static void android_location_GpsLocationProvider_inject_location(JNIEnv* env, jobject obj, + jdouble latitude, jdouble longitude, jfloat accuracy) +{ + sGpsInterface->inject_location(latitude, longitude, accuracy); +} + static jboolean android_location_GpsLocationProvider_supports_xtra(JNIEnv* env, jobject obj) { if (!sGpsXtraInterface) { @@ -330,13 +336,15 @@ static void android_location_GpsLocationProvider_agps_data_conn_failed(JNIEnv* e } static void android_location_GpsLocationProvider_set_agps_server(JNIEnv* env, jobject obj, - jint type, jint addr, jint port) + jint type, jstring hostname, jint port) { if (!sAGpsInterface) { sAGpsInterface = (const AGpsInterface*)sGpsInterface->get_extension(AGPS_INTERFACE); } if (sAGpsInterface) { - sAGpsInterface->set_server(type, addr, port); + const char *c_hostname = env->GetStringUTFChars(hostname, NULL); + sAGpsInterface->set_server(type, c_hostname, port); + env->ReleaseStringUTFChars(hostname, c_hostname); } } @@ -353,12 +361,13 @@ static JNINativeMethod sMethods[] = { {"native_wait_for_event", "()V", (void*)android_location_GpsLocationProvider_wait_for_event}, {"native_read_sv_status", "([I[F[F[F[I)I", (void*)android_location_GpsLocationProvider_read_sv_status}, {"native_inject_time", "(JJI)V", (void*)android_location_GpsLocationProvider_inject_time}, + {"native_inject_location", "(DDF)V", (void*)android_location_GpsLocationProvider_inject_location}, {"native_supports_xtra", "()Z", (void*)android_location_GpsLocationProvider_supports_xtra}, {"native_inject_xtra_data", "([BI)V", (void*)android_location_GpsLocationProvider_inject_xtra_data}, {"native_agps_data_conn_open", "(Ljava/lang/String;)V", (void*)android_location_GpsLocationProvider_agps_data_conn_open}, {"native_agps_data_conn_closed", "()V", (void*)android_location_GpsLocationProvider_agps_data_conn_closed}, {"native_agps_data_conn_failed", "()V", (void*)android_location_GpsLocationProvider_agps_data_conn_failed}, - {"native_set_agps_server", "(III)V", (void*)android_location_GpsLocationProvider_set_agps_server}, + {"native_set_agps_server", "(ILjava/lang/String;I)V", (void*)android_location_GpsLocationProvider_set_agps_server}, }; int register_android_location_GpsLocationProvider(JNIEnv* env) diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp index 288433af6a0d0f66150aaca3cfd424de2f7c9d2b..e71e3481862e425ad58e0fa95a444fe3f97096b7 100644 --- a/core/jni/android_media_AudioRecord.cpp +++ b/core/jni/android_media_AudioRecord.cpp @@ -45,8 +45,6 @@ struct fields_t { jmethodID postNativeEventInJava; //... event post callback method int PCM16; //... format constants int PCM8; //... format constants - int SOURCE_DEFAULT; //... record source constants - int SOURCE_MIC; //... record source constants jfieldID nativeRecorderInJavaObj; // provides access to the C++ AudioRecord object jfieldID nativeCallbackCookie; // provides access to the AudioRecord callback data }; @@ -66,7 +64,7 @@ struct audiorecord_callback_cookie { #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_INVALIDSOURCE -19 #define AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED -20 jint android_media_translateRecorderErrorCode(int code) { @@ -154,17 +152,16 @@ android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject weak_this, int frameSize = nbChannels * bytesPerSample; size_t frameCount = buffSizeInBytes / frameSize; - // compare the source against the Java constants - AudioRecord::stream_type arSource; - if (source == javaAudioRecordFields.SOURCE_DEFAULT) { - arSource = AudioRecord::DEFAULT_INPUT; - } else if (source == javaAudioRecordFields.SOURCE_MIC) { - arSource = AudioRecord::MIC_INPUT; - } else { + // convert and check input source value + // input_source values defined in AudioRecord.h are equal to + // JAVA MediaRecord.AudioSource values minus 1. + AudioRecord::input_source arSource = (AudioRecord::input_source)(source - 1); + if (arSource < AudioRecord::DEFAULT_INPUT || + arSource >= AudioRecord::NUM_INPUT_SOURCES) { LOGE("Error creating AudioRecord: unknown source."); - return AUDIORECORD_ERROR_SETUP_INVALIDSTREAMTYPE; + return AUDIORECORD_ERROR_SETUP_INVALIDSOURCE; } - + audiorecord_callback_cookie *lpCallbackData = NULL; AudioRecord* lpRecorder = NULL; @@ -511,8 +508,6 @@ static JNINativeMethod gMethods[] = { #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_SOURCEDEFAULT_NAME "SOURCE_DEFAULT" -#define JAVA_CONST_SOURCEMIC_NAME "SOURCE_MIC" #define JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME "mNativeRecorderInJavaObj" #define JAVA_NATIVECALLBACKINFO_FIELD_NAME "mNativeCallbackCookie" @@ -583,17 +578,6 @@ int register_android_media_AudioRecord(JNIEnv *env) return -1; } - // Get the recording source constants from the AudioRecord class - if ( !android_media_getIntConstantFromClass(env, javaAudioRecordFields.audioRecordClass, - kClassPathName, - JAVA_CONST_SOURCEDEFAULT_NAME, &(javaAudioRecordFields.SOURCE_DEFAULT)) - || !android_media_getIntConstantFromClass(env, javaAudioRecordFields.audioRecordClass, - kClassPathName, - JAVA_CONST_SOURCEMIC_NAME, &(javaAudioRecordFields.SOURCE_MIC)) ) { - // error log performed in getIntConstantFromClass() - return -1; - } - return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods)); } diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp index 42ada5450744ad48011b8671bb6ff8e3f0c6d41a..e7d4694a2aafa80aa4b97e4e7a985301bb5e663a 100644 --- a/core/jni/android_media_AudioTrack.cpp +++ b/core/jni/android_media_AudioTrack.cpp @@ -539,16 +539,17 @@ static jint android_media_AudioTrack_get_native_frame_count(JNIEnv *env, jobjec // ---------------------------------------------------------------------------- -static void android_media_AudioTrack_set_playback_rate(JNIEnv *env, jobject thiz, +static jint android_media_AudioTrack_set_playback_rate(JNIEnv *env, jobject thiz, jint sampleRateInHz) { AudioTrack *lpTrack = (AudioTrack *)env->GetIntField( thiz, javaAudioTrackFields.nativeTrackInJavaObj); if (lpTrack) { - lpTrack->setSampleRate(sampleRateInHz); + return android_media_translateErrorCode(lpTrack->setSampleRate(sampleRateInHz)); } else { jniThrowException(env, "java/lang/IllegalStateException", "Unable to retrieve AudioTrack pointer for setSampleRate()"); + return AUDIOTRACK_ERROR; } } @@ -788,7 +789,7 @@ static JNINativeMethod gMethods[] = { {"native_get_native_frame_count", "()I", (void *)android_media_AudioTrack_get_native_frame_count}, {"native_set_playback_rate", - "(I)V", (void *)android_media_AudioTrack_set_playback_rate}, + "(I)I", (void *)android_media_AudioTrack_set_playback_rate}, {"native_get_playback_rate", "()I", (void *)android_media_AudioTrack_get_playback_rate}, {"native_set_marker_pos","(I)I", (void *)android_media_AudioTrack_set_marker_pos}, diff --git a/core/jni/android_net_wifi_Wifi.cpp b/core/jni/android_net_wifi_Wifi.cpp index 25670df9d3739db8505169f8f15719bf65ac9603..9f93e2f042d120d8ef5e9487f62cc18a59fc3ff3 100644 --- a/core/jni/android_net_wifi_Wifi.cpp +++ b/core/jni/android_net_wifi_Wifi.cpp @@ -317,8 +317,13 @@ static jint android_net_wifi_getRssiCommand(JNIEnv* env, jobject clazz) } // reply comes back in the form " rssi XX" where XX is the // number we're interested in. if we're associating, it returns "OK". + // beware - can contain spaces. if (strcmp(reply, "OK") != 0) { - sscanf(reply, "%*s %*s %d", &rssi); + char* lastSpace = strrchr(reply, ' '); + // lastSpace should be preceded by "rssi" and followed by the value + if (lastSpace && !strncmp(lastSpace - 4, "rssi", 4)) { + sscanf(lastSpace + 1, "%d", &rssi); + } } return (jint)rssi; } diff --git a/core/jni/android_opengl_GLES10.cpp b/core/jni/android_opengl_GLES10.cpp index 482d8eb1b88bd87e2f726a72e4df466faa8d6a3b..2685d7595e32f36a3057c6ce4d83331fa6e9b0a7 100644 --- a/core/jni/android_opengl_GLES10.cpp +++ b/core/jni/android_opengl_GLES10.cpp @@ -133,6 +133,19 @@ releasePointer(JNIEnv *_env, jarray array, void *data, jboolean commit) commit ? 0 : JNI_ABORT); } +static void * +getDirectBufferPointer(JNIEnv *_env, jobject buffer) { + char* buf = (char*) _env->GetDirectBufferAddress(buffer); + if (buf) { + jint position = _env->GetIntField(buffer, positionID); + jint elementSizeShift = _env->GetIntField(buffer, elementSizeShiftID); + buf += position << elementSizeShift; + } else { + _env->ThrowNew(IAEClass, "Must use a native order direct Buffer"); + } + return (void*) buf; +} + static int getNumCompressedTextureFormats() { int numCompressedTextureFormats = 0; @@ -305,9 +318,8 @@ android_glColorPointerBounds__IIILjava_nio_Buffer_2I GLvoid *pointer = (GLvoid *) 0; if (pointer_buf) { - pointer = (GLvoid *) _env->GetDirectBufferAddress(pointer_buf); + pointer = (GLvoid *) getDirectBufferPointer(_env, pointer_buf); if ( ! pointer ) { - _env->ThrowNew(IAEClass, "Must use a native order direct Buffer"); return; } } @@ -2779,9 +2791,8 @@ android_glNormalPointerBounds__IILjava_nio_Buffer_2I GLvoid *pointer = (GLvoid *) 0; if (pointer_buf) { - pointer = (GLvoid *) _env->GetDirectBufferAddress(pointer_buf); + pointer = (GLvoid *) getDirectBufferPointer(_env, pointer_buf); if ( ! pointer ) { - _env->ThrowNew(IAEClass, "Must use a native order direct Buffer"); return; } } @@ -3034,9 +3045,8 @@ android_glTexCoordPointerBounds__IIILjava_nio_Buffer_2I GLvoid *pointer = (GLvoid *) 0; if (pointer_buf) { - pointer = (GLvoid *) _env->GetDirectBufferAddress(pointer_buf); + pointer = (GLvoid *) getDirectBufferPointer(_env, pointer_buf); if ( ! pointer ) { - _env->ThrowNew(IAEClass, "Must use a native order direct Buffer"); return; } } @@ -3392,9 +3402,8 @@ android_glVertexPointerBounds__IIILjava_nio_Buffer_2I GLvoid *pointer = (GLvoid *) 0; if (pointer_buf) { - pointer = (GLvoid *) _env->GetDirectBufferAddress(pointer_buf); + pointer = (GLvoid *) getDirectBufferPointer(_env, pointer_buf); if ( ! pointer ) { - _env->ThrowNew(IAEClass, "Must use a native order direct Buffer"); return; } } diff --git a/core/jni/android_os_MemoryFile.cpp b/core/jni/android_os_MemoryFile.cpp index edf7dc45991b9c885d349a5824881cb3328e3af3..8643393f7e8a46d5d75d4fadd623446bc1f05c22 100644 --- a/core/jni/android_os_MemoryFile.cpp +++ b/core/jni/android_os_MemoryFile.cpp @@ -26,7 +26,7 @@ namespace android { -static jint android_os_MemoryFile_open(JNIEnv* env, jobject clazz, jstring name, jint length) +static jobject android_os_MemoryFile_open(JNIEnv* env, jobject clazz, jstring name, jint length) { const char* namestr = (name ? env->GetStringUTFChars(name, NULL) : NULL); @@ -37,37 +37,52 @@ static jint android_os_MemoryFile_open(JNIEnv* env, jobject clazz, jstring name, if (name) env->ReleaseStringUTFChars(name, namestr); - if (result < 0) + if (result < 0) { jniThrowException(env, "java/io/IOException", "ashmem_create_region failed"); - return result; + return NULL; + } + + return jniCreateFileDescriptor(env, result); } -static jint android_os_MemoryFile_mmap(JNIEnv* env, jobject clazz, jint fd, jint length) +static jint android_os_MemoryFile_mmap(JNIEnv* env, jobject clazz, jobject fileDescriptor, + jint length, jint prot) { - jint result = (jint)mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); + int fd = jniGetFDFromFileDescriptor(env, fileDescriptor); + jint result = (jint)mmap(NULL, length, prot, MAP_SHARED, fd, 0); if (!result) jniThrowException(env, "java/io/IOException", "mmap failed"); return result; } -static void android_os_MemoryFile_close(JNIEnv* env, jobject clazz, jint fd) +static void android_os_MemoryFile_munmap(JNIEnv* env, jobject clazz, jint addr, jint length) +{ + int result = munmap((void *)addr, length); + if (result < 0) + jniThrowException(env, "java/io/IOException", "munmap failed"); +} + +static void android_os_MemoryFile_close(JNIEnv* env, jobject clazz, jobject fileDescriptor) { - close(fd); + int fd = jniGetFDFromFileDescriptor(env, fileDescriptor); + if (fd >= 0) { + jniSetFileDescriptorOfFD(env, fileDescriptor, -1); + close(fd); + } } static jint android_os_MemoryFile_read(JNIEnv* env, jobject clazz, - jint fd, jint address, jbyteArray buffer, jint srcOffset, jint destOffset, + jobject fileDescriptor, jint address, jbyteArray buffer, jint srcOffset, jint destOffset, jint count, jboolean unpinned) { + int fd = jniGetFDFromFileDescriptor(env, fileDescriptor); if (unpinned && ashmem_pin_region(fd, 0, 0) == ASHMEM_WAS_PURGED) { ashmem_unpin_region(fd, 0, 0); jniThrowException(env, "java/io/IOException", "ashmem region was purged"); return -1; } - jbyte* bytes = env->GetByteArrayElements(buffer, 0); - memcpy(bytes + destOffset, (const char *)address + srcOffset, count); - env->ReleaseByteArrayElements(buffer, bytes, 0); + env->SetByteArrayRegion(buffer, destOffset, count, (const jbyte *)address + srcOffset); if (unpinned) { ashmem_unpin_region(fd, 0, 0); @@ -76,18 +91,17 @@ static jint android_os_MemoryFile_read(JNIEnv* env, jobject clazz, } static jint android_os_MemoryFile_write(JNIEnv* env, jobject clazz, - jint fd, jint address, jbyteArray buffer, jint srcOffset, jint destOffset, + jobject fileDescriptor, jint address, jbyteArray buffer, jint srcOffset, jint destOffset, jint count, jboolean unpinned) { + int fd = jniGetFDFromFileDescriptor(env, fileDescriptor); if (unpinned && ashmem_pin_region(fd, 0, 0) == ASHMEM_WAS_PURGED) { ashmem_unpin_region(fd, 0, 0); jniThrowException(env, "java/io/IOException", "ashmem region was purged"); return -1; } - jbyte* bytes = env->GetByteArrayElements(buffer, 0); - memcpy((char *)address + destOffset, bytes + srcOffset, count); - env->ReleaseByteArrayElements(buffer, bytes, 0); + env->GetByteArrayRegion(buffer, srcOffset, count, (jbyte *)address + destOffset); if (unpinned) { ashmem_unpin_region(fd, 0, 0); @@ -95,21 +109,45 @@ static jint android_os_MemoryFile_write(JNIEnv* env, jobject clazz, return count; } -static void android_os_MemoryFile_pin(JNIEnv* env, jobject clazz, jint fd, jboolean pin) +static void android_os_MemoryFile_pin(JNIEnv* env, jobject clazz, jobject fileDescriptor, jboolean pin) { + int fd = jniGetFDFromFileDescriptor(env, fileDescriptor); int result = (pin ? ashmem_pin_region(fd, 0, 0) : ashmem_unpin_region(fd, 0, 0)); if (result < 0) { jniThrowException(env, "java/io/IOException", NULL); } } +static jboolean android_os_MemoryFile_is_ashmem_region(JNIEnv* env, jobject clazz, + jobject fileDescriptor) { + int fd = jniGetFDFromFileDescriptor(env, fileDescriptor); + // Use ASHMEM_GET_SIZE to find out if the fd refers to an ashmem region. + // ASHMEM_GET_SIZE should succeed for all ashmem regions, and the kernel + // should return ENOTTY for all other valid file descriptors + int result = ashmem_get_size_region(fd); + if (result < 0) { + if (errno == ENOTTY) { + // ENOTTY means that the ioctl does not apply to this object, + // i.e., it is not an ashmem region. + return JNI_FALSE; + } + // Some other error, throw exception + jniThrowIOException(env, errno); + return JNI_FALSE; + } + return JNI_TRUE; +} + static const JNINativeMethod methods[] = { - {"native_open", "(Ljava/lang/String;I)I", (void*)android_os_MemoryFile_open}, - {"native_mmap", "(II)I", (void*)android_os_MemoryFile_mmap}, - {"native_close", "(I)V", (void*)android_os_MemoryFile_close}, - {"native_read", "(II[BIIIZ)I", (void*)android_os_MemoryFile_read}, - {"native_write", "(II[BIIIZ)V", (void*)android_os_MemoryFile_write}, - {"native_pin", "(IZ)V", (void*)android_os_MemoryFile_pin}, + {"native_open", "(Ljava/lang/String;I)Ljava/io/FileDescriptor;", (void*)android_os_MemoryFile_open}, + {"native_mmap", "(Ljava/io/FileDescriptor;II)I", (void*)android_os_MemoryFile_mmap}, + {"native_munmap", "(II)V", (void*)android_os_MemoryFile_munmap}, + {"native_close", "(Ljava/io/FileDescriptor;)V", (void*)android_os_MemoryFile_close}, + {"native_read", "(Ljava/io/FileDescriptor;I[BIIIZ)I", (void*)android_os_MemoryFile_read}, + {"native_write", "(Ljava/io/FileDescriptor;I[BIIIZ)V", (void*)android_os_MemoryFile_write}, + {"native_pin", "(Ljava/io/FileDescriptor;Z)V", (void*)android_os_MemoryFile_pin}, + {"native_is_ashmem_region", "(Ljava/io/FileDescriptor;)Z", + (void*)android_os_MemoryFile_is_ashmem_region} }; static const char* const kClassPathName = "android/os/MemoryFile"; diff --git a/core/jni/android_server_BluetoothA2dpService.cpp b/core/jni/android_server_BluetoothA2dpService.cpp index fe94642e258f17445a9412b59af7eec1d9a333f0..91a8e8e60353055d13b4c01f9b361690fcb2cb01 100644 --- a/core/jni/android_server_BluetoothA2dpService.cpp +++ b/core/jni/android_server_BluetoothA2dpService.cpp @@ -84,6 +84,7 @@ static bool initNative(JNIEnv* env, jobject object) { dbus_error_free(&err); return false; } + dbus_connection_set_exit_on_disconnect(nat->conn, FALSE); #endif /*HAVE_BLUETOOTH*/ return true; } diff --git a/core/jni/android_server_BluetoothDeviceService.cpp b/core/jni/android_server_BluetoothDeviceService.cpp index b6e979811e268501c9c1771b6d9d2c89e01018c9..58ae4f60a78d558bc1f958261cd7a1f2aa32bc0e 100644 --- a/core/jni/android_server_BluetoothDeviceService.cpp +++ b/core/jni/android_server_BluetoothDeviceService.cpp @@ -109,6 +109,7 @@ static bool initializeNativeDataNative(JNIEnv* env, jobject object) { dbus_error_free(&err); return false; } + dbus_connection_set_exit_on_disconnect(nat->conn, FALSE); nat->adapter = BLUEZ_ADAPTER_OBJECT_NAME; #endif /*HAVE_BLUETOOTH*/ diff --git a/core/jni/android_server_BluetoothEventLoop.cpp b/core/jni/android_server_BluetoothEventLoop.cpp index 7c5da5bdb5647da7b8cb08847c18dfd49708ca2d..ad24136ac28f501e1007d1a40c3eb6f4a80bad40 100644 --- a/core/jni/android_server_BluetoothEventLoop.cpp +++ b/core/jni/android_server_BluetoothEventLoop.cpp @@ -132,6 +132,7 @@ static void initializeNativeDataNative(JNIEnv* env, jobject object) { LOGE("%s: Could not get onto the system bus!", __FUNCTION__); dbus_error_free(&err); } + dbus_connection_set_exit_on_disconnect(nat->conn, FALSE); } #endif } @@ -161,6 +162,19 @@ static const DBusObjectPathVTable agent_vtable = { NULL, agent_event_filter, NULL, NULL, NULL, NULL }; +static unsigned int unix_events_to_dbus_flags(short events) { + return (events & DBUS_WATCH_READABLE ? POLLIN : 0) | + (events & DBUS_WATCH_WRITABLE ? POLLOUT : 0) | + (events & DBUS_WATCH_ERROR ? POLLERR : 0) | + (events & DBUS_WATCH_HANGUP ? POLLHUP : 0); +} + +static short dbus_flags_to_unix_events(unsigned int flags) { + return (flags & POLLIN ? DBUS_WATCH_READABLE : 0) | + (flags & POLLOUT ? DBUS_WATCH_WRITABLE : 0) | + (flags & POLLERR ? DBUS_WATCH_ERROR : 0) | + (flags & POLLHUP ? DBUS_WATCH_HANGUP : 0); +} static jboolean setUpEventLoop(native_data_t *nat) { LOGV(__FUNCTION__); @@ -384,8 +398,7 @@ static void handleWatchAdd(native_data_t *nat) { read(nat->controlFdR, &newFD, sizeof(int)); read(nat->controlFdR, &flags, sizeof(unsigned int)); read(nat->controlFdR, &watch, sizeof(DBusWatch *)); - int events = (flags & DBUS_WATCH_READABLE ? POLLIN : 0) - | (flags & DBUS_WATCH_WRITABLE ? POLLOUT : 0); + short events = dbus_flags_to_unix_events(flags); for (int y = 0; ypollMemberCount; y++) { if ((nat->pollData[y].fd == newFD) && @@ -429,8 +442,7 @@ static void handleWatchRemove(native_data_t *nat) { read(nat->controlFdR, &removeFD, sizeof(int)); read(nat->controlFdR, &flags, sizeof(unsigned int)); - int events = (flags & DBUS_WATCH_READABLE ? POLLIN : 0) - | (flags & DBUS_WATCH_WRITABLE ? POLLOUT : 0); + short events = dbus_flags_to_unix_events(flags); for (int y = 0; y < nat->pollMemberCount; y++) { if ((nat->pollData[y].fd == removeFD) && @@ -494,13 +506,12 @@ static void *eventLoopMain(void *ptr) { } } } else { - int event = nat->pollData[i].revents; - int flags = (event & POLLIN ? DBUS_WATCH_READABLE : 0) | - (event & POLLOUT ? DBUS_WATCH_WRITABLE : 0); - dbus_watch_handle(nat->watchData[i], event); - nat->pollData[i].revents = 0; - // can only do one - it may have caused a 'remove' - break; + short events = nat->pollData[i].revents; + unsigned int flags = unix_events_to_dbus_flags(events); + dbus_watch_handle(nat->watchData[i], flags); + nat->pollData[i].revents = 0; + // can only do one - it may have caused a 'remove' + break; } } while (dbus_connection_dispatch(nat->conn) == diff --git a/core/jni/android_text_format_Time.cpp b/core/jni/android_text_format_Time.cpp index 923e1aa05cc83df44d3278abb65302a1bc79206f..98f4e035c0c959b06e62a6c19971ec02853bfdb5 100644 --- a/core/jni/android_text_format_Time.cpp +++ b/core/jni/android_text_format_Time.cpp @@ -23,7 +23,7 @@ #include "jni.h" #include "utils/misc.h" #include "android_runtime/AndroidRuntime.h" -#include +#include "TimeUtils.h" #include #include @@ -44,6 +44,7 @@ static jfieldID g_timezoneField = 0; static jfieldID g_shortMonthsField = 0; static jfieldID g_longMonthsField = 0; +static jfieldID g_longStandaloneMonthsField = 0; static jfieldID g_shortWeekdaysField = 0; static jfieldID g_longWeekdaysField = 0; static jfieldID g_timeOnlyFormatField = 0; @@ -193,6 +194,7 @@ static jstring android_text_format_Time_format(JNIEnv* env, jobject This, static jobject js_locale_previous = NULL; static struct strftime_locale locale; static jstring js_mon[12], js_month[12], js_wday[7], js_weekday[7]; + static jstring js_standalone_month[12]; static jstring js_X_fmt, js_x_fmt, js_c_fmt, js_am, js_pm, js_date_fmt; Time t; @@ -206,8 +208,10 @@ static jstring android_text_format_Time_format(JNIEnv* env, jobject This, for (int i = 0; i < 12; i++) { env->ReleaseStringUTFChars(js_mon[i], locale.mon[i]); env->ReleaseStringUTFChars(js_month[i], locale.month[i]); + env->ReleaseStringUTFChars(js_standalone_month[i], locale.standalone_month[i]); env->DeleteGlobalRef(js_mon[i]); env->DeleteGlobalRef(js_month[i]); + env->DeleteGlobalRef(js_standalone_month[i]); } for (int i = 0; i < 7; i++) { @@ -245,6 +249,12 @@ static jstring android_text_format_Time_format(JNIEnv* env, jobject This, locale.month[i] = env->GetStringUTFChars(js_month[i], NULL); } + ja = (jobjectArray) env->GetStaticObjectField(timeClass, g_longStandaloneMonthsField); + for (int i = 0; i < 12; i++) { + js_standalone_month[i] = (jstring) env->NewGlobalRef(env->GetObjectArrayElement(ja, i)); + locale.standalone_month[i] = env->GetStringUTFChars(js_standalone_month[i], NULL); + } + ja = (jobjectArray) env->GetStaticObjectField(timeClass, g_shortWeekdaysField); for (int i = 0; i < 7; i++) { js_wday[i] = (jstring) env->NewGlobalRef(env->GetObjectArrayElement(ja, i)); @@ -639,6 +649,7 @@ int register_android_text_format_Time(JNIEnv* env) g_shortMonthsField = env->GetStaticFieldID(timeClass, "sShortMonths", "[Ljava/lang/String;"); g_longMonthsField = env->GetStaticFieldID(timeClass, "sLongMonths", "[Ljava/lang/String;"); + g_longStandaloneMonthsField = env->GetStaticFieldID(timeClass, "sLongStandaloneMonths", "[Ljava/lang/String;"); g_shortWeekdaysField = env->GetStaticFieldID(timeClass, "sShortWeekdays", "[Ljava/lang/String;"); g_longWeekdaysField = env->GetStaticFieldID(timeClass, "sLongWeekdays", "[Ljava/lang/String;"); g_timeOnlyFormatField = env->GetStaticFieldID(timeClass, "sTimeOnlyFormat", "Ljava/lang/String;"); diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp index d147bcc883c281240d3f7a2bdec21c64a4e860c9..2d90ba49aecc770cbe8702b0468ffc9e4439d337 100644 --- a/core/jni/android_util_AssetManager.cpp +++ b/core/jni/android_util_AssetManager.cpp @@ -535,7 +535,7 @@ static void android_content_AssetManager_setConfiguration(JNIEnv* env, jobject c jint keyboard, jint keyboardHidden, jint navigation, jint screenWidth, jint screenHeight, - jint sdkVersion) + jint screenLayout, jint sdkVersion) { AssetManager* am = assetManagerForJavaObject(env, clazz); if (am == NULL) { @@ -557,6 +557,7 @@ static void android_content_AssetManager_setConfiguration(JNIEnv* env, jobject c config.navigation = (uint8_t)navigation; config.screenWidth = (uint16_t)screenWidth; config.screenHeight = (uint16_t)screenHeight; + config.screenLayout = (uint8_t)screenLayout; config.sdkVersion = (uint16_t)sdkVersion; config.minorVersion = 0; am->setConfiguration(config, locale8); @@ -1567,7 +1568,7 @@ static JNINativeMethod gAssetManagerMethods[] = { (void*) android_content_AssetManager_setLocale }, { "getLocales", "()[Ljava/lang/String;", (void*) android_content_AssetManager_getLocales }, - { "setConfiguration", "(IILjava/lang/String;IIIIIIIII)V", + { "setConfiguration", "(IILjava/lang/String;IIIIIIIIII)V", (void*) android_content_AssetManager_setConfiguration }, { "getResourceIdentifier","(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I", (void*) android_content_AssetManager_getResourceIdentifier }, diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp index d760feb3a3aabd4880e923694177ae9173fe1bb0..aee0ed7f42f8821f28e41ceab0f4137dff6e9d22 100644 --- a/core/jni/android_util_Process.cpp +++ b/core/jni/android_util_Process.cpp @@ -50,8 +50,6 @@ pid_t gettid() { return syscall(__NR_gettid);} #undef __KERNEL__ #endif -#define ENABLE_CGROUP_ERR_LOGGING 0 - /* * List of cgroup names which map to ANDROID_TGROUP_ values in Thread.h * and Process.java @@ -198,50 +196,82 @@ jint android_os_Process_getGidForName(JNIEnv* env, jobject clazz, jstring name) static int add_pid_to_cgroup(int pid, int grp) { - FILE *fp; + int fd; char path[255]; - int rc; + char text[64]; - sprintf(path, "/dev/cpuctl/%s/tasks", (cgroup_names[grp] ? cgroup_names[grp] : "")); + sprintf(path, "/dev/cpuctl/%s/tasks", + (cgroup_names[grp] ? cgroup_names[grp] : "")); - if (!(fp = fopen(path, "w"))) { -#if ENABLE_CGROUP_ERR_LOGGING - LOGW("Unable to open %s (%s)\n", path, strerror(errno)); -#endif - return -errno; + if ((fd = open(path, O_WRONLY)) < 0) + return -1; + + sprintf(text, "%d", pid); + if (write(fd, text, strlen(text)) < 0) { + close(fd); + return -1; } - rc = fprintf(fp, "%d", pid); - fclose(fp); + close(fd); + return 0; +} - if (rc < 0) { -#if ENABLE_CGROUP_ERR_LOGGING - LOGW("Unable to move pid %d to cgroup %s (%s)\n", pid, - (cgroup_names[grp] ? cgroup_names[grp] : ""), - strerror(errno)); -#endif +void android_os_Process_setThreadGroup(JNIEnv* env, jobject clazz, int pid, jint grp) +{ + if (grp > ANDROID_TGROUP_MAX || grp < 0) { + signalExceptionForGroupError(env, clazz, EINVAL); + return; } - return (rc < 0) ? errno : 0; + if (add_pid_to_cgroup(pid, grp)) { + // If the thread exited on us, don't generate an exception + if (errno != ESRCH && errno != ENOENT) + signalExceptionForGroupError(env, clazz, errno); + } } -void android_os_Process_setThreadGroup(JNIEnv* env, jobject clazz, int pid, jint grp) +void android_os_Process_setProcessGroup(JNIEnv* env, jobject clazz, int pid, jint grp) { + DIR *d; + FILE *fp; + char proc_path[255]; + struct dirent *de; + if (grp > ANDROID_TGROUP_MAX || grp < 0) { signalExceptionForGroupError(env, clazz, EINVAL); return; } - if (add_pid_to_cgroup(pid, grp)) - signalExceptionForGroupError(env, clazz, errno); + sprintf(proc_path, "/proc/%d/task", pid); + if (!(d = opendir(proc_path))) { + // If the process exited on us, don't generate an exception + if (errno != ENOENT) + signalExceptionForGroupError(env, clazz, errno); + return; + } + + while ((de = readdir(d))) { + if (de->d_name[0] == '.') + continue; + + if (add_pid_to_cgroup(atoi(de->d_name), grp)) { + // If the thread exited on us, ignore it and keep going + if (errno != ESRCH && errno != ENOENT) { + signalExceptionForGroupError(env, clazz, errno); + closedir(d); + return; + } + } + } + closedir(d); } void android_os_Process_setThreadPriority(JNIEnv* env, jobject clazz, jint pid, jint pri) { - if (pri == ANDROID_PRIORITY_BACKGROUND) { + if (pri >= ANDROID_PRIORITY_BACKGROUND) { add_pid_to_cgroup(pid, ANDROID_TGROUP_BG_NONINTERACT); - } else if (getpriority(PRIO_PROCESS, pid) == ANDROID_PRIORITY_BACKGROUND) { + } else if (getpriority(PRIO_PROCESS, pid) >= ANDROID_PRIORITY_BACKGROUND) { add_pid_to_cgroup(pid, ANDROID_TGROUP_DEFAULT); } @@ -466,7 +496,7 @@ void android_os_Process_readProcLines(JNIEnv* env, jobject clazz, jstring fileSt const String8& field = fields[i]; if (strncmp(p, field.string(), field.length()) == 0) { p += field.length(); - while (*p == ' ') p++; + while (*p == ' ' || *p == '\t') p++; char* num = p; while (*p >= '0' && *p <= '9') p++; skipToEol = *p != '\n'; @@ -820,6 +850,7 @@ static const JNINativeMethod methods[] = { {"setThreadPriority", "(I)V", (void*)android_os_Process_setCallingThreadPriority}, {"getThreadPriority", "(I)I", (void*)android_os_Process_getThreadPriority}, {"setThreadGroup", "(II)V", (void*)android_os_Process_setThreadGroup}, + {"setProcessGroup", "(II)V", (void*)android_os_Process_setProcessGroup}, {"setOomAdj", "(II)Z", (void*)android_os_Process_setOomAdj}, {"setArgV0", "(Ljava/lang/String;)V", (void*)android_os_Process_setArgV0}, {"setUid", "(I)I", (void*)android_os_Process_setUid}, diff --git a/core/jni/com_google_android_gles_jni_GLImpl.cpp b/core/jni/com_google_android_gles_jni_GLImpl.cpp index 11822e014d9c5ca126701a6004d8abbb5e4691bf..89b1f96879e5078ec37b18cffbd9b8a053121f78 100644 --- a/core/jni/com_google_android_gles_jni_GLImpl.cpp +++ b/core/jni/com_google_android_gles_jni_GLImpl.cpp @@ -45,9 +45,11 @@ static jclass OOMEClass; static jclass UOEClass; static jclass IAEClass; static jclass AIOOBEClass; +static jclass G11ImplClass; static jmethodID getBasePointerID; static jmethodID getBaseArrayID; static jmethodID getBaseArrayOffsetID; +static jmethodID allowIndirectBuffersID; static jfieldID positionID; static jfieldID limitID; static jfieldID elementSizeShiftID; @@ -63,13 +65,17 @@ nativeClassInitBuffer(JNIEnv *_env) jclass bufferClassLocal = _env->FindClass("java/nio/Buffer"); bufferClass = (jclass) _env->NewGlobalRef(bufferClassLocal); + jclass g11impClassLocal = _env->FindClass("com/google/android/gles_jni/GLImpl"); + G11ImplClass = (jclass) _env->NewGlobalRef(g11impClassLocal); + getBasePointerID = _env->GetStaticMethodID(nioAccessClass, "getBasePointer", "(Ljava/nio/Buffer;)J"); getBaseArrayID = _env->GetStaticMethodID(nioAccessClass, "getBaseArray", "(Ljava/nio/Buffer;)Ljava/lang/Object;"); getBaseArrayOffsetID = _env->GetStaticMethodID(nioAccessClass, "getBaseArrayOffset", "(Ljava/nio/Buffer;)I"); - + allowIndirectBuffersID = _env->GetStaticMethodID(g11impClassLocal, + "allowIndirectBuffers", "(Ljava/lang/String;)Z"); positionID = _env->GetFieldID(bufferClass, "position", "I"); limitID = _env->GetFieldID(bufferClass, "limit", "I"); elementSizeShiftID = @@ -119,6 +125,9 @@ getPointer(JNIEnv *_env, jobject buffer, jarray *array, jint *remaining) *array = (jarray) _env->CallStaticObjectMethod(nioAccessClass, getBaseArrayID, buffer); + if (*array == NULL) { + return (void*) NULL; + } offset = _env->CallStaticIntMethod(nioAccessClass, getBaseArrayOffsetID, buffer); data = _env->GetPrimitiveArrayCritical(*array, (jboolean *) 0); @@ -133,6 +142,45 @@ releasePointer(JNIEnv *_env, jarray array, void *data, jboolean commit) commit ? 0 : JNI_ABORT); } +extern "C" { +extern char* __progname; +} + +static bool +allowIndirectBuffers(JNIEnv *_env) { + static jint sIndirectBufferCompatability; + if (sIndirectBufferCompatability == 0) { + jobject appName = _env->NewStringUTF(::__progname); + sIndirectBufferCompatability = _env->CallStaticBooleanMethod(G11ImplClass, allowIndirectBuffersID, appName) ? 2 : 1; + } + return sIndirectBufferCompatability == 2; +} + +static void * +getDirectBufferPointer(JNIEnv *_env, jobject buffer) { + if (!buffer) { + return NULL; + } + void* buf = _env->GetDirectBufferAddress(buffer); + if (buf) { + jint position = _env->GetIntField(buffer, positionID); + jint elementSizeShift = _env->GetIntField(buffer, elementSizeShiftID); + buf = ((char*) buf) + (position << elementSizeShift); + } else { + if (allowIndirectBuffers(_env)) { + jarray array = 0; + jint remaining; + buf = getPointer(_env, buffer, &array, &remaining); + if (array) { + releasePointer(_env, array, buf, 0); + } + } else { + _env->ThrowNew(IAEClass, "Must use a native order direct Buffer"); + } + } + return buf; +} + static int getNumCompressedTextureFormats() { int numCompressedTextureFormats = 0; @@ -305,9 +353,8 @@ android_glColorPointerBounds__IIILjava_nio_Buffer_2I GLvoid *pointer = (GLvoid *) 0; if (pointer_buf) { - pointer = (GLvoid *) _env->GetDirectBufferAddress(pointer_buf); + pointer = (GLvoid *) getDirectBufferPointer(_env, pointer_buf); if ( ! pointer ) { - _env->ThrowNew(IAEClass, "Must use a native order direct Buffer"); return; } } @@ -2779,9 +2826,8 @@ android_glNormalPointerBounds__IILjava_nio_Buffer_2I GLvoid *pointer = (GLvoid *) 0; if (pointer_buf) { - pointer = (GLvoid *) _env->GetDirectBufferAddress(pointer_buf); + pointer = (GLvoid *) getDirectBufferPointer(_env, pointer_buf); if ( ! pointer ) { - _env->ThrowNew(IAEClass, "Must use a native order direct Buffer"); return; } } @@ -3034,9 +3080,8 @@ android_glTexCoordPointerBounds__IIILjava_nio_Buffer_2I GLvoid *pointer = (GLvoid *) 0; if (pointer_buf) { - pointer = (GLvoid *) _env->GetDirectBufferAddress(pointer_buf); + pointer = (GLvoid *) getDirectBufferPointer(_env, pointer_buf); if ( ! pointer ) { - _env->ThrowNew(IAEClass, "Must use a native order direct Buffer"); return; } } @@ -3392,9 +3437,8 @@ android_glVertexPointerBounds__IIILjava_nio_Buffer_2I GLvoid *pointer = (GLvoid *) 0; if (pointer_buf) { - pointer = (GLvoid *) _env->GetDirectBufferAddress(pointer_buf); + pointer = (GLvoid *) getDirectBufferPointer(_env, pointer_buf); if ( ! pointer ) { - _env->ThrowNew(IAEClass, "Must use a native order direct Buffer"); return; } } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index bff6b9dfda5e63313e49405923c00544a97f3998..599360f06244560917fd1d407eaf6a4bc88dad25 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -175,6 +175,22 @@ android:label="@string/permlab_writeDictionary" android:description="@string/permdesc_writeDictionary" /> + + + + + + @@ -220,12 +236,6 @@ android:label="@string/permlab_installLocationProvider" android:description="@string/permdesc_installLocationProvider" /> - - - @@ -388,12 +398,12 @@ android:label="@string/permgrouplab_storage" android:description="@string/permgroupdesc_storage" /> - - + + android:protectionLevel="dangerous" /> @@ -650,6 +660,13 @@ android:description="@string/permdesc_changeWifiState" android:label="@string/permlab_changeWifiState" /> + + + - - - - - - - - - + + + + + + + @@ -988,6 +1000,7 @@ android:hasCode="false" android:label="@string/android_system_label" android:allowClearUserData="false" + android:backupAgent="com.android.internal.backup.SystemBackupAgent" android:icon="@drawable/ic_launcher_android"> + + + diff --git a/core/res/res/anim/slide_out_down.xml b/core/res/res/anim/slide_out_down.xml new file mode 100644 index 0000000000000000000000000000000000000000..9b8d5b7919a2178cc133ec351a2400578b205095 --- /dev/null +++ b/core/res/res/anim/slide_out_down.xml @@ -0,0 +1,23 @@ + + + + diff --git a/core/res/res/drawable/call_contact.png b/core/res/res/drawable/call_contact.png new file mode 100644 index 0000000000000000000000000000000000000000..1abeb5da3bcb26f167a8fc5fba79710405e5feb4 Binary files /dev/null and b/core/res/res/drawable/call_contact.png differ diff --git a/core/res/res/drawable/create_contact.png b/core/res/res/drawable/create_contact.png new file mode 100644 index 0000000000000000000000000000000000000000..5c5718bafc00341ff0a1ba6df4e32d0f2e64fcd2 Binary files /dev/null and b/core/res/res/drawable/create_contact.png differ diff --git a/core/res/res/drawable/progress.xml b/core/res/res/drawable/progress.xml deleted file mode 100644 index d2705209be75f904b897e5e1c1786f17da5b9fae..0000000000000000000000000000000000000000 --- a/core/res/res/drawable/progress.xml +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - - - - - - - diff --git a/core/res/res/drawable/progress_circular_background.png b/core/res/res/drawable/progress_circular_background.png deleted file mode 100644 index 7c637fd602d9ccebe8ba3d86f3f27b2abe103a0d..0000000000000000000000000000000000000000 Binary files a/core/res/res/drawable/progress_circular_background.png and /dev/null differ diff --git a/core/res/res/drawable/progress_circular_background_small.png b/core/res/res/drawable/progress_circular_background_small.png deleted file mode 100644 index 6b8ba9b4390697c706251447167aa1c443b6c568..0000000000000000000000000000000000000000 Binary files a/core/res/res/drawable/progress_circular_background_small.png and /dev/null differ diff --git a/core/res/res/drawable/progress_circular_indeterminate.png b/core/res/res/drawable/progress_circular_indeterminate.png deleted file mode 100644 index 125a264ca7d0cfdcecbba2f4aa8a805eb4a67369..0000000000000000000000000000000000000000 Binary files a/core/res/res/drawable/progress_circular_indeterminate.png and /dev/null differ diff --git a/core/res/res/drawable/progress_large.xml b/core/res/res/drawable/progress_large.xml index 466910420e0fa82da0fcc4cfb1aad16cb0a93c72..4f016bcc2e83e807c6f2e49f5bccf01713142bfa 100644 --- a/core/res/res/drawable/progress_large.xml +++ b/core/res/res/drawable/progress_large.xml @@ -1,45 +1,25 @@ - - - - - - - - - - - - - - - + diff --git a/core/java/android/app/IIntentSender.aidl b/core/res/res/drawable/progress_large_white.xml similarity index 61% rename from core/java/android/app/IIntentSender.aidl rename to core/res/res/drawable/progress_large_white.xml index 53e135ab1bb0f504a34492e0ff4d46e85c1a6a64..c690ed4e0e9aee1aa55f05de3571a8e01cb0d22b 100644 --- a/core/java/android/app/IIntentSender.aidl +++ b/core/res/res/drawable/progress_large_white.xml @@ -1,6 +1,8 @@ -/* //device/java/android/android/app/IActivityPendingResult.aidl + + + diff --git a/core/res/res/drawable/progress_medium.xml b/core/res/res/drawable/progress_medium.xml index 92aebb51a512aa8fed6c3e1ef67736b1d7fbb97c..eb1bd50d17d7385fd672be7c4e9dcbd71d2b7b4a 100644 --- a/core/res/res/drawable/progress_medium.xml +++ b/core/res/res/drawable/progress_medium.xml @@ -1,43 +1,25 @@ - - - - - - - - - - - - - + diff --git a/core/res/res/drawable/progress_indeterminate.xml b/core/res/res/drawable/progress_medium_white.xml similarity index 54% rename from core/res/res/drawable/progress_indeterminate.xml rename to core/res/res/drawable/progress_medium_white.xml index 1bf715e51269cb4e5e8644837a840e68f8c84fc4..b4f9b318a902a8638eefc3634eba69f0ed1f269d 100644 --- a/core/res/res/drawable/progress_indeterminate.xml +++ b/core/res/res/drawable/progress_medium_white.xml @@ -1,8 +1,8 @@ - - - - - - - + diff --git a/core/res/res/drawable/progress_small.xml b/core/res/res/drawable/progress_small.xml index e5b0021d59cf5bdab637608a585d1219d483f384..e0ee5e47d8305c2142aadf63f8e067984dab8a48 100644 --- a/core/res/res/drawable/progress_small.xml +++ b/core/res/res/drawable/progress_small.xml @@ -1,45 +1,25 @@ - - - - - - - - - - - - - - - + diff --git a/core/res/res/drawable/progress_small_titlebar.xml b/core/res/res/drawable/progress_small_titlebar.xml index cf8e41cb373dc810e68da39b52ab2491cd7af1a0..8cfba864b5b2ced44797cc4c336a8045f8643703 100644 --- a/core/res/res/drawable/progress_small_titlebar.xml +++ b/core/res/res/drawable/progress_small_titlebar.xml @@ -1,45 +1,25 @@ - - - - - - - - - - - - - - - + diff --git a/core/java/android/app/IIntentReceiver.aidl b/core/res/res/drawable/progress_small_white.xml old mode 100755 new mode 100644 similarity index 52% rename from core/java/android/app/IIntentReceiver.aidl rename to core/res/res/drawable/progress_small_white.xml index 5f5d0eb133e51f9785a0b274a4cbfc0f792bb969..8cfba864b5b2ced44797cc4c336a8045f8643703 --- a/core/java/android/app/IIntentReceiver.aidl +++ b/core/res/res/drawable/progress_small_white.xml @@ -1,6 +1,8 @@ + + + diff --git a/core/res/res/drawable/rate_star_big_half.png b/core/res/res/drawable/rate_star_big_half.png index e73ca799b398845d84d09bf9f518f28e678998f3..9762292a40a6cf6c00256a056f7aa9f3e5574377 100644 Binary files a/core/res/res/drawable/rate_star_big_half.png and b/core/res/res/drawable/rate_star_big_half.png differ diff --git a/core/res/res/drawable/rate_star_big_off.png b/core/res/res/drawable/rate_star_big_off.png index b4dfa9dd3ae100ce351cbc22e4de7639bb2ac767..6b5039fc73a46ed8bc136c7ae5834295925a71c7 100644 Binary files a/core/res/res/drawable/rate_star_big_off.png and b/core/res/res/drawable/rate_star_big_off.png differ diff --git a/core/res/res/drawable/rate_star_big_on.png b/core/res/res/drawable/rate_star_big_on.png index 7442c93c411cb7ab9e6c3effa857c19498e14c7f..a972db27486bf56c6b48ad993202179c97af34ee 100644 Binary files a/core/res/res/drawable/rate_star_big_on.png and b/core/res/res/drawable/rate_star_big_on.png differ diff --git a/core/res/res/drawable/search_dropdown_background.9.png b/core/res/res/drawable/search_dropdown_background.9.png old mode 100755 new mode 100644 index a6923b7c01363d780ffd82c30f94562ddc2eb458..804260afa97f71f3ebfbf89d9ef14eae02093a07 Binary files a/core/res/res/drawable/search_dropdown_background.9.png and b/core/res/res/drawable/search_dropdown_background.9.png differ diff --git a/core/res/res/drawable/search_dropdown_background_apps.9.png b/core/res/res/drawable/search_dropdown_background_apps.9.png deleted file mode 100644 index 56b697d97b8e738ffa2aed8fc1c8dc4f5e87eb9a..0000000000000000000000000000000000000000 Binary files a/core/res/res/drawable/search_dropdown_background_apps.9.png and /dev/null differ diff --git a/core/res/res/drawable/search_spinner.xml b/core/res/res/drawable/search_spinner.xml new file mode 100644 index 0000000000000000000000000000000000000000..31a77c30cf2a68c204c9813b20cc217e485e0b51 --- /dev/null +++ b/core/res/res/drawable/search_spinner.xml @@ -0,0 +1,25 @@ + + + diff --git a/core/res/res/drawable/spinner_black_16.png b/core/res/res/drawable/spinner_black_16.png new file mode 100644 index 0000000000000000000000000000000000000000..5ee33cea6fa7d1672d9ad3661782f873b3a02468 Binary files /dev/null and b/core/res/res/drawable/spinner_black_16.png differ diff --git a/core/res/res/drawable/spinner_black_20.png b/core/res/res/drawable/spinner_black_20.png new file mode 100755 index 0000000000000000000000000000000000000000..e55b60dc6cce81d2390bb2bfb06fcfdbd85c38bc Binary files /dev/null and b/core/res/res/drawable/spinner_black_20.png differ diff --git a/core/res/res/drawable/spinner_black_48.png b/core/res/res/drawable/spinner_black_48.png new file mode 100644 index 0000000000000000000000000000000000000000..3a681926b537f4fbf5d2f423dc724d03490be0ca Binary files /dev/null and b/core/res/res/drawable/spinner_black_48.png differ diff --git a/core/res/res/drawable/spinner_black_76.png b/core/res/res/drawable/spinner_black_76.png new file mode 100644 index 0000000000000000000000000000000000000000..ec57460277a669e2623233a57ae57104024109c9 Binary files /dev/null and b/core/res/res/drawable/spinner_black_76.png differ diff --git a/core/res/res/drawable/progress_particle.png b/core/res/res/drawable/spinner_white_16.png similarity index 87% rename from core/res/res/drawable/progress_particle.png rename to core/res/res/drawable/spinner_white_16.png index 91601086918678421ead6276b506514263235ea0..dd2e1fd7da16d76dce2ffba5692e80ff202ba726 100644 Binary files a/core/res/res/drawable/progress_particle.png and b/core/res/res/drawable/spinner_white_16.png differ diff --git a/core/res/res/drawable/spinner_white_48.png b/core/res/res/drawable/spinner_white_48.png new file mode 100644 index 0000000000000000000000000000000000000000..d25a33e2453482616501841bc67f721e9dea35ce Binary files /dev/null and b/core/res/res/drawable/spinner_white_48.png differ diff --git a/core/res/res/drawable/spinner_white_76.png b/core/res/res/drawable/spinner_white_76.png new file mode 100644 index 0000000000000000000000000000000000000000..f53e8ffdb193c4e3f49c599956f043649e508e5b Binary files /dev/null and b/core/res/res/drawable/spinner_white_76.png differ diff --git a/core/res/res/drawable/stat_ecb_mode.png b/core/res/res/drawable/stat_ecb_mode.png new file mode 100644 index 0000000000000000000000000000000000000000..a948770f7dfd182ec5055c26c0a76b6518c215d1 Binary files /dev/null and b/core/res/res/drawable/stat_ecb_mode.png differ diff --git a/core/res/res/drawable/stat_sys_data_dormant_1xrtt.png b/core/res/res/drawable/stat_sys_data_dormant_1xrtt.png new file mode 100755 index 0000000000000000000000000000000000000000..11c2eaebb1ab1613278bffa3ddeb6b996e69c1e6 Binary files /dev/null and b/core/res/res/drawable/stat_sys_data_dormant_1xrtt.png differ diff --git a/core/res/res/drawable/stat_sys_data_dormant_evdo.png b/core/res/res/drawable/stat_sys_data_dormant_evdo.png new file mode 100755 index 0000000000000000000000000000000000000000..811fcb56f8ae74837d6b31f96bcb904d3c57e87e Binary files /dev/null and b/core/res/res/drawable/stat_sys_data_dormant_evdo.png differ diff --git a/core/res/res/drawable/stat_sys_roaming_cdma_0.png b/core/res/res/drawable/stat_sys_roaming_cdma_0.png new file mode 100755 index 0000000000000000000000000000000000000000..c61cce774ec7ee105197f5e2e390adc06872352d Binary files /dev/null and b/core/res/res/drawable/stat_sys_roaming_cdma_0.png differ diff --git a/core/res/res/drawable/stat_sys_roaming_cdma_flash.xml b/core/res/res/drawable/stat_sys_roaming_cdma_flash.xml new file mode 100644 index 0000000000000000000000000000000000000000..07dc4465ee32097788314f748f8ed35a3d35a931 --- /dev/null +++ b/core/res/res/drawable/stat_sys_roaming_cdma_flash.xml @@ -0,0 +1,25 @@ + + + + + + diff --git a/core/res/res/drawable/stat_sys_roaming_cdma_flash_anim0.png b/core/res/res/drawable/stat_sys_roaming_cdma_flash_anim0.png new file mode 100755 index 0000000000000000000000000000000000000000..d62502dcb78d27ddc91b8c97804b425dbc74d302 Binary files /dev/null and b/core/res/res/drawable/stat_sys_roaming_cdma_flash_anim0.png differ diff --git a/core/res/res/drawable/stat_sys_roaming_cdma_flash_anim1.png b/core/res/res/drawable/stat_sys_roaming_cdma_flash_anim1.png new file mode 100755 index 0000000000000000000000000000000000000000..c61cce774ec7ee105197f5e2e390adc06872352d Binary files /dev/null and b/core/res/res/drawable/stat_sys_roaming_cdma_flash_anim1.png differ diff --git a/core/res/res/drawable/stat_sys_signal_cdma_0.png b/core/res/res/drawable/stat_sys_signal_cdma_0.png new file mode 100755 index 0000000000000000000000000000000000000000..0ef7d534c3711c039d13ffec19c575592eb98868 Binary files /dev/null and b/core/res/res/drawable/stat_sys_signal_cdma_0.png differ diff --git a/core/res/res/drawable/stat_sys_signal_cdma_1.png b/core/res/res/drawable/stat_sys_signal_cdma_1.png new file mode 100755 index 0000000000000000000000000000000000000000..f4839d4954fea0a9d07718b2ede327e602821b13 Binary files /dev/null and b/core/res/res/drawable/stat_sys_signal_cdma_1.png differ diff --git a/core/res/res/drawable/stat_sys_signal_cdma_2.png b/core/res/res/drawable/stat_sys_signal_cdma_2.png new file mode 100755 index 0000000000000000000000000000000000000000..e25a99cff692649d46b275c0e1e8fd73d26b81d1 Binary files /dev/null and b/core/res/res/drawable/stat_sys_signal_cdma_2.png differ diff --git a/core/res/res/drawable/stat_sys_signal_cdma_3.png b/core/res/res/drawable/stat_sys_signal_cdma_3.png new file mode 100755 index 0000000000000000000000000000000000000000..d828d99a5b0875603558711ff70b7f21d6c9f71f Binary files /dev/null and b/core/res/res/drawable/stat_sys_signal_cdma_3.png differ diff --git a/core/res/res/drawable/stat_sys_signal_cdma_4.png b/core/res/res/drawable/stat_sys_signal_cdma_4.png new file mode 100755 index 0000000000000000000000000000000000000000..53a31ea89e90b65979f7847bd5366a99e06d6e66 Binary files /dev/null and b/core/res/res/drawable/stat_sys_signal_cdma_4.png differ diff --git a/core/res/res/drawable/stat_sys_signal_evdo_0.png b/core/res/res/drawable/stat_sys_signal_evdo_0.png new file mode 100755 index 0000000000000000000000000000000000000000..1b8aec7cfc806ba03a968847cf54c9eb1b765cac Binary files /dev/null and b/core/res/res/drawable/stat_sys_signal_evdo_0.png differ diff --git a/core/res/res/drawable/stat_sys_signal_evdo_1.png b/core/res/res/drawable/stat_sys_signal_evdo_1.png new file mode 100755 index 0000000000000000000000000000000000000000..7ce01fd96a1b3c57f252e842634baa40e2321a6d Binary files /dev/null and b/core/res/res/drawable/stat_sys_signal_evdo_1.png differ diff --git a/core/res/res/drawable/stat_sys_signal_evdo_2.png b/core/res/res/drawable/stat_sys_signal_evdo_2.png new file mode 100755 index 0000000000000000000000000000000000000000..890cd59669de310cab160ba0a705d80661126eb6 Binary files /dev/null and b/core/res/res/drawable/stat_sys_signal_evdo_2.png differ diff --git a/core/res/res/drawable/stat_sys_signal_evdo_3.png b/core/res/res/drawable/stat_sys_signal_evdo_3.png new file mode 100755 index 0000000000000000000000000000000000000000..712c6403bee0026e80058d5d7001622eedd36c4e Binary files /dev/null and b/core/res/res/drawable/stat_sys_signal_evdo_3.png differ diff --git a/core/res/res/drawable/stat_sys_signal_evdo_4.png b/core/res/res/drawable/stat_sys_signal_evdo_4.png new file mode 100755 index 0000000000000000000000000000000000000000..f0537ddd847259fb001ecc916ac8d0af87447aaa Binary files /dev/null and b/core/res/res/drawable/stat_sys_signal_evdo_4.png differ diff --git a/core/res/res/drawable/stat_sys_vp_phone_call_bluetooth.png b/core/res/res/drawable/stat_sys_vp_phone_call_bluetooth.png new file mode 100644 index 0000000000000000000000000000000000000000..7abfd194fb800bd11d62b8646b5fd4b3133eccae Binary files /dev/null and b/core/res/res/drawable/stat_sys_vp_phone_call_bluetooth.png differ diff --git a/core/res/res/layout/character_picker.xml b/core/res/res/layout/character_picker.xml index bb4955a6c71c9af0ff908dc15b595892db89fa31..03448497f5fdd095a608c175aaa2f60c39975228 100644 --- a/core/res/res/layout/character_picker.xml +++ b/core/res/res/layout/character_picker.xml @@ -23,8 +23,8 @@ android:id="@+id/characterPicker" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:padding="12dp" - android:verticalSpacing="8dp" + android:padding="4dp" + android:verticalSpacing="4dp" android:horizontalSpacing="8dp" android:stretchMode="spacingWidth" android:gravity="left" diff --git a/core/res/res/layout/google_web_content_helper_layout.xml b/core/res/res/layout/google_web_content_helper_layout.xml index 40f84bf8f3da729fce72dc0a8f34abe967940c12..546c4586bcc25c3fbe779d8164c79abf616fd337 100644 --- a/core/res/res/layout/google_web_content_helper_layout.xml +++ b/core/res/res/layout/google_web_content_helper_layout.xml @@ -18,10 +18,28 @@ android:foregroundGravity="center" android:measureAllChildren="false"> - - + + + + + + + + + diff --git a/core/res/res/layout/progress_dialog.xml b/core/res/res/layout/progress_dialog.xml index 2d7afd60ee16cd93de142794b2d32de78aadc675..8f66451e4b3edfdd27d3f3f8359f855da9ee02f0 100644 --- a/core/res/res/layout/progress_dialog.xml +++ b/core/res/res/layout/progress_dialog.xml @@ -33,6 +33,7 @@ android:paddingBottom="10dip"> - - + android:layout_height="wrap_content" + android:padding="3dip" + android:orientation="vertical"> + + + + + + + + + + + + + + + + - - - - - - - + android:orientation="horizontal" > + + + + + + - - - - - - - - - - - - - - - - - - + \ No newline at end of file diff --git a/core/res/res/layout/recent_apps_icon.xml b/core/res/res/layout/recent_apps_icon.xml index b8cf089646972ddb6cd4e28886231bc30086f949..d32643cb15ddfcbf7d3806c0d74ba93f0761aae1 100644 --- a/core/res/res/layout/recent_apps_icon.xml +++ b/core/res/res/layout/recent_apps_icon.xml @@ -18,27 +18,22 @@ --> - - - - - + android:textColor="@color/primary_text_dark_focused" + + android:paddingTop="5dip" + android:paddingBottom="2dip" + android:drawablePadding="0dip" + + android:textSize="13dip" + android:maxLines="2" + android:ellipsize="marquee" + android:fadingEdge="horizontal" + android:gravity="top|center_horizontal" /> diff --git a/core/res/res/layout/search_bar.xml b/core/res/res/layout/search_bar.xml index b5124904e4279fae60ca5449af78fff664222f94..13e66aad0d258ace963564e2856b7977998a9eeb 100644 --- a/core/res/res/layout/search_bar.xml +++ b/core/res/res/layout/search_bar.xml @@ -71,9 +71,12 @@ android:layout_weight="1.0" android:paddingLeft="8dip" android:paddingRight="6dip" + android:drawablePadding="2dip" android:singleLine="true" + android:ellipsize="end" android:inputType="text|textAutoComplete" android:dropDownWidth="fill_parent" + android:dropDownHeight="fill_parent" android:dropDownAnchor="@id/search_plate" android:dropDownVerticalOffset="-9dip" android:popupBackground="@android:drawable/search_dropdown_background" diff --git a/core/res/res/layout/search_dropdown_item_icons_2line.xml b/core/res/res/layout/search_dropdown_item_icons_2line.xml index 0d074909361e1abf91cb57bc3057c2dc6c9644b4..2710b3bffcb68b40642788ddbb27750777176094 100644 --- a/core/res/res/layout/search_dropdown_item_icons_2line.xml +++ b/core/res/res/layout/search_dropdown_item_icons_2line.xml @@ -67,13 +67,10 @@ android:textAppearance="?android:attr/textAppearanceSearchResultTitle" android:singleLine="true" android:layout_width="fill_parent" - android:layout_height="29dip" - android:paddingTop="4dip" - android:gravity="center_vertical" - android:layout_alignParentTop="true" + android:layout_height="wrap_content" + android:layout_centerVertical="true" android:layout_toRightOf="@android:id/icon1" android:layout_toLeftOf="@android:id/icon2" - android:layout_above="@android:id/text2" - android:layout_alignWithParentIfMissing="true" /> + android:layout_above="@android:id/text2" /> diff --git a/core/res/res/values-ar-rEG/donottranslate-cldr.xml b/core/res/res/values-ar-rEG/donottranslate-cldr.xml new file mode 100644 index 0000000000000000000000000000000000000000..2c20ffcdf38efda6d85988f453439d083b7827cd --- /dev/null +++ b/core/res/res/values-ar-rEG/donottranslate-cldr.xml @@ -0,0 +1,147 @@ + + + يناير + فبراير + مارس + أبريل + مايو + يونيو + يوليو + أغسطس + سبتمبر + أكتوبر + نوفمبر + ديسمبر + + يناير + فبراير + مارس + أبريل + مايو + يونيو + يوليو + أغسطس + سبتمبر + أكتوبر + نوفمبر + ديسمبر + + يناير + فبراير + مارس + أبريل + مايو + يونيو + يوليو + أغسطس + سبتمبر + أكتوبر + نوفمبر + ديسمبر + + ي + ف + م + أ + و + ن + ل + غ + س + ك + ب + د + + الأحد + الإثنين + الثلاثاء + الأربعاء + الخميس + الجمعة + السبت + + أحد + إثنين + ثلاثاء + أربعاء + خميس + جمعة + سبت + + أحد + إثنين + ثلاثاء + أربعاء + خميس + جمعة + سبت + + ح + ن + ث + ر + خ + ج + س + + ص + م + أمس + اليوم + غدًا + + %-k:%M + %-l:%M %p + %-l:%M %p + h:mm a + H:mm + %-e‏/%-m‏/%Y + d‏/M‏/yyyy + "%s‏/%s‏/%s" + %-e %B، %Y + %-l:%M:%S %p + %-l:%M:%S %p %d‏/%m‏/%Y + %2$s %1$s + %1$s %3$s + %d‏/%m‏/%Y + %-e %B + %-B + %B %Y + %-e %b + %-b + %b %Y + %1$s – %2$s + %2$s – %5$s + %3$s‏/%2$s - %8$s‏/%7$s + %1$s، %3$s/‏%2$s - %6$s، %8$s/‏%7$s + %3$s‏/%2$s‏/%4$s - %8$s‏/%7$s‏/%9$s + %1$s، %3$s‏/%2$s‏/%4$s - %6$s، %8$s‏/%7$s‏/%9$s + %5$s %1$s، %3$s/‏%2$s/‏%4$s – %10$s %6$s، %8$s/‏%7$s/‏%9$s + %5$s %3$s/‏%2$s – %10$s %8$s/‏%7$s + %5$s %1$s، %3$s-%2$s – %10$s %6$s، %8$s-%7$s + %5$s %3$s‏/%2$s‏/%4$s – %10$s %8$s‏/%7$s‏/%9$s + %3$s %1$s %2$s – %6$s %4$s %5$s + %1$s %2$s – %4$s %5$s + %3$s %2$s – %6$s %5$s + %1$s %2$s %3$s + %2$s %3$s + %1$s %2$s + %3$s %2$s – %8$s %7$s + %1$s %3$s %2$s – %6$s %8$s %7$s + %5$s %3$s %2$s – %10$s %8$s %7$s + %5$s %3$s %2$s – %10$s %8$s %7$s + %5$s %1$s %3$s %2$s – %10$s %6$s %8$s %7$s + %5$s %1$s %3$s %2$s – %10$s %6$s %8$s %7$s + %5$s %3$s %2$s، %4$s – %10$s %8$s %7$s، %9$s + %5$s %3$s %2$s، %4$s – %10$s %8$s %7$s، %9$s + %5$s %1$s، %3$s %2$s %4$s – %10$s %6$s، %8$s %7$s %9$s + %5$s %1$s، %3$s %2$s %4$s – %10$s %6$s، %8$s %7$s %9$s + %1$s، %3$s %2$s %4$s – %6$s، %8$s %7$s %9$s + %3$s-%8$s %2$s + %1$s %3$s %2$s – %6$s %8$s %7$s + %3$s %2$s - %8$s %7$s، %9$s + %3$s-%8$s %2$s، %9$s + %1$s، %3$s %2$s - %6$s، %8$s %7$s، %9$s + %b + diff --git a/core/res/res/values-bg-rBG/donottranslate-cldr.xml b/core/res/res/values-bg-rBG/donottranslate-cldr.xml new file mode 100644 index 0000000000000000000000000000000000000000..b8b50cccdd789e685afd25e6707f3a39ecc394bb --- /dev/null +++ b/core/res/res/values-bg-rBG/donottranslate-cldr.xml @@ -0,0 +1,147 @@ + + + януари + февруари + март + април + май + юни + юли + август + септември + октомври + ноември + декември + + януари + февруари + март + април + май + юни + юли + август + септември + октомври + ноември + декември + + ян. + февр. + март + апр. + май + юни + юли + авг. + септ. + окт. + ноем. + дек. + + я + ф + м + а + м + ю + ю + а + с + о + н + д + + неделя + понеделник + вторник + сряда + четвъртък + петък + събота + + нд + пн + вт + ср + чт + пт + сб + + нд + пн + вт + ср + чт + пт + сб + + н + п + в + с + ч + п + с + + пр. об. + сл. об. + Вчера + Днес + Утре + + %-k:%M + %-l:%M %p + %-l:%M %p + h:mm a + H:mm + %d.%m.%Y + dd.MM.yyyy + "%s.%s.%s" + %d %B %Y + %H:%M:%S + %H:%M:%S %d.%m.%Y + %2$s %1$s + %1$s %3$s + %d.%m.%Y + %-e %B + %-B + %B %Y + %-e %b + %b + %b %Y + %1$s - %2$s + %2$s - %5$s + %3$s.%2$s - %8$s.%7$s + %3$s.%2$s, %1$s - %8$s.%7$s, %6$s + %3$s.%2$s.%4$s - %8$s.%7$s.%9$s + %3$s.%2$s.%4$s, %1$s - %8$s.%7$s.%9$s, %6$s + %5$s %3$s.%2$s.%4$s, %1$s - %10$s %8$s.%7$s.%9$s, %6$s + %5$s %3$s.%2$s - %10$s %8$s.%7$s + %5$s %3$s.%2$s, %1$s - %10$s %8$s.%7$s, %6$s + %5$s %3$s.%2$s.%4$s - %10$s %8$s.%7$s.%9$s + %3$s %2$s, %1$s - %6$s %5$s, %4$s + %2$s, %1$s - %5$s, %4$s + %3$s %2$s - %6$s %5$s + %1$s %3$s, %2$s + %3$s, %2$s + %1$s %2$s + %3$s %2$s - %8$s %7$s + %3$s %2$s, %1$s - %8$s %7$s, %6$s + %5$s %3$s %2$s - %10$s %8$s %7$s + %5$s %3$s %2$s - %10$s %8$s %7$s + %5$s %3$s %2$s, %1$s - %10$s %8$s %7$s, %6$s + %5$s %3$s %2$s, %1$s - %10$s %8$s %7$s, %6$s + %5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s + %5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s + %5$s %3$s %2$s %4$s, %1$s - %10$s %8$s %7$s %9$s, %6$s + %5$s %3$s %2$s %4$s, %1$s - %10$s %8$s %7$s %9$s, %6$s + %3$s %2$s %4$s, %1$s - %8$s %7$s %9$s, %6$s + %3$s-%8$s %2$s + %3$s %2$s, %1$s - %8$s %7$s, %6$s + %3$s %2$s - %8$s %7$s %9$s + %3$s-%8$s %2$s %9$s + %3$s %2$s %9$s, %1$s - %8$s %7$s y, %6$s + %b + diff --git a/core/res/res/values-ca-rES/donottranslate-cldr.xml b/core/res/res/values-ca-rES/donottranslate-cldr.xml new file mode 100644 index 0000000000000000000000000000000000000000..d5abeef1a96a0713cce2c202791d0fef8ffcaeb9 --- /dev/null +++ b/core/res/res/values-ca-rES/donottranslate-cldr.xml @@ -0,0 +1,147 @@ + + + gener + febrer + març + abril + maig + juny + juliol + agost + setembre + octubre + novembre + desembre + + gener + febrer + març + abril + maig + juny + juliol + agost + setembre + octubre + novembre + desembre + + gen. + febr. + març + abr. + maig + juny + jul. + ag. + set. + oct. + nov. + des. + + g + f + m + a + m + j + j + a + s + o + n + d + + diumenge + dilluns + dimarts + dimecres + dijous + divendres + dissabte + + dg. + dl. + dt. + dc. + dj. + dv. + ds. + + dg. + dl. + dt. + dc. + dj. + dv. + ds. + + g + l + t + c + j + v + s + + a.m. + p.m. + ahir + avui + demà + + %-k:%M + %-l:%M %p + %-l:%M %^p + h:mm a + H:mm + %d/%m/%Y + dd/MM/yyyy + "%s/%s/%s" + %-e de %B de %Y + %-k:%M:%S + %-k:%M:%S %d/%m/%Y + %2$s %1$s + %1$s %3$s + %d/%m/%Y + %-e de %B + %-B + %-B del %Y + %-e %b + %-b + %b %Y + %1$s - %2$s + %2$s - %5$s + %3$s/%2$s - %8$s/%7$s + %1$s %3$s/%2$s - %6$s %8$s/%7$s + %3$s/%2$s/%4$s - %8$s/%7$s/%9$s + %1$s %3$s/%2$s/%4$s - %6$s %8$s/%7$s/%9$s + %5$s %1$s %3$s/%2$s/%4$s - %10$s %6$s %8$s/%7$s/%9$s + %5$s %3$s/%2$s - %10$s %8$s/%7$s + %5$s %1$s %3$s/%2$s - %10$s %6$s %8$s/%7$s + %5$s %3$s/%2$s/%4$s - %10$s %8$s/%7$s/%9$s + %3$s %1$s %2$s - %6$s %4$s %5$s + %1$s %2$s - %4$s %5$s + %3$s %2$s - %6$s %5$s + %1$s %2$s %3$s + %2$s %3$s + %1$s %2$s + %3$s de %2$s - %8$s de %7$s + %1$s %3$s %2$s - %6$s %8$s %7$s + %5$s %3$s de %2$s - %10$s %8$s de %7$s + %5$s %3$s de %2$s - %10$s %8$s de %7$s + %5$s %1$s %3$s %2$s - %10$s %6$s %8$s %7$s + %5$s %1$s %3$s %2$s - %10$s %6$s %8$s %7$s + %5$s %3$s de %2$s de %4$s - %10$s %8$s de %7$s de %9$s + %5$s %3$s de %2$s de %4$s - %10$s %8$s de %7$s de %9$s + %5$s %1$s %3$s %2$s %4$s - %10$s %6$s %8$s %7$s %9$s + %5$s %1$s %3$s %2$s %4$s - %10$s %6$s %8$s %7$s %9$s + %1$s %3$s %2$s %4$s - %6$s %8$s %7$s %9$s + %3$s-%8$s de %2$s + %1$s %3$s %2$s - %6$s %8$s %7$s + %3$s de %2$s - %8$s de %7$s de %9$s + %3$s-%8$s de %2$s de %9$s + %1$s %3$s de %2$s - %6$s %8$s de %7$s de %9$s + %b + diff --git a/core/res/res/values-cs-rCZ/donottranslate-cldr.xml b/core/res/res/values-cs-rCZ/donottranslate-cldr.xml new file mode 100644 index 0000000000000000000000000000000000000000..41f5dea81f009c95d8fd46fb6d397bd5b16122b1 --- /dev/null +++ b/core/res/res/values-cs-rCZ/donottranslate-cldr.xml @@ -0,0 +1,147 @@ + + + leden + únor + březen + duben + květen + červen + červenec + srpen + září + říjen + listopad + prosinec + + ledna + února + března + dubna + května + června + července + srpna + září + října + listopadu + prosince + + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + + l + ú + b + d + k + č + č + s + z + ř + l + p + + neděle + pondělí + úterý + středa + čtvrtek + pátek + sobota + + ne + po + út + st + čt + + so + + ne + po + út + st + čt + + so + + N + P + Ú + S + Č + P + S + + dop. + odp. + Včera + Dnes + Zítra + + %-k:%M + %-l:%M %p + %-l:%M %^p + h:mm a + H:mm + %-e.%-m.%Y + d.M.yyyy + "%s.%s.%s" + %-e. %B %Y + %-k:%M:%S + %-k:%M:%S %-e.%-m.%Y + %2$s %1$s + %1$s %3$s + %-e.%-m.%Y + %-e. %B + %-B + %-B %Y + %-e.%-m + %-B + %-B %Y + %1$s - %2$s + %2$s - %5$s + %3$s.%2$s. - %8$s.%7$s. + %1$s, %3$s.%2$s. - %6$s, %8$s.%7$s. + %3$s.%2$s.%4$s - %8$s.%7$s.%9$s + %1$s, %3$s.%2$s.%4$s - %6$s, %8$s.%7$s.%9$s + %5$s %1$s, %3$s.%2$s.%4$s - %10$s %6$s, %8$s.%7$s.%9$s + %5$s %3$s.%2$s - %10$s %8$s.%7$s + %5$s %1$s, %3$s.%2$s. - %10$s %6$s, %8$s.%7$s. + %5$s %3$s.%2$s.%4$s - %10$s %8$s.%7$s.%9$s + %3$s %1$s, %2$s - %6$s %4$s, %5$s + %1$s, %2$s - %4$s, %5$s + %3$s %2$s - %6$s %5$s + %1$s %2$s, %3$s + %2$s, %3$s + %1$s %2$s + %3$s. %2$s - %8$s. %7$s + %1$s, %3$s. %2$s - %6$s, %8$s. %7$s + %5$s %3$s. %2$s - %10$s %8$s. %7$s + %5$s %3$s. %2$s - %10$s %8$s. %7$s + %5$s %1$s, %3$s. %2$s - %10$s %6$s, %8$s. %7$s + %5$s %1$s, %3$s. %2$s - %10$s %6$s, %8$s. %7$s + %5$s %3$s. %2$s %4$s - %10$s %8$s. %7$s %9$s + %5$s %3$s. %2$s %4$s - %10$s %8$s. %7$s %9$s + %5$s %1$s, %3$s. %2$s %4$s - %10$s %6$s, %8$s. %7$s %9$s + %5$s %1$s, %3$s. %2$s %4$s - %10$s %6$s, %8$s. %7$s %9$s + %1$s, %3$s. %2$s %4$s - %6$s, %8$s. %7$s %9$s + %3$s.-%8$s. %2$s + %1$s, %3$s. %2$s - %6$s, %8$s. %7$s + %3$s. %2$s - %8$s. %7$s %9$s + %3$s.-%8$s. %2$s %9$s + %1$s, %3$s. %2$s - %6$s, %8$s. %7$s %9$s + %B + diff --git a/core/res/res/values-cs/donottranslate-cldr.xml b/core/res/res/values-cs/donottranslate-cldr.xml new file mode 100644 index 0000000000000000000000000000000000000000..41f5dea81f009c95d8fd46fb6d397bd5b16122b1 --- /dev/null +++ b/core/res/res/values-cs/donottranslate-cldr.xml @@ -0,0 +1,147 @@ + + + leden + únor + březen + duben + květen + červen + červenec + srpen + září + říjen + listopad + prosinec + + ledna + února + března + dubna + května + června + července + srpna + září + října + listopadu + prosince + + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + + l + ú + b + d + k + č + č + s + z + ř + l + p + + neděle + pondělí + úterý + středa + čtvrtek + pátek + sobota + + ne + po + út + st + čt + + so + + ne + po + út + st + čt + + so + + N + P + Ú + S + Č + P + S + + dop. + odp. + Včera + Dnes + Zítra + + %-k:%M + %-l:%M %p + %-l:%M %^p + h:mm a + H:mm + %-e.%-m.%Y + d.M.yyyy + "%s.%s.%s" + %-e. %B %Y + %-k:%M:%S + %-k:%M:%S %-e.%-m.%Y + %2$s %1$s + %1$s %3$s + %-e.%-m.%Y + %-e. %B + %-B + %-B %Y + %-e.%-m + %-B + %-B %Y + %1$s - %2$s + %2$s - %5$s + %3$s.%2$s. - %8$s.%7$s. + %1$s, %3$s.%2$s. - %6$s, %8$s.%7$s. + %3$s.%2$s.%4$s - %8$s.%7$s.%9$s + %1$s, %3$s.%2$s.%4$s - %6$s, %8$s.%7$s.%9$s + %5$s %1$s, %3$s.%2$s.%4$s - %10$s %6$s, %8$s.%7$s.%9$s + %5$s %3$s.%2$s - %10$s %8$s.%7$s + %5$s %1$s, %3$s.%2$s. - %10$s %6$s, %8$s.%7$s. + %5$s %3$s.%2$s.%4$s - %10$s %8$s.%7$s.%9$s + %3$s %1$s, %2$s - %6$s %4$s, %5$s + %1$s, %2$s - %4$s, %5$s + %3$s %2$s - %6$s %5$s + %1$s %2$s, %3$s + %2$s, %3$s + %1$s %2$s + %3$s. %2$s - %8$s. %7$s + %1$s, %3$s. %2$s - %6$s, %8$s. %7$s + %5$s %3$s. %2$s - %10$s %8$s. %7$s + %5$s %3$s. %2$s - %10$s %8$s. %7$s + %5$s %1$s, %3$s. %2$s - %10$s %6$s, %8$s. %7$s + %5$s %1$s, %3$s. %2$s - %10$s %6$s, %8$s. %7$s + %5$s %3$s. %2$s %4$s - %10$s %8$s. %7$s %9$s + %5$s %3$s. %2$s %4$s - %10$s %8$s. %7$s %9$s + %5$s %1$s, %3$s. %2$s %4$s - %10$s %6$s, %8$s. %7$s %9$s + %5$s %1$s, %3$s. %2$s %4$s - %10$s %6$s, %8$s. %7$s %9$s + %1$s, %3$s. %2$s %4$s - %6$s, %8$s. %7$s %9$s + %3$s.-%8$s. %2$s + %1$s, %3$s. %2$s - %6$s, %8$s. %7$s + %3$s. %2$s - %8$s. %7$s %9$s + %3$s.-%8$s. %2$s %9$s + %1$s, %3$s. %2$s - %6$s, %8$s. %7$s %9$s + %B + diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml index 35a3f9a9cb506768d55f7ff26ee4d746221fc315..7dbeaebe5d72ca6fe4bc22afe25651bdfad882a6 100644 --- a/core/res/res/values-cs/strings.xml +++ b/core/res/res/values-cs/strings.xml @@ -161,14 +161,10 @@ "Umožňuje aplikaci změnit aktuální konfiguraci, např. národní prostředí či obecnou velikost písma." "restartování ostatních aplikací" "Umožňuje aplikaci vynutit restartování jiných aplikací." - "zamezení zastavení aplikace" - "Umožňuje aplikaci spustit jakýkoli proces v popředí tak, že ho nelze ukončit. Běžné aplikace by toto nastavení nikdy neměly používat." "vynucení zavření aplikace" "Umožňuje aplikaci vynutit zavření a přesunutí libovolné činnosti v popředí na pozadí. Běžné aplikace by toto nastavení neměly nikdy využívat." "načtení interního stavu systému" "Umožňuje aplikaci načíst interní stav systému. Škodlivé aplikace mohou načíst řádu soukromých a zabezpečených informací, které by nikdy neměly potřebovat." - "zveřejnění nízkoúrovňových služeb" - "Umožňuje aplikaci zveřejnit své vlastní nízkoúrovňové systémové služby. Škodlivé aplikace mohou převzít kontrolu nad systémem a získat či poškodit jakákoli data v něm obsažená." "sledování a řízení spouštění všech aplikací" "Umožňuje aplikaci sledovat a řídit spouštění činností systémem. Škodlivé aplikace mohou zcela ovládnout systém. Toto oprávnění je zapotřebí pouze pro účely vývoje, nikdy pro běžné použití telefonu." "odeslání vysílání o odstranění balíčku" @@ -181,8 +177,6 @@ "Umožňuje aplikaci řídit maximální počet spuštěných procesů. Běžné aplikace toto nastavení nikdy nevyužívají." "zavření všech aplikací na pozadí" "Umožňuje aplikaci ovládat, zda jsou činnosti vždy dokončeny po přesunutí do pozadí. Běžné aplikace toto nastavení nikdy nevyužívají." - "automatická instalace aktualizací systému" - "Umožňuje aplikaci přijímat oznámení o čekajících aktualizacích systému a spouštět jejich instalaci. Škodlivé aplikace mohou díky tomuto nastavení poškodit systém pomocí neoprávněných aktualizací nebo celkově narušovat proces aktualizace." "změna statistických údajů o baterii" "Umožňuje změnu shromážděných statistických údajů o baterii. Není určeno pro běžné aplikace." "zobrazení nepovolených oken" @@ -418,9 +412,6 @@ "Heslo" "Přihlásit se" "Neplatné uživatelské jméno nebo heslo." - "h:mm AA" - "%-l:%M%P" - "%-l:%M%p" "%-l%P" "%-l%p" "Vymazat oznámení" @@ -452,9 +443,6 @@ "enter" "smazat" "Hledat" - "Dnes" - "Včera" - "Zítra" "před 1 měsícem" "Déle než před 1 měsícem" @@ -536,13 +524,6 @@ "týd." "rokem" "lety" - "neděle" - "pondělí" - "úterý" - "středa" - "čtvrtek" - "pátek" - "sobota" "Každý pracovní den (Po – Pá)" "Denně" "Každý týden v %s" @@ -552,137 +533,15 @@ "Omlouváme se, ale toto video nelze přenášet datovým proudem do tohoto zařízení." "Toto video bohužel nelze přehrát." "OK" - "dop." - "odp." - "%m/%d/%Y" - "%1$s, %2$s, %3$s%4$s, %5$s, %6$s" - "%1$s, %2$s%4$s, %5$s" - "%2$s, %3$s%5$s, %6$s" - "%2$s%5$s" - "%1$s%2$s" - "%1$s, %2$s, %3$s" - "%2$s, %3$s" - "%1$s, %3$s" - "%1$s, %2$s" "%1$s, %2$s" - "%1$s, %2$s" - "MMMM' 'd', 'yyyy" - "d'. 'MMMM' 'yyyy" - "d'. 'MMM' 'yyyy" - "d'. 'MMM' 'yyyy" - "h':'mm' 'a" - "HH':'mm" "poledne" "Poledne" "půlnoc" "Půlnoc" - "%B %-d" - "%B %-d, %Y" - "%B %Y" - "%H:%M:%S" - "%H:%M:%S %-d. %B %Y" - "%3$s. %2$s%8$s. %7$s" - "%1$s, %3$s. %2$s%6$s, %8$s. %7$s" - "%3$s. %2$s%8$s. %7$s %9$s" - "%1$s, %3$s. %2$s%6$s, %8$s. %7$s %9$s" - "%3$s. %2$s, %5$s%8$s. %7$s, %10$s" - "%1$s, %3$s. %2$s, %5$s%6$s, %8$s. %7$s, %10$s" - "%3$s. %2$s %4$s, %5$s%8$s. %7$s %9$s, %10$s" - "%1$s, %3$s. %2$s %4$s, %5$s%6$s, %8$s. %7$s %9$s, %10$s" - "%3$s. %2$s. – %8$s. %7$s." - "%1$s, %3$s. %2$s%6$s, %8$s. %7$s" - "%3$s. %2$s. %4$s%8$s. %7$s. %9$s" - "%1$s, %3$s. %2$s %4$s%6$s, %8$s. %7$s %9$s" - "%3$s. %2$s., %5$s%8$s. %7$s., %10$s" - "%1$s, %3$s. %2$s, %5$s%6$s, %8$s. %7$s, %10$s" - "%2$s/%3$s/%4$s, %5$s%7$s/%8$s/%9$s, %10$s" - "%1$s, %3$s. %2$s %4$s, %5$s%6$s, %8$s. %7$s %9$s, %10$s" - "%3$s. – %8$s. %2$s" - "%1$s, %3$s. %2$s%6$s, %8$s. %7$s" - "%3$s. – %8$s. %2$s %9$s" - "%1$s, %3$s. %2$s %4$s%6$s, %8$s. %7$s %9$s" - "%3$s. %2$s, %5$s%8$s. %7$s, %10$s" - "%1$s, %3$s. %2$s %5$s%6$s, %8$s. %7$s %10$s" - "%2$s %3$s, %4$s, %5$s%7$s %8$s, %9$s, %10$s" - "%1$s, %3$s. %2$s %4$s, %5$s%6$s, %8$s. %7$s %9$s, %10$s" - "%-d. %b %Y" - "%b %Y" - "%b %-d" - "neděle" - "pondělí" - "úterý" - "středa" - "čtvrtek" - "pátek" - "sobota" - "Ne" - "Po" - "Út" - "St" - "Čt" - "Pá" - "So" - "Ne" - "Po" - "Út" - "St" - "Čt" - "Pá" - "So" - "Ne" - "Po" - "Út" - "St" - "Čt" - "Pá" - "So" - "Ne" - "Po" - "Čt" - "St" - "Čt" - "Pá" - "So" - "leden" - "únor" - "březen" - "duben" - "květen" - "červen" - "červenec" - "srpen" - "září" - "říjen" - "listopad" - "prosinec" - "leden" - "únor" - "březen" - "duben" - "květen" - "červen" - "červenec" - "srpen" - "září" - "říjen" - "listopad" - "prosinec" - "1." - "2." - "Po" - "4." - "5." - "6." - "7." - "8." - "9." - "10." - "11." - "12." "%1$02d:%2$02d" "%1$d:%2$02d:%3$02d" "Vybrat vše" diff --git a/core/res/res/values-da-rDK/donottranslate-cldr.xml b/core/res/res/values-da-rDK/donottranslate-cldr.xml new file mode 100644 index 0000000000000000000000000000000000000000..2d0db938a69f7517dde9f5da73f04cea3732cbbb --- /dev/null +++ b/core/res/res/values-da-rDK/donottranslate-cldr.xml @@ -0,0 +1,147 @@ + + + januar + februar + marts + april + maj + juni + juli + august + september + oktober + november + december + + januar + februar + marts + april + maj + juni + juli + august + september + oktober + november + december + + jan. + feb. + mar. + apr. + maj + jun. + jul. + aug. + sep. + okt. + nov. + dec. + + J + F + M + A + M + J + J + A + S + O + N + D + + søndag + mandag + tirsdag + onsdag + torsdag + fredag + lørdag + + søn + man + tir + ons + tor + fre + lør + + søn + man + tir + ons + tor + fre + lør + + S + M + T + O + T + F + L + + f.m. + e.m. + i går + i dag + i morgen + + %H.%M + %-l.%M %p + %-l.%M %^p + h.mm a + HH.mm + %d/%m/%Y + dd/MM/yyyy + "%s/%s/%s" + %-e. %b %Y + %H.%M.%S + %H.%M.%S %d/%m/%Y + %2$s %1$s + %1$s %3$s + %d/%m/%Y + %-e. %B + %B + %B %Y + %-e. %b + %b + %b %Y + %1$s - %2$s + %2$s - %5$s + %3$s/%2$s - %8$s/%7$s + %1$s %3$s/%2$s - %6$s %8$s/%7$s + %3$s/%2$s/%4$s - %8$s/%7$s/%9$s + %1$s %3$s/%2$s/%4$s - %6$s %8$s/%7$s/%9$s + %5$s %1$s. %3$s-%2$s-%4$s - %10$s %6$s. %8$s-%7$s-%9$s + %5$s %3$s/%2$s - %10$s %8$s/%7$s + %5$s %1$s. %3$s-%2$s - %10$s %6$s. %8$s-%7$s + %5$s %3$s/%2$s/%4$s - %10$s %8$s/%7$s/%9$s + %3$s %1$s den %2$s - %6$s %4$s den %5$s + %1$s den %2$s - %4$s den %5$s + %3$s %2$s - %6$s %5$s + %1$s %2$s den %3$s + %2$s den %3$s + %1$s %2$s + %3$s. %2$s - %8$s. %7$s + %1$s %3$s %2$s - %6$s %8$s %7$s + %5$s %3$s. %2$s - %10$s %8$s. %7$s + %5$s %3$s. %2$s - %10$s %8$s. %7$s + %5$s %1$s %3$s %2$s - %10$s %6$s %8$s %7$s + %5$s %1$s %3$s %2$s - %10$s %6$s %8$s %7$s + %5$s %3$s. %2$s %4$s - %10$s %8$s. %7$s %9$s + %5$s %3$s. %2$s %4$s - %10$s %8$s. %7$s %9$s + %5$s %1$s. %3$s. %2$s %4$s - %10$s %6$s. %8$s. %7$s %9$s + %5$s %1$s. %3$s. %2$s %4$s - %10$s %6$s. %8$s. %7$s %9$s + %1$s. %3$s. %2$s %4$s - %6$s. %8$s. %7$s %9$s + %3$s.-%8$s. %2$s + %1$s %3$s %2$s - %6$s %8$s %7$s + %3$s. %2$s - %8$s. %7$s %9$s + %3$s.-%8$s. %2$s %9$s + %1$s den %3$s. %2$s - %6$s den %8$s. %7$s %9$s + %b + diff --git a/core/res/res/values-da/donottranslate-cldr.xml b/core/res/res/values-da/donottranslate-cldr.xml new file mode 100644 index 0000000000000000000000000000000000000000..2d0db938a69f7517dde9f5da73f04cea3732cbbb --- /dev/null +++ b/core/res/res/values-da/donottranslate-cldr.xml @@ -0,0 +1,147 @@ + + + januar + februar + marts + april + maj + juni + juli + august + september + oktober + november + december + + januar + februar + marts + april + maj + juni + juli + august + september + oktober + november + december + + jan. + feb. + mar. + apr. + maj + jun. + jul. + aug. + sep. + okt. + nov. + dec. + + J + F + M + A + M + J + J + A + S + O + N + D + + søndag + mandag + tirsdag + onsdag + torsdag + fredag + lørdag + + søn + man + tir + ons + tor + fre + lør + + søn + man + tir + ons + tor + fre + lør + + S + M + T + O + T + F + L + + f.m. + e.m. + i går + i dag + i morgen + + %H.%M + %-l.%M %p + %-l.%M %^p + h.mm a + HH.mm + %d/%m/%Y + dd/MM/yyyy + "%s/%s/%s" + %-e. %b %Y + %H.%M.%S + %H.%M.%S %d/%m/%Y + %2$s %1$s + %1$s %3$s + %d/%m/%Y + %-e. %B + %B + %B %Y + %-e. %b + %b + %b %Y + %1$s - %2$s + %2$s - %5$s + %3$s/%2$s - %8$s/%7$s + %1$s %3$s/%2$s - %6$s %8$s/%7$s + %3$s/%2$s/%4$s - %8$s/%7$s/%9$s + %1$s %3$s/%2$s/%4$s - %6$s %8$s/%7$s/%9$s + %5$s %1$s. %3$s-%2$s-%4$s - %10$s %6$s. %8$s-%7$s-%9$s + %5$s %3$s/%2$s - %10$s %8$s/%7$s + %5$s %1$s. %3$s-%2$s - %10$s %6$s. %8$s-%7$s + %5$s %3$s/%2$s/%4$s - %10$s %8$s/%7$s/%9$s + %3$s %1$s den %2$s - %6$s %4$s den %5$s + %1$s den %2$s - %4$s den %5$s + %3$s %2$s - %6$s %5$s + %1$s %2$s den %3$s + %2$s den %3$s + %1$s %2$s + %3$s. %2$s - %8$s. %7$s + %1$s %3$s %2$s - %6$s %8$s %7$s + %5$s %3$s. %2$s - %10$s %8$s. %7$s + %5$s %3$s. %2$s - %10$s %8$s. %7$s + %5$s %1$s %3$s %2$s - %10$s %6$s %8$s %7$s + %5$s %1$s %3$s %2$s - %10$s %6$s %8$s %7$s + %5$s %3$s. %2$s %4$s - %10$s %8$s. %7$s %9$s + %5$s %3$s. %2$s %4$s - %10$s %8$s. %7$s %9$s + %5$s %1$s. %3$s. %2$s %4$s - %10$s %6$s. %8$s. %7$s %9$s + %5$s %1$s. %3$s. %2$s %4$s - %10$s %6$s. %8$s. %7$s %9$s + %1$s. %3$s. %2$s %4$s - %6$s. %8$s. %7$s %9$s + %3$s.-%8$s. %2$s + %1$s %3$s %2$s - %6$s %8$s %7$s + %3$s. %2$s - %8$s. %7$s %9$s + %3$s.-%8$s. %2$s %9$s + %1$s den %3$s. %2$s - %6$s den %8$s. %7$s %9$s + %b + diff --git a/core/res/res/values-de-rAT/donottranslate-cldr.xml b/core/res/res/values-de-rAT/donottranslate-cldr.xml new file mode 100644 index 0000000000000000000000000000000000000000..27624a36ab42659720bd27899edf48020c27f06d --- /dev/null +++ b/core/res/res/values-de-rAT/donottranslate-cldr.xml @@ -0,0 +1,147 @@ + + + Jänner + Februar + März + April + Mai + Juni + Juli + August + September + Oktober + November + Dezember + + Jänner + Februar + März + April + Mai + Juni + Juli + August + September + Oktober + November + Dezember + + Jän + Feb + Mär + Apr + Mai + Jun + Jul + Aug + Sep + Okt + Nov + Dez + + J + F + M + A + M + J + J + A + S + O + N + D + + Sonntag + Montag + Dienstag + Mittwoch + Donnerstag + Freitag + Samstag + + So. + Mo. + Di. + Mi. + Do. + Fr. + Sa. + + So. + Mo. + Di. + Mi. + Do. + Fr. + Sa. + + S + M + D + M + D + F + S + + vorm. + nachm. + Gestern + Heute + Morgen + + %-k:%M + %-l:%M %p + %-l:%M %^p + h:mm a + H:mm + %d.%m.%Y + dd.MM.yyyy + "%s.%s.%s" + %d. %B %Y + %H:%M:%S + %H:%M:%S %d.%m.%Y + %2$s %1$s + %1$s %3$s + %d.%m.%Y + %-e. %B + %-B + %B %Y + %d. %b + %-b + %b %Y + %1$s - %2$s + %2$s - %5$s + %3$s.%2$s. - %8$s.%7$s. + %1$s, %3$s.%2$s. - %6$s, %8$s.%7$s. + %3$s.%2$s.%4$s - %8$s.%7$s.%9$s + %1$s, %3$s.%2$s.%4$s - %6$s, %8$s.%7$s.%9$s + %5$s %1$s, %3$s.%2$s.%4$s - %10$s %6$s, %8$s.%7$s.%9$s + %5$s %3$s.%2$s. - %10$s %8$s.%7$s. + %5$s %1$s, %3$s.%2$s. - %10$s %6$s, %8$s.%7$s. + %5$s %3$s.%2$s.%4$s - %10$s %8$s.%7$s.%9$s + %3$s %1$s, %2$s - %6$s %4$s, %5$s + %1$s, %2$s - %4$s, %5$s + %3$s %2$s - %6$s %5$s + %1$s %2$s, %3$s + %2$s, %3$s + %1$s %2$s + %3$s. %2$s - %8$s. %7$s + %1$s, %3$s. %2$s - %6$s, %8$s. %7$s + %5$s %3$s. %2$s - %10$s %8$s. %7$s + %5$s %3$s. %2$s - %10$s %8$s. %7$s + %5$s %1$s, %3$s. %2$s - %10$s %6$s, %8$s. %7$s + %5$s %1$s, %3$s. %2$s - %10$s %6$s, %8$s. %7$s + %5$s %3$s. %2$s %4$s - %10$s %8$s. %7$s %9$s + %5$s %3$s. %2$s %4$s - %10$s %8$s. %7$s %9$s + %5$s %1$s, %3$s. %2$s %4$s - %10$s %6$s, %8$s. %7$s %9$s + %5$s %1$s, %3$s. %2$s %4$s - %10$s %6$s, %8$s. %7$s %9$s + %1$s, %3$s. %2$s %4$s - %6$s, %8$s. %7$s %9$s + %3$s.-%8$s. %2$s + %1$s, %3$s. %2$s - %6$s, %8$s. %7$s + %3$s. %2$s - %8$s. %7$s %9$s + %3$s.-%8$s. %2$s %9$s + %1$s, %3$s. %2$s - %6$s, %8$s. %7$s %9$s + %b + diff --git a/core/res/res/values-de-rCH/donottranslate-cldr.xml b/core/res/res/values-de-rCH/donottranslate-cldr.xml new file mode 100644 index 0000000000000000000000000000000000000000..f32095bd336e5d2e3ebfc2acc6aaa6732a5bdc57 --- /dev/null +++ b/core/res/res/values-de-rCH/donottranslate-cldr.xml @@ -0,0 +1,147 @@ + + + Januar + Februar + März + April + Mai + Juni + Juli + August + September + Oktober + November + Dezember + + Januar + Februar + März + April + Mai + Juni + Juli + August + September + Oktober + November + Dezember + + Jan + Feb + Mär + Apr + Mai + Jun + Jul + Aug + Sep + Okt + Nov + Dez + + J + F + M + A + M + J + J + A + S + O + N + D + + Sonntag + Montag + Dienstag + Mittwoch + Donnerstag + Freitag + Samstag + + So. + Mo. + Di. + Mi. + Do. + Fr. + Sa. + + So. + Mo. + Di. + Mi. + Do. + Fr. + Sa. + + S + M + D + M + D + F + S + + vorm. + nachm. + Gestern + Heute + Morgen + + %-k:%M + %-l:%M %p + %-l:%M %^p + h:mm a + H:mm + %d.%m.%Y + dd.MM.yyyy + "%s.%s.%s" + %-e. %B %Y + %H:%M:%S + %H:%M:%S %d.%m.%Y + %2$s %1$s + %1$s %3$s + %d.%m.%Y + %-e. %B + %-B + %B %Y + %-e. %b + %-b + %b %Y + %1$s - %2$s + %2$s - %5$s + %3$s.%2$s. - %8$s.%7$s. + %1$s, %3$s.%2$s. - %6$s, %8$s.%7$s. + %3$s.%2$s.%4$s - %8$s.%7$s.%9$s + %1$s, %3$s.%2$s.%4$s - %6$s, %8$s.%7$s.%9$s + %5$s %1$s, %3$s.%2$s.%4$s - %10$s %6$s, %8$s.%7$s.%9$s + %5$s %3$s.%2$s. - %10$s %8$s.%7$s. + %5$s %1$s, %3$s.%2$s. - %10$s %6$s, %8$s.%7$s. + %5$s %3$s.%2$s.%4$s - %10$s %8$s.%7$s.%9$s + %3$s %1$s %2$s - %6$s %4$s %5$s + %1$s %2$s - %4$s %5$s + %3$s %2$s - %6$s %5$s + %1$s %2$s %3$s + %2$s %3$s + %1$s %2$s + %3$s. %2$s - %8$s. %7$s + %1$s %3$s. %2$s - %6$s %8$s. %7$s + %5$s %3$s. %2$s - %10$s %8$s. %7$s + %5$s %3$s. %2$s - %10$s %8$s. %7$s + %5$s %1$s %3$s. %2$s - %10$s %6$s %8$s. %7$s + %5$s %1$s %3$s. %2$s - %10$s %6$s %8$s. %7$s + %5$s %3$s. %2$s %4$s - %10$s %8$s. %7$s %9$s + %5$s %3$s. %2$s %4$s - %10$s %8$s. %7$s %9$s + %5$s %1$s, %3$s. %2$s %4$s - %10$s %6$s, %8$s. %7$s %9$s + %5$s %1$s, %3$s. %2$s %4$s - %10$s %6$s, %8$s. %7$s %9$s + %1$s, %3$s. %2$s %4$s - %6$s, %8$s. %7$s %9$s + %3$s.-%8$s. %2$s + %1$s %3$s. %2$s - %6$s %8$s. %7$s + %3$s. %2$s - %8$s. %7$s %9$s + %3$s.-%8$s. %2$s %9$s + %1$s, %3$s. %2$s - %6$s, %8$s. %7$s %9$s + %b + diff --git a/core/res/res/values-de-rDE/donottranslate-cldr.xml b/core/res/res/values-de-rDE/donottranslate-cldr.xml new file mode 100644 index 0000000000000000000000000000000000000000..f32095bd336e5d2e3ebfc2acc6aaa6732a5bdc57 --- /dev/null +++ b/core/res/res/values-de-rDE/donottranslate-cldr.xml @@ -0,0 +1,147 @@ + + + Januar + Februar + März + April + Mai + Juni + Juli + August + September + Oktober + November + Dezember + + Januar + Februar + März + April + Mai + Juni + Juli + August + September + Oktober + November + Dezember + + Jan + Feb + Mär + Apr + Mai + Jun + Jul + Aug + Sep + Okt + Nov + Dez + + J + F + M + A + M + J + J + A + S + O + N + D + + Sonntag + Montag + Dienstag + Mittwoch + Donnerstag + Freitag + Samstag + + So. + Mo. + Di. + Mi. + Do. + Fr. + Sa. + + So. + Mo. + Di. + Mi. + Do. + Fr. + Sa. + + S + M + D + M + D + F + S + + vorm. + nachm. + Gestern + Heute + Morgen + + %-k:%M + %-l:%M %p + %-l:%M %^p + h:mm a + H:mm + %d.%m.%Y + dd.MM.yyyy + "%s.%s.%s" + %-e. %B %Y + %H:%M:%S + %H:%M:%S %d.%m.%Y + %2$s %1$s + %1$s %3$s + %d.%m.%Y + %-e. %B + %-B + %B %Y + %-e. %b + %-b + %b %Y + %1$s - %2$s + %2$s - %5$s + %3$s.%2$s. - %8$s.%7$s. + %1$s, %3$s.%2$s. - %6$s, %8$s.%7$s. + %3$s.%2$s.%4$s - %8$s.%7$s.%9$s + %1$s, %3$s.%2$s.%4$s - %6$s, %8$s.%7$s.%9$s + %5$s %1$s, %3$s.%2$s.%4$s - %10$s %6$s, %8$s.%7$s.%9$s + %5$s %3$s.%2$s. - %10$s %8$s.%7$s. + %5$s %1$s, %3$s.%2$s. - %10$s %6$s, %8$s.%7$s. + %5$s %3$s.%2$s.%4$s - %10$s %8$s.%7$s.%9$s + %3$s %1$s %2$s - %6$s %4$s %5$s + %1$s %2$s - %4$s %5$s + %3$s %2$s - %6$s %5$s + %1$s %2$s %3$s + %2$s %3$s + %1$s %2$s + %3$s. %2$s - %8$s. %7$s + %1$s %3$s. %2$s - %6$s %8$s. %7$s + %5$s %3$s. %2$s - %10$s %8$s. %7$s + %5$s %3$s. %2$s - %10$s %8$s. %7$s + %5$s %1$s %3$s. %2$s - %10$s %6$s %8$s. %7$s + %5$s %1$s %3$s. %2$s - %10$s %6$s %8$s. %7$s + %5$s %3$s. %2$s %4$s - %10$s %8$s. %7$s %9$s + %5$s %3$s. %2$s %4$s - %10$s %8$s. %7$s %9$s + %5$s %1$s, %3$s. %2$s %4$s - %10$s %6$s, %8$s. %7$s %9$s + %5$s %1$s, %3$s. %2$s %4$s - %10$s %6$s, %8$s. %7$s %9$s + %1$s, %3$s. %2$s %4$s - %6$s, %8$s. %7$s %9$s + %3$s.-%8$s. %2$s + %1$s %3$s. %2$s - %6$s %8$s. %7$s + %3$s. %2$s - %8$s. %7$s %9$s + %3$s.-%8$s. %2$s %9$s + %1$s, %3$s. %2$s - %6$s, %8$s. %7$s %9$s + %b + diff --git a/core/res/res/values-de-rLI/donottranslate-cldr.xml b/core/res/res/values-de-rLI/donottranslate-cldr.xml new file mode 100644 index 0000000000000000000000000000000000000000..f32095bd336e5d2e3ebfc2acc6aaa6732a5bdc57 --- /dev/null +++ b/core/res/res/values-de-rLI/donottranslate-cldr.xml @@ -0,0 +1,147 @@ + + + Januar + Februar + März + April + Mai + Juni + Juli + August + September + Oktober + November + Dezember + + Januar + Februar + März + April + Mai + Juni + Juli + August + September + Oktober + November + Dezember + + Jan + Feb + Mär + Apr + Mai + Jun + Jul + Aug + Sep + Okt + Nov + Dez + + J + F + M + A + M + J + J + A + S + O + N + D + + Sonntag + Montag + Dienstag + Mittwoch + Donnerstag + Freitag + Samstag + + So. + Mo. + Di. + Mi. + Do. + Fr. + Sa. + + So. + Mo. + Di. + Mi. + Do. + Fr. + Sa. + + S + M + D + M + D + F + S + + vorm. + nachm. + Gestern + Heute + Morgen + + %-k:%M + %-l:%M %p + %-l:%M %^p + h:mm a + H:mm + %d.%m.%Y + dd.MM.yyyy + "%s.%s.%s" + %-e. %B %Y + %H:%M:%S + %H:%M:%S %d.%m.%Y + %2$s %1$s + %1$s %3$s + %d.%m.%Y + %-e. %B + %-B + %B %Y + %-e. %b + %-b + %b %Y + %1$s - %2$s + %2$s - %5$s + %3$s.%2$s. - %8$s.%7$s. + %1$s, %3$s.%2$s. - %6$s, %8$s.%7$s. + %3$s.%2$s.%4$s - %8$s.%7$s.%9$s + %1$s, %3$s.%2$s.%4$s - %6$s, %8$s.%7$s.%9$s + %5$s %1$s, %3$s.%2$s.%4$s - %10$s %6$s, %8$s.%7$s.%9$s + %5$s %3$s.%2$s. - %10$s %8$s.%7$s. + %5$s %1$s, %3$s.%2$s. - %10$s %6$s, %8$s.%7$s. + %5$s %3$s.%2$s.%4$s - %10$s %8$s.%7$s.%9$s + %3$s %1$s %2$s - %6$s %4$s %5$s + %1$s %2$s - %4$s %5$s + %3$s %2$s - %6$s %5$s + %1$s %2$s %3$s + %2$s %3$s + %1$s %2$s + %3$s. %2$s - %8$s. %7$s + %1$s %3$s. %2$s - %6$s %8$s. %7$s + %5$s %3$s. %2$s - %10$s %8$s. %7$s + %5$s %3$s. %2$s - %10$s %8$s. %7$s + %5$s %1$s %3$s. %2$s - %10$s %6$s %8$s. %7$s + %5$s %1$s %3$s. %2$s - %10$s %6$s %8$s. %7$s + %5$s %3$s. %2$s %4$s - %10$s %8$s. %7$s %9$s + %5$s %3$s. %2$s %4$s - %10$s %8$s. %7$s %9$s + %5$s %1$s, %3$s. %2$s %4$s - %10$s %6$s, %8$s. %7$s %9$s + %5$s %1$s, %3$s. %2$s %4$s - %10$s %6$s, %8$s. %7$s %9$s + %1$s, %3$s. %2$s %4$s - %6$s, %8$s. %7$s %9$s + %3$s.-%8$s. %2$s + %1$s %3$s. %2$s - %6$s %8$s. %7$s + %3$s. %2$s - %8$s. %7$s %9$s + %3$s.-%8$s. %2$s %9$s + %1$s, %3$s. %2$s - %6$s, %8$s. %7$s %9$s + %b + diff --git a/core/res/res/values-de/donottranslate-cldr.xml b/core/res/res/values-de/donottranslate-cldr.xml new file mode 100644 index 0000000000000000000000000000000000000000..f32095bd336e5d2e3ebfc2acc6aaa6732a5bdc57 --- /dev/null +++ b/core/res/res/values-de/donottranslate-cldr.xml @@ -0,0 +1,147 @@ + + + Januar + Februar + März + April + Mai + Juni + Juli + August + September + Oktober + November + Dezember + + Januar + Februar + März + April + Mai + Juni + Juli + August + September + Oktober + November + Dezember + + Jan + Feb + Mär + Apr + Mai + Jun + Jul + Aug + Sep + Okt + Nov + Dez + + J + F + M + A + M + J + J + A + S + O + N + D + + Sonntag + Montag + Dienstag + Mittwoch + Donnerstag + Freitag + Samstag + + So. + Mo. + Di. + Mi. + Do. + Fr. + Sa. + + So. + Mo. + Di. + Mi. + Do. + Fr. + Sa. + + S + M + D + M + D + F + S + + vorm. + nachm. + Gestern + Heute + Morgen + + %-k:%M + %-l:%M %p + %-l:%M %^p + h:mm a + H:mm + %d.%m.%Y + dd.MM.yyyy + "%s.%s.%s" + %-e. %B %Y + %H:%M:%S + %H:%M:%S %d.%m.%Y + %2$s %1$s + %1$s %3$s + %d.%m.%Y + %-e. %B + %-B + %B %Y + %-e. %b + %-b + %b %Y + %1$s - %2$s + %2$s - %5$s + %3$s.%2$s. - %8$s.%7$s. + %1$s, %3$s.%2$s. - %6$s, %8$s.%7$s. + %3$s.%2$s.%4$s - %8$s.%7$s.%9$s + %1$s, %3$s.%2$s.%4$s - %6$s, %8$s.%7$s.%9$s + %5$s %1$s, %3$s.%2$s.%4$s - %10$s %6$s, %8$s.%7$s.%9$s + %5$s %3$s.%2$s. - %10$s %8$s.%7$s. + %5$s %1$s, %3$s.%2$s. - %10$s %6$s, %8$s.%7$s. + %5$s %3$s.%2$s.%4$s - %10$s %8$s.%7$s.%9$s + %3$s %1$s %2$s - %6$s %4$s %5$s + %1$s %2$s - %4$s %5$s + %3$s %2$s - %6$s %5$s + %1$s %2$s %3$s + %2$s %3$s + %1$s %2$s + %3$s. %2$s - %8$s. %7$s + %1$s %3$s. %2$s - %6$s %8$s. %7$s + %5$s %3$s. %2$s - %10$s %8$s. %7$s + %5$s %3$s. %2$s - %10$s %8$s. %7$s + %5$s %1$s %3$s. %2$s - %10$s %6$s %8$s. %7$s + %5$s %1$s %3$s. %2$s - %10$s %6$s %8$s. %7$s + %5$s %3$s. %2$s %4$s - %10$s %8$s. %7$s %9$s + %5$s %3$s. %2$s %4$s - %10$s %8$s. %7$s %9$s + %5$s %1$s, %3$s. %2$s %4$s - %10$s %6$s, %8$s. %7$s %9$s + %5$s %1$s, %3$s. %2$s %4$s - %10$s %6$s, %8$s. %7$s %9$s + %1$s, %3$s. %2$s %4$s - %6$s, %8$s. %7$s %9$s + %3$s.-%8$s. %2$s + %1$s %3$s. %2$s - %6$s %8$s. %7$s + %3$s. %2$s - %8$s. %7$s %9$s + %3$s.-%8$s. %2$s %9$s + %1$s, %3$s. %2$s - %6$s, %8$s. %7$s %9$s + %b + diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml index 674c64b91c4e641b82a37598dde74ca3307593e5..dfb454991aa4d04595060f1935e3bc5c522116d3 100644 --- a/core/res/res/values-de/strings.xml +++ b/core/res/res/values-de/strings.xml @@ -161,14 +161,10 @@ "Ermöglicht einer Anwendung, die aktuelle Konfiguration zu ändern, etwa das Gebietsschema oder die Schriftgröße." "Andere Anwendungen neu starten" "Ermöglicht einer Anwendung, den Neustart anderer Anwendungen zu erzwingen." - "Beenden nicht zulassen" - "Ermöglicht einer Anwendung, beliebige Prozesse im Vordergrund auszuführen, damit diese nicht beendet werden können. Sollte nicht für normale Anwendungen benötigt werden." "Schließen von Anwendung erzwingen" "Ermöglicht einer Anwendung, alle Aktivitäten, die im Vordergrund ablaufen, zu beenden und in den Hintergrund zu schieben. Sollte nicht für normale Anwendungen benötigt werden." "Systeminternen Status abrufen" "Ermöglicht einer Anwendung, den internen Status des Systems abzurufen. Schädliche Anwendungen rufen hierbei möglicherweise eine Vielzahl an privaten und geschützten Daten ab, die Sie in der Regel nicht benötigen würden." - "systemnahe Dienste veröffentlichen" - "Ermöglicht der Anwendung, ihre eigenen systemnahen Dienste anzubieten. Schädliche Anwendungen könnten in das System eindringen und darin befindliche Daten stehlen oder manipulieren." "Start von Anwendungen überwachen und steuern" "Ermöglicht der Anwendung, den Start von Systemaktivitäten zu überwachen und zu steuern. Schädliche Anwendungen können so das gesamte System beeinträchtigen. Diese Berechtigung wird nur zu Entwicklungszwecken und nie für die normale Telefonnutzung benötigt." "Broadcast ohne Paket senden" @@ -181,8 +177,6 @@ "Ermöglicht einer Anwendung, die maximale Anzahl an laufenden Prozessen zu steuern. Wird nicht für normale Anwendungen benötigt." "alle Anwendungen im Hintergrund schließen" "Überlässt einer Anwendung die Entscheidung, ob Aktivitäten beendet werden, sobald Sie in den Hintergrund rücken. Wird nicht für normale Anwendungen benötigt." - "System-Updates automatisch installieren" - "Ermöglicht einer Anwendung, Benachrichtigungen zu ausstehenden System-Updates zu erhalten und deren Installation einzuleiten. Schädliche Anwendungen können so das System durch nicht autorisierte Updates beschädigen oder in den Update-Prozess eingreifen." "Akku-Daten ändern" "Ermöglicht die Änderung von gesammelten Akku-Daten. Nicht für normale Anwendungen vorgesehen." "nicht autorisierte Fenster anzeigen" @@ -418,9 +412,6 @@ "Passwort" "Anmelden" "Ungültiger Nutzername oder ungültiges Passwort." - "h:mm AA" - "%-l:%M%P" - "%-l:%M%p" "%-l%P" "%-l%p" "Benachrichtigungen löschen" @@ -452,9 +443,6 @@ "Enter" "löschen" "Suche" - "Heute" - "Gestern" - "Morgen" "Vor 1 Monat" "Vor mehr als 1 Monat" @@ -536,13 +524,6 @@ "Wochen" "Jahr" "Jahre" - "Sonntag" - "Montag" - "Dienstag" - "Mittwoch" - "Donnerstag" - "Freitag" - "Samstag" "Jeden Wochentag (Mo-Fr)" "Täglich" "Jede Woche am %s" @@ -552,137 +533,15 @@ "Leider ist dieses Video nicht für Streaming auf diesem Gerät gültig." "Dieses Video kann leider nicht abgespielt werden." "OK" - "AM" - ".." - "%d/%m/%Y" - "%1$s, %2$s, %3$s%4$s, %5$s, %6$s" - "%1$s, %2$s%4$s, %5$s" - "%2$s, %3$s%5$s, %6$s" - "%2$s%5$s" - "%1$s%2$s" - "%1$s, %2$s, %3$s" - "%2$s, %3$s" - "%1$s, %3$s" - "%1$s, %2$s" "%1$s, %2$s" - "%1$s, %2$s" - "d'. 'MMMM' 'yyyy" - "d'. 'MMMM' 'yyyy" - "d'. 'MMM' 'yyyy" - "d'. 'MMM' 'yyyy" - "h':'mm' 'a" - "HH':'mm" "Mittag" "Mittag" "Mitternacht" "Mitternacht" - "%B %-d" - "%-d. %B %Y" - "%B %Y" - "%H:%M:%S" - "%H:%M:%S %-d. %B %Y" - "%3$s. %2$s%8$s. %7$s" - "%1$s, %3$s. %2$s%6$s, %8$s. %7$s" - "%3$s. %2$s%8$s. %7$s %9$s" - "%1$s, %3$s. %2$s%6$s, %8$s. %7$s %9$s" - "%3$s. %2$s %5$s%8$s. %7$s %10$s" - "%1$s, %3$s. %2$s, %5$s%6$s, %8$s. %7$s %10$s" - "%3$s. %2$s, %4$s, %5$s%8$s. %7$s %9$s, %10$s" - "%1$s, %3$s. %2$s %4$s, %5$s%6$s, %8$s. %7$s %9$s, %10$s" - "%3$s/%2$s%8$s/%7$s" - "%1$s, %3$s/%2$s%6$s, %8$s/%7$s" - "%3$s/%2$s/%4$s%8$s/%7$s/%9$s" - "%1$s, %3$s/%2$s/%4$s%6$s, %8$s/%7$s/%9$s" - "%3$s/%2$s, %5$s%8$s/%7$s, %10$s" - "%1$s, %3$s/%2$s, %5$s%6$s, %8$s/%7$s, %10$s" - "%3$s/%2$s/%4$s, %5$s%8$s/%7$s/%9$s, %10$s" - "%1$s, %3$s/%2$s/%4$s, %5$s%6$s, %8$s/%7$s/%9$s, %10$s" - "%3$s. – %8$s. %2$s" - "%1$s, %3$s. %2$s%6$s, %8$s. %7$s" - "%3$s. – %8$s. %2$s %9$s" - "%1$s, %3$s. %2$s %4$s%6$s, %8$s. %7$s %9$s" - "%3$s. %2$s, %5$s%8$s. %7$s, %10$s" - "%1$s, %3$s. %2$s, %5$s%6$s, %8$s. %7$s, %10$s" - "%3$s. %2$s %4$s, %5$s%8$s. %7$s %9$s, %10$s" - "%1$s, %3$s. %2$s %4$s, %5$s%6$s, %8$s. %7$s %9$s, %10$s" - "%-d. %b %Y" - "%b %Y" - "%b %-d" - "Sonntag" - "Montag" - "Dienstag" - "Mittwoch" - "Donnerstag" - "Freitag" - "Samstag" - "So" - "Mo" - "Di" - "Mi" - "Do" - "Fr" - "Sa" - "So" - "Mo" - "Di" - "Mi" - "Do" - "Fr" - "Sa" - "So" - "März" - "Di" - "Mi" - "Do" - "Fr" - "Sa" - "Sep" - "Mo" - "Do" - "Mi" - "Do" - "Fr" - "Sa" - "Januar" - "Februar" - "März" - "April" - "Mai" - "Juni" - "Juli" - "August" - "September" - "Oktober" - "November" - "Dezember" - "Jan." - "Feb." - "März" - "Apr." - "Mai" - "Juni" - "Juli" - "Aug" - "Sep." - "Okt." - "Nov." - "Dez." - "Juli" - "Fr" - "März" - "Apr" - "Mo" - "Juni" - "Juli" - "Aug." - "Sep" - "Okt." - "No" - "Dez." "%1$02d:%2$02d" "%1$d:%2$02d:%3$02d" "Alles auswählen" diff --git a/core/res/res/values-el-rGR/donottranslate-cldr.xml b/core/res/res/values-el-rGR/donottranslate-cldr.xml new file mode 100644 index 0000000000000000000000000000000000000000..e8f02fb0d47b6493c915eec2a5276559a37ab67e --- /dev/null +++ b/core/res/res/values-el-rGR/donottranslate-cldr.xml @@ -0,0 +1,147 @@ + + + Ιανουάριος + Φεβρουάριος + Μάρτιος + Απρίλιος + Μάιος + Ιούνιος + Ιούλιος + Αύγουστος + Σεπτέμβριος + Οκτώβριος + Νοέμβριος + Δεκέμβριος + + Ιανουαρίου + Φεβρουαρίου + Μαρτίου + Απριλίου + Μαΐου + Ιουνίου + Ιουλίου + Αυγούστου + Σεπτεμβρίου + Οκτωβρίου + Νοεμβρίου + Δεκεμβρίου + + Ιαν + Φεβ + Μαρ + Απρ + Μαϊ + Ιουν + Ιουλ + Αυγ + Σεπ + Οκτ + Νοε + Δεκ + + Ι + Φ + Μ + Α + Μ + Ι + Ι + Α + Σ + Ο + Ν + Δ + + Κυριακή + Δευτέρα + Τρίτη + Τετάρτη + Πέμπτη + Παρασκευή + Σάββατο + + Κυρ + Δευ + Τρι + Τετ + Πεμ + Παρ + Σαβ + + Κυρ + Δευ + Τρι + Τετ + Πεμ + Παρ + Σαβ + + Κ + Δ + Τ + Τ + Π + Π + Σ + + π.μ. + μ.μ. + Χτες + Σήμερα + Αύριο + + %-k:%M + %-l:%M %p + %-l:%M %p + h:mm a + H:mm + %d/%m/%Y + dd/MM/yyyy + "%s/%s/%s" + %d %B %Y + %-l:%M:%S %p + %-l:%M:%S %p %d %b %Y + %2$s %1$s + %1$s %3$s + %d %b %Y + %-e %B + %-B + %-B %Y + %-e %b + %-b + %b %Y + %1$s - %2$s + %2$s - %5$s + %3$s/%2$s - %8$s/%7$s + %1$s, %3$s/%2$s - %6$s, %8$s/%7$s + %3$s/%2$s/%4$s - %8$s/%7$s/%9$s + %1$s, %3$s/%2$s/%4$s - %6$s, %8$s/%7$s/%9$s + %5$s %1$s, %3$s/%2$s/%4$s - %10$s %6$s, %8$s/%7$s/%9$s + %5$s %3$s/%2$s - %10$s %8$s/%7$s + %5$s %1$s, %3$s/%2$s - %10$s %6$s, %8$s/%7$s + %5$s %3$s/%2$s/%4$s - %10$s %8$s/%7$s/%9$s + %3$s %1$s, %2$s - %6$s %4$s, %5$s + %1$s, %2$s - %4$s, %5$s + %3$s %2$s - %6$s %5$s + %1$s %2$s, %3$s + %2$s, %3$s + %1$s %2$s + %3$s %2$s - %8$s %7$s + %1$s, %3$s %2$s - %6$s, %8$s %7$s + %5$s %3$s %2$s - %10$s %8$s %7$s + %5$s %3$s %2$s - %10$s %8$s %7$s + %5$s %1$s, %3$s %2$s - %10$s %6$s, %8$s %7$s + %5$s %1$s, %3$s %2$s - %10$s %6$s, %8$s %7$s + %5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s + %5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s + %5$s %1$s, %3$s %2$s %4$s - %10$s %6$s, %8$s %7$s %9$s + %5$s %1$s, %3$s %2$s %4$s - %10$s %6$s, %8$s %7$s %9$s + %1$s, %3$s %2$s %4$s - %6$s, %8$s %7$s %9$s + %3$s-%8$s %2$s + %1$s, %3$s %2$s - %6$s, %8$s %7$s + %3$s %2$s - %8$s %7$s %9$s + %3$s-%8$s %2$s %9$s + %1$s, %3$s %2$s - %6$s, %8$s %7$s %9$s + %b + diff --git a/core/res/res/values-el/donottranslate-cldr.xml b/core/res/res/values-el/donottranslate-cldr.xml new file mode 100644 index 0000000000000000000000000000000000000000..e8f02fb0d47b6493c915eec2a5276559a37ab67e --- /dev/null +++ b/core/res/res/values-el/donottranslate-cldr.xml @@ -0,0 +1,147 @@ + + + Ιανουάριος + Φεβρουάριος + Μάρτιος + Απρίλιος + Μάιος + Ιούνιος + Ιούλιος + Αύγουστος + Σεπτέμβριος + Οκτώβριος + Νοέμβριος + Δεκέμβριος + + Ιανουαρίου + Φεβρουαρίου + Μαρτίου + Απριλίου + Μαΐου + Ιουνίου + Ιουλίου + Αυγούστου + Σεπτεμβρίου + Οκτωβρίου + Νοεμβρίου + Δεκεμβρίου + + Ιαν + Φεβ + Μαρ + Απρ + Μαϊ + Ιουν + Ιουλ + Αυγ + Σεπ + Οκτ + Νοε + Δεκ + + Ι + Φ + Μ + Α + Μ + Ι + Ι + Α + Σ + Ο + Ν + Δ + + Κυριακή + Δευτέρα + Τρίτη + Τετάρτη + Πέμπτη + Παρασκευή + Σάββατο + + Κυρ + Δευ + Τρι + Τετ + Πεμ + Παρ + Σαβ + + Κυρ + Δευ + Τρι + Τετ + Πεμ + Παρ + Σαβ + + Κ + Δ + Τ + Τ + Π + Π + Σ + + π.μ. + μ.μ. + Χτες + Σήμερα + Αύριο + + %-k:%M + %-l:%M %p + %-l:%M %p + h:mm a + H:mm + %d/%m/%Y + dd/MM/yyyy + "%s/%s/%s" + %d %B %Y + %-l:%M:%S %p + %-l:%M:%S %p %d %b %Y + %2$s %1$s + %1$s %3$s + %d %b %Y + %-e %B + %-B + %-B %Y + %-e %b + %-b + %b %Y + %1$s - %2$s + %2$s - %5$s + %3$s/%2$s - %8$s/%7$s + %1$s, %3$s/%2$s - %6$s, %8$s/%7$s + %3$s/%2$s/%4$s - %8$s/%7$s/%9$s + %1$s, %3$s/%2$s/%4$s - %6$s, %8$s/%7$s/%9$s + %5$s %1$s, %3$s/%2$s/%4$s - %10$s %6$s, %8$s/%7$s/%9$s + %5$s %3$s/%2$s - %10$s %8$s/%7$s + %5$s %1$s, %3$s/%2$s - %10$s %6$s, %8$s/%7$s + %5$s %3$s/%2$s/%4$s - %10$s %8$s/%7$s/%9$s + %3$s %1$s, %2$s - %6$s %4$s, %5$s + %1$s, %2$s - %4$s, %5$s + %3$s %2$s - %6$s %5$s + %1$s %2$s, %3$s + %2$s, %3$s + %1$s %2$s + %3$s %2$s - %8$s %7$s + %1$s, %3$s %2$s - %6$s, %8$s %7$s + %5$s %3$s %2$s - %10$s %8$s %7$s + %5$s %3$s %2$s - %10$s %8$s %7$s + %5$s %1$s, %3$s %2$s - %10$s %6$s, %8$s %7$s + %5$s %1$s, %3$s %2$s - %10$s %6$s, %8$s %7$s + %5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s + %5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s + %5$s %1$s, %3$s %2$s %4$s - %10$s %6$s, %8$s %7$s %9$s + %5$s %1$s, %3$s %2$s %4$s - %10$s %6$s, %8$s %7$s %9$s + %1$s, %3$s %2$s %4$s - %6$s, %8$s %7$s %9$s + %3$s-%8$s %2$s + %1$s, %3$s %2$s - %6$s, %8$s %7$s + %3$s %2$s - %8$s %7$s %9$s + %3$s-%8$s %2$s %9$s + %1$s, %3$s %2$s - %6$s, %8$s %7$s %9$s + %b + diff --git a/core/res/res/values-en-rAU/donottranslate-cldr.xml b/core/res/res/values-en-rAU/donottranslate-cldr.xml new file mode 100644 index 0000000000000000000000000000000000000000..9811b68e0aa1affe8e50c79aa2d92930986212e1 --- /dev/null +++ b/core/res/res/values-en-rAU/donottranslate-cldr.xml @@ -0,0 +1,147 @@ + + + January + February + March + April + May + June + July + August + September + October + November + December + + January + February + March + April + May + June + July + August + September + October + November + December + + Jan + Feb + Mar + Apr + May + Jun + Jul + Aug + Sep + Oct + Nov + Dec + + J + F + M + A + M + J + J + A + S + O + N + D + + Sunday + Monday + Tuesday + Wednesday + Thursday + Friday + Saturday + + Sun + Mon + Tue + Wed + Thu + Fri + Sat + + Su + Mo + Tu + We + Th + Fr + Sa + + S + M + T + W + T + F + S + + am + pm + Yesterday + Today + Tomorrow + + %H:%M + %-l:%M%p + %-l:%M%^p + h:mm a + HH:mm + %-e/%m/%Y + d/MM/yyyy + "%s/%s/%s" + %-e %B %Y + %-l:%M:%S %p + %d/%m/%Y, %-l:%M:%S %p + %1$s, %2$s + %1$s, %3$s + %d/%m/%Y + %-e %B + %-B + %B %Y + %-e %b + %b + %b %Y + %1$s - %2$s + %2$s - %5$s + %3$s/%2$s - %8$s/%7$s + %1$s, %3$s/%2$s - %6$s, %8$s/%7$s + %3$s/%2$s/%4$s - %8$s/%7$s/%9$s + %1$s, %3$s/%2$s/%4$s - %6$s, %8$s/%7$s/%9$s + %1$s, %3$s/%2$s/%4$s, %5$s - %6$s, %8$s/%7$s/%9$s, %10$s + %3$s/%2$s, %5$s - %8$s/%7$s, %10$s + %1$s, %3$s/%2$s, %5$s - %6$s, %8$s/%7$s, %10$s + %3$s/%2$s/%4$s, %5$s - %8$s/%7$s/%9$s, %10$s + %1$s, %2$s, %3$s - %4$s, %5$s, %6$s + %1$s, %2$s - %4$s, %5$s + %2$s, %3$s - %5$s, %6$s + %1$s, %2$s, %3$s + %2$s, %3$s + %1$s, %2$s + %3$s %2$s - %8$s %7$s + %1$s, %3$s %2$s - %6$s, %8$s %7$s + %3$s %2$s, %5$s - %8$s %7$s, %10$s + %3$s %2$s, %5$s - %8$s %7$s, %10$s + %1$s, %3$s %2$s, %5$s - %6$s, %8$s %7$s, %10$s + %1$s, %3$s %2$s, %5$s - %6$s, %8$s %7$s, %10$s + %3$s %2$s %4$s, %5$s - %8$s %7$s %9$s, %10$s + %3$s %2$s %4$s, %5$s - %8$s %7$s %9$s, %10$s + %1$s, %3$s %2$s %4$s, %5$s - %6$s, %8$s %7$s %9$s, %10$s + %1$s, %3$s %2$s %4$s, %5$s - %6$s, %8$s %7$s %9$s, %10$s + %1$s, %3$s %2$s %4$s - %6$s, %8$s %7$s %9$s + %3$s-%8$s %2$s + %1$s, %3$s %2$s - %6$s, %8$s %7$s + %3$s %2$s - %8$s %7$s %9$s + %3$s-%8$s %2$s %9$s + %1$s, %3$s %2$s - %6$s, %8$s %7$s %9$s + %b + diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml index 9da879b34a16ce7cad076d6e05901ddc0233dd4a..3de378bee211359899326212050e005d27b05e8a 100644 --- a/core/res/res/values-en-rAU/strings.xml +++ b/core/res/res/values-en-rAU/strings.xml @@ -314,10 +314,6 @@ - - - - @@ -710,7 +706,6 @@ - "h:mm AA" @@ -864,35 +859,24 @@ - "%d/%m/%Y" - "%1$s, %2$s, %3$s%4$s, %5$s, %6$s" - "%1$s, %2$s%4$s, %5$s" - "%2$s, %3$s%5$s, %6$s" - "%2$s%5$s" - "%1$s%2$s" - "%1$s, %2$s, %3$s" - "%2$s, %3$s" - "%1$s, %3$s" - "dd MMMM yyyy" - "dd MMM yyyy" @@ -906,73 +890,43 @@ - "%-d %B" - "%-d %B %Y" - "%H:%M:%S" - "%3$s %2$s%8$s %7$s" - "%1$s, %3$s %2$s%6$s, %8$s %7$s" - "%H:%M:%S %-d %B %Y" - "%3$s %2$s%8$s %7$s %9$s" - "%1$s, %3$s %2$s%6$s, %8$s %7$s %9$s" - " %3$s %2$s %5$s%8$s %7$s %10$s" - "%1$s, %3$s %2$s, %5$s%6$s, %8$s %7$s %10$s" - "%3$s %2$s, %4$s, %5$s%8$s %7$s %9$s, %10$s" - "%1$s, %3$s %2$s %4$s, %5$s%6$s, %8$s %7$s %9$s, %10$s" - "%3$s/%2$s%8$s/%7$s" - "%1$s, %3$s/%2$s%6$s, %8$s/%7$s" - "%3$s/%2$s/%4$s%8$s/%7$s/%9$s" - "%1$s, %3$s/%2$s/%4$s%6$s, %8$s/%7$s/%9$s" - "%3$s/%2$s, %5$s%8$s/%7$s, %10$s" - "%1$s, %3$s/%2$s, %5$s%6$s, %8$s/%7$s, %10$s" - "%3$s/%2$s/%4$s, %5$s%8$s/%7$s/%9$s, %10$s" - "%1$s, %3$s/%2$s/%4$s, %5$s%6$s, %8$s/%7$s/%9$s, %10$s" - "%3$s \u2013 %8$s %2$s" - "%1$s, %3$s %2$s%6$s, %8$s %7$s" - "%3$s \u2013 %8$s %2$s %9$s" - "%1$s, %3$s %2$s %4$s%6$s, %8$s %7$s %9$s" - "%3$s %2$s, %5$s%8$s %7$s, %10$s" - "%1$s, %3$s %2$s, %5$s%6$s, %8$s %7$s, %10$s" - "%3$s %2$s %4$s, %5$s%8$s %7$s %9$s, %10$s" - "%1$s, %3$s %2$s %4$s, %5$s%6$s, %8$s %7$s %9$s, %10$s" - "%-d %b %Y" - "%-d %b" diff --git a/core/res/res/values-en-rCA/donottranslate-cldr.xml b/core/res/res/values-en-rCA/donottranslate-cldr.xml new file mode 100644 index 0000000000000000000000000000000000000000..1e250c739b7391e9327456e11e61aa55beb1f6a7 --- /dev/null +++ b/core/res/res/values-en-rCA/donottranslate-cldr.xml @@ -0,0 +1,147 @@ + + + January + February + March + April + May + June + July + August + September + October + November + December + + January + February + March + April + May + June + July + August + September + October + November + December + + Jan + Feb + Mar + Apr + May + Jun + Jul + Aug + Sep + Oct + Nov + Dec + + J + F + M + A + M + J + J + A + S + O + N + D + + Sunday + Monday + Tuesday + Wednesday + Thursday + Friday + Saturday + + Sun + Mon + Tue + Wed + Thu + Fri + Sat + + Su + Mo + Tu + We + Th + Fr + Sa + + S + M + T + W + T + F + S + + am + pm + Yesterday + Today + Tomorrow + + %H:%M + %-l:%M%p + %-l:%M%^p + h:mm a + HH:mm + %Y-%m-%d + yyyy-MM-dd + "%s-%s-%s" + %B %-e, %Y + %-l:%M:%S %p + %Y-%m-%d, %-l:%M:%S %p + %1$s, %2$s + %1$s, %3$s + %Y-%m-%d + %B %-e + %-B + %B %Y + %b %-e + %b + %b %Y + %1$s - %2$s + %2$s - %5$s + %2$s-%3$s - %7$s-%8$s + %1$s, %2$s-%3$s - %6$s, %7$s-%8$s + %4$s-%2$s-%3$s - %9$s-%7$s-%8$s + %1$s, %4$s-%2$s-%3$s - %6$s, %9$s-%7$s-%8$s + %1$s, %4$s-%2$s-%3$s, %5$s - %6$s, %9$s-%7$s-%8$s, %10$s + %2$s-%3$s, %5$s - %7$s-%8$s, %10$s + %1$s, %2$s-%3$s, %5$s - %6$s, %7$s-%8$s, %10$s + %4$s-%2$s-%3$s, %5$s - %9$s-%7$s-%8$s, %10$s + %1$s, %2$s, %3$s - %4$s, %5$s, %6$s + %1$s, %2$s - %4$s, %5$s + %2$s, %3$s - %5$s, %6$s + %1$s, %2$s, %3$s + %2$s, %3$s + %1$s, %2$s + %2$s %3$s - %7$s %8$s + %1$s, %2$s %3$s - %6$s, %7$s %8$s + %2$s %3$s, %5$s - %7$s %8$s, %10$s + %2$s %3$s, %5$s - %7$s %8$s, %10$s + %1$s, %2$s %3$s, %5$s - %6$s, %7$s %8$s, %10$s + %1$s, %2$s %3$s, %5$s - %6$s, %7$s %8$s, %10$s + %2$s %3$s, %4$s, %5$s - %7$s %8$s, %9$s, %10$s + %2$s %3$s, %4$s, %5$s - %7$s %8$s, %9$s, %10$s + %1$s, %2$s %3$s, %4$s, %5$s - %6$s, %7$s %8$s, %9$s, %10$s + %1$s, %2$s %3$s, %4$s, %5$s - %6$s, %7$s %8$s, %9$s, %10$s + %1$s, %2$s %3$s, %4$s - %6$s, %7$s %8$s, %9$s + %2$s %3$s-%8$s + %1$s, %2$s %3$s - %6$s, %7$s %8$s + %2$s %3$s - %7$s %8$s, %9$s + %2$s %3$s-%8$s, %9$s + %1$s, %2$s %3$s - %6$s, %7$s %8$s, %9$s + %b + diff --git a/core/res/res/values-en-rGB/donottranslate-cldr.xml b/core/res/res/values-en-rGB/donottranslate-cldr.xml new file mode 100644 index 0000000000000000000000000000000000000000..0e3e035b7587db899d0607f9896174d4f5abadc3 --- /dev/null +++ b/core/res/res/values-en-rGB/donottranslate-cldr.xml @@ -0,0 +1,147 @@ + + + January + February + March + April + May + June + July + August + September + October + November + December + + January + February + March + April + May + June + July + August + September + October + November + December + + Jan + Feb + Mar + Apr + May + Jun + Jul + Aug + Sep + Oct + Nov + Dec + + J + F + M + A + M + J + J + A + S + O + N + D + + Sunday + Monday + Tuesday + Wednesday + Thursday + Friday + Saturday + + Sun + Mon + Tue + Wed + Thu + Fri + Sat + + Su + Mo + Tu + We + Th + Fr + Sa + + S + M + T + W + T + F + S + + am + pm + Yesterday + Today + Tomorrow + + %H:%M + %-l:%M%p + %-l:%M%^p + h:mm a + HH:mm + %d/%m/%Y + dd/MM/yyyy + "%s/%s/%s" + %-e %B %Y + %H:%M:%S + %-e %b %Y, %H:%M:%S + %1$s, %2$s + %1$s, %3$s + %-e %b %Y + %-e %B + %-B + %B %Y + %-e %b + %b + %b %Y + %1$s - %2$s + %2$s - %5$s + %3$s/%2$s - %8$s/%7$s + %1$s, %3$s/%2$s - %6$s, %8$s/%7$s + %3$s/%2$s/%4$s - %8$s/%7$s/%9$s + %1$s, %3$s/%2$s/%4$s - %6$s, %8$s/%7$s/%9$s + %1$s, %3$s/%2$s/%4$s, %5$s - %6$s, %8$s/%7$s/%9$s, %10$s + %3$s/%2$s, %5$s - %8$s/%7$s, %10$s + %1$s, %3$s/%2$s, %5$s - %6$s, %8$s/%7$s, %10$s + %3$s/%2$s/%4$s, %5$s - %8$s/%7$s/%9$s, %10$s + %1$s %2$s, %3$s - %4$s %5$s, %6$s + %1$s %2$s - %4$s %5$s + %2$s, %3$s - %5$s, %6$s + %1$s, %2$s %3$s + %2$s %3$s + %1$s, %2$s + %3$s %2$s - %8$s %7$s + %1$s %3$s %2$s - %6$s %8$s %7$s + %3$s %2$s, %5$s - %8$s %7$s, %10$s + %3$s %2$s, %5$s - %8$s %7$s, %10$s + %1$s %3$s %2$s, %5$s - %6$s %8$s %7$s, %10$s + %1$s %3$s %2$s, %5$s - %6$s %8$s %7$s, %10$s + %3$s %2$s %4$s, %5$s - %8$s %7$s %9$s, %10$s + %3$s %2$s %4$s, %5$s - %8$s %7$s %9$s, %10$s + %1$s, %3$s %2$s %4$s, %5$s - %6$s, %8$s %7$s %9$s, %10$s + %1$s, %3$s %2$s %4$s, %5$s - %6$s, %8$s %7$s %9$s, %10$s + %1$s, %3$s %2$s %4$s - %6$s, %8$s %7$s %9$s + %3$s-%8$s %2$s + %1$s %3$s %2$s - %6$s %8$s %7$s + %3$s %2$s - %8$s %7$s %9$s + %3$s-%8$s %2$s %9$s + %1$s, %3$s %2$s - %6$s, %8$s %7$s %9$s + %b + diff --git a/core/res/res/values-en-rIE/donottranslate-cldr.xml b/core/res/res/values-en-rIE/donottranslate-cldr.xml new file mode 100644 index 0000000000000000000000000000000000000000..2e59dcfeaffa59d1a07bc0f187cd2f85cfaa22c6 --- /dev/null +++ b/core/res/res/values-en-rIE/donottranslate-cldr.xml @@ -0,0 +1,147 @@ + + + January + February + March + April + May + June + July + August + September + October + November + December + + January + February + March + April + May + June + July + August + September + October + November + December + + Jan + Feb + Mar + Apr + May + Jun + Jul + Aug + Sep + Oct + Nov + Dec + + J + F + M + A + M + J + J + A + S + O + N + D + + Sunday + Monday + Tuesday + Wednesday + Thursday + Friday + Saturday + + Sun + Mon + Tue + Wed + Thu + Fri + Sat + + Su + Mo + Tu + We + Th + Fr + Sa + + S + M + T + W + T + F + S + + a.m. + p.m. + Yesterday + Today + Tomorrow + + %H:%M + %-l:%M%p + %-l:%M%^p + h:mm a + HH:mm + %d/%m/%Y + dd/MM/yyyy + "%s/%s/%s" + %-e %B %Y + %H:%M:%S + %-e %b %Y, %H:%M:%S + %1$s, %2$s + %1$s, %3$s + %-e %b %Y + %-e %B + %-B + %B %Y + %-e %b + %b + %b %Y + %1$s - %2$s + %2$s - %5$s + %3$s/%2$s - %8$s/%7$s + %1$s %3$s/%2$s - %6$s %8$s/%7$s + %3$s/%2$s/%4$s - %8$s/%7$s/%9$s + %1$s %3$s/%2$s/%4$s - %6$s %8$s/%7$s/%9$s + %1$s, %3$s/%2$s/%4$s, %5$s - %6$s, %8$s/%7$s/%9$s, %10$s + %3$s/%2$s, %5$s - %8$s/%7$s, %10$s + %1$s, %3$s/%2$s, %5$s - %6$s, %8$s/%7$s, %10$s + %3$s/%2$s/%4$s, %5$s - %8$s/%7$s/%9$s, %10$s + %1$s %2$s, %3$s - %4$s %5$s, %6$s + %1$s %2$s - %4$s %5$s + %2$s, %3$s - %5$s, %6$s + %1$s, %2$s %3$s + %2$s %3$s + %1$s, %2$s + %3$s %2$s - %8$s %7$s + %1$s %3$s %2$s - %6$s %8$s %7$s + %3$s %2$s, %5$s - %8$s %7$s, %10$s + %3$s %2$s, %5$s - %8$s %7$s, %10$s + %1$s %3$s %2$s, %5$s - %6$s %8$s %7$s, %10$s + %1$s %3$s %2$s, %5$s - %6$s %8$s %7$s, %10$s + %3$s %2$s %4$s, %5$s - %8$s %7$s %9$s, %10$s + %3$s %2$s %4$s, %5$s - %8$s %7$s %9$s, %10$s + %1$s %3$s %2$s %4$s, %5$s - %6$s %8$s %7$s %9$s, %10$s + %1$s %3$s %2$s %4$s, %5$s - %6$s %8$s %7$s %9$s, %10$s + %1$s %3$s %2$s %4$s - %6$s %8$s %7$s %9$s + %3$s-%8$s %2$s + %1$s %3$s %2$s - %6$s %8$s %7$s + %3$s %2$s - %8$s %7$s %9$s + %3$s-%8$s %2$s %9$s + %1$s %3$s %2$s - %6$s %8$s %7$s %9$s + %b + diff --git a/core/res/res/values-en-rIN/donottranslate-cldr.xml b/core/res/res/values-en-rIN/donottranslate-cldr.xml new file mode 100644 index 0000000000000000000000000000000000000000..e39a59a84019da5ccf1cae4010d5539dd9f2cb24 --- /dev/null +++ b/core/res/res/values-en-rIN/donottranslate-cldr.xml @@ -0,0 +1,147 @@ + + + January + February + March + April + May + June + July + August + September + October + November + December + + January + February + March + April + May + June + July + August + September + October + November + December + + Jan + Feb + Mar + Apr + May + Jun + Jul + Aug + Sep + Oct + Nov + Dec + + J + F + M + A + M + J + J + A + S + O + N + D + + Sunday + Monday + Tuesday + Wednesday + Thursday + Friday + Saturday + + Sun + Mon + Tue + Wed + Thu + Fri + Sat + + Su + Mo + Tu + We + Th + Fr + Sa + + S + M + T + W + T + F + S + + am + pm + Yesterday + Today + Tomorrow + + %H:%M + %-l:%M%p + %-l:%M%^p + h:mm a + HH:mm + %d/%m/%Y + dd/MM/yyyy + "%s/%s/%s" + %-e %B %Y + %-l:%M:%S %p + %d-%b-%Y, %-l:%M:%S %p + %1$s, %2$s + %1$s, %3$s + %d-%b-%Y + %-e %B + %-B + %B %Y + %-e %b + %b + %b %Y + %1$s - %2$s + %2$s - %5$s + %3$s/%2$s - %8$s/%7$s + %1$s %3$s/%2$s - %6$s %8$s/%7$s + %3$s/%2$s/%4$s - %8$s/%7$s/%9$s + %1$s %3$s/%2$s/%4$s - %6$s %8$s/%7$s/%9$s + %1$s %3$s/%2$s/%4$s, %5$s - %6$s %8$s/%7$s/%9$s, %10$s + %3$s/%2$s, %5$s - %8$s/%7$s, %10$s + %1$s %3$s/%2$s, %5$s - %6$s %8$s/%7$s, %10$s + %3$s/%2$s/%4$s, %5$s - %8$s/%7$s/%9$s, %10$s + %1$s %2$s, %3$s - %4$s %5$s, %6$s + %1$s %2$s - %4$s %5$s + %2$s, %3$s - %5$s, %6$s + %1$s, %2$s %3$s + %2$s %3$s + %1$s, %2$s + %3$s %2$s - %8$s %7$s + %1$s %3$s %2$s - %6$s %8$s %7$s + %3$s %2$s, %5$s - %8$s %7$s, %10$s + %3$s %2$s, %5$s - %8$s %7$s, %10$s + %1$s %3$s %2$s, %5$s - %6$s %8$s %7$s, %10$s + %1$s %3$s %2$s, %5$s - %6$s %8$s %7$s, %10$s + %3$s %2$s %4$s, %5$s - %8$s %7$s %9$s, %10$s + %3$s %2$s %4$s, %5$s - %8$s %7$s %9$s, %10$s + %1$s %3$s %2$s %4$s, %5$s - %6$s %8$s %7$s %9$s, %10$s + %1$s %3$s %2$s %4$s, %5$s - %6$s %8$s %7$s %9$s, %10$s + %1$s %3$s %2$s %4$s - %6$s %8$s %7$s %9$s + %3$s-%8$s %2$s + %1$s %3$s %2$s - %6$s %8$s %7$s + %3$s %2$s - %8$s %7$s %9$s + %3$s-%8$s %2$s %9$s + %1$s %3$s %2$s - %6$s %8$s %7$s %9$s + %b + diff --git a/core/res/res/values-en-rNZ/donottranslate-cldr.xml b/core/res/res/values-en-rNZ/donottranslate-cldr.xml new file mode 100644 index 0000000000000000000000000000000000000000..3a8b50bdb9f518a0afd4005441763de9e0b15cf1 --- /dev/null +++ b/core/res/res/values-en-rNZ/donottranslate-cldr.xml @@ -0,0 +1,147 @@ + + + January + February + March + April + May + June + July + August + September + October + November + December + + January + February + March + April + May + June + July + August + September + October + November + December + + Jan + Feb + Mar + Apr + May + Jun + Jul + Aug + Sep + Oct + Nov + Dec + + J + F + M + A + M + J + J + A + S + O + N + D + + Sunday + Monday + Tuesday + Wednesday + Thursday + Friday + Saturday + + Sun + Mon + Tue + Wed + Thu + Fri + Sat + + Su + Mo + Tu + We + Th + Fr + Sa + + S + M + T + W + T + F + S + + am + pm + Yesterday + Today + Tomorrow + + %H:%M + %-l:%M%p + %-l:%M%^p + h:mm a + HH:mm + %-e/%m/%Y + d/MM/yyyy + "%s/%s/%s" + %-e %B %Y + %-l:%M:%S %p + %-e/%m/%Y, %-l:%M:%S %p + %1$s, %2$s + %1$s, %3$s + %-e/%m/%Y + %-e %B + %-B + %B %Y + %-e %b + %b + %b %Y + %1$s - %2$s + %2$s - %5$s + %3$s/%2$s - %8$s/%7$s + %1$s, %3$s/%2$s - %6$s, %8$s/%7$s + %3$s/%2$s/%4$s - %8$s/%7$s/%9$s + %1$s, %3$s/%2$s/%4$s - %6$s, %8$s/%7$s/%9$s + %1$s, %3$s/%2$s/%4$s, %5$s - %6$s, %8$s/%7$s/%9$s, %10$s + %3$s/%2$s, %5$s - %8$s/%7$s, %10$s + %1$s, %3$s/%2$s, %5$s - %6$s, %8$s/%7$s, %10$s + %3$s/%2$s/%4$s, %5$s - %8$s/%7$s/%9$s, %10$s + %1$s, %2$s, %3$s - %4$s, %5$s, %6$s + %1$s, %2$s - %4$s, %5$s + %2$s, %3$s - %5$s, %6$s + %1$s, %2$s, %3$s + %2$s, %3$s + %1$s, %2$s + %3$s %2$s - %8$s %7$s + %1$s, %3$s %2$s - %6$s, %8$s %7$s + %3$s %2$s, %5$s - %8$s %7$s, %10$s + %3$s %2$s, %5$s - %8$s %7$s, %10$s + %1$s, %3$s %2$s, %5$s - %6$s, %8$s %7$s, %10$s + %1$s, %3$s %2$s, %5$s - %6$s, %8$s %7$s, %10$s + %3$s %2$s %4$s, %5$s - %8$s %7$s %9$s, %10$s + %3$s %2$s %4$s, %5$s - %8$s %7$s %9$s, %10$s + %1$s, %3$s %2$s %4$s, %5$s - %6$s, %8$s %7$s %9$s, %10$s + %1$s, %3$s %2$s %4$s, %5$s - %6$s, %8$s %7$s %9$s, %10$s + %1$s, %3$s %2$s %4$s - %6$s, %8$s %7$s %9$s + %3$s-%8$s %2$s + %1$s, %3$s %2$s - %6$s, %8$s %7$s + %3$s %2$s - %8$s %7$s %9$s + %3$s-%8$s %2$s %9$s + %1$s, %3$s %2$s - %6$s, %8$s %7$s %9$s + %b + diff --git a/core/res/res/values-en-rSG/donottranslate-cldr.xml b/core/res/res/values-en-rSG/donottranslate-cldr.xml new file mode 100644 index 0000000000000000000000000000000000000000..286cc0e554f293ae71b9a24c13db146e72bd44a9 --- /dev/null +++ b/core/res/res/values-en-rSG/donottranslate-cldr.xml @@ -0,0 +1,147 @@ + + + January + February + March + April + May + June + July + August + September + October + November + December + + January + February + March + April + May + June + July + August + September + October + November + December + + Jan + Feb + Mar + Apr + May + Jun + Jul + Aug + Sep + Oct + Nov + Dec + + J + F + M + A + M + J + J + A + S + O + N + D + + Sunday + Monday + Tuesday + Wednesday + Thursday + Friday + Saturday + + Sun + Mon + Tue + Wed + Thu + Fri + Sat + + Su + Mo + Tu + We + Th + Fr + Sa + + S + M + T + W + T + F + S + + am + pm + Yesterday + Today + Tomorrow + + %H:%M + %-l:%M%p + %-l:%M%^p + h:mm a + HH:mm + %-m/%-e/%Y + M/d/yyyy + "%s/%s/%s" + %B %-e, %Y + %-l:%M:%S %p + %b %-e, %Y, %-l:%M:%S %p + %1$s, %2$s + %1$s, %3$s + %b %-e, %Y + %B %-e + %-B + %B %Y + %b %-e + %-b + %b %Y + %1$s – %2$s + %2$s – %5$s + %2$s/%3$s – %7$s/%8$s + %1$s, %2$s/%3$s – %6$s, %7$s/%8$s + %2$s/%3$s/%4$s – %7$s/%8$s/%9$s + %1$s, %2$s/%3$s/%4$s – %6$s, %7$s/%8$s/%9$s + %1$s, %2$s/%3$s/%4$s, %5$s – %6$s, %7$s/%8$s/%9$s, %10$s + %2$s/%3$s, %5$s – %7$s/%8$s, %10$s + %1$s, %2$s/%3$s, %5$s – %6$s, %7$s/%8$s, %10$s + %2$s/%3$s/%4$s, %5$s – %7$s/%8$s/%9$s, %10$s + %1$s, %2$s, %3$s – %4$s, %5$s, %6$s + %1$s, %2$s – %4$s, %5$s + %2$s, %3$s – %5$s, %6$s + %1$s, %2$s, %3$s + %2$s, %3$s + %1$s, %2$s + %2$s %3$s – %7$s %8$s + %1$s, %2$s %3$s – %6$s, %7$s %8$s + %2$s %3$s, %5$s – %7$s %8$s, %10$s + %2$s %3$s, %5$s – %7$s %8$s, %10$s + %1$s, %2$s %3$s, %5$s – %6$s, %7$s %8$s, %10$s + %1$s, %2$s %3$s, %5$s – %6$s, %7$s %8$s, %10$s + %2$s %3$s, %4$s, %5$s – %7$s %8$s, %9$s, %10$s + %2$s %3$s, %4$s, %5$s – %7$s %8$s, %9$s, %10$s + %1$s, %2$s %3$s, %4$s, %5$s – %6$s, %7$s %8$s, %9$s, %10$s + %1$s, %2$s %3$s, %4$s, %5$s – %6$s, %7$s %8$s, %9$s, %10$s + %1$s, %2$s %3$s, %4$s – %6$s, %7$s %8$s, %9$s + %2$s %3$s – %8$s + %1$s, %2$s %3$s – %6$s, %7$s %8$s + %2$s %3$s – %7$s %8$s, %9$s + %2$s %3$s – %8$s, %9$s + %1$s, %2$s %3$s – %6$s, %7$s %8$s, %9$s + %b + diff --git a/core/res/res/values-en-rSG/strings.xml b/core/res/res/values-en-rSG/strings.xml index 6850a5d2835b59f1ecc8f347506a22b613895f3b..2ec6b0b02c48b53381d0ae297f4e2f272421bf64 100644 --- a/core/res/res/values-en-rSG/strings.xml +++ b/core/res/res/values-en-rSG/strings.xml @@ -314,10 +314,6 @@ - - - - @@ -710,7 +706,6 @@ - "h:mm AA" @@ -863,33 +858,21 @@ - "%1$s, %2$s, %3$s%4$s, %5$s, %6$s" - "%1$s, %2$s%4$s, %5$s" - "%d/%m/%Y" - "%2$s, %3$s%5$s, %6$s" - "%2$s%5$s" - "%1$s%2$s" - "%1$s, %2$s, %3$s" - "%2$s, %3$s" - "%1$s, %3$s" - "%1$s, %2$s" - "dd MMMM yyyy" - "dd MMM yyyy" @@ -903,71 +886,42 @@ - "%-d %B" - "%-d %B %Y" - "%H:%M:%S" - "%H:%M:%S %-d %B %Y" - "%3$s %2$s%8$s %7$s" - "%1$s, %3$s %2$s%6$s, %8$s %7$s" - "%3$s %2$s%8$s %7$s %9$s" - "%1$s, %3$s %2$s%6$s, %8$s %7$s %9$s" - "%1$s, %3$s %2$s, %5$s%6$s, %8$s %7$s %10$s" - "%3$s %2$s, %4$s, %5$s%8$s %7$s %9$s, %10$s" - "%1$s, %3$s %2$s %4$s, %5$s%6$s, %8$s %7$s %9$s, %10$s" - "%3$s/%2$s%8$s/%7$s" - "%1$s, %3$s/%2$s%6$s, %8$s/%7$s" - "%3$s/%2$s/%4$s%8$s/%7$s/%9$s" - "%1$s, %3$s/%2$s/%4$s%6$s, %8$s/%7$s/%9$s" - "%3$s/%2$s, %5$s%8$s/%7$s, %10$s" - "%1$s, %3$s/%2$s, %5$s%6$s, %8$s/%7$s, %10$s" - "%3$s/%2$s/%4$s, %5$s%8$s/%7$s/%9$s, %10$s" - "%1$s, %3$s/%2$s/%4$s, %5$s%6$s, %8$s/%7$s/%9$s, %10$s" - "%3$s \u2013 %8$s %2$s" - "%1$s, %3$s %2$s%6$s, %8$s %7$s" - "%3$s \u2013 %8$s %2$s %9$s" - "%1$s, %3$s %2$s %4$s%6$s, %8$s %7$s %9$s" - "%3$s %2$s, %5$s%8$s %7$s, %10$s" - "%1$s, %3$s %2$s, %5$s%6$s, %8$s %7$s, %10$s" - "%3$s %2$s %4$s, %5$s%8$s %7$s %9$s, %10$s" - "%1$s, %3$s %2$s %4$s, %5$s%6$s, %8$s %7$s %9$s, %10$s" - "%-d %b %Y" - "%-d %b" diff --git a/core/res/res/values-en-rUS/donottranslate-cldr.xml b/core/res/res/values-en-rUS/donottranslate-cldr.xml new file mode 100644 index 0000000000000000000000000000000000000000..286cc0e554f293ae71b9a24c13db146e72bd44a9 --- /dev/null +++ b/core/res/res/values-en-rUS/donottranslate-cldr.xml @@ -0,0 +1,147 @@ + + + January + February + March + April + May + June + July + August + September + October + November + December + + January + February + March + April + May + June + July + August + September + October + November + December + + Jan + Feb + Mar + Apr + May + Jun + Jul + Aug + Sep + Oct + Nov + Dec + + J + F + M + A + M + J + J + A + S + O + N + D + + Sunday + Monday + Tuesday + Wednesday + Thursday + Friday + Saturday + + Sun + Mon + Tue + Wed + Thu + Fri + Sat + + Su + Mo + Tu + We + Th + Fr + Sa + + S + M + T + W + T + F + S + + am + pm + Yesterday + Today + Tomorrow + + %H:%M + %-l:%M%p + %-l:%M%^p + h:mm a + HH:mm + %-m/%-e/%Y + M/d/yyyy + "%s/%s/%s" + %B %-e, %Y + %-l:%M:%S %p + %b %-e, %Y, %-l:%M:%S %p + %1$s, %2$s + %1$s, %3$s + %b %-e, %Y + %B %-e + %-B + %B %Y + %b %-e + %-b + %b %Y + %1$s – %2$s + %2$s – %5$s + %2$s/%3$s – %7$s/%8$s + %1$s, %2$s/%3$s – %6$s, %7$s/%8$s + %2$s/%3$s/%4$s – %7$s/%8$s/%9$s + %1$s, %2$s/%3$s/%4$s – %6$s, %7$s/%8$s/%9$s + %1$s, %2$s/%3$s/%4$s, %5$s – %6$s, %7$s/%8$s/%9$s, %10$s + %2$s/%3$s, %5$s – %7$s/%8$s, %10$s + %1$s, %2$s/%3$s, %5$s – %6$s, %7$s/%8$s, %10$s + %2$s/%3$s/%4$s, %5$s – %7$s/%8$s/%9$s, %10$s + %1$s, %2$s, %3$s – %4$s, %5$s, %6$s + %1$s, %2$s – %4$s, %5$s + %2$s, %3$s – %5$s, %6$s + %1$s, %2$s, %3$s + %2$s, %3$s + %1$s, %2$s + %2$s %3$s – %7$s %8$s + %1$s, %2$s %3$s – %6$s, %7$s %8$s + %2$s %3$s, %5$s – %7$s %8$s, %10$s + %2$s %3$s, %5$s – %7$s %8$s, %10$s + %1$s, %2$s %3$s, %5$s – %6$s, %7$s %8$s, %10$s + %1$s, %2$s %3$s, %5$s – %6$s, %7$s %8$s, %10$s + %2$s %3$s, %4$s, %5$s – %7$s %8$s, %9$s, %10$s + %2$s %3$s, %4$s, %5$s – %7$s %8$s, %9$s, %10$s + %1$s, %2$s %3$s, %4$s, %5$s – %6$s, %7$s %8$s, %9$s, %10$s + %1$s, %2$s %3$s, %4$s, %5$s – %6$s, %7$s %8$s, %9$s, %10$s + %1$s, %2$s %3$s, %4$s – %6$s, %7$s %8$s, %9$s + %2$s %3$s – %8$s + %1$s, %2$s %3$s – %6$s, %7$s %8$s + %2$s %3$s – %7$s %8$s, %9$s + %2$s %3$s – %8$s, %9$s + %1$s, %2$s %3$s – %6$s, %7$s %8$s, %9$s + %b + diff --git a/core/res/res/values-en-rUS/strings.xml b/core/res/res/values-en-rUS/strings.xml index b9df983a3fc3c964277ce4ce180c8727a81fbb0c..05f30fca350f959b1a5ffe76d5c39eb42e49c5c1 100644 --- a/core/res/res/values-en-rUS/strings.xml +++ b/core/res/res/values-en-rUS/strings.xml @@ -314,10 +314,6 @@ - - - - diff --git a/core/res/res/values-en-rZA/donottranslate-cldr.xml b/core/res/res/values-en-rZA/donottranslate-cldr.xml new file mode 100644 index 0000000000000000000000000000000000000000..2e2d6080d71c427e1fd2c6071e453cbc4b28e437 --- /dev/null +++ b/core/res/res/values-en-rZA/donottranslate-cldr.xml @@ -0,0 +1,147 @@ + + + January + February + March + April + May + June + July + August + September + October + November + December + + January + February + March + April + May + June + July + August + September + October + November + December + + Jan + Feb + Mar + Apr + May + Jun + Jul + Aug + Sep + Oct + Nov + Dec + + J + F + M + A + M + J + J + A + S + O + N + D + + Sunday + Monday + Tuesday + Wednesday + Thursday + Friday + Saturday + + Sun + Mon + Tue + Wed + Thu + Fri + Sat + + Su + Mo + Tu + We + Th + Fr + Sa + + S + M + T + W + T + F + S + + am + pm + Yesterday + Today + Tomorrow + + %H:%M + %-l:%M%p + %-l:%M%^p + h:mm a + HH:mm + %Y/%m/%d + yyyy/MM/dd + "%s/%s/%s" + %d %B %Y + %-l:%M:%S %p + %d %b %Y, %-l:%M:%S %p + %1$s, %2$s + %1$s, %3$s + %d %b %Y + %B %-e + %-B + %B %Y + %d %b + %b + %b %Y + %1$s - %2$s + %2$s - %5$s + %2$s/%3$s - %7$s/%8$s + %1$s %2$s/%3$s - %6$s %7$s/%8$s + %4$s/%2$s/%3$s - %9$s/%7$s/%8$s + %1$s %4$s/%2$s/%3$s - %6$s %9$s/%7$s/%8$s + %1$s %4$s/%2$s/%3$s, %5$s - %6$s %9$s/%7$s/%8$s, %10$s + %2$s/%3$s, %5$s - %7$s/%8$s, %10$s + %1$s %2$s/%3$s, %5$s - %6$s %7$s/%8$s, %10$s + %4$s/%2$s/%3$s, %5$s - %9$s/%7$s/%8$s, %10$s + %1$s %2$s, %3$s - %4$s %5$s, %6$s + %1$s %2$s - %4$s %5$s + %2$s, %3$s - %5$s, %6$s + %1$s, %2$s %3$s + %2$s %3$s + %1$s, %2$s + %2$s %3$s - %7$s %8$s + %1$s %3$s %2$s - %6$s %8$s %7$s + %2$s %3$s, %5$s - %7$s %8$s, %10$s + %2$s %3$s, %5$s - %7$s %8$s, %10$s + %1$s %3$s %2$s, %5$s - %6$s %8$s %7$s, %10$s + %1$s %3$s %2$s, %5$s - %6$s %8$s %7$s, %10$s + %3$s %2$s %4$s, %5$s - %8$s %7$s %9$s, %10$s + %3$s %2$s %4$s, %5$s - %8$s %7$s %9$s, %10$s + %1$s %3$s %2$s %4$s, %5$s - %6$s %8$s %7$s %9$s, %10$s + %1$s %3$s %2$s %4$s, %5$s - %6$s %8$s %7$s %9$s, %10$s + %1$s %3$s %2$s %4$s - %6$s %8$s %7$s %9$s + %3$s-%8$s %2$s + %1$s %3$s %2$s - %6$s %8$s %7$s + %3$s %2$s - %8$s %7$s %9$s + %3$s-%8$s %2$s %9$s + %1$s %3$s %2$s - %6$s %8$s %7$s %9$s + %b + diff --git a/core/res/res/values-es-rES/donottranslate-cldr.xml b/core/res/res/values-es-rES/donottranslate-cldr.xml new file mode 100644 index 0000000000000000000000000000000000000000..c1dc58b815d028ba036c58c5065b60da00e80cdd --- /dev/null +++ b/core/res/res/values-es-rES/donottranslate-cldr.xml @@ -0,0 +1,147 @@ + + + enero + febrero + marzo + abril + mayo + junio + julio + agosto + septiembre + octubre + noviembre + diciembre + + enero + febrero + marzo + abril + mayo + junio + julio + agosto + septiembre + octubre + noviembre + diciembre + + ene + feb + mar + abr + may + jun + jul + ago + sep + oct + nov + dic + + E + F + M + A + M + J + J + A + S + O + N + D + + domingo + lunes + martes + miércoles + jueves + viernes + sábado + + dom + lun + mar + mié + jue + vie + sáb + + dom + lun + mar + mié + jue + vie + sáb + + D + L + M + M + J + V + S + + a.m. + p.m. + ayer + hoy + mañana + + %-k:%M + %-l:%M %p + %-l:%M %^p + h:mm a + H:mm + %d/%m/%Y + dd/MM/yyyy + "%s/%s/%s" + %-e de %B de %Y + %H:%M:%S + %H:%M:%S %d/%m/%Y + %2$s %1$s + %1$s %3$s + %d/%m/%Y + %-e de %B + %-B + %B de %Y + %-e %b + %-b + %b %Y + %1$s – %2$s + %2$s – %5$s + %3$s/%2$s – %8$s/%7$s + %1$s %3$s/%2$s – %6$s %8$s/%7$s + %3$s/%2$s/%4$s – %8$s/%7$s/%9$s + %1$s %3$s/%2$s/%4$s – %6$s %8$s/%7$s/%9$s + %5$s %1$s %3$s/%2$s/%4$s – %10$s %6$s %8$s/%7$s/%9$s + %5$s %3$s/%2$s – %10$s %8$s/%7$s + %5$s %1$s, %3$s-%2$s – %10$s %6$s, %8$s-%7$s + %5$s %3$s/%2$s/%4$s – %10$s %8$s/%7$s/%9$s + %3$s %1$s %2$s – %6$s %4$s %5$s + %1$s %2$s – %4$s %5$s + %3$s %2$s – %6$s %5$s + %1$s %2$s %3$s + %2$s %3$s + %1$s %2$s + %3$s de %2$s – %8$s de %7$s + %1$s %3$s %2$s – %6$s %8$s %7$s + %5$s %3$s de %2$s – %10$s %8$s de %7$s + %5$s %3$s de %2$s – %10$s %8$s de %7$s + %5$s %1$s %3$s %2$s – %10$s %6$s %8$s %7$s + %5$s %1$s %3$s %2$s – %10$s %6$s %8$s %7$s + %5$s %3$s %2$s %4$s – %10$s %8$s %7$s %9$s + %5$s %3$s %2$s %4$s – %10$s %8$s %7$s %9$s + %5$s %1$s, %3$s %2$s %4$s – %10$s %6$s, %8$s %7$s %9$s + %5$s %1$s, %3$s %2$s %4$s – %10$s %6$s, %8$s %7$s %9$s + %1$s, %3$s %2$s %4$s – %6$s, %8$s %7$s %9$s + %3$s–%8$s %2$s + %1$s %3$s %2$s – %6$s %8$s %7$s + %3$s %2$s – %8$s %7$s %9$s + %3$s–%8$s %2$s %9$s + %1$s %3$s %2$s – %6$s %8$s %7$s %9$s + %b + diff --git a/core/res/res/values-es-rUS/donottranslate-cldr.xml b/core/res/res/values-es-rUS/donottranslate-cldr.xml new file mode 100644 index 0000000000000000000000000000000000000000..d6d89540af918e9ff5a0c63748b7d19d307f7e6c --- /dev/null +++ b/core/res/res/values-es-rUS/donottranslate-cldr.xml @@ -0,0 +1,147 @@ + + + enero + febrero + marzo + abril + mayo + junio + julio + agosto + septiembre + octubre + noviembre + diciembre + + enero + febrero + marzo + abril + mayo + junio + julio + agosto + septiembre + octubre + noviembre + diciembre + + ene + feb + mar + abr + may + jun + jul + ago + sep + oct + nov + dic + + E + F + M + A + M + J + J + A + S + O + N + D + + domingo + lunes + martes + miércoles + jueves + viernes + sábado + + dom + lun + mar + mié + jue + vie + sáb + + dom + lun + mar + mié + jue + vie + sáb + + D + L + M + M + J + V + S + + a.m. + p.m. + ayer + hoy + mañana + + %-k:%M + %-l:%M %p + %-l:%M %^p + h:mm a + H:mm + %-m/%-e/%Y + M/d/yyyy + "%s/%s/%s" + %-e de %B de %Y + %-l:%M:%S %p + %-l:%M:%S %p %b %-e, %Y + %2$s %1$s + %1$s %3$s + %b %-e, %Y + %-e de %B + %-B + %B de %Y + %-e de %b + %b + %b de %Y + %1$s a el %2$s + %2$s a el %5$s + %2$s/%3$s - %7$s/%8$s + %1$s %2$s/%3$s - %6$s %7$s/%8$s + %2$s/%3$s/%4$s - %7$s/%8$s/%9$s + %1$s %2$s/%3$s/%4$s - %6$s %7$s/%8$s/%9$s + %5$s %1$s %2$s/%3$s/%4$s a el %10$s %6$s %7$s/%8$s/%9$s + %5$s %2$s/%3$s a el %10$s %7$s/%8$s + %5$s %1$s %2$s/%3$s a el %10$s %6$s %7$s/%8$s + %5$s %2$s/%3$s/%4$s a el %10$s %7$s/%8$s/%9$s + %3$s %1$s %2$s a el %6$s %4$s %5$s + %1$s %2$s a el %4$s %5$s + %3$s %2$s a el %6$s %5$s + %1$s %2$s %3$s + %2$s %3$s + %1$s %2$s + %3$s de %2$s a el %8$s de %7$s + %1$s %3$s de %2$s a el %6$s %8$s de %7$s + %5$s %3$s de %2$s a el %10$s %8$s de %7$s + %5$s %3$s de %2$s a el %10$s %8$s de %7$s + %5$s %1$s %3$s de %2$s a el %10$s %6$s %8$s de %7$s + %5$s %1$s %3$s de %2$s a el %10$s %6$s %8$s de %7$s + %5$s %3$s de %2$s de %4$s a el %10$s %8$s de %7$s de %9$s + %5$s %3$s de %2$s de %4$s a el %10$s %8$s de %7$s de %9$s + %5$s %1$s %3$s de %2$s de %4$s a el %10$s %6$s %8$s de %7$s de %9$s + %5$s %1$s %3$s de %2$s de %4$s a el %10$s %6$s %8$s de %7$s de %9$s + %1$s %3$s de %2$s de %4$s a el %6$s %8$s de %7$s de %9$s + %3$s-%8$s de %2$s + %1$s %3$s de %2$s a el %6$s %8$s de %7$s + %3$s de %2$s al %8$s de %7$s de %9$s + %3$s-%8$s de %2$s de %9$s + %1$s %3$s de %2$s al %6$s %8$s de %7$s de %9$s + %b + diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml index 7b60a39f042b6509cbeb330a1414444e267f5540..84435aa8dee83cc91364675da6785bd1fae86a5b 100644 --- a/core/res/res/values-es-rUS/strings.xml +++ b/core/res/res/values-es-rUS/strings.xml @@ -178,14 +178,10 @@ "Admite una aplicación para cambiar la configuración actual, como el tamaño de fuente local o general." "reiniciar otras aplicaciones" "Admite una aplicación que reinicia otras aplicaciones por la fuerza." - "impedir la detención" - "Admite una aplicación que ejecuta cualquier tipo de proceso en primer plano, de manera que no se pueda suprimir. Se debe evitar utilizarlo en aplicaciones normales." "provocar que la aplicación se acerque" "Admite una aplicación que provoca que cualquier actividad del fondo se acerque y vuelva a alejarse. Se debe evitar utilizarlo en aplicaciones normales." "recuperar el estado interno del sistema" "Admite que la aplicación recupere el estado interno del sistema. Las aplicaciones maliciosas pueden recuperar una gran variedad de información privada y segura que normalmente nunca necesitaría." - "publicar servicios de bajo nivel" - "Admite que la aplicación publique sus propios servicios del sistema de bajo nivel. Las aplicaciones maliciosas pueden apropiarse del sistema y robar o corromper cualquiera de sus datos." "verificar y controlar todos los lanzamientos de actividades" "Admite una aplicación que verifica y controla el lanzamiento de actividades por parte del sistema. Las aplicaciones maliciosas pueden comprometer totalmente al sistema. Este permiso sólo es necesario para el desarrollo, nunca para el uso normal del teléfono." "enviar emisión de paquete eliminado" @@ -198,8 +194,6 @@ "Admite una aplicación que controla la cantidad máxima de procesos que se ejecutarán. No se utiliza nunca en aplicaciones normales." "cerrar todas las aplicaciones del fondo" "Admite una aplicación que controla si las actividades siempre finalizan cuando van al fondo. No se utiliza nunca en aplicaciones normales." - "instalar automáticamente las actualizaciones del sistema" - "Admite una aplicación que recibe notificaciones sobre las actualizaciones pendientes del sistema y activa su instalación. Las aplicaciones maliciosas pueden utilizarlo para corromper el sistema con actualizaciones no autorizadas o, en general, para interferir en el proceso de actualización." "modificar la estadística de la batería" "Admite la modificación de estadísticas recopiladas sobre la batería. Las aplicaciones normales no deben utilizarlo." "mostrar ventanas no autorizadas" @@ -436,9 +430,6 @@ "Contraseña" "Inicia sesión" "Nombre de usuario o contraseña incorrecta." - "h:mm AA" - "%-l:%M%P" - "%-l:%M%p" "%-l%P" "%-l%p" "Borrar notificaciones" @@ -470,9 +461,6 @@ "ingresar" "borrar" "Buscar" - "Hoy" - "Ayer" - "Mañana" "hace 1 mes" "Anterior a 1 mes atrás" @@ -554,13 +542,6 @@ "semanas" "año" "años" - "Domingo" - "Lunes" - "Martes" - "Miércoles" - "Jueves" - "Viernes" - "Sábado" "Los días de semana (lunes a viernes)" "Diariamente" "Semanalmente el día %s" @@ -570,137 +551,15 @@ "Lo sentimos, este video no es válido para las transmisiones a este dispositivo." "Lo sentimos, no se puede reproducir este video." "Aceptar" - "AM" - "PM" - "%m/%d/%Y" - "%1$s, %2$s, %3$s%4$s, %5$s, %6$s" - "%1$s, %2$s%4$s, %5$s" - "%2$s, %3$s%5$s, %6$s" - "%2$s%5$s" - "%1$s%2$s" - "%1$s, %2$s, %3$s" - "%2$s, %3$s" - "%1$s, %3$s" - "%1$s, %2$s" "%1$s, %2$s" - "%1$s, %2$s" - "MMMM' 'd', 'yyyy" - "d' 'MMMM', 'yyyy" - "MMM' 'd', 'yyyy" - "d' 'MMM', 'yyyy" - "h':'mm' 'a" - "HH':'mm" "mediodía" "Mediodía" "medianoche" "Medianoche" - "%B %-d" - "%B %-d, %Y" - "%B %Y" - "%H:%M:%S" - "%H:%M:%S %B %-d, %Y" - "%2$s %3$s%7$s %8$s" - "%1$s, %2$s %3$s%6$s, %7$s %8$s" - "%2$s %3$s%7$s %8$s, %9$s" - "%1$s, %2$s %3$s%6$s, %7$s %8$s, %9$s" - "%2$s %3$s, %5$s%7$s %8$s, %10$s" - "%1$s, %2$s %3$s, %5$s%6$s, %7$s %8$s, %10$s" - "%2$s %3$s, %4$s, %5$s%7$s %8$s, %9$s, %10$s" - "%1$s, %2$s %3$s, %4$s, %5$s%6$s, %7$s %8$s, %9$s, %10$s" - "%2$s/%3$s%7$s/%8$s" - "%1$s, %2$s/%3$s%6$s, %7$s/%8$s" - "%2$s/%3$s/%4$s%7$s/%8$s/%9$s" - "%1$s, %2$s/%3$s/%4$s%6$s, %7$s/%8$s/%9$s" - "%2$s/%3$s, %5$s%7$s/%8$s, %10$s" - "%1$s, %2$s/%3$s, %5$s%6$s, %7$s/%8$s, %10$s" - "%2$s/%3$s/%4$s, %5$s%7$s/%8$s/%9$s, %10$s" - "%1$s, %2$s/%3$s/%4$s, %5$s%6$s, %7$s/%8$s/%9$s, %10$s" - "%2$s %3$s%8$s" - "%1$s, %2$s %3$s%6$s, %7$s %8$s" - "%2$s %3$s%8$s, %9$s" - "%1$s, %2$s %3$s, %4$s%6$s, %7$s %8$s, %9$s" - "%2$s %3$s, %5$s%7$s %8$s, %10$s" - "%1$s, %2$s %3$s, %5$s%6$s, %7$s %8$s, %10$s" - "%2$s %3$s, %4$s, %5$s%7$s %8$s, %9$s, %10$s" - "%1$s, %2$s %3$s, %4$s, %5$s%6$s, %7$s %8$s, %9$s, %10$s" - "%b %-d, %Y" - "%b %Y" - "%b %-d" - "Domingo" - "Lunes" - "Martes" - "Miércoles" - "Jueves" - "Viernes" - "Sábado" - "Dom." - "Lun." - "Mar." - "Mié." - "Jue." - "Vie." - "Sáb." - "Dom." - "Lun." - "Mar." - "Nosotros" - "Jue." - "V" - "Sáb." - "Dom." - "L" - "Mar." - "M" - "Jue." - "V" - "Sáb." - "D" - "L" - "Mar." - "M" - "Jue." - "V" - "D" - "Enero" - "Febrero" - "Marzo" - "Abril" - "Mayo" - "Junio" - "Julio" - "Agosto" - "Septiembre" - "Octubre" - "Noviembre" - "Diciembre" - "Ene." - "Feb." - "Mar." - "Abr." - "Mayo" - "Jun." - "Jul." - "Ago." - "Sep." - "Oct." - "Nov." - "Dic." - "E" - "V" - "M" - "A" - "M" - "E" - "J" - "Ago." - "D" - "O" - "N" - "Dic." "%1$02d:%2$02d" "%1$d:%2$02d:%3$02d" "Seleccionar todos" diff --git a/core/res/res/values-es/donottranslate-cldr.xml b/core/res/res/values-es/donottranslate-cldr.xml new file mode 100644 index 0000000000000000000000000000000000000000..c1dc58b815d028ba036c58c5065b60da00e80cdd --- /dev/null +++ b/core/res/res/values-es/donottranslate-cldr.xml @@ -0,0 +1,147 @@ + + + enero + febrero + marzo + abril + mayo + junio + julio + agosto + septiembre + octubre + noviembre + diciembre + + enero + febrero + marzo + abril + mayo + junio + julio + agosto + septiembre + octubre + noviembre + diciembre + + ene + feb + mar + abr + may + jun + jul + ago + sep + oct + nov + dic + + E + F + M + A + M + J + J + A + S + O + N + D + + domingo + lunes + martes + miércoles + jueves + viernes + sábado + + dom + lun + mar + mié + jue + vie + sáb + + dom + lun + mar + mié + jue + vie + sáb + + D + L + M + M + J + V + S + + a.m. + p.m. + ayer + hoy + mañana + + %-k:%M + %-l:%M %p + %-l:%M %^p + h:mm a + H:mm + %d/%m/%Y + dd/MM/yyyy + "%s/%s/%s" + %-e de %B de %Y + %H:%M:%S + %H:%M:%S %d/%m/%Y + %2$s %1$s + %1$s %3$s + %d/%m/%Y + %-e de %B + %-B + %B de %Y + %-e %b + %-b + %b %Y + %1$s – %2$s + %2$s – %5$s + %3$s/%2$s – %8$s/%7$s + %1$s %3$s/%2$s – %6$s %8$s/%7$s + %3$s/%2$s/%4$s – %8$s/%7$s/%9$s + %1$s %3$s/%2$s/%4$s – %6$s %8$s/%7$s/%9$s + %5$s %1$s %3$s/%2$s/%4$s – %10$s %6$s %8$s/%7$s/%9$s + %5$s %3$s/%2$s – %10$s %8$s/%7$s + %5$s %1$s, %3$s-%2$s – %10$s %6$s, %8$s-%7$s + %5$s %3$s/%2$s/%4$s – %10$s %8$s/%7$s/%9$s + %3$s %1$s %2$s – %6$s %4$s %5$s + %1$s %2$s – %4$s %5$s + %3$s %2$s – %6$s %5$s + %1$s %2$s %3$s + %2$s %3$s + %1$s %2$s + %3$s de %2$s – %8$s de %7$s + %1$s %3$s %2$s – %6$s %8$s %7$s + %5$s %3$s de %2$s – %10$s %8$s de %7$s + %5$s %3$s de %2$s – %10$s %8$s de %7$s + %5$s %1$s %3$s %2$s – %10$s %6$s %8$s %7$s + %5$s %1$s %3$s %2$s – %10$s %6$s %8$s %7$s + %5$s %3$s %2$s %4$s – %10$s %8$s %7$s %9$s + %5$s %3$s %2$s %4$s – %10$s %8$s %7$s %9$s + %5$s %1$s, %3$s %2$s %4$s – %10$s %6$s, %8$s %7$s %9$s + %5$s %1$s, %3$s %2$s %4$s – %10$s %6$s, %8$s %7$s %9$s + %1$s, %3$s %2$s %4$s – %6$s, %8$s %7$s %9$s + %3$s–%8$s %2$s + %1$s %3$s %2$s – %6$s %8$s %7$s + %3$s %2$s – %8$s %7$s %9$s + %3$s–%8$s %2$s %9$s + %1$s %3$s %2$s – %6$s %8$s %7$s %9$s + %b + diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml index a9f267a308b1b77b4b4ff18257d94d180b3e770c..920ac3e5dcd0b976723ffdfa1a66634a9a5d1f8e 100644 --- a/core/res/res/values-es/strings.xml +++ b/core/res/res/values-es/strings.xml @@ -161,14 +161,10 @@ "Permite que una aplicación cambie la configuración actual como, por ejemplo, la configuración local o el tamaño de fuente general." "reiniciar otras aplicaciones" "Permite que una aplicación reinicie de forma forzosa otras aplicaciones." - "impedir su interrupción" - "Permite que una aplicación ejecute cualquier proceso en segundo plano, de forma que no se pueda interrumpir. No debería ser necesario nunca para las aplicaciones normales." "forzar el cierre de la aplicación" "Permite que una aplicación fuerce a cualquier actividad en segundo plano a cerrarse y volver a la pantalla anterior. No debería ser necesario nunca para las aplicaciones normales." "recuperar estado interno del sistema" "Permite que la aplicación recupere el estado interno del sistema. Las aplicaciones malintencionadas pueden recuperar una amplia variedad de información protegida y privada que normalmente no deberían necesitar." - "publicar servicios de nivel inferior" - "Permite que la aplicación publique sus propios servicios de sistema de nivel inferior. Las aplicaciones malintencionadas pueden hacerse con el control del sistema, y robar o dañar los datos contenidos en él." "supervisar y controlar la ejecución de todas las aplicaciones" "Permite que una aplicación supervise y controle la ejecución de las actividades por parte del sistema. Las aplicaciones malintencionadas pueden vulnerar la seguridad del sistema. Este permiso sólo es necesario para tareas de desarrollo, nunca para el uso habitual del teléfono." "enviar emisión eliminada de paquete" @@ -181,8 +177,6 @@ "Permite que una aplicación controle el número máximo de procesos que se ejecutarán. No es necesario nunca para las aplicaciones normales." "hacer que se cierren todas las aplicaciones en segundo plano" "Permite que una aplicación controle si las actividades finalizan siempre en cuanto pasan a segundo plano. No es necesario nunca para las aplicaciones normales." - "instalar actualizaciones del sistema de forma automática" - "Permite que una aplicación reciba notificaciones sobre actualizaciones pendientes del sistema e inicie su instalación. Las aplicaciones malintencionadas pueden utilizar este permiso para provocar daños en el sistema con actualizaciones no autorizadas o interferir de forma general en el proceso de actualización." "modificar estadísticas de la batería" "Permite la modificación de estadísticas recopiladas sobre la batería. No está destinado al uso por parte de aplicaciones normales." "mostrar ventanas no autorizadas" @@ -418,9 +412,6 @@ "Contraseña" "Acceder" "Nombre de usuario o contraseña no válido" - "h:mm AA" - "%-l:%M%P" - "%-l:%M%p" "%-l%P" "%-l%p" "Cerrar notificaciones" @@ -452,9 +443,6 @@ "intro" "suprimir" "Buscar" - "Hoy" - "Ayer" - "Mañana" "Hace un mes" "Hace más de un mes" @@ -536,13 +524,6 @@ "semanas" "año" "años" - "Domingo" - "Lunes" - "Martes" - "Miércoles" - "Jueves" - "Viernes" - "Sábado" "Todos los días laborables (Lun-Vie)" "Diariamente" "Semanalmente, el %s" @@ -552,137 +533,15 @@ "Este vídeo no se puede transmitir al dispositivo." "Este vídeo no se puede reproducir." "Aceptar" - "a.m." - "p.m." - "%d/%m/%Y" - "%1$s, %2$s, %3$s%4$s, %5$s, %6$s" - "%1$s, %2$s%4$s, %5$s" - "%2$s, %3$s%5$s, %6$s" - "%2$s%5$s" - "%1$s%2$s" - "%1$s, %2$s, %3$s" - "%2$s, %3$s" - "%1$s, %3$s" - "%1$s, %2$s" "%1$s, %2$s" - "%1$s, %2$s" - "d' de 'MMMM' de 'yyyy" - "d' de 'MMMM' de 'yyyy" - "d' de 'MMM' de 'yyyy" - "d' 'MMM', 'yyyy" - "h':'mm' 'a" - "HH':'mm" "mediodía" "Mediodía" "medianoche" "Medianoche" - "%-d %B" - "%-d %B, %Y" - "%B %Y" - "%H:%M:%S" - "%H:%M:%S %B %-d, %Y" - "%3$s %2$s%8$s %7$s" - "%1$s, %3$s %2$s%6$s, %8$s %7$s" - "%3$s %2$s%8$s %7$s, %9$s" - "%1$s, %2$s %3$s%6$s, %7$s %8$s, %9$s" - "%3$s de %2$s, %5$s%8$s de %7$s, %10$s" - "%1$s, %3$s %2$s, %5$s%6$s, %8$s %7$s, %10$s" - "%3$s de %2$s de %4$s, %5$s%8$s de %7$s de %9$s, %10$s" - "%1$s, %3$s %2$s, %4$s, %5$s%6$s, %8$s %7$s, %9$s, %10$s" - "%3$s/%2$s%8$s/%7$s" - "%1$s, %2$s/%3$s%6$s, %7$s/%8$s" - "%3$s/%2$s/%4$s%8$s/%7$s/%9$s" - "%1$s, %3$s/%2$s/%4$s%6$s, %8$s/%7$s/%9$s" - "%3$s/%2$s, %5$s%8$s/%7$s, %10$s" - "%1$s, %3$s/%2$s, %5$s%6$s, %8$s/%7$s, %10$s" - "%3$s/%2$s/%4$s, %5$s%8$s/%7$s/%9$s, %10$s" - "%1$s, %3$s/%2$s/%4$s, %5$s%6$s, %8$s/%7$s/%9$s, %10$s" - "%3$s%8$s %2$s" - "%1$s, %2$s %3$s%6$s, %7$s %8$s" - "%3$s%8$s %2$s, %9$s" - "%1$s, %3$s de %2$s de %4$s%6$s, %8$s de %7$s de %9$s" - "%3$s %2$s, %5$s%8$s %7$s, %10$s" - "%1$s, %3$s %2$s, %5$s%6$s, %8$s %7$s, %10$s" - "%2$s %3$s, %4$s, %5$s%7$s %8$s, %9$s, %10$s" - "%1$s, %2$s %3$s, %4$s, %5$s%6$s, %7$s %8$s, %9$s, %10$s" - "%-d de %b de %Y" - "%b %Y" - "%b %-d" - "Domingo" - "Lunes" - "Martes" - "Miércoles" - "Jueves" - "Viernes" - "Sábado" - "Dom" - "Lun" - "Mar" - "Mié" - "Jue" - "Vie" - "Sáb" - "Do" - "Lu" - "Ma" - "Mi" - "Ju" - "Vi" - "Sá" - "Do" - "L" - "Ma" - "Mi" - "Ju" - "V" - "S" - "D" - "Mz" - "M" - "Mi" - "M" - "V" - "D" - "Enero" - "Febrero" - "Marzo" - "Abril" - "Mayo" - "Junio" - "Julio" - "Agosto" - "Septiembre" - "Octubre" - "Noviembre" - "Diciembre" - "Ene" - "Feb" - "Mar" - "Abr" - "May" - "Jun" - "Jul" - "Ago" - "Sep" - "Oct" - "Nov" - "Dic" - "E" - "V" - "Mz" - "A" - "My" - "J" - "E" - "Ag" - "S" - "O" - "N" - "D" "%1$02d:%2$02d" "%1$d:%2$02d:%3$02d" "Seleccionar todo" diff --git a/core/res/res/values-fi-rFI/donottranslate-cldr.xml b/core/res/res/values-fi-rFI/donottranslate-cldr.xml new file mode 100644 index 0000000000000000000000000000000000000000..df3866e72bdaac33b96e27a939f119545f5db9ea --- /dev/null +++ b/core/res/res/values-fi-rFI/donottranslate-cldr.xml @@ -0,0 +1,147 @@ + + + tammikuu + helmikuu + maaliskuu + huhtikuu + toukokuu + kesäkuu + heinäkuu + elokuu + syyskuu + lokakuu + marraskuu + joulukuu + + tammikuuta + helmikuuta + maaliskuuta + huhtikuuta + toukokuuta + kesäkuuta + heinäkuuta + elokuuta + syyskuuta + lokakuuta + marraskuuta + joulukuuta + + tammikuuta + helmikuuta + maaliskuuta + huhtikuuta + toukokuuta + kesäkuuta + heinäkuuta + elokuuta + syyskuuta + lokakuuta + marraskuuta + joulukuuta + + T + H + M + H + T + K + H + E + S + L + M + J + + sunnuntaina + maanantaina + tiistaina + keskiviikkona + torstaina + perjantaina + lauantaina + + su + ma + ti + ke + to + pe + la + + su + ma + ti + ke + to + pe + la + + S + M + T + K + T + P + L + + ap. + ip. + eilen + tänään + huomenna + + %-k.%M + %-l.%M %p + %-l.%M %^p + h.mm a + H.mm + %-e.%-m.%Y + d.M.yyyy + "%s.%s.%s" + %-e. %B %Y + %-k.%M.%S + %-k.%M.%S %-e.%-m.%Y + %2$s %1$s + %1$s %3$s + %-e.%-m.%Y + %-e. %B + %-b + %-B %Y + %-e. %b + %-b + %-b %Y + %1$s–%2$s + %2$s–%5$s + %3$s.%2$s.–%8$s.%7$s. + %1$s %3$s.%2$s. – %6$s %8$s.%7$s. + %3$s.%2$s.%4$s–%8$s.%7$s.%9$s + %1$s %3$s.%2$s.%4$s – %6$s %8$s.%7$s.%9$s + %5$s %1$s %3$s.%2$s.%4$s–%10$s %6$s %8$s.%7$s.%9$s + %5$s %3$s.%2$s.–%10$s %8$s.%7$s. + %5$s %1$s %3$s.%2$s.–%10$s %6$s %8$s.%7$s. + %5$s %3$s.%2$s.%4$s–%10$s %8$s.%7$s.%9$s + %3$s %1$s %2$s–%6$s %4$s %5$s + %1$s %2$s–%4$s %5$s + %3$s %2$s–%6$s %5$s + %1$s %2$s %3$s + %2$s %3$s + %1$s %2$s + %3$s. %2$s–%8$s. %7$s + %1$s %3$s. %2$s–%6$s %8$s. %7$s + %5$s %3$s. %2$s–%10$s %8$s. %7$s + %5$s %3$s. %2$s–%10$s %8$s. %7$s + %5$s %1$s %3$s. %2$s–%10$s %6$s %8$s. %7$s + %5$s %1$s %3$s. %2$s–%10$s %6$s %8$s. %7$s + %5$s %3$s. %2$s %4$s–%10$s %8$s. %7$s %9$s + %5$s %3$s. %2$s %4$s–%10$s %8$s. %7$s %9$s + %5$s %1$s %3$s. %2$s %4$s–%10$s %6$s %8$s. %7$s %9$s + %5$s %1$s %3$s. %2$s %4$s–%10$s %6$s %8$s. %7$s %9$s + %1$s %3$s. %2$s %4$s–%6$s %8$s. %7$s %9$s + %3$s.–%8$s. %2$s + %1$s %3$s. %2$s–%6$s %8$s. %7$s + %3$s. %2$s – %8$s. %7$s %9$s + %3$s.–%8$s. %2$s %9$s + %1$s %3$s. %2$s – %6$s %8$s. %7$s %9$s + %b + diff --git a/core/res/res/values-fr-rBE/donottranslate-cldr.xml b/core/res/res/values-fr-rBE/donottranslate-cldr.xml new file mode 100644 index 0000000000000000000000000000000000000000..e1908373a3fe762dec2148e33193f53f0607dfaf --- /dev/null +++ b/core/res/res/values-fr-rBE/donottranslate-cldr.xml @@ -0,0 +1,147 @@ + + + janvier + février + mars + avril + mai + juin + juillet + août + septembre + octobre + novembre + décembre + + janvier + février + mars + avril + mai + juin + juillet + août + septembre + octobre + novembre + décembre + + janv. + févr. + mars + avr. + mai + juin + juil. + août + sept. + oct. + nov. + déc. + + J + F + M + A + M + J + J + A + S + O + N + D + + dimanche + lundi + mardi + mercredi + jeudi + vendredi + samedi + + dim. + lun. + mar. + mer. + jeu. + ven. + sam. + + dim. + lun. + mar. + mer. + jeu. + ven. + sam. + + D + L + M + M + J + V + S + + matin + soir + hier + aujourd’hui + demain + + %-k:%M + %-l:%M %p + %-l:%M %^p + h:mm a + H:mm + %-e/%m/%Y + d/MM/yyyy + "%s/%s/%s" + %-e %B %Y + %H:%M:%S + %H:%M:%S %-e %b %Y + %2$s %1$s + %1$s %3$s + %-e %b %Y + %-e %B + %-B + %B %Y + %-e %b + %b + %b %Y + du %1$s au %2$s + du %2$s au %5$s + %3$s/%2$s - %8$s/%7$s + %1$s %3$s/%2$s - %6$s %8$s/%7$s + %3$s/%2$s/%4$s - %8$s/%7$s/%9$s + %1$s %3$s/%2$s/%4$s - %6$s %8$s/%7$s/%9$s + du %5$s %1$s %3$s/%2$s/%4$s au %10$s %6$s %8$s/%7$s/%9$s + du %5$s %3$s/%2$s au %10$s %8$s/%7$s + du %5$s %1$s %3$s/%2$s au %10$s %6$s %8$s/%7$s + du %5$s %3$s/%2$s/%4$s au %10$s %8$s/%7$s/%9$s + du %3$s %1$s %2$s au %6$s %4$s %5$s + du %1$s %2$s au %4$s %5$s + du %3$s %2$s au %6$s %5$s + %1$s %2$s %3$s + %2$s %3$s + %1$s %2$s + du %3$s %2$s au %8$s %7$s + du %1$s %3$s %2$s au %6$s %8$s %7$s + du %5$s %3$s %2$s au %10$s %8$s %7$s + du %5$s %3$s %2$s au %10$s %8$s %7$s + du %5$s %1$s %3$s %2$s au %10$s %6$s %8$s %7$s + du %5$s %1$s %3$s %2$s au %10$s %6$s %8$s %7$s + du %5$s %3$s %2$s %4$s au %10$s %8$s %7$s %9$s + du %5$s %3$s %2$s %4$s au %10$s %8$s %7$s %9$s + du %5$s %1$s %3$s %2$s %4$s au %10$s %6$s %8$s %7$s %9$s + du %5$s %1$s %3$s %2$s %4$s au %10$s %6$s %8$s %7$s %9$s + du %1$s %3$s %2$s %4$s au %6$s %8$s %7$s %9$s + %3$s-%8$s %2$s + du %1$s %3$s %2$s au %6$s %8$s %7$s + %3$s %2$s au %8$s %7$s %9$s + %3$s-%8$s %2$s %9$s + %1$s %3$s %2$s au %6$s %8$s %7$s %9$s + %b + diff --git a/core/res/res/values-fr-rCA/donottranslate-cldr.xml b/core/res/res/values-fr-rCA/donottranslate-cldr.xml new file mode 100644 index 0000000000000000000000000000000000000000..346b9710733122ea9f226dd9f547482a706adea4 --- /dev/null +++ b/core/res/res/values-fr-rCA/donottranslate-cldr.xml @@ -0,0 +1,147 @@ + + + janvier + février + mars + avril + mai + juin + juillet + août + septembre + octobre + novembre + décembre + + janvier + février + mars + avril + mai + juin + juillet + août + septembre + octobre + novembre + décembre + + janv. + févr. + mars + avr. + mai + juin + juil. + août + sept. + oct. + nov. + déc. + + J + F + M + A + M + J + J + A + S + O + N + D + + dimanche + lundi + mardi + mercredi + jeudi + vendredi + samedi + + dim. + lun. + mar. + mer. + jeu. + ven. + sam. + + dim. + lun. + mar. + mer. + jeu. + ven. + sam. + + D + L + M + M + J + V + S + + matin + soir + hier + aujourd’hui + demain + + %-k:%M + %-l:%M %p + %-l:%M %^p + h:mm a + H:mm + %Y-%m-%d + yyyy-MM-dd + "%s-%s-%s" + %-e %B %Y + %H:%M:%S + %H:%M:%S %Y-%m-%d + %2$s %1$s + %1$s %3$s + %Y-%m-%d + %-e %B + %-B + %B %Y + %-e %b + %-b + %b %Y + %1$s – %2$s + %2$s – %5$s + %2$s-%3$s – %7$s-%8$s + %1$s %2$s-%3$s – %6$s %7$s-%8$s + %4$s-%2$s-%3$s – %9$s-%7$s-%8$s + du %1$s %4$s-%2$s-%3$s au %6$s %9$s-%7$s-%8$s + %5$s %1$s %4$s-%2$s-%3$s – %10$s %6$s %9$s-%7$s-%8$s + %5$s %2$s-%3$s – %10$s %7$s-%8$s + %5$s %1$s %2$s-%3$s – %10$s %6$s %7$s-%8$s + %5$s %4$s-%2$s-%3$s – %10$s %9$s-%7$s-%8$s + %3$s %1$s %2$s – %6$s %4$s %5$s + %1$s %2$s – %4$s %5$s + %3$s %2$s – %6$s %5$s + %1$s %2$s %3$s + %2$s %3$s + %1$s %2$s + %3$s %2$s – %8$s %7$s + %1$s %3$s %2$s – %6$s %8$s %7$s + %5$s %3$s %2$s – %10$s %8$s %7$s + %5$s %3$s %2$s – %10$s %8$s %7$s + %5$s %1$s %3$s %2$s – %10$s %6$s %8$s %7$s + %5$s %1$s %3$s %2$s – %10$s %6$s %8$s %7$s + %5$s %3$s %2$s %4$s – %10$s %8$s %7$s %9$s + %5$s %3$s %2$s %4$s – %10$s %8$s %7$s %9$s + %5$s %1$s %3$s %2$s %4$s – %10$s %6$s %8$s %7$s %9$s + %5$s %1$s %3$s %2$s %4$s – %10$s %6$s %8$s %7$s %9$s + %1$s %3$s %2$s %4$s – %6$s %8$s %7$s %9$s + %3$s–%8$s %2$s + %1$s %3$s %2$s – %6$s %8$s %7$s + du %3$s %2$s au %8$s %7$s %9$s + %3$s–%8$s %2$s %9$s + du %1$s %3$s %2$s au %6$s %8$s %7$s %9$s + %b + diff --git a/core/res/res/values-fr-rCH/donottranslate-cldr.xml b/core/res/res/values-fr-rCH/donottranslate-cldr.xml new file mode 100644 index 0000000000000000000000000000000000000000..48db6b8d41692b362a0de9f03d7760bc1b39b62b --- /dev/null +++ b/core/res/res/values-fr-rCH/donottranslate-cldr.xml @@ -0,0 +1,147 @@ + + + janvier + février + mars + avril + mai + juin + juillet + août + septembre + octobre + novembre + décembre + + janvier + février + mars + avril + mai + juin + juillet + août + septembre + octobre + novembre + décembre + + janv. + févr. + mars + avr. + mai + juin + juil. + août + sept. + oct. + nov. + déc. + + J + F + M + A + M + J + J + A + S + O + N + D + + dimanche + lundi + mardi + mercredi + jeudi + vendredi + samedi + + dim. + lun. + mar. + mer. + jeu. + ven. + sam. + + dim. + lun. + mar. + mer. + jeu. + ven. + sam. + + D + L + M + M + J + V + S + + matin + soir + hier + aujourd’hui + demain + + %-k:%M + %-l:%M %p + %-l:%M %^p + h:mm a + H:mm + %d.%m.%Y + dd.MM.yyyy + "%s.%s.%s" + %-e %B %Y + %H:%M:%S + %H:%M:%S %-e %b %Y + %2$s %1$s + %1$s %3$s + %-e %b %Y + %-e %B + %-B + %B %Y + %-e %b + %b + %b %Y + du %1$s au %2$s + du %2$s au %5$s + %3$s.%2$s - %8$s.%7$s + %1$s, %3$s.%2$s - %6$s, %8$s.%7$s + %3$s.%2$s.%4$s - %8$s.%7$s.%9$s + %1$s, %3$s.%2$s.%4$s - %6$s, %8$s.%7$s.%9$s + du %5$s %1$s, %3$s.%2$s.%4$s au %10$s %6$s, %8$s.%7$s.%9$s + du %5$s %3$s.%2$s au %10$s %8$s.%7$s + du %5$s %1$s, %3$s.%2$s au %10$s %6$s, %8$s.%7$s + du %5$s %3$s.%2$s.%4$s au %10$s %8$s.%7$s.%9$s + du %3$s %1$s, %2$s au %6$s %4$s, %5$s + du %1$s, %2$s au %4$s, %5$s + du %3$s %2$s au %6$s %5$s + %1$s %2$s, %3$s + %2$s, %3$s + %1$s %2$s + du %3$s %2$s au %8$s %7$s + du %1$s, %3$s %2$s au %6$s, %8$s %7$s + du %5$s %3$s %2$s au %10$s %8$s %7$s + du %5$s %3$s %2$s au %10$s %8$s %7$s + du %5$s %1$s, %3$s %2$s au %10$s %6$s, %8$s %7$s + du %5$s %1$s, %3$s %2$s au %10$s %6$s, %8$s %7$s + du %5$s %3$s %2$s %4$s au %10$s %8$s %7$s %9$s + du %5$s %3$s %2$s %4$s au %10$s %8$s %7$s %9$s + du %5$s %1$s, %3$s %2$s %4$s au %10$s %6$s, %8$s %7$s %9$s + du %5$s %1$s, %3$s %2$s %4$s au %10$s %6$s, %8$s %7$s %9$s + du %1$s, %3$s %2$s %4$s au %6$s, %8$s %7$s %9$s + %3$s-%8$s %2$s + du %1$s, %3$s %2$s au %6$s, %8$s %7$s + %3$s %2$s au %8$s %7$s %9$s + %3$s-%8$s %2$s %9$s + %1$s, %3$s %2$s au %6$s, %8$s %7$s %9$s + %b + diff --git a/core/res/res/values-fr-rFR/donottranslate-cldr.xml b/core/res/res/values-fr-rFR/donottranslate-cldr.xml new file mode 100644 index 0000000000000000000000000000000000000000..f340e838411686fd45668da507a1d95aa5e957ba --- /dev/null +++ b/core/res/res/values-fr-rFR/donottranslate-cldr.xml @@ -0,0 +1,147 @@ + + + janvier + février + mars + avril + mai + juin + juillet + août + septembre + octobre + novembre + décembre + + janvier + février + mars + avril + mai + juin + juillet + août + septembre + octobre + novembre + décembre + + janv. + févr. + mars + avr. + mai + juin + juil. + août + sept. + oct. + nov. + déc. + + J + F + M + A + M + J + J + A + S + O + N + D + + dimanche + lundi + mardi + mercredi + jeudi + vendredi + samedi + + dim. + lun. + mar. + mer. + jeu. + ven. + sam. + + dim. + lun. + mar. + mer. + jeu. + ven. + sam. + + D + L + M + M + J + V + S + + matin + soir + hier + aujourd’hui + demain + + %-k:%M + %-l:%M %p + %-l:%M %^p + h:mm a + H:mm + %d/%m/%Y + dd/MM/yyyy + "%s/%s/%s" + %-e %B %Y + %H:%M:%S + %H:%M:%S %-e %b %Y + %2$s %1$s + %1$s %3$s + %-e %b %Y + %-e %B + %-B + %B %Y + %-e %b + %-b + %b %Y + %1$s – %2$s + %2$s – %5$s + %3$s/%2$s - %8$s/%7$s + %1$s %3$s/%2$s - %6$s %8$s/%7$s + %3$s/%2$s/%4$s – %8$s/%7$s/%9$s + %1$s %3$s/%2$s/%4$s – %6$s %8$s/%7$s/%9$s + %5$s %1$s %3$s/%2$s/%4$s – %10$s %6$s %8$s/%7$s/%9$s + %5$s %3$s/%2$s – %10$s %8$s/%7$s + %5$s %1$s %3$s/%2$s – %10$s %6$s %8$s/%7$s + %5$s %3$s/%2$s/%4$s – %10$s %8$s/%7$s/%9$s + %3$s %1$s %2$s – %6$s %4$s %5$s + %1$s %2$s – %4$s %5$s + %3$s %2$s – %6$s %5$s + %1$s %2$s %3$s + %2$s %3$s + %1$s %2$s + %3$s %2$s – %8$s %7$s + %1$s %3$s %2$s – %6$s %8$s %7$s + %5$s %3$s %2$s – %10$s %8$s %7$s + %5$s %3$s %2$s – %10$s %8$s %7$s + %5$s %1$s %3$s %2$s – %10$s %6$s %8$s %7$s + %5$s %1$s %3$s %2$s – %10$s %6$s %8$s %7$s + %5$s %3$s %2$s %4$s – %10$s %8$s %7$s %9$s + %5$s %3$s %2$s %4$s – %10$s %8$s %7$s %9$s + %5$s %1$s %3$s %2$s %4$s – %10$s %6$s %8$s %7$s %9$s + %5$s %1$s %3$s %2$s %4$s – %10$s %6$s %8$s %7$s %9$s + %1$s %3$s %2$s %4$s – %6$s %8$s %7$s %9$s + %3$s–%8$s %2$s + %1$s %3$s %2$s – %6$s %8$s %7$s + %3$s %2$s – %8$s %7$s %9$s + %3$s–%8$s %2$s %9$s + %1$s %3$s %2$s – %6$s %8$s %7$s %9$s + %b + diff --git a/core/res/res/values-fr/donottranslate-cldr.xml b/core/res/res/values-fr/donottranslate-cldr.xml new file mode 100644 index 0000000000000000000000000000000000000000..f340e838411686fd45668da507a1d95aa5e957ba --- /dev/null +++ b/core/res/res/values-fr/donottranslate-cldr.xml @@ -0,0 +1,147 @@ + + + janvier + février + mars + avril + mai + juin + juillet + août + septembre + octobre + novembre + décembre + + janvier + février + mars + avril + mai + juin + juillet + août + septembre + octobre + novembre + décembre + + janv. + févr. + mars + avr. + mai + juin + juil. + août + sept. + oct. + nov. + déc. + + J + F + M + A + M + J + J + A + S + O + N + D + + dimanche + lundi + mardi + mercredi + jeudi + vendredi + samedi + + dim. + lun. + mar. + mer. + jeu. + ven. + sam. + + dim. + lun. + mar. + mer. + jeu. + ven. + sam. + + D + L + M + M + J + V + S + + matin + soir + hier + aujourd’hui + demain + + %-k:%M + %-l:%M %p + %-l:%M %^p + h:mm a + H:mm + %d/%m/%Y + dd/MM/yyyy + "%s/%s/%s" + %-e %B %Y + %H:%M:%S + %H:%M:%S %-e %b %Y + %2$s %1$s + %1$s %3$s + %-e %b %Y + %-e %B + %-B + %B %Y + %-e %b + %-b + %b %Y + %1$s – %2$s + %2$s – %5$s + %3$s/%2$s - %8$s/%7$s + %1$s %3$s/%2$s - %6$s %8$s/%7$s + %3$s/%2$s/%4$s – %8$s/%7$s/%9$s + %1$s %3$s/%2$s/%4$s – %6$s %8$s/%7$s/%9$s + %5$s %1$s %3$s/%2$s/%4$s – %10$s %6$s %8$s/%7$s/%9$s + %5$s %3$s/%2$s – %10$s %8$s/%7$s + %5$s %1$s %3$s/%2$s – %10$s %6$s %8$s/%7$s + %5$s %3$s/%2$s/%4$s – %10$s %8$s/%7$s/%9$s + %3$s %1$s %2$s – %6$s %4$s %5$s + %1$s %2$s – %4$s %5$s + %3$s %2$s – %6$s %5$s + %1$s %2$s %3$s + %2$s %3$s + %1$s %2$s + %3$s %2$s – %8$s %7$s + %1$s %3$s %2$s – %6$s %8$s %7$s + %5$s %3$s %2$s – %10$s %8$s %7$s + %5$s %3$s %2$s – %10$s %8$s %7$s + %5$s %1$s %3$s %2$s – %10$s %6$s %8$s %7$s + %5$s %1$s %3$s %2$s – %10$s %6$s %8$s %7$s + %5$s %3$s %2$s %4$s – %10$s %8$s %7$s %9$s + %5$s %3$s %2$s %4$s – %10$s %8$s %7$s %9$s + %5$s %1$s %3$s %2$s %4$s – %10$s %6$s %8$s %7$s %9$s + %5$s %1$s %3$s %2$s %4$s – %10$s %6$s %8$s %7$s %9$s + %1$s %3$s %2$s %4$s – %6$s %8$s %7$s %9$s + %3$s–%8$s %2$s + %1$s %3$s %2$s – %6$s %8$s %7$s + %3$s %2$s – %8$s %7$s %9$s + %3$s–%8$s %2$s %9$s + %1$s %3$s %2$s – %6$s %8$s %7$s %9$s + %b + diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml index c664d1ae8dd91c06a5d75d3c9e1cf8802bb9664e..ce650c1287022b6a4c5b1fbfcc4416ed035b1df4 100644 --- a/core/res/res/values-fr/strings.xml +++ b/core/res/res/values-fr/strings.xml @@ -21,6 +21,7 @@ "Go" "To" "Po" + %1$s %2$s "<sans titre>" "…" "(Aucun numéro de téléphone)" @@ -161,14 +162,10 @@ "Permet à une application de modifier la configuration actuelle (par ex. : la taille de la police générale ou des paramètres régionaux)." "Démarrage d\'autres applications" "Permet à une application de forcer le lancement d\'autres applications." - "Non-possibilité d\'interruption" - "Permet à une application d\'exécuter tout processus au premier plan afin qu\'il ne puisse pas être interrompu. Les applications normales ne devraient jamais nécessiter cette fonctionnalité." "Fermeture forcée de l\'application" "Permet à une application de forcer une autre application exécutée au premier plan à se fermer et à passer en arrière-plan. Les applications normales ne devraient jamais avoir recours à cette fonctionnalité." "Vérification de l\'état interne du système" "Permet à l\'application de récupérer l\'état interne du système. Des applications malveillantes peuvent obtenir de nombreuses informations personnelles et sécurisées auxquelles elles ne devraient pas avoir accès." - "Éditer des services à faible niveau" - "Permet à l\'application de publier ses propres services de système de niveau inférieur. Des applications malveillantes peuvent prendre le contrôle du système et subtiliser ou endommager ses données." "Contrôle du lancement des applications" "Permet à une application de suivre et de contrôler la façon dont le système lance des activités. Des applications malveillantes peuvent entièrement déstabiliser le système. Cette autorisation est uniquement nécessaire au développement et non pour l\'utilisation normale du téléphone." "Envoyer une diffusion sans paquet" @@ -181,8 +178,6 @@ "Permet à une application de contrôler le nombre de processus maximal exécutés en même temps. Les applications normales n\'ont jamais recours à cette fonctionnalité." "Fermeture de toutes les applications en tâche de fond" "Permet à une application de vérifier si des activités sont systématiquement interrompues lorsqu\'elles sont placées en tâche de fond. Cette fonctionnalité n\'est jamais utilisée par les applications normales." - "Installation des mises à jour du système" - "Permet à une application de recevoir des notifications sur des mises à jour système en cours et de lancer leur installation. Des applications malveillantes peuvent utiliser cette fonctionnalité pour endommager le système avec des mises à jour non autorisées ou interférer avec le processus de mise à jour." "Modification des statistiques de la batterie" "Autoriser la modification des statistiques de la batterie. Les applications normales n\'utilisent pas cette fonctionnalité." "Affichage de fenêtres non autorisées" @@ -418,9 +413,6 @@ "Mot de passe" "Se connecter" "Nom d\'utilisateur ou mot de passe incorrect." - "h:mm AA" - "%-l:%M%P" - "%-l:%M%p" "%-l%P" "%-l%p" "Effacer les notifications" @@ -452,9 +444,6 @@ "entrée" "supprimer" "Rechercher" - "Aujourd\'hui" - "Hier" - "Demain" "Il y a 1 mois" "Il y a plus d\'un mois" @@ -536,13 +525,6 @@ "semaines" "année" "années" - "dimanche" - "lundi" - "mardi" - "mercredi" - "jeudi" - "vendredi" - "samedi" "Tous les jours ouvrés (lun.- ven.)" "Tous les jours" "Toutes les semaines le %s" @@ -552,137 +534,15 @@ "Désolé, cette vidéo ne peut être lue sur cet appareil." "Désolé, impossible de lire cette vidéo." "OK" - "AM" - "PM" - "%d/%m/%Y" - "%1$s %2$s, %3$s%4$s %5$s, %6$s" - "%1$s, %2$s%4$s, %5$s" - "%2$s, %3$s%5$s, %6$s" - "%2$s%5$s" - "%1$s%2$s" - "%1$s, %2$s, %3$s" - "%2$s, %3$s" - "%1$s, %3$s" - "%1$s, %2$s" "%1$s, %2$s" - "%1$s, %2$s" - "d' 'MMMM' 'yyyy" - "d' 'MMMM' 'yyyy" - "d' 'MMM' 'yyyy" - "d' 'MMM' 'yyyy" - "h':'mm' 'a" - "HH':'mm" "midi" "Midi" "minuit" "Minuit" - "%-d %B" - "%-d %B %Y" - "%B %Y" - "%H:%M:%S" - "%H:%M:%S %-d %B %Y" - "%3$s %2$s%8$s %7$s" - "%1$s %3$s %2$s%6$s %8$s %7$s" - "%3$s %2$s%8$s %7$s %9$s" - "%1$s, %3$s %2$s%6$s, %8$s %7$s %9$s" - "%3$s %2$s %5$s%8$s %7$s %10$s" - "%1$s, %3$s %2$s, %5$s%6$s, %8$s %7$s, %10$s" - "%3$s %2$s %4$s, %5$s%8$s %7$s %9$s, %10$s" - "%1$s, %3$s %2$s %4$s, %5$s%6$s, %8$s %7$s %9$s, %10$s" - "%3$s/%2$s%8$s/%7$s" - "%1$s %3$s/%2$s%6$s %8$s/%7$s" - "%3$s/%2$s/%4$s%8$s/%7$s/%9$s" - "%1$s, %3$s/%2$s/%4$s%6$s, %8$s/%7$s/%9$s" - "%3$s/%2$s %5$s%8$s/%7$s %10$s" - "%1$s, %3$s/%2$s, %5$s%6$s, %8$s/%7$s, %10$s" - "%3$s/%2$s/%4$s, %5$s%8$s/%7$s/%9$s, %10$s" - "%1$s %3$s/%2$s/%4$s, %5$s%6$s %8$s/%7$s/%9$s, %10$s" - "%3$s%8$s %2$s" - "%1$s, %3$s %2$s%6$s, %8$s %7$s" - "%3$s%8$s %2$s %9$s" - "%1$s %3$s %2$s %4$s%6$s %8$s %7$s %9$s" - "%3$s %2$s %5$s%8$s %7$s %10$s" - "%1$s, %3$s %2$s, %5$s%6$s, %8$s %7$s, %10$s" - "%3$s %2$s %4$s, %5$s%8$s %7$s %9$s, %10$s" - "%1$s %3$s %2$s %4$s, %5$s%6$s %8$s %7$s %9$s, %10$s" - "%-d %b %Y" - "%b %Y" - "%b %-d" - "dimanche" - "lundi" - "mardi" - "mercredi" - "jeudi" - "vendredi" - "samedi" - "dim." - "Lun" - "Mar" - "Mer" - "Jeu" - "Ven" - "Sam" - "Dim" - "Lun" - "Mar" - "Mer" - "Jeu" - "Ven" - "Sam" - "Dim" - "Lun" - "Mar" - "Mer" - "Jeu" - "Ven" - "sam." - "Dim" - "Lun" - "Mar" - "Mer" - "Jeu" - "Ven" - "Sam" - "janvier" - "février" - "mars" - "avril" - "mai" - "juin" - "juillet" - "août" - "septembre" - "octobre" - "novembre" - "décembre" - "janv." - "févr." - "mars" - "avr." - "mai" - "juin" - "juil." - "août" - "sept." - "oct." - "nov." - "déc." - "jan." - "Ven" - "mars" - "avr." - "mai" - "juin" - "juil." - "août" - "sept." - "oct." - "nov." - "déc." "%1$02d:%2$02d" "%1$d:%2$02d:%3$02d" "Tout sélectionner" diff --git a/core/res/res/values-he-rIL/donottranslate-cldr.xml b/core/res/res/values-he-rIL/donottranslate-cldr.xml new file mode 100644 index 0000000000000000000000000000000000000000..e3feb1e33146311f8bb14e257535992435e8851d --- /dev/null +++ b/core/res/res/values-he-rIL/donottranslate-cldr.xml @@ -0,0 +1,147 @@ + + + ינואר + פברואר + מרס + אפריל + מאי + יוני + יולי + אוגוסט + ספטמבר + אוקטובר + נובמבר + דצמבר + + ינואר + פברואר + מרס + אפריל + מאי + יוני + יולי + אוגוסט + ספטמבר + אוקטובר + נובמבר + דצמבר + + ינו + פבר + מרס + אפר + מאי + יונ + יול + אוג + ספט + אוק + נוב + דצמ + + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + + יום ראשון + יום שני + יום שלישי + יום רביעי + יום חמישי + יום שישי + יום שבת + + יום א' + יום ב' + יום ג' + יום ד' + יום ה' + יום ו' + שבת + + יום א' + יום ב' + יום ג' + יום ד' + יום ה' + יום ו' + שבת + + א + ב + ג + ד + ה + ו + ש + + לפנה"צ + אחה"צ + אתמול + היום + מחר + + %-k:%M + %-l:%M %p + %-l:%M %p + h:mm a + H:mm + %d/%m/%Y + dd/MM/yyyy + "%s/%s/%s" + %-e ב%B %Y + %H:%M:%S + %H:%M:%S %-e.%-m.%Y + %2$s %1$s + %1$s %3$s + %-e.%-m.%Y + %-e ב%B + %-B + %B %Y + %b %-e + %-b + %Y %b + %1$s – %2$s + %2$s – %5$s + %3$s/%2$s - %8$s/%7$s + %1$s %3$s/%2$s - %6$s %8$s/%7$s + %3$s/%2$s/%4$s - %8$s/%7$s/%9$s + %1$s %3$s/%2$s/%4$s - %6$s %8$s/%7$s/%9$s + %5$s %1$s, %3$s.%2$s.%4$s – %10$s %6$s, %8$s.%7$s.%9$s + %5$s %3$s/%2$s – %10$s %8$s/%7$s + %5$s %1$s, %2$s-%3$s – %10$s %6$s, %7$s-%8$s + %5$s %3$s/%2$s/%4$s – %10$s %8$s/%7$s/%9$s + %3$s %1$s %2$s – %6$s %4$s %5$s + %1$s %2$s – %4$s %5$s + %3$s %2$s – %6$s %5$s + %1$s %2$s %3$s + %2$s %3$s + %1$s %2$s + %3$s ב%2$s – %8$s ב%7$s + %1$s %3$s %2$s – %6$s %8$s %7$s + %5$s %3$s ב%2$s – %10$s %8$s ב%7$s + %5$s %3$s ב%2$s – %10$s %8$s ב%7$s + %5$s %1$s %3$s %2$s – %10$s %6$s %8$s %7$s + %5$s %1$s %3$s %2$s – %10$s %6$s %8$s %7$s + %5$s %3$s %2$s %4$s – %10$s %8$s %7$s %9$s + %5$s %3$s %2$s %4$s – %10$s %8$s %7$s %9$s + %5$s %1$s, %4$s %2$s %3$s – %10$s %6$s, %9$s %7$s %8$s + %5$s %1$s, %4$s %2$s %3$s – %10$s %6$s, %9$s %7$s %8$s + %1$s, %4$s %2$s %3$s – %6$s, %9$s %7$s %8$s + %3$s-%8$s %2$s + %1$s %3$s %2$s – %6$s %8$s %7$s + %3$s %2$s - %8$s %7$s %9$s + %3$s-%8$s %2$s %9$s + %1$s %3$s %2$s - %6$s %8$s %7$s %9$s + %b + diff --git a/core/res/res/values-hi-rIN/donottranslate-cldr.xml b/core/res/res/values-hi-rIN/donottranslate-cldr.xml new file mode 100644 index 0000000000000000000000000000000000000000..2a19da4c482c19d01372bc7da7454d744ecf5ce7 --- /dev/null +++ b/core/res/res/values-hi-rIN/donottranslate-cldr.xml @@ -0,0 +1,147 @@ + + + जनवरी + फरवरी + मार्च + अप्रैल + मई + जून + जुलाई + अगस्त + सितम्बर + अक्तूबर + नवम्बर + दिसम्बर + + जनवरी + फरवरी + मार्च + अप्रैल + मई + जून + जुलाई + अगस्त + सितम्बर + अक्तूबर + नवम्बर + दिसम्बर + + जनवरी + फरवरी + मार्च + अप्रैल + मई + जून + जुलाई + अगस्त + सितम्बर + अक्तूबर + नवम्बर + दिसम्बर + + + फ़ + मा + + + जू + जु + + सि + + + दि + + रविवार + सोमवार + मंगलवार + बुधवार + गुरुवार + शुक्रवार + शनिवार + + रवि + सोम + मंगल + बुध + गुरु + शुक्र + शनि + + रवि + सोम + मंगल + बुध + गुरु + शुक्र + शनि + + + सो + मं + बु + गु + शु + + + AM + PM + Yesterday + Today + Tomorrow + + %-k:%M + %-l:%M %p + %-l:%M %^p + h:mm a + H:mm + %-e-%-m-%Y + d-M-yyyy + "%s-%s-%s" + %-e %B %Y + %-l:%M:%S %p + %-l:%M:%S %p %d-%m-%Y + %2$s %1$s + %1$s %3$s + %d-%m-%Y + %-e %B + %-B + %B %Y + %-e %b + %-b + %b %Y + %1$s – %2$s + %2$s – %5$s + %2$s-%3$s – %7$s-%8$s + %1$s, %2$s-%3$s – %6$s, %7$s-%8$s + %4$s-%2$s-%3$s – %9$s-%7$s-%8$s + %1$s, %4$s-%2$s-%3$s – %6$s, %9$s-%7$s-%8$s + %5$s %1$s, %4$s-%2$s-%3$s – %10$s %6$s, %9$s-%7$s-%8$s + %5$s %3$s/%2$s – %10$s %8$s/%7$s + %5$s %1$s, %3$s/%2$s – %10$s %6$s, %8$s/%7$s + %5$s %3$s-%2$s-%4$s – %10$s %8$s-%7$s-%9$s + %3$s %1$s, %2$s – %6$s %4$s, %5$s + %1$s, %2$s – %4$s, %5$s + %3$s %2$s – %6$s %5$s + %1$s %2$s, %3$s + %2$s, %3$s + %1$s %2$s + %3$s %2$s – %8$s %7$s + %1$s, %3$s %2$s – %6$s, %8$s %7$s + %5$s %3$s %2$s – %10$s %8$s %7$s + %5$s %3$s %2$s – %10$s %8$s %7$s + %5$s %1$s, %3$s %2$s – %10$s %6$s, %8$s %7$s + %5$s %1$s, %3$s %2$s – %10$s %6$s, %8$s %7$s + %5$s %4$s-%2$s-%3$s – %10$s %9$s-%7$s-%8$s + %5$s %4$s-%2$s-%3$s – %10$s %9$s-%7$s-%8$s + %5$s %1$s, %3$s %2$s %4$s – %10$s %6$s, %8$s %7$s %9$s + %5$s %1$s, %3$s %2$s %4$s – %10$s %6$s, %8$s %7$s %9$s + %1$s, %3$s %2$s %4$s – %6$s, %8$s %7$s %9$s + %2$s-%3$s – %8$s + %1$s, %3$s %2$s – %6$s, %8$s %7$s + %9$s-%2$s-%3$s – %7$s-%8$s + %9$s-%2$s-%3$s – %8$s + %1$s, %9$s-%2$s-%3$s – %6$s, yyyy-%7$s-%8$s + %b + diff --git a/core/res/res/values-hu-rHU/donottranslate-cldr.xml b/core/res/res/values-hu-rHU/donottranslate-cldr.xml new file mode 100644 index 0000000000000000000000000000000000000000..08a70b81f7b4ac71f71a21a1203bf4d8d92fe7e3 --- /dev/null +++ b/core/res/res/values-hu-rHU/donottranslate-cldr.xml @@ -0,0 +1,147 @@ + + + január + február + március + április + május + június + július + augusztus + szeptember + október + november + december + + január + február + március + április + május + június + július + augusztus + szeptember + október + november + december + + jan. + febr. + márc. + ápr. + máj. + jún. + júl. + aug. + szept. + okt. + nov. + dec. + + J + F + M + Á + M + J + J + A + S + O + N + D + + vasárnap + hétfő + kedd + szerda + csütörtök + péntek + szombat + + V + H + K + Sze + Cs + P + Szo + + V + H + K + Sze + Cs + P + Szo + + V + H + K + S + C + P + S + + de. + du. + tegnap + ma + holnap + + %H:%M + %-l:%M %p + %-l:%M %^p + h:mm a + HH:mm + %Y.%m.%d. + yyyy.MM.dd. + "%s.%s.%s." + %Y. %B %-e. + %-k:%M:%S + %-k:%M:%S %Y.%m.%d. + %2$s %1$s + %1$s %3$s + %Y.%m.%d. + %B %-e. + %-B + %Y. %B + %b %-e. + %-b + %Y. %b + %1$s - %2$s + %2$s - %5$s + %2$s.%3$s. - %7$s.%8$s. + %2$s.%3$s., %1$s - %7$s.%8$s., %6$s + %4$s.%2$s.%3$s. - %9$s.%7$s.%8$s. + %4$s.%2$s.%3$s., %1$s - %9$s.%7$s.%8$s., %6$s + %5$s %4$s.%2$s.%3$s., %1$s - %10$s %9$s.%7$s.%8$s., %6$s + %5$s %2$s. %3$s. - %10$s %7$s. %8$s. + %5$s %2$s. %3$s., %1$s - %10$s %7$s. %8$s., %6$s + %5$s %4$s.%2$s.%3$s. - %10$s %9$s.%7$s.%8$s. + %3$s %2$s, %1$s - %6$s %5$s, %4$s + %2$s, %1$s - %5$s, %4$s + %3$s %2$s - %6$s %5$s + %1$s %3$s, %2$s + %3$s, %2$s + %1$s %2$s + %2$s %3$s. - %7$s %8$s. + %2$s %3$s., %1$s - %7$s %8$s., %6$s + %5$s %2$s %3$s. - %10$s %7$s %8$s. + %5$s %2$s %3$s. - %10$s %7$s %8$s. + %5$s %2$s %3$s., %1$s - %10$s %7$s %8$s., %6$s + %5$s %2$s %3$s., %1$s - %10$s %7$s %8$s., %6$s + %5$s %4$s. %2$s %3$s. - %10$s %9$s. %7$s %8$s. + %5$s %4$s. %2$s %3$s. - %10$s %9$s. %7$s %8$s. + %5$s %4$s. %2$s %3$s., %1$s - %10$s %9$s. %7$s %8$s., %6$s + %5$s %4$s. %2$s %3$s., %1$s - %10$s %9$s. %7$s %8$s., %6$s + %4$s. %2$s %3$s., %1$s - %9$s. %7$s %8$s., %6$s + %2$s %3$s-%8$s. + %2$s %3$s., %1$s - %7$s %8$s., %6$s + %9$s. %2$s %3$s. - %7$s %8$s. + %9$s. %2$s %3$s-%8$s. + %9$s. %2$s %3$s., %1$s - %7$s %8$s., %6$s + %b + diff --git a/core/res/res/values-id-rID/donottranslate-cldr.xml b/core/res/res/values-id-rID/donottranslate-cldr.xml new file mode 100644 index 0000000000000000000000000000000000000000..6adec84b8ab890191f15a20efec8cef97298499e --- /dev/null +++ b/core/res/res/values-id-rID/donottranslate-cldr.xml @@ -0,0 +1,147 @@ + + + Januari + Februari + Maret + April + Mei + Juni + Juli + Agustus + September + Oktober + November + Desember + + Januari + Februari + Maret + April + Mei + Juni + Juli + Agustus + September + Oktober + November + Desember + + Jan + Feb + Mar + Apr + Mei + Jun + Jul + Agu + Sep + Okt + Nov + Des + + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + + Minggu + Senin + Selasa + Rabu + Kamis + Jumat + Sabtu + + Min + Sen + Sel + Rab + Kam + Jum + Sab + + Min + Sen + Sel + Rab + Kam + Jum + Sab + + 1 + 2 + 3 + 4 + 5 + 6 + 7 + + AM + PM + Yesterday + Today + Tomorrow + + %-k:%M + %-l:%M %p + %-l:%M %^p + h:mm a + H:mm + %d/%m/%Y + dd/MM/yyyy + "%s/%s/%s" + %-e %B %Y + %H:%M:%S + %H:%M:%S %-e %b %Y + %2$s %1$s + %1$s %3$s + %-e %b %Y + %B %-e + %-B + %B %Y + %b %-e + %-b + %Y %b + %1$s – %2$s + %2$s – %5$s + %2$s-%3$s – %7$s-%8$s + %1$s, %2$s-%3$s – %6$s, %7$s-%8$s + %4$s-%2$s-%3$s – %9$s-%7$s-%8$s + %1$s, %4$s-%2$s-%3$s – %6$s, %9$s-%7$s-%8$s + %5$s %1$s, %4$s-%2$s-%3$s – %10$s %6$s, %9$s-%7$s-%8$s + %5$s %2$s-%3$s – %10$s %7$s-%8$s + %5$s %1$s, %2$s-%3$s – %10$s %6$s, %7$s-%8$s + %5$s %3$s/%2$s/%4$s – %10$s %8$s/%7$s/%9$s + %3$s %1$s %2$s – %6$s %4$s %5$s + %1$s %2$s – %4$s %5$s + %3$s %2$s – %6$s %5$s + %1$s %2$s %3$s + %2$s %3$s + %1$s %2$s + %2$s %3$s – %7$s %8$s + %1$s %2$s %3$s – %6$s %7$s %8$s + %5$s %2$s %3$s – %10$s %7$s %8$s + %5$s %2$s %3$s – %10$s %7$s %8$s + %5$s %1$s %2$s %3$s – %10$s %6$s %7$s %8$s + %5$s %1$s %2$s %3$s – %10$s %6$s %7$s %8$s + %5$s %4$s-%2$s-%3$s – %10$s %9$s-%7$s-%8$s + %5$s %4$s-%2$s-%3$s – %10$s %9$s-%7$s-%8$s + %5$s %1$s, %4$s %2$s %3$s – %10$s %6$s, %9$s %7$s %8$s + %5$s %1$s, %4$s %2$s %3$s – %10$s %6$s, %9$s %7$s %8$s + %1$s, %4$s %2$s %3$s – %6$s, %9$s %7$s %8$s + %2$s-%3$s – %8$s + %1$s %2$s %3$s – %6$s %7$s %8$s + %9$s-%2$s-%3$s – %7$s-%8$s + %9$s-%2$s-%3$s – %8$s + %1$s, %9$s-%2$s-%3$s – %6$s, yyyy-%7$s-%8$s + %b + diff --git a/core/res/res/values-it-rCH/donottranslate-cldr.xml b/core/res/res/values-it-rCH/donottranslate-cldr.xml new file mode 100644 index 0000000000000000000000000000000000000000..12170d6c415986badc65a2e3d0ddc00a03c71658 --- /dev/null +++ b/core/res/res/values-it-rCH/donottranslate-cldr.xml @@ -0,0 +1,147 @@ + + + Gennaio + Febbraio + Marzo + Aprile + Maggio + Giugno + Luglio + Agosto + Settembre + Ottobre + Novembre + Dicembre + + gennaio + febbraio + marzo + aprile + maggio + giugno + luglio + agosto + settembre + ottobre + novembre + dicembre + + gen + feb + mar + apr + mag + giu + lug + ago + set + ott + nov + dic + + G + F + M + A + M + G + L + A + S + O + N + D + + domenica + lunedì + martedì + mercoledì + giovedì + venerdì + sabato + + dom + lun + mar + mer + gio + ven + sab + + dom + lun + mar + mer + gio + ven + sab + + D + L + M + M + G + V + S + + m. + p. + ieri + oggi + domani + + %H:%M + %-l:%M %p + %-l:%M %^p + h:mm a + HH:mm + %d.%m.%Y + dd.MM.yyyy + "%s.%s.%s" + %-e %B %Y + %H.%M.%S + %H.%M.%S %-e-%b-%Y + %2$s %1$s + %1$s %3$s + %-e-%b-%Y + %-e %B + %-B + %B %Y + %-e %b + %-b + %b %Y + %1$s - %2$s + %2$s - %5$s + %3$s.%2$s - %8$s.%7$s + %1$s, %3$s.%2$s - %6$s, %8$s.%7$s + %3$s.%2$s.%4$s - %8$s.%7$s.%9$s + %1$s, %3$s.%2$s.%4$s - %6$s, %8$s.%7$s.%9$s + %5$s %1$s, %3$s.%2$s.%4$s - %10$s %6$s, %8$s.%7$s.%9$s + %5$s %3$s.%2$s - %10$s %8$s.%7$s + %5$s %1$s, %3$s.%2$s - %10$s %6$s, %8$s.%7$s + %5$s %3$s.%2$s.%4$s - %10$s %8$s.%7$s.%9$s + %3$s %1$s, %2$s - %6$s %4$s, %5$s + %1$s, %2$s - %4$s, %5$s + %3$s %2$s - %6$s %5$s + %1$s %2$s, %3$s + %2$s, %3$s + %1$s %2$s + %3$s %2$s - %8$s %7$s + %1$s, %3$s %2$s - %6$s, %8$s %7$s + %5$s %3$s %2$s - %10$s %8$s %7$s + %5$s %3$s %2$s - %10$s %8$s %7$s + %5$s %1$s, %3$s %2$s - %10$s %6$s, %8$s %7$s + %5$s %1$s, %3$s %2$s - %10$s %6$s, %8$s %7$s + %5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s + %5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s + %5$s %1$s, %3$s %2$s %4$s - %10$s %6$s, %8$s %7$s %9$s + %5$s %1$s, %3$s %2$s %4$s - %10$s %6$s, %8$s %7$s %9$s + %1$s, %3$s %2$s %4$s - %6$s, %8$s %7$s %9$s + %3$s-%8$s %2$s + %1$s, %3$s %2$s - %6$s, %8$s %7$s + %3$s %2$s - %8$s %7$s %9$s + %3$s-%8$s %2$s %9$s + %1$s, %3$s %2$s - %6$s, %8$s %7$s %9$s + %b + diff --git a/core/res/res/values-it-rIT/donottranslate-cldr.xml b/core/res/res/values-it-rIT/donottranslate-cldr.xml new file mode 100644 index 0000000000000000000000000000000000000000..2178bbea2481edb3b87d5b48d56c20caab61ccfa --- /dev/null +++ b/core/res/res/values-it-rIT/donottranslate-cldr.xml @@ -0,0 +1,147 @@ + + + Gennaio + Febbraio + Marzo + Aprile + Maggio + Giugno + Luglio + Agosto + Settembre + Ottobre + Novembre + Dicembre + + gennaio + febbraio + marzo + aprile + maggio + giugno + luglio + agosto + settembre + ottobre + novembre + dicembre + + gen + feb + mar + apr + mag + giu + lug + ago + set + ott + nov + dic + + G + F + M + A + M + G + L + A + S + O + N + D + + domenica + lunedì + martedì + mercoledì + giovedì + venerdì + sabato + + dom + lun + mar + mer + gio + ven + sab + + dom + lun + mar + mer + gio + ven + sab + + D + L + M + M + G + V + S + + m. + p. + ieri + oggi + domani + + %H:%M + %-l:%M %p + %-l:%M %^p + h:mm a + HH:mm + %d/%m/%Y + dd/MM/yyyy + "%s/%s/%s" + %d %B %Y + %H.%M.%S + %H.%M.%S %d/%b/%Y + %2$s %1$s + %1$s %3$s + %d/%b/%Y + %-e %B + %-B + %B %Y + %-e %b + %-b + %b %Y + %1$s - %2$s + %2$s - %5$s + %3$s/%2$s - %8$s/%7$s + %1$s %3$s/%2$s - %6$s %8$s/%7$s + %3$s/%2$s/%4$s - %8$s/%7$s/%9$s + %1$s %3$s/%2$s/%4$s - %6$s %8$s/%7$s/%9$s + %5$s %1$s, %3$s/%2$s/%4$s - %10$s %6$s, %8$s/%7$s/%9$s + %5$s %3$s/%2$s - %10$s %8$s/%7$s + %5$s %1$s %3$s/%2$s - %10$s %6$s %8$s/%7$s + %5$s %3$s/%2$s/%4$s - %10$s %8$s/%7$s/%9$s + %3$s %1$s %2$s - %6$s %4$s %5$s + %1$s %2$s - %4$s %5$s + %3$s %2$s - %6$s %5$s + %1$s %2$s %3$s + %2$s %3$s + %1$s %2$s + %3$s %2$s - %8$s %7$s + %1$s %3$s %2$s - %6$s %8$s %7$s + %5$s %3$s %2$s - %10$s %8$s %7$s + %5$s %3$s %2$s - %10$s %8$s %7$s + %5$s %1$s %3$s %2$s - %10$s %6$s %8$s %7$s + %5$s %1$s %3$s %2$s - %10$s %6$s %8$s %7$s + %5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s + %5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s + %5$s %1$s %3$s %2$s %4$s - %10$s %6$s %8$s %7$s %9$s + %5$s %1$s %3$s %2$s %4$s - %10$s %6$s %8$s %7$s %9$s + %1$s %3$s %2$s %4$s - %6$s %8$s %7$s %9$s + %3$s-%8$s %2$s + %1$s %3$s %2$s - %6$s %8$s %7$s + %3$s %2$s - %8$s %7$s %9$s + %3$s-%8$s %2$s %9$s + %1$s %3$s %2$s - %6$s %8$s %7$s %9$s + %b + diff --git a/core/res/res/values-it/donottranslate-cldr.xml b/core/res/res/values-it/donottranslate-cldr.xml new file mode 100644 index 0000000000000000000000000000000000000000..2178bbea2481edb3b87d5b48d56c20caab61ccfa --- /dev/null +++ b/core/res/res/values-it/donottranslate-cldr.xml @@ -0,0 +1,147 @@ + + + Gennaio + Febbraio + Marzo + Aprile + Maggio + Giugno + Luglio + Agosto + Settembre + Ottobre + Novembre + Dicembre + + gennaio + febbraio + marzo + aprile + maggio + giugno + luglio + agosto + settembre + ottobre + novembre + dicembre + + gen + feb + mar + apr + mag + giu + lug + ago + set + ott + nov + dic + + G + F + M + A + M + G + L + A + S + O + N + D + + domenica + lunedì + martedì + mercoledì + giovedì + venerdì + sabato + + dom + lun + mar + mer + gio + ven + sab + + dom + lun + mar + mer + gio + ven + sab + + D + L + M + M + G + V + S + + m. + p. + ieri + oggi + domani + + %H:%M + %-l:%M %p + %-l:%M %^p + h:mm a + HH:mm + %d/%m/%Y + dd/MM/yyyy + "%s/%s/%s" + %d %B %Y + %H.%M.%S + %H.%M.%S %d/%b/%Y + %2$s %1$s + %1$s %3$s + %d/%b/%Y + %-e %B + %-B + %B %Y + %-e %b + %-b + %b %Y + %1$s - %2$s + %2$s - %5$s + %3$s/%2$s - %8$s/%7$s + %1$s %3$s/%2$s - %6$s %8$s/%7$s + %3$s/%2$s/%4$s - %8$s/%7$s/%9$s + %1$s %3$s/%2$s/%4$s - %6$s %8$s/%7$s/%9$s + %5$s %1$s, %3$s/%2$s/%4$s - %10$s %6$s, %8$s/%7$s/%9$s + %5$s %3$s/%2$s - %10$s %8$s/%7$s + %5$s %1$s %3$s/%2$s - %10$s %6$s %8$s/%7$s + %5$s %3$s/%2$s/%4$s - %10$s %8$s/%7$s/%9$s + %3$s %1$s %2$s - %6$s %4$s %5$s + %1$s %2$s - %4$s %5$s + %3$s %2$s - %6$s %5$s + %1$s %2$s %3$s + %2$s %3$s + %1$s %2$s + %3$s %2$s - %8$s %7$s + %1$s %3$s %2$s - %6$s %8$s %7$s + %5$s %3$s %2$s - %10$s %8$s %7$s + %5$s %3$s %2$s - %10$s %8$s %7$s + %5$s %1$s %3$s %2$s - %10$s %6$s %8$s %7$s + %5$s %1$s %3$s %2$s - %10$s %6$s %8$s %7$s + %5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s + %5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s + %5$s %1$s %3$s %2$s %4$s - %10$s %6$s %8$s %7$s %9$s + %5$s %1$s %3$s %2$s %4$s - %10$s %6$s %8$s %7$s %9$s + %1$s %3$s %2$s %4$s - %6$s %8$s %7$s %9$s + %3$s-%8$s %2$s + %1$s %3$s %2$s - %6$s %8$s %7$s + %3$s %2$s - %8$s %7$s %9$s + %3$s-%8$s %2$s %9$s + %1$s %3$s %2$s - %6$s %8$s %7$s %9$s + %b + diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml index f80a4f4cd1568a3ec2d0f29c2e57c23582dbd210..5bfbc494314353e3b5f8c9b26562554a1dcb8d1b 100644 --- a/core/res/res/values-it/strings.xml +++ b/core/res/res/values-it/strings.xml @@ -161,14 +161,10 @@ "Consente a un\'applicazione di modificare la configurazione corrente, come le dimensioni dei caratteri locali o complessive." "riavvio altre applicazioni" "Consente a un\'applicazione di riavviare forzatamente altre applicazioni." - "impedire l\'interruzione" - "Consente a un\'applicazione di eseguire i processi in primo piano in modo che non possano essere interrotti. Non dovrebbe essere mai necessario per le normali applicazioni." "chiusura forzata dell\'applicazione" "Consente a un\'applicazione di forzare la chiusura di attività in primo piano. Non dovrebbe essere mai necessario per le normali applicazioni." "recupero stato interno del sistema" "Consente all\'applicazione di recuperare lo stato interno del sistema. Le applicazioni dannose potrebbero recuperare molte informazioni riservate e protette di cui non dovrebbero avere mai bisogno." - "pubblicaz. servizi di basso livello" - "Consente a un\'applicazione di pubblicare i suoi servizi di sistema di basso livello. Le applicazioni dannose potrebbero assumere il controllo del sistema e impossessarsi di dati o danneggiarli." "monitoraggio e controllo avvio applicazioni" "Consente a un\'applicazione di monitorare e controllare la modalità di avvio delle attività nel sistema. Le applicazioni dannose potrebbero compromettere totalmente il sistema. Questa autorizzazione è necessaria soltanto per lo sviluppo, mai per il normale utilizzo del telefono." "invio broadcast rimossi dal pacchetto" @@ -181,8 +177,6 @@ "Consente a un\'applicazione di stabilire il numero massimo di processi in esecuzione. Mai necessario per le normali applicazioni." "chiusura applicazioni in background" "Consente a un\'applicazione di controllare se le attività sono sempre completate quando vengono messe in secondo piano. Mai necessario per le normali applicazioni." - "installazione autom. aggiornamenti di sistema" - "Consente a un\'applicazione di ricevere notifiche sugli aggiornamenti del sistema in sospeso e di attivarne l\'installazione. Le applicazioni dannose possono sfruttare questa possibilità per danneggiare il sistema con aggiornamenti non autorizzati, o interferire con il processo di aggiornamento." "modifica statistiche batteria" "Consente la modifica delle statistiche sulla batteria raccolte. Da non usare per normali applicazioni." "visualizzazione finestre non autorizzate" @@ -418,9 +412,6 @@ "Password" "Accedi" "Password o nome utente non valido." - "h:mm AA" - "%-l:%M%P" - "%-l:%M%p" "%-l%P" "%-l%p" "Cancella notifiche" @@ -452,9 +443,6 @@ "Invio" "Canc" "Cerca" - "Oggi" - "Ieri" - "Domani" "1 mese fa" "Oltre 1 mese fa" @@ -536,13 +524,6 @@ "settimane" "anno" "anni" - "Domenica" - "Lunedì" - "Martedì" - "Mercoledì" - "Giovedì" - "Venerdì" - "Sabato" "Ogni giorno feriale (lun-ven)" "Quotidianamente" "Ogni settimana il %s" @@ -552,137 +533,15 @@ "Spiacenti, questo video non è valido per lo streaming su questo dispositivo." "Spiacenti. Impossibile riprodurre il video." "OK" - "AM" - "PM" - "%d/%m/%Y" - "%1$s, %2$s, %3$s%4$s, %5$s, %6$s" - "%1$s, %2$s%4$s, %5$s" - "%2$s, %3$s%5$s, %6$s" - "%2$s%5$s" - "%1$s%2$s" - "%1$s, %2$s, %3$s" - "%2$s, %3$s" - "%1$s, %3$s" - "%1$s, %2$s" "%1$s, %2$s" - "%1$s, %2$s" - "MMMM' 'd', 'yyyy" - "d' 'MMMM' 'yyyy" - "MMM' 'd', 'yyyy" - "d' 'MMM' 'yyyy" - "h':'mm' 'a" - "HH':'mm" "mezzogiorno" "Mezzogiorno" "mezzanotte" "Mezzanotte" - "%-d %B" - "%B %-d, %Y" - "%B %Y" - "%H:%M:%S" - "%H:%M:%S %-d %B, %Y" - "%3$s %2$s%8$s %7$s" - "%1$s, %3$s %2$s%6$s, %8$s %7$s" - "%3$s %2$s%8$s %7$s, %9$s" - "%1$s, %3$s %2$s%6$s, %8$s %7$s, %9$s" - "%3$s %2$s, %5$s%8$s %7$s, %10$s" - "%1$s, %3$s %2$s, %5$s%6$s, %8$s %7$s, %10$s" - "%3$s %2$s, %4$s, %5$s%8$s %7$s, %9$s, %10$s" - "%1$s, %3$s %2$s %4$s, %5$s%6$s, %8$s %7$s %9$s, %10$s" - "%3$s/%2$s%8$s/%7$s" - "%1$s, %3$s/%2$s%6$s, %8$s/%7$s" - "%3$s/%2$s/%4$s%8$s/%7$s/%9$s" - "%1$s, %3$s/%2$s/%4$s%6$s, %8$s/%7$s/%9$s" - "%3$s/%2$s, %5$s%8$s/%7$s, %10$s" - "%1$s, %3$s/%2$s, %5$s%6$s, %8$s/%7$s, %10$s" - "%3$s/%2$s/%4$s, %5$s%8$s/%7$s/%9$s, %10$s" - "%1$s, %3$s/%2$s/%4$s, %5$s%6$s, %8$s/%7$s/%9$s, %10$s" - "%3$s%8$s %2$s" - "%1$s, %3$s %2$s%6$s, %8$s %7$s" - "%3$s%8$s %2$s, %9$s" - "%1$s, %3$s %2$s, %4$s%6$s, %8$s %7$s, %9$s" - "%3$s %2$s, %5$s%8$s %7$s, %10$s" - "%1$s, %3$s %2$s, %5$s%6$s, %8$s %7$s, %10$s" - "%3$s %2$s %4$s, %5$s%8$s %7$s %9$s, %10$s" - "%1$s, %3$s %2$s, %4$s, %5$s%6$s, %8$s %7$s, %9$s, %10$s" - "%-d %b, %Y" - "%b %Y" - "%-d %b" - "Domenica" - "Lunedì" - "Martedì" - "Mercoledì" - "Giovedì" - "Venerdì" - "Sabato" - "Dom" - "Lun" - "Mar" - "Mer" - "Gio" - "Ven" - "Sab" - "Do" - "Lu" - "Ma" - "Me" - "Gi" - "Ve" - "Sa" - "Do" - "Lu" - "Ma" - "Me" - "Gi" - "V" - "Sa" - "D" - "Lun" - "M" - "Me" - "G" - "V" - "Sa" - "Gennaio" - "Febbraio" - "Marzo" - "Aprile" - "Maggio" - "Giugno" - "Luglio" - "Agosto" - "Settembre" - "Ottobre" - "Novembre" - "Dicembre" - "Gen" - "Feb" - "Mar" - "Apr" - "Mag" - "Giu" - "Lug" - "Ago" - "Set" - "Ott" - "Nov" - "Dic" - "G" - "F" - "M" - "Ap" - "Mag" - "Gi" - "Lug" - "Ago" - "Set" - "O" - "N" - "Di" "%1$02d:%2$02d" "%1$d:%2$02d:%3$02d" "Seleziona tutto" diff --git a/core/res/res/values-ja-rJP/donottranslate-cldr.xml b/core/res/res/values-ja-rJP/donottranslate-cldr.xml new file mode 100644 index 0000000000000000000000000000000000000000..d2510f60132adc1dc5d8d6d6bcac03cfdf80c36c --- /dev/null +++ b/core/res/res/values-ja-rJP/donottranslate-cldr.xml @@ -0,0 +1,147 @@ + + + 1月 + 2月 + 3月 + 4月 + 5月 + 6月 + 7月 + 8月 + 9月 + 10月 + 11月 + 12月 + + 1月 + 2月 + 3月 + 4月 + 5月 + 6月 + 7月 + 8月 + 9月 + 10月 + 11月 + 12月 + + 1月 + 2月 + 3月 + 4月 + 5月 + 6月 + 7月 + 8月 + 9月 + 10月 + 11月 + 12月 + + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + + 日曜日 + 月曜日 + 火曜日 + 水曜日 + 木曜日 + 金曜日 + 土曜日 + + + + + + + + + + + + + + + + + + + + + + + + + + 午前 + 午後 + 昨日 + 今日 + 明日 + + %-k:%M + %p%-l:%M + %p%-l:%M + ah:mm + H:mm + %Y/%m/%d + yyyy/MM/dd + "%s/%s/%s" + %Y年%-m月%-e日 + %-k:%M:%S + %-k:%M:%S %Y/%m/%d + %2$s %1$s + %1$s %3$s + %Y/%m/%d + %-m月%-e日 + %-B + %Y年%-m月 + %-m月%-e日 + %-b + %Y年%-m月 + %1$s~%2$s + %2$s~%5$s + %2$s/%3$s~%7$s/%8$s + %2$s/%3$s(%1$s)~%7$s/%8$s(%6$s) + %4$s/%2$s/%3$s~%9$s/%7$s/%8$s + %4$s/%2$s/%3$s(%1$s)~%9$s/%7$s/%8$s(%6$s) + %5$s %4$s/%2$s/%3$s(%1$s)~%10$s %9$s/%7$s/%8$s(%6$s) + %5$s %2$s/%3$s~%10$s %7$s/%8$s + %5$s %2$s/%3$s(%1$s)~%10$s %7$s/%8$s(%6$s) + %5$s %4$s/%2$s/%3$s~%10$s %9$s/%7$s/%8$s + %3$s %2$s(%1$s)~%6$s %5$s(%4$s) + %2$s(%1$s)~%5$s(%4$s) + %3$s %2$s~%6$s %5$s + %1$s %3$s(%2$s) + %3$s(%2$s) + %1$s %2$s + %2$s%3$s日~%7$s%8$s日 + %2$s%3$s日(%1$s)~%7$s%8$s日(%6$s) + %5$s %2$s%3$s日~%10$s %7$s%8$s日 + %5$s %2$s%3$s日~%10$s %7$s%8$s日 + %5$s %2$s%3$s日(%1$s)~%10$s %7$s%8$s日(%6$s) + %5$s %2$s%3$s日(%1$s)~%10$s %7$s%8$s日(%6$s) + %5$s %4$s年%2$s%3$s日~%10$s %9$s年%7$s%8$s日 + %5$s %4$s年%2$s%3$s日~%10$s %9$s年%7$s%8$s日 + %5$s %4$s年%2$s%3$s日(%1$s)~%10$s %9$s年%7$s%8$s日(%6$s) + %5$s %4$s年%2$s%3$s日(%1$s)~%10$s %9$s年%7$s%8$s日(%6$s) + %4$s年%2$s%3$s日(%1$s)~%9$s年%7$s%8$s日(%6$s) + %2$s%3$s日~%8$s日 + %2$s%3$s日(%1$s)~%7$s%8$s日(%6$s) + %9$s年%2$s%3$s日~%7$s%8$s日 + %9$s年%2$s%3$s日~%8$s日 + %9$s年%2$s%3$s日(%1$s)~%7$s%8$s日(%6$s) + %b + diff --git a/core/res/res/values-ja/donottranslate-cldr.xml b/core/res/res/values-ja/donottranslate-cldr.xml new file mode 100644 index 0000000000000000000000000000000000000000..d2510f60132adc1dc5d8d6d6bcac03cfdf80c36c --- /dev/null +++ b/core/res/res/values-ja/donottranslate-cldr.xml @@ -0,0 +1,147 @@ + + + 1月 + 2月 + 3月 + 4月 + 5月 + 6月 + 7月 + 8月 + 9月 + 10月 + 11月 + 12月 + + 1月 + 2月 + 3月 + 4月 + 5月 + 6月 + 7月 + 8月 + 9月 + 10月 + 11月 + 12月 + + 1月 + 2月 + 3月 + 4月 + 5月 + 6月 + 7月 + 8月 + 9月 + 10月 + 11月 + 12月 + + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + + 日曜日 + 月曜日 + 火曜日 + 水曜日 + 木曜日 + 金曜日 + 土曜日 + + + + + + + + + + + + + + + + + + + + + + + + + + 午前 + 午後 + 昨日 + 今日 + 明日 + + %-k:%M + %p%-l:%M + %p%-l:%M + ah:mm + H:mm + %Y/%m/%d + yyyy/MM/dd + "%s/%s/%s" + %Y年%-m月%-e日 + %-k:%M:%S + %-k:%M:%S %Y/%m/%d + %2$s %1$s + %1$s %3$s + %Y/%m/%d + %-m月%-e日 + %-B + %Y年%-m月 + %-m月%-e日 + %-b + %Y年%-m月 + %1$s~%2$s + %2$s~%5$s + %2$s/%3$s~%7$s/%8$s + %2$s/%3$s(%1$s)~%7$s/%8$s(%6$s) + %4$s/%2$s/%3$s~%9$s/%7$s/%8$s + %4$s/%2$s/%3$s(%1$s)~%9$s/%7$s/%8$s(%6$s) + %5$s %4$s/%2$s/%3$s(%1$s)~%10$s %9$s/%7$s/%8$s(%6$s) + %5$s %2$s/%3$s~%10$s %7$s/%8$s + %5$s %2$s/%3$s(%1$s)~%10$s %7$s/%8$s(%6$s) + %5$s %4$s/%2$s/%3$s~%10$s %9$s/%7$s/%8$s + %3$s %2$s(%1$s)~%6$s %5$s(%4$s) + %2$s(%1$s)~%5$s(%4$s) + %3$s %2$s~%6$s %5$s + %1$s %3$s(%2$s) + %3$s(%2$s) + %1$s %2$s + %2$s%3$s日~%7$s%8$s日 + %2$s%3$s日(%1$s)~%7$s%8$s日(%6$s) + %5$s %2$s%3$s日~%10$s %7$s%8$s日 + %5$s %2$s%3$s日~%10$s %7$s%8$s日 + %5$s %2$s%3$s日(%1$s)~%10$s %7$s%8$s日(%6$s) + %5$s %2$s%3$s日(%1$s)~%10$s %7$s%8$s日(%6$s) + %5$s %4$s年%2$s%3$s日~%10$s %9$s年%7$s%8$s日 + %5$s %4$s年%2$s%3$s日~%10$s %9$s年%7$s%8$s日 + %5$s %4$s年%2$s%3$s日(%1$s)~%10$s %9$s年%7$s%8$s日(%6$s) + %5$s %4$s年%2$s%3$s日(%1$s)~%10$s %9$s年%7$s%8$s日(%6$s) + %4$s年%2$s%3$s日(%1$s)~%9$s年%7$s%8$s日(%6$s) + %2$s%3$s日~%8$s日 + %2$s%3$s日(%1$s)~%7$s%8$s日(%6$s) + %9$s年%2$s%3$s日~%7$s%8$s日 + %9$s年%2$s%3$s日~%8$s日 + %9$s年%2$s%3$s日(%1$s)~%7$s%8$s日(%6$s) + %b + diff --git a/core/res/res/values-ja/donottranslate.xml b/core/res/res/values-ja/donottranslate.xml new file mode 100644 index 0000000000000000000000000000000000000000..f7c3566f644c14f76e3b59bae9dc3e190935e2ec --- /dev/null +++ b/core/res/res/values-ja/donottranslate.xml @@ -0,0 +1,23 @@ + + + + + Shift_JIS + diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml index 8da040e410f6e6cb435bb6d85e9aeec5e64e5930..a2e3e51846c4f122271391715f56e2cb972ec424 100644 --- a/core/res/res/values-ja/strings.xml +++ b/core/res/res/values-ja/strings.xml @@ -161,14 +161,10 @@ "地域/言語やフォントのサイズなど、現在の設定の変更をアプリケーションに許可します。" "他のアプリケーションの再起動" "他のアプリケーションの強制的な再起動をアプリケーションに許可します。" - "停止の阻止" - "フォアグラウンドでプロセスを実行して、強制終了できないようにすることをアプリケーションに許可します。通常のアプリケーションではまったく必要ありません。" "アプリケーションの強制終了" "フォアグラウンドで実行されている操作を強制終了して戻ることをアプリケーションに許可します。通常のアプリケーションではまったく必要ありません。" "システムの内部状態の取得" "システムの内部状態の取得をアプリケーションに許可します。悪意のあるアプリケーションが、通常は必要としない広範囲にわたる非公開の機密情報を取得する恐れがあります。" - "低レベルサービスの公開" - "独自の低レベルのシステムサービスを公開することをアプリケーションに許可します。悪意のあるアプリケーションがシステムを乗っ取って、データの盗用や破壊をする恐れがあります。" "起動中のすべてのアプリケーションの監視と制御" "システムが起動する操作の監視と制御をアプリケーションに許可します。悪意のあるアプリケーションがシステムを完全に破壊する恐れがあります。この許可は開発にのみ必要で、携帯電話の通常の使用にはまったく必要ありません。" "パッケージ削除ブロードキャストの送信" @@ -181,8 +177,6 @@ "実行するプロセス数の上限の制御をアプリケーションに許可します。通常のアプリケーションにはまったく必要ありません。" "バックグラウンドアプリケーションをすべて終了する" "バックグラウンドになり次第必ず操作を終了させるかどうかの制御をアプリケーションに許可します。通常のアプリケーションではまったく必要ありません。" - "システムアップデートの自動インストール" - "保留中のシステムアップデートに関する通知の受信とインストールの開始をアプリケーションに許可します。悪意のあるアプリケーションが許可なく更新を行ってシステムを破壊したり、更新処理を妨害する恐れがあります。" "電池統計情報の変国" "収集した電池統計情報の変更を許可します。通常のアプリケーションでは使用しません。" "未許可のウィンドウの表示" @@ -418,9 +412,6 @@ "パスワード" "ログイン" "ユーザー名またはパスワードが正しくありません。" - "h:mm AA" - "%-l:%M%P" - "%-l:%M%p" "%-l%P" "%-l%p" "通知を消去" @@ -452,9 +443,6 @@ "Enter" "Del" "検索" - "今日" - "昨日" - "明日" "1か月前" "1か月前" @@ -536,13 +524,6 @@ "週間" "年" "年" - "日曜日" - "月曜日" - "火曜日" - "水曜日" - "木曜日" - "金曜日" - "土曜日" "平日(月~金)" "毎日" "毎週%s" @@ -552,137 +533,15 @@ "この動画はご使用の端末でストリーミングできません。" "この動画は再生できません。" "OK" - "AM" - "PM" - "%Y/%m/%d" - "%2$s%1$s%3$s%5$s%4$s%6$s" - "%2$s%1$s%5$s%4$s" - "%2$s %3$s%5$s %6$s" - "%2$s%5$s" - "%1$s - %2$s" - "%3$s%2$s%1$s" - "%3$s%2$s" - "%3$s%1$s" - "%1$s %2$s" "%1$s %2$s" - "%2$s%1$s" - "MMMMd'日 'yyyy" - "yyyy'年'MMMM'月'd'日'" - "MMM'/'d' 'yyyy'年'" - "d'/'MMM'/'yyyy" - "h':'mm' 'a" - "HH':'mm" "正午" "正午" "午前0時" "午前0時" - "%B%-d日" - "%Y%B%-d日" - "%Y%B" - "%H:%M:%S" - "%Y/%B/%-d %H:%M:%S" - "%2$s/%3$s%7$s/%8$s" - "%2$s/%3$s%1$s%7$s/%8$s%6$s" - "%2$s/%3$s%7$s/%8$s, %9$s" - "%9$s/%2$s/%3$s%1$s%7$s/%8$s%6$s" - "%2$s/%3$s %5$s%7$s/%8$s %10$s" - "%2$s/%3$s%1$s%5$s%7$s/%8$s%6$s%10$s" - "%4$s/%2$s/%3$s %5$s%9$s/%7$s/%8$s %10$s" - "%4$s/%2$s/%3$s%1$s%5$s%9$s/%7$s/%8$s%6$s%10$s" - "%2$s/%3$s%7$s/%8$s" - "%2$s/%3$s%1$s%7$s/%8$s%6$s" - "%4$s/%2$s/%3$s%9$s/%7$s/%8$s" - "%4$s/%2$s/%3$s%1$s%9$s/%7$s/%8$s%6$s" - "%2$s/%3$s %5$s%7$s/%8$s %10$s" - "%2$s/%3$s%1$s%5$s%7$s/%8$s%6$s%10$s" - "%4$s/%2$s/%3$s %5$s%9$s/%7$s/%8$s %10$s" - "%4$s/%2$s/%3$s%1$s%5$s%9$s/%7$s/%8$s%6$s%10$s" - "%2$s%3$s日~%8$s日" - "%2$s/%3$s%1$s%7$s/%8$s%6$s" - "%2$s/%3$s - %8$s, %9$s" - "%4$s/%2$s/%3$s%1$s%9$s/%7$s/%8$s%6$s" - "%2$s/%3$s%5$s%7$s/%8$s%10$s" - "%2$s/%3$s%1$s%5$s%7$s/%8$s%6$s%10$s" - "%4$s/%2$s/%3$s%5$s%9$s/%7$s/%8$s%10$s" - "%4$s/%2$s/%3$s%1$s%5$s%9$s/%7$s/%8$s%6$s%10$s" - "%Y/%b/%-d" - "%Y%b月" - "%b/%-d" - "日曜日" - "月曜日" - "火曜日" - "水曜日" - "木曜日" - "金曜日" - "土曜日" - "(日)" - "(月)" - "(火)" - "(水)" - "(木)" - "(金)" - "(土)" - "日" - "月" - "火" - "水" - "木" - "金" - "土" - "日" - "月" - "火" - "水" - "木" - "金" - "土" - "日" - "月" - "火" - "水" - "火" - "金" - "土" - "1月" - "2月" - "3月" - "4月" - "5月" - "6月" - "7月" - "8月" - "9月" - "10月" - "11月" - "12月" - "1" - "2" - "3" - "4" - "5" - "6" - "7" - "8" - "9" - "10" - "11" - "12" - "1" - "2" - "3" - "4" - "5" - "6" - "7" - "8" - "9" - "10" - "11" - "12" "%1$02d:%2$02d" "%1$d:%2$02d:%3$02d" "すべて選択" diff --git a/core/res/res/values-ko-rKR/donottranslate-cldr.xml b/core/res/res/values-ko-rKR/donottranslate-cldr.xml new file mode 100644 index 0000000000000000000000000000000000000000..57cd35692e36d15e802f51e2f8d54ccd4945f41a --- /dev/null +++ b/core/res/res/values-ko-rKR/donottranslate-cldr.xml @@ -0,0 +1,135 @@ + + + 1월 + 2월 + 3월 + 4월 + 5월 + 6월 + 7월 + 8월 + 9월 + 10월 + 11월 + 12월 + + 1월 + 2월 + 3월 + 4월 + 5월 + 6월 + 7월 + 8월 + 9월 + 10월 + 11월 + 12월 + + + 1월 + 2월 + 3월 + 4월 + 5월 + 6월 + 7월 + 8월 + 9월 + 10월 + 11월 + 12월 + + 일요일 + 월요일 + 화요일 + 수요일 + 목요일 + 금요일 + 토요일 + + + + + + + + + + + + + + + + + + + + + + + + + + 오전 + 오후 + 어제 + 오늘 + 내일 + + %-k:%M + %-l:%M %p + %-l:%M %p + h:mm a + H:mm + %Y. %-m. %-e. + yyyy. M. d. + "%s. %s. %s." + %Y년 %-m월 %-e일 + %p %-l:%M:%S + %p %-l:%M:%S %Y. %-m. %-e. + %2$s %1$s + %1$s %3$s + %Y. %-m. %-e. + %B %-e일 + %-B + %Y년 %B + %b %-e일 + %-b + %Y년 %b + %1$s – %2$s + %2$s – %5$s + %2$s. %3$s ~ %7$s. %8$s + %2$s. %3$s %1$s ~ %7$s. %8$s %6$s + %4$s. %2$s. %3$s. ~ %9$s. %7$s. %8$s. + %4$s. %2$s. %3$s. %1$s ~ %9$s. %7$s. %8$s. %6$s + %5$s %4$s. %2$s. %3$s. %1$s – %10$s %9$s. %7$s. %8$s. %6$s + %5$s %2$s. %3$s. – %10$s %7$s. %8$s. + %5$s %2$s. %3$s. (%1$s) – %10$s %7$s. %8$s. (%6$s) + %5$s %4$s. %2$s. %3$s. – %10$s %9$s. %7$s. %8$s. + %3$s %2$s (%1$s) – %6$s %5$s (%4$s) + %2$s (%1$s) – %5$s (%4$s) + %3$s %2$s – %6$s %5$s + %1$s %3$s (%2$s) + %3$s (%2$s) + %1$s %2$s + %2$s %3$s일 – %7$s %8$s일 + %2$s %3$s일 (%1$s) – %7$s %8$s일 (%6$s) + %5$s %2$s %3$s일 – %10$s %7$s %8$s일 + %5$s %2$s %3$s일 – %10$s %7$s %8$s일 + %5$s %2$s %3$s일 (%1$s) – %10$s %7$s %8$s일 (%6$s) + %5$s %2$s %3$s일 (%1$s) – %10$s %7$s %8$s일 (%6$s) + %5$s %4$s년 %2$s %3$s일 – %10$s %9$s년 %7$s %8$s일 + %5$s %4$s년 %2$s %3$s일 – %10$s %9$s년 %7$s %8$s일 + %5$s %4$s년 %2$s %3$s일 %1$s – %10$s %9$s년 %7$s %8$s일 %6$s + %5$s %4$s년 %2$s %3$s일 %1$s – %10$s %9$s년 %7$s %8$s일 %6$s + %4$s년 %2$s %3$s일 %1$s – %9$s년 %7$s %8$s일 %6$s + %2$s %3$s일 ~ %8$s일 + %2$s %3$s일 (%1$s) – %7$s %8$s일 (%6$s) + %9$s년 %2$s %3$s일 ~ %7$s %8$s일 + %9$s년 %2$s %3$s일~%8$s일 + %9$s년 %2$s %3$s일 %1$s ~ %7$s %8$s일 %6$s + %b + diff --git a/core/res/res/values-ko/donottranslate-cldr.xml b/core/res/res/values-ko/donottranslate-cldr.xml new file mode 100644 index 0000000000000000000000000000000000000000..57cd35692e36d15e802f51e2f8d54ccd4945f41a --- /dev/null +++ b/core/res/res/values-ko/donottranslate-cldr.xml @@ -0,0 +1,135 @@ + + + 1월 + 2월 + 3월 + 4월 + 5월 + 6월 + 7월 + 8월 + 9월 + 10월 + 11월 + 12월 + + 1월 + 2월 + 3월 + 4월 + 5월 + 6월 + 7월 + 8월 + 9월 + 10월 + 11월 + 12월 + + + 1월 + 2월 + 3월 + 4월 + 5월 + 6월 + 7월 + 8월 + 9월 + 10월 + 11월 + 12월 + + 일요일 + 월요일 + 화요일 + 수요일 + 목요일 + 금요일 + 토요일 + + + + + + + + + + + + + + + + + + + + + + + + + + 오전 + 오후 + 어제 + 오늘 + 내일 + + %-k:%M + %-l:%M %p + %-l:%M %p + h:mm a + H:mm + %Y. %-m. %-e. + yyyy. M. d. + "%s. %s. %s." + %Y년 %-m월 %-e일 + %p %-l:%M:%S + %p %-l:%M:%S %Y. %-m. %-e. + %2$s %1$s + %1$s %3$s + %Y. %-m. %-e. + %B %-e일 + %-B + %Y년 %B + %b %-e일 + %-b + %Y년 %b + %1$s – %2$s + %2$s – %5$s + %2$s. %3$s ~ %7$s. %8$s + %2$s. %3$s %1$s ~ %7$s. %8$s %6$s + %4$s. %2$s. %3$s. ~ %9$s. %7$s. %8$s. + %4$s. %2$s. %3$s. %1$s ~ %9$s. %7$s. %8$s. %6$s + %5$s %4$s. %2$s. %3$s. %1$s – %10$s %9$s. %7$s. %8$s. %6$s + %5$s %2$s. %3$s. – %10$s %7$s. %8$s. + %5$s %2$s. %3$s. (%1$s) – %10$s %7$s. %8$s. (%6$s) + %5$s %4$s. %2$s. %3$s. – %10$s %9$s. %7$s. %8$s. + %3$s %2$s (%1$s) – %6$s %5$s (%4$s) + %2$s (%1$s) – %5$s (%4$s) + %3$s %2$s – %6$s %5$s + %1$s %3$s (%2$s) + %3$s (%2$s) + %1$s %2$s + %2$s %3$s일 – %7$s %8$s일 + %2$s %3$s일 (%1$s) – %7$s %8$s일 (%6$s) + %5$s %2$s %3$s일 – %10$s %7$s %8$s일 + %5$s %2$s %3$s일 – %10$s %7$s %8$s일 + %5$s %2$s %3$s일 (%1$s) – %10$s %7$s %8$s일 (%6$s) + %5$s %2$s %3$s일 (%1$s) – %10$s %7$s %8$s일 (%6$s) + %5$s %4$s년 %2$s %3$s일 – %10$s %9$s년 %7$s %8$s일 + %5$s %4$s년 %2$s %3$s일 – %10$s %9$s년 %7$s %8$s일 + %5$s %4$s년 %2$s %3$s일 %1$s – %10$s %9$s년 %7$s %8$s일 %6$s + %5$s %4$s년 %2$s %3$s일 %1$s – %10$s %9$s년 %7$s %8$s일 %6$s + %4$s년 %2$s %3$s일 %1$s – %9$s년 %7$s %8$s일 %6$s + %2$s %3$s일 ~ %8$s일 + %2$s %3$s일 (%1$s) – %7$s %8$s일 (%6$s) + %9$s년 %2$s %3$s일 ~ %7$s %8$s일 + %9$s년 %2$s %3$s일~%8$s일 + %9$s년 %2$s %3$s일 %1$s ~ %7$s %8$s일 %6$s + %b + diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml index 96b897a495d510937aa79e7603b714ab72cdb7cf..e2c6e570fe279f7e971f8ce0ab212cba950b4fab 100644 --- a/core/res/res/values-ko/strings.xml +++ b/core/res/res/values-ko/strings.xml @@ -161,14 +161,10 @@ "응용프로그램이 로케일 또는 전체 글꼴 크기 같은 현재 구성을 변경할 수 있습니다." "다른 응용프로그램 다시 시작" "응용프로그램이 다른 응용프로그램을 강제로 다시 시작할 수 있습니다." - "중지되지 않도록 하기" - "응용프로그램이 프로세스를 포그라운드에서 실행되도록 하여 프로세스를 중지할 수 있습니다. 일반 응용프로그램에는 필요하지 않습니다." "강제로 응용프로그램 닫기" "응용프로그램이 포그라운드에 있는 활동을 강제로 닫을 수 있습니다. 일반 응용프로그램에는 필요하지 않습니다." "시스템 내부 상태 검색" "응용프로그램이 시스템의 내부 상태를 검색할 수 있습니다. 악성 응용프로그램은 이 기능을 이용하여 일반적으로 필요하지 않은 다양한 개인 정보와 보안 정보를 검색할 수 있습니다." - "하위 수준 서비스 게시" - "응용프로그램이 자체 하위 수준 시스템 서비스를 게시할 수 있습니다. 악성 응용프로그램은 이 기능을 이용하여 시스템을 하이재킹하거나 시스템의 데이터를 도용 또는 손상시킬 수 있습니다." "실행 중인 모든 응용프로그램 모니터링 및 제어" "응용프로그램이 시스템에서 활동이 시작되는 방식을 모니터링하고 제어할 수 있습니다. 악성 응용프로그램은 이 기능을 이용하여 시스템을 완전히 손상시킬 수 있습니다. 이 권한은 개발 과정에만 필요하며 일반 전화기 사용 시에는 필요하지 않습니다." "패키지 제거 브로드캐스트 보내기" @@ -181,8 +177,6 @@ "응용프로그램이 실행할 최대 프로세스 수를 제어할 수 있습니다. 일반 응용프로그램에는 필요하지 않습니다." "모든 백그라운드 응용프로그램이 닫히도록 하기" "응용프로그램이 백그라운드로 이동한 활동을 항상 바로 마칠지 여부를 제어할 수 있습니다. 일반 응용프로그램에는 필요하지 않습니다." - "시스템 업데이트 자동으로 설치" - "응용프로그램이 대기 중인 시스템 업데이트에 대한 알림을 받고 설치를 트리거할 수 있습니다. 악성 응용프로그램은 이 기능을 이용하여 인증되지 않은 업데이트로 시스템을 손상시키거나 업데이트 절차를 방해할 수 있습니다." "배터리 통계 수정" "수집된 배터리 통계를 수정할 수 있습니다. 일반 응용프로그램에서는 사용하지 않습니다." "인증되지 않은 창 표시" @@ -418,9 +412,6 @@ "비밀번호" "로그인" "사용자 이름 또는 비밀번호가 잘못되었습니다." - "h:mm AA" - "%-l:%M%P" - "%-l:%M%p" "%-l%P" "%-l%p" "알림 지우기" @@ -452,9 +443,6 @@ "입력" "삭제" "검색" - "오늘" - "어제" - "내일" "한 달 전" "한 달 전" @@ -536,13 +524,6 @@ "주" "년" "년" - "일요일" - "월요일" - "화요일" - "수요일" - "목요일" - "금요일" - "토요일" "주중 매일(월-금)" "매일" "매주 %s" @@ -552,137 +533,15 @@ "죄송합니다. 이 기기로의 스트리밍에 적합하지 않은 동영상입니다." "죄송합니다. 동영상을 재생할 수 없습니다." "확인" - "AM" - "PM" - "%m/%d/%Y" - "%2$s, %1$s, %3$s%5$s, %4$s, %6$s" - "%2$s, %1$s%5$s, %4$s" - "%2$s, %3$s%5$s, %6$s" - "%2$s%5$s" - "%1$s%2$s" - "%3$s, %2$s, %1$s" - "%2$s, %3$s" - "%3$s, %1$s" - "%1$s, %2$s" "%1$s, %2$s" - "%2$s, %1$s" - "yyyy' 'MMMM', 'd" - "yyyy' 'MMMM', 'd" - "MMM' 'd', 'yyyy" - "d' 'MMM', 'yyyy" - "h':'mm' 'a" - "HH':'mm" "정오" "정오" "자정" "자정" - "%B %-d" - "%Y, %B %-d" - "%B %Y" - "%H:%M:%S" - "%Y, %B %-d, %H:%M:%S" - "%2$s %3$s%7$s %8$s" - "%2$s %3$s, %1$s%7$s %8$s, %6$s" - "%9$s, %2$s %3$s%7$s %8$s" - "%9$s, %2$s %3$s, %1$s%7$s %8$s, %6$s" - "%2$s %3$s, %5$s%7$s %8$s, %10$s" - "%2$s %3$s, %1$s, %5$s%7$s %8$s, %6$s, %10$s" - "%4$s, %2$s %3$s, %5$s%9$s, %7$s %8$s, %10$s" - "%1$s, %2$s %3$s, %4$s, %5$s%6$s, %7$s %8$s, %9$s, %10$s" - "%2$s/%3$s%7$s/%8$s" - "%2$s/%3$s, %1$s%7$s/%8$s, %6$s" - "%4$s/%2$s/%3$s%9$s/%7$s/%8$s/" - "%4$s/%2$s/%3$s, %1$s%9$s/%7$s/%8$s, %6$s" - "%2$s/%3$s, %5$s%7$s/%8$s, %10$s" - "%2$s/%3$s, %1$s, %5$s%7$s/%8$s, %6$s, %10$s" - "%2$s/%3$s/%4$s, %5$s%7$s/%8$s/%9$s, %10$s" - "%1$s, %2$s/%3$s/%4$s, %5$s%6$s, %7$s/%8$s/%9$s, %10$s" - "%2$s %3$s%8$s" - "%2$s %3$s, %1$s%7$s %8$s, %6$s" - "%9$s, %2$s %3$s%8$s" - "%4$s, %2$s %3$s, %1$s%9$s, %7$s %8$s, %6$s" - "%2$s %3$s, %5$s%7$s %8$s, %10$s" - "%1$s, %2$s %3$s, %5$s%6$s, %7$s %8$s, %10$s" - "%2$s %3$s, %4$s, %5$s%7$s %8$s, %9$s, %10$s" - "%4$s, %2$s %3$s, %1$s, %5$s%9$s, %7$s %8$s, %6$s, %10$s" - "%Y %b, %-d" - "%b %Y" - "%b %-d" - "일요일" - "월요일" - "화요일" - "수요일" - "목요일" - "금요일" - "토요일" - "일요일" - "월" - "화" - "수" - "목" - "금" - "토" - "일" - "월" - "화" - "수" - "목" - "금" - "토" - "일" - "월" - "화" - "수" - "목" - "금" - "토" - "일" - "3월" - "목" - "수" - "목" - "금" - "토" - "1월" - "2월" - "3월" - "4월" - "5월" - "6월" - "7월" - "8월" - "9월" - "10월" - "11월" - "12월" - "1월" - "2월" - "3월" - "4월" - "5월" - "6월" - "7월" - "8월" - "9월" - "10월" - "11월" - "12월" - "1월" - "금" - "3월" - "4월" - "5월" - "6월" - "7월" - "8월" - "9월" - "10월" - "11월" - "12월" "%1$02d:%2$02d" "%1$d:%2$02d:%3$02d" "모두 선택" diff --git a/core/res/res/values-lt-rLT/donottranslate-cldr.xml b/core/res/res/values-lt-rLT/donottranslate-cldr.xml new file mode 100644 index 0000000000000000000000000000000000000000..20d58e05fea59914b18d7b7f0bb3d91f13400b4e --- /dev/null +++ b/core/res/res/values-lt-rLT/donottranslate-cldr.xml @@ -0,0 +1,147 @@ + + + Sausis + Vasaris + Kovas + Balandis + Gegužė + Birželis + Liepa + Rugpjūtis + Rugsėjis + Spalis + Lapkritis + Gruodis + + sausio + vasario + kovo + balandžio + gegužės + birželio + liepos + rugpjūčio + rugsėjo + spalio + lapkričio + gruodžio + + Sau + Vas + Kov + Bal + Geg + Bir + Lie + Rgp + Rgs + Spl + Lap + Grd + + S + V + K + B + G + B + L + R + R + S + L + G + + sekmadienis + pirmadienis + antradienis + trečiadienis + ketvirtadienis + penktadienis + šeštadienis + + Sk + Pr + An + Tr + Kt + Pn + Št + + Sk + Pr + An + Tr + Kt + Pn + Št + + S + P + A + T + K + P + Š + + priešpiet + popiet + vakar + šiandien + rytoj + + %-k:%M + %-l:%M %p + %-l:%M %p + h:mm a + H:mm + %Y-%m-%d + yyyy-MM-dd + "%s-%s-%s" + %Y m. %B %-e d. + %H:%M:%S + %H:%M:%S %Y.%m.%d + %2$s %1$s + %1$s %3$s + %Y.%m.%d + %B %-e + %-B + %Y %B + %b %-e d. + %b + %Y m. %b + %1$s - %2$s + %2$s - %5$s + %2$s-%3$s - %7$s-%8$s + %2$s-%3$s%1$s - %7$s-%8$s%6$s + %4$s-%2$s-%3$s - %9$s-%7$s-%8$s + %4$s-%2$s-%3$s%1$s - %9$s-%7$s-%8$s%6$s + %5$s %4$s-%2$s-%3$s%1$s - %10$s %9$s-%7$s-%8$s%6$s + %5$s %2$s-%3$s - %10$s %7$s-%8$s + %5$s %2$s-%3$s%1$s - %10$s %7$s-%8$s%6$s + %5$s %4$s-%2$s-%3$s - %10$s %9$s-%7$s-%8$s + %3$s %2$s%1$s - %6$s %5$s%4$s + %2$s%1$s - %5$s%4$s + %3$s %2$s - %6$s %5$s + %1$s %3$s%2$s + %3$s%2$s + %1$s %2$s + %2$s %3$s - %7$s %8$s + %2$s %3$s d.%1$s - %7$s %8$s d.%6$s + %5$s %2$s %3$s - %10$s %7$s %8$s + %5$s %2$s %3$s - %10$s %7$s %8$s + %5$s %2$s %3$s d.%1$s - %10$s %7$s %8$s d.%6$s + %5$s %2$s %3$s d.%1$s - %10$s %7$s %8$s d.%6$s + %5$s %4$s m. %2$s %3$s d. - %10$s %9$s m. %7$s %8$s d. + %5$s %4$s m. %2$s %3$s d. - %10$s %9$s m. %7$s %8$s d. + %5$s %4$s m. %2$s %3$s d.,%1$s - %10$s %9$s m. %7$s %8$s d.,%6$s + %5$s %4$s m. %2$s %3$s d.,%1$s - %10$s %9$s m. %7$s %8$s d.,%6$s + %4$s m. %2$s %3$s d.,%1$s - %9$s m. %7$s %8$s d.,%6$s + %2$s %3$s d.-%8$s d. + %2$s %3$s d.%1$s - %7$s %8$s d.%6$s + %9$s m. %2$s %3$s d. - %7$s %8$s d. + %9$s m. %2$s %3$s d.-%8$s d. + %9$s m. %2$s %3$s d.,%1$s - %7$s %8$s d.,%6$s + %b + diff --git a/core/res/res/values-lv-rLV/donottranslate-cldr.xml b/core/res/res/values-lv-rLV/donottranslate-cldr.xml new file mode 100644 index 0000000000000000000000000000000000000000..3dec1d2e37264859e4748d1bb9a072151fec6e26 --- /dev/null +++ b/core/res/res/values-lv-rLV/donottranslate-cldr.xml @@ -0,0 +1,147 @@ + + + janvāris + februāris + marts + aprīlis + maijs + jūnijs + jūlijs + augusts + septembris + oktobris + novembris + decembris + + janvāris + februāris + marts + aprīlis + maijs + jūnijs + jūlijs + augusts + septembris + oktobris + novembris + decembris + + janv. + febr. + marts + apr. + maijs + jūn. + jūl. + aug. + sept. + okt. + nov. + dec. + + J + F + M + A + M + J + J + A + S + O + N + D + + svētdiena + pirmdiena + otrdiena + trešdiena + ceturtdiena + piektdiena + sestdiena + + Sv + Pr + Ot + Tr + Ce + Pk + Se + + Sv + Pr + Ot + Tr + Ce + Pk + Se + + S + P + O + T + C + P + S + + AM + PM + vakar + šodien + rīt + + %H:%M + %-l:%M %p + %-l:%M %^p + h:mm a + HH:mm + %d.%m.%Y + dd.MM.yyyy + "%s.%s.%s" + %Y. gada %-e. %B + %H:%M:%S + %H:%M:%S %Y. gada %-e. %b + %2$s %1$s + %1$s %3$s + %Y. gada %-e. %b + %-e. %B + %-B + %Y. g. %B + %-e. %b + %-b + %Y. g. %b + %1$s - %2$s + %2$s - %5$s + %3$s.%2$s–%8$s.%7$s + %1$s, %3$s.%2$s - %6$s, %8$s.%7$s + %3$s.%2$s.%4$s.–%8$s.%7$s.%9$s. + %1$s, %3$s.%2$s.%4$s. – %6$s, %8$s.%7$s.%9$s. + %5$s %1$s, %3$s.%2$s.%4$s. - %10$s %6$s, %8$s.%7$s.%9$s. + %5$s %3$s.%2$s. - %10$s %8$s.%7$s. + %5$s %1$s, %3$s.%2$s. - %10$s %6$s, %8$s.%7$s. + %5$s %3$s.%2$s.%4$s - %10$s %8$s.%7$s.%9$s + %3$s %1$s, %2$s - %6$s %4$s, %5$s + %1$s, %2$s - %4$s, %5$s + %3$s %2$s - %6$s %5$s + %1$s %2$s, %3$s + %2$s, %3$s + %1$s %2$s + %3$s. %2$s - %8$s. %7$s + %1$s, %3$s. %2$s - %6$s, %8$s. %7$s + %5$s %3$s. %2$s - %10$s %8$s. %7$s + %5$s %3$s. %2$s - %10$s %8$s. %7$s + %5$s %1$s, %3$s. %2$s - %10$s %6$s, %8$s. %7$s + %5$s %1$s, %3$s. %2$s - %10$s %6$s, %8$s. %7$s + %5$s %4$s. gada %3$s. %2$s - %10$s %9$s. gada %8$s. %7$s + %5$s %4$s. gada %3$s. %2$s - %10$s %9$s. gada %8$s. %7$s + %5$s %1$s, %4$s. g. %3$s. %2$s - %10$s %6$s, %9$s. g. %8$s. %7$s + %5$s %1$s, %4$s. g. %3$s. %2$s - %10$s %6$s, %9$s. g. %8$s. %7$s + %1$s, %4$s. g. %3$s. %2$s - %6$s, %9$s. g. %8$s. %7$s + %3$s.-%8$s. %2$s + %1$s, %3$s. %2$s - %6$s, %8$s. %7$s + %9$s. gada %3$s. %2$s - %8$s. %7$s + %9$s. gada %3$s.-%8$s. %2$s + %1$s, %9$s. gada %3$s. %2$s - %6$s, y. gada %8$s. %7$s + %b + diff --git a/core/res/res/values-mcc204-pt/strings.xml b/core/res/res/values-mcc204-pt/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..7d962307a3dec62ea217710815cd743ee10b6a23 --- /dev/null +++ b/core/res/res/values-mcc204-pt/strings.xml @@ -0,0 +1,19 @@ + + + + "nl_nl" + diff --git a/core/res/res/values-mcc230-pt/strings.xml b/core/res/res/values-mcc230-pt/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..d3ecdbba28ed282eed8e8235d93968148706ce19 --- /dev/null +++ b/core/res/res/values-mcc230-pt/strings.xml @@ -0,0 +1,19 @@ + + + + "cs_cz" + diff --git a/core/res/res/values-mcc232-pt/strings.xml b/core/res/res/values-mcc232-pt/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..4773838f9c477ca1174012b41fde08273354b313 --- /dev/null +++ b/core/res/res/values-mcc232-pt/strings.xml @@ -0,0 +1,19 @@ + + + + "de_at" + diff --git a/core/res/res/values-mcc234-pt/strings.xml b/core/res/res/values-mcc234-pt/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..2538b73292392d10937e072443230a9043f10335 --- /dev/null +++ b/core/res/res/values-mcc234-pt/strings.xml @@ -0,0 +1,19 @@ + + + + "en_gb" + diff --git a/core/res/res/values-mcc260-pt/strings.xml b/core/res/res/values-mcc260-pt/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..1161f9a6356f32ef2a1447927b58dc1445956e10 --- /dev/null +++ b/core/res/res/values-mcc260-pt/strings.xml @@ -0,0 +1,19 @@ + + + + "pl_pl" + diff --git a/core/res/res/values-mcc262-pt/strings.xml b/core/res/res/values-mcc262-pt/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..9505cf4b422629b7f307e5a98e1351ed7b4806c6 --- /dev/null +++ b/core/res/res/values-mcc262-pt/strings.xml @@ -0,0 +1,19 @@ + + + + "de_de" + diff --git a/core/res/res/values-nb/donottranslate-cldr.xml b/core/res/res/values-nb/donottranslate-cldr.xml new file mode 100644 index 0000000000000000000000000000000000000000..ecf01118298f43447a57fe31f157710689976c4c --- /dev/null +++ b/core/res/res/values-nb/donottranslate-cldr.xml @@ -0,0 +1,147 @@ + + + januar + februar + mars + april + mai + juni + juli + august + september + oktober + november + desember + + januar + februar + mars + april + mai + juni + juli + august + september + oktober + november + desember + + jan. + feb. + mars + apr. + mai + juni + juli + aug. + sep. + okt. + nov. + des. + + J + F + M + A + M + J + J + A + S + O + N + D + + søndag + mandag + tirsdag + onsdag + torsdag + fredag + lørdag + + søn. + man. + tir. + ons. + tor. + fre. + lør. + + søn. + man. + tir. + ons. + tor. + fre. + lør. + + S + M + T + O + T + F + L + + AM + PM + i går + i dag + i morgen + + %H.%M + %-l.%M %p + %-l.%M %^p + h.mm a + HH.mm + %d.%m.%Y + dd.MM.yyyy + "%s.%s.%s" + %-e. %B %Y + %H.%M.%S + %H.%M.%S %-e. %b %Y + %2$s %1$s + %1$s %3$s + %-e. %b %Y + %-e. %B + %-B + %B %Y + %-e. %b + %-b + %b %Y + %1$s–%2$s + %2$s–%5$s + %3$s.%2$s.–%8$s.%7$s. + %1$s %3$s.%2$s.–%6$s %8$s.%7$s. + %3$s.%2$s.%4$s–%8$s.%7$s.%9$s + %1$s %3$s.%2$s.%4$s–%6$s %8$s.%7$s.%9$s + %5$s %1$s %3$s.%2$s.%4$s–%10$s %6$s %8$s.%7$s.%9$s + %5$s %3$s.%2$s.–%10$s %8$s.%7$s. + %5$s %1$s %3$s.%2$s–%10$s %6$s %8$s.%7$s + %5$s %3$s.%2$s.%4$s–%10$s %8$s.%7$s.%9$s + %3$s %1$s %2$s–%6$s %4$s %5$s + %1$s %2$s–%4$s %5$s + %3$s %2$s–%6$s %5$s + %1$s %2$s %3$s + %2$s %3$s + %1$s %2$s + %3$s. %2$s–%8$s. %7$s + %1$s %3$s. %2$s–%6$s %8$s. %7$s + %5$s %3$s. %2$s–%10$s %8$s. %7$s + %5$s %3$s. %2$s–%10$s %8$s. %7$s + %5$s %1$s %3$s. %2$s–%10$s %6$s %8$s. %7$s + %5$s %1$s %3$s. %2$s–%10$s %6$s %8$s. %7$s + %5$s %3$s. %2$s %4$s–%10$s %8$s. %7$s %9$s + %5$s %3$s. %2$s %4$s–%10$s %8$s. %7$s %9$s + %5$s %1$s %3$s. %2$s %4$s–%10$s %6$s %8$s. %7$s %9$s + %5$s %1$s %3$s. %2$s %4$s–%10$s %6$s %8$s. %7$s %9$s + %1$s %3$s. %2$s %4$s–%6$s %8$s. %7$s %9$s + %3$s.–%8$s. %2$s + %1$s %3$s. %2$s–%6$s %8$s. %7$s + %3$s. %2$s–%8$s. %7$s %9$s + %3$s.–%8$s. %2$s %9$s + %1$s %3$s. %2$s–%6$s %8$s. %7$s %9$s + %b + diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml index 7bed159c51ca8d20d1849913abb29e9748aa34e3..33d015997b34f8f54c7bc299b6978de5e8ace0d2 100644 --- a/core/res/res/values-nb/strings.xml +++ b/core/res/res/values-nb/strings.xml @@ -166,14 +166,10 @@ "Tillater applikasjonen å endre gjeldende innstillinger, slik som språk eller skriftstørrelse." "omstarte andre applikasjoner" "Lar applikasjonen tvinge andre applikasjoner til å starte på nytt." - "unngå å bli stoppet" - "Lar applikasjonen sette en vilkårlig prosess i forgrunnen, så den ikke kan bli drept. Vanlige applikasjoner bør aldri trenge dette." "tvinge applikasjoner til å lukkes" "Lar applikasjonen tvinge enhver aktivitet som er i forgrunnen til å lukkes og gå tilbake. Vanlige applikasjoner bør aldri trenge dette." "hente intern systemtilstand" "Lar applikasjonen hente intern tilstand fra systemet. Onsdinnede applikasjoner kan hente et bredt spekter av privat og sikker informasjon som de vanligvis aldri burde ha behov for." - "publisere lavnivåtjenester" - "Lar applikasjonen publisere sine egne lavnivås systemtjenester. Ondsinnede applikasjoner kan kapre systemet, og stjele eller ødelegge alle data på det." "overvåke og kontrollere all applikasjonsoppstart" "Lar applikasjonen overvåke og kontrollere hvordan systemet starter applikasjoner. Ondsinnede applikasjoner kan ta over systemet helt. Denne rettigheten behøves bare for utvikling, aldri for vanlig bruk av telefonen." "kringkaste melding om fjernet pakke" @@ -186,8 +182,6 @@ "Lar applikasjonen kontrollere maksimalt antall kjørende prosesser. Behøves aldri for vanlige applikasjoner." "få alle bakgrunnsapplikasjoner til å lukkes" "Lar applikasjonen kontrollere om aktiviteter alltid avsluttes når de sendes til bakgrunnen. Behøves aldri for vanlige applikasjoner." - "installere systemoppdateringer automatisk" - "Lar applikasjonen motta meldinger om pågående systemoppdateringer, og starte installeringen av dem. Ondsinnede applikasjoner kan bruke dette for å skade systemet med uautoriserte oppdateringer, eller generelt forstyrre oppdateringsprosessen." "endre batteristatistikk" "Lar applikasjonen endre på innsamlet batteristatistikk. Ikke ment for vanlige applikasjoner." "vis uautoriserte vinduer" @@ -424,9 +418,6 @@ "Password" "Sign in" "Invalid username or password." - "h:mm AA" - "%-l:%M%P" - "%-l:%M%p" "%-l%P" "%-l%p" "Fjern varslinger" @@ -458,9 +449,6 @@ "enter" "slett" "Søk" - "I dag" - "I går" - "I morgen" "For en måned siden" "For over en måned siden" @@ -542,13 +530,6 @@ "uker" "år" "år" - "søndag" - "mandag" - "tirsdag" - "onsdag" - "torsdag" - "fredag" - "lørdag" "Hverdager (man–fre)" "Hver dag" "Hver %s" @@ -558,26 +539,7 @@ "Beklager, denne videoen er ikke gyldig for streaming til denne enheten." "Beklager, kan ikke spille denne videoen." "OK" - "AM" - "PM" - "%Y-%m-%d" - "%1$s %2$s %3$s%4$s %5$s %6$s" - "%1$s %2$s%4$s %5$s" - "%2$s %3$s%5$s %6$s" - "%2$s%5$s" - "%1$s%2$s" - "%1$s, %2$s %3$s" - "%2$s %3$sPLACEHOLDERplaceholder" - "%1$s, %3$s" - "%1$s, %2$s" "%1$s, %2$s" - "%1$s, %2$s" - "MMMM' 'd'., 'yyyy" - "d'. 'MMMM' 'yyyy" - "MMM' 'd', 'yyyy" - "d'. 'MMM' 'yyyy" - "h':'mm' 'a" - "HH':'mm" "middag" "Middag" "midnatt" @@ -586,111 +548,10 @@ - "%-d. %B %Y" - "%B %Y" - "%H:%M:%S" - "%H:%M:%S %-d. %B %Y" - "%2$s %3$s%7$s %8$s" - "%1$s %3$s. %2$s%6$s %8$s. %7$s" - "%3$s. %2$s%8$s. %7$s %9$s" - "%1$s %2$s %3$s%6$s %7$s %8$s, %9$s" - "%3$s. %2$s %5$s%8$s. %7$s %10$s" - "%1$s %3$s. %2$s %5$s%6$s %8$s. %7$s %10$s" - "%3$s. %2$s %4$s %5$s%8$s. %7$s %9$s %10$s" - "%1$s %3$s. %2$s %4$s %5$s%6$s %8$s. %7$s %9$s %10$s" - "%3$s.%2$s. – %8$s.%7$s." - "%1$s %3$s.%2$s. – %6$s %8$s.%7$s." - "%3$s.%2$s.%4$s%8$s.%7$s.%9$s" - "%1$s %3$s.%2$s.%4$s%6$s %8$s.%7$s.%9$s" - "%3$s.%2$s. %5$s%8$s.%7$s. %10$s" - "%1$s %3$s.%2$s. %5$s%6$s %8$s.%7$s. %10$s" - "%3$s.%2$s.%4$s %5$s%8$s.%7$s.%9$s %10$s" - "%1$s %3$s.%2$s.%4$s %5$s%6$s %8$s.%7$s.%9$s %10$s" - "%3$s.–%8$s. %2$s" - "%1$s %3$s. %2$s%6$s %8$s. %7$s" - "%3$s.–%8$s %2$s %9$s" - "%1$s %3$s. %2$s %4$s%6$s %8$s. %7$s %9$s" - "%3$s. %2$s %5$s%8$s. %7$s %10$s" - "%1$s %3$s. %2$s %5$s%6$s %8$s. %7$s %10$s" - "%3$s. %2$s %4$s %5$s%8$s. %7$s %9$s %10$s" - "%1$s %3$s. %2$s %4$s %5$s%6$s %8$s. %7$s %9$s %10$s" - "%-d. %b %Y" - "%b %Y" - "søndag" - "mandag" - "tirsdag" - "onsdag" - "torsdag" - "fredag" - "lørdag" - "søn" - "man" - "tir" - "ons" - "tor" - "fre" - "lør" - "sø" - "ma" - "ti" - "on" - "to" - "fr" - "lø" - "S" - "M" - "Ti" - "O" - "To" - "F" - "L" - "S" - "M" - "T" - "O" - "T" - "F" - "L" - "januar" - "februar" - "mars" - "april" - "mai" - "juni" - "juli" - "august" - "september" - "oktober" - "november" - "desember" - "jan" - "feb" - "mar" - "apr" - "mai" - "jun" - "jul" - "aug" - "sep" - "okt" - "nov" - "des" - "J" - "F" - "M" - "A" - "M" - "J" - "J" - "A" - "S" - "O" - "N" - "D" "%1$02d:%2$02d" "%1$d:%2$02d:%3$02d" "Merk alt" diff --git a/core/res/res/values-nl-rBE/donottranslate-cldr.xml b/core/res/res/values-nl-rBE/donottranslate-cldr.xml new file mode 100644 index 0000000000000000000000000000000000000000..680a3928635737fc8901fea7dc507bfe6922afd8 --- /dev/null +++ b/core/res/res/values-nl-rBE/donottranslate-cldr.xml @@ -0,0 +1,147 @@ + + + januari + februari + maart + april + mei + juni + juli + augustus + september + oktober + november + december + + januari + februari + maart + april + mei + juni + juli + augustus + september + oktober + november + december + + jan. + feb. + mrt. + apr. + mei + jun. + jul. + aug. + sep. + okt. + nov. + dec. + + J + F + M + A + M + J + J + A + S + O + N + D + + zondag + maandag + dinsdag + woensdag + donderdag + vrijdag + zaterdag + + zo + ma + di + wo + do + vr + za + + zo + ma + di + wo + do + vr + za + + Z + M + D + W + D + V + Z + + AM + PM + Gisteren + Vandaag + Morgen + + %H:%M + %-l:%M %p + %-l:%M %^p + h:mm a + HH:mm + %-e/%m/%Y + d/MM/yyyy + "%s/%s/%s" + %-e %B %Y + %H:%M:%S + %H:%M:%S %-e-%b-%Y + %2$s %1$s + %1$s %3$s + %-e-%b-%Y + %-e %B + %-B + %B %Y + %-e-%b + %-b + %b %Y + %1$s - %2$s + %2$s - %5$s + %3$s/%2$s - %8$s/%7$s + %1$s %3$s/%2$s - %6$s %8$s/%7$s + %3$s/%2$s/%4$s - %8$s/%7$s/%9$s + %1$s %3$s/%2$s/%4$s - %6$s %8$s/%7$s/%9$s + %5$s %1$s %3$s/%2$s/%4$s - %10$s %6$s %8$s/%7$s/%9$s + %5$s %3$s/%2$s - %10$s %8$s/%7$s + %5$s %1$s %3$s/%2$s - %10$s %6$s %8$s/%7$s + %5$s %3$s/%2$s/%4$s - %10$s %8$s/%7$s/%9$s + %3$s %1$s %2$s - %6$s %4$s %5$s + %1$s %2$s - %4$s %5$s + %3$s %2$s - %6$s %5$s + %1$s %2$s %3$s + %2$s %3$s + %1$s %2$s + %3$s %2$s - %8$s %7$s + %1$s %3$s %2$s - %6$s %8$s %7$s + %5$s %3$s %2$s - %10$s %8$s %7$s + %5$s %3$s %2$s - %10$s %8$s %7$s + %5$s %1$s %3$s %2$s - %10$s %6$s %8$s %7$s + %5$s %1$s %3$s %2$s - %10$s %6$s %8$s %7$s + %5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s + %5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s + %5$s %1$s %3$s %2$s %4$s - %10$s %6$s %8$s %7$s %9$s + %5$s %1$s %3$s %2$s %4$s - %10$s %6$s %8$s %7$s %9$s + %1$s %3$s %2$s %4$s - %6$s %8$s %7$s %9$s + %3$s-%8$s %2$s + %1$s %3$s %2$s - %6$s %8$s %7$s + %3$s %2$s - %8$s %7$s %9$s + %3$s-%8$s %2$s %9$s + %1$s %3$s %2$s - %6$s %8$s %7$s %9$s + %b + diff --git a/core/res/res/values-nl-rNL/donottranslate-cldr.xml b/core/res/res/values-nl-rNL/donottranslate-cldr.xml new file mode 100644 index 0000000000000000000000000000000000000000..b6231b62279048b0aac974523fede20d456622c3 --- /dev/null +++ b/core/res/res/values-nl-rNL/donottranslate-cldr.xml @@ -0,0 +1,147 @@ + + + januari + februari + maart + april + mei + juni + juli + augustus + september + oktober + november + december + + januari + februari + maart + april + mei + juni + juli + augustus + september + oktober + november + december + + jan. + feb. + mrt. + apr. + mei + jun. + jul. + aug. + sep. + okt. + nov. + dec. + + J + F + M + A + M + J + J + A + S + O + N + D + + zondag + maandag + dinsdag + woensdag + donderdag + vrijdag + zaterdag + + zo + ma + di + wo + do + vr + za + + zo + ma + di + wo + do + vr + za + + Z + M + D + W + D + V + Z + + AM + PM + Gisteren + Vandaag + Morgen + + %H:%M + %-l:%M %p + %-l:%M %^p + h:mm a + HH:mm + %d-%m-%Y + dd-MM-yyyy + "%s-%s-%s" + %-e %B %Y + %H:%M:%S + %H:%M:%S %-e %b %Y + %2$s %1$s + %1$s %3$s + %-e %b %Y + %-e %B + %-B + %B %Y + %-e-%b + %-b + %b %Y + %1$s - %2$s + %2$s - %5$s + %3$s-%2$s - %8$s-%7$s + %1$s %3$s-%2$s - %6$s %8$s-%7$s + %3$s-%2$s-%4$s - %8$s-%7$s-%9$s + %1$s %3$s-%2$s-%4$s - %6$s %8$s-%7$s-%9$s + %5$s %1$s %3$s-%2$s-%4$s - %10$s %6$s %8$s-%7$s-%9$s + %5$s %3$s-%2$s - %10$s %8$s-%7$s + %5$s %1$s %3$s-%2$s - %10$s %6$s %8$s-%7$s + %5$s %3$s-%2$s-%4$s - %10$s %8$s-%7$s-%9$s + %3$s %1$s %2$s - %6$s %4$s %5$s + %1$s %2$s - %4$s %5$s + %3$s %2$s - %6$s %5$s + %1$s %2$s %3$s + %2$s %3$s + %1$s %2$s + %3$s %2$s - %8$s %7$s + %1$s %3$s %2$s - %6$s %8$s %7$s + %5$s %3$s %2$s - %10$s %8$s %7$s + %5$s %3$s %2$s - %10$s %8$s %7$s + %5$s %1$s %3$s %2$s - %10$s %6$s %8$s %7$s + %5$s %1$s %3$s %2$s - %10$s %6$s %8$s %7$s + %5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s + %5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s + %5$s %1$s %3$s %2$s %4$s - %10$s %6$s %8$s %7$s %9$s + %5$s %1$s %3$s %2$s %4$s - %10$s %6$s %8$s %7$s %9$s + %1$s %3$s %2$s %4$s - %6$s %8$s %7$s %9$s + %3$s-%8$s %2$s + %1$s %3$s %2$s - %6$s %8$s %7$s + %3$s %2$s - %8$s %7$s %9$s + %3$s-%8$s %2$s %9$s + %1$s %3$s %2$s - %6$s %8$s %7$s %9$s + %b + diff --git a/core/res/res/values-nl/donottranslate-cldr.xml b/core/res/res/values-nl/donottranslate-cldr.xml new file mode 100644 index 0000000000000000000000000000000000000000..b6231b62279048b0aac974523fede20d456622c3 --- /dev/null +++ b/core/res/res/values-nl/donottranslate-cldr.xml @@ -0,0 +1,147 @@ + + + januari + februari + maart + april + mei + juni + juli + augustus + september + oktober + november + december + + januari + februari + maart + april + mei + juni + juli + augustus + september + oktober + november + december + + jan. + feb. + mrt. + apr. + mei + jun. + jul. + aug. + sep. + okt. + nov. + dec. + + J + F + M + A + M + J + J + A + S + O + N + D + + zondag + maandag + dinsdag + woensdag + donderdag + vrijdag + zaterdag + + zo + ma + di + wo + do + vr + za + + zo + ma + di + wo + do + vr + za + + Z + M + D + W + D + V + Z + + AM + PM + Gisteren + Vandaag + Morgen + + %H:%M + %-l:%M %p + %-l:%M %^p + h:mm a + HH:mm + %d-%m-%Y + dd-MM-yyyy + "%s-%s-%s" + %-e %B %Y + %H:%M:%S + %H:%M:%S %-e %b %Y + %2$s %1$s + %1$s %3$s + %-e %b %Y + %-e %B + %-B + %B %Y + %-e-%b + %-b + %b %Y + %1$s - %2$s + %2$s - %5$s + %3$s-%2$s - %8$s-%7$s + %1$s %3$s-%2$s - %6$s %8$s-%7$s + %3$s-%2$s-%4$s - %8$s-%7$s-%9$s + %1$s %3$s-%2$s-%4$s - %6$s %8$s-%7$s-%9$s + %5$s %1$s %3$s-%2$s-%4$s - %10$s %6$s %8$s-%7$s-%9$s + %5$s %3$s-%2$s - %10$s %8$s-%7$s + %5$s %1$s %3$s-%2$s - %10$s %6$s %8$s-%7$s + %5$s %3$s-%2$s-%4$s - %10$s %8$s-%7$s-%9$s + %3$s %1$s %2$s - %6$s %4$s %5$s + %1$s %2$s - %4$s %5$s + %3$s %2$s - %6$s %5$s + %1$s %2$s %3$s + %2$s %3$s + %1$s %2$s + %3$s %2$s - %8$s %7$s + %1$s %3$s %2$s - %6$s %8$s %7$s + %5$s %3$s %2$s - %10$s %8$s %7$s + %5$s %3$s %2$s - %10$s %8$s %7$s + %5$s %1$s %3$s %2$s - %10$s %6$s %8$s %7$s + %5$s %1$s %3$s %2$s - %10$s %6$s %8$s %7$s + %5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s + %5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s + %5$s %1$s %3$s %2$s %4$s - %10$s %6$s %8$s %7$s %9$s + %5$s %1$s %3$s %2$s %4$s - %10$s %6$s %8$s %7$s %9$s + %1$s %3$s %2$s %4$s - %6$s %8$s %7$s %9$s + %3$s-%8$s %2$s + %1$s %3$s %2$s - %6$s %8$s %7$s + %3$s %2$s - %8$s %7$s %9$s + %3$s-%8$s %2$s %9$s + %1$s %3$s %2$s - %6$s %8$s %7$s %9$s + %b + diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml index 3a9e32c52471fb33ae896e092e50698d64492b3e..a418d72a882fb97ef3bde6692eb9820979d00032 100644 --- a/core/res/res/values-nl/strings.xml +++ b/core/res/res/values-nl/strings.xml @@ -161,14 +161,10 @@ "Hiermee kan een toepassing de huidige configuratie, zoals de landinstelling of de algemene lettergrootte, wijzigen." "andere toepassingen opnieuw starten" "Hiermee kan een toepassing andere toepassingen opnieuw starten." - "stoppen voorkomen" - "Hiermee kan een toepassing ervoor zorgen dat elk willekeurig proces op de voorgrond wordt uitgevoerd en dus niet kan worden afgesloten. Nooit vereist voor normale toepassingen." "toepassing nu sluiten" "Hiermee kan een toepassing elke willekeurige activiteit die op de voorgrond wordt uitgevoerd, sluiten en naar de achtergrond verplaatsen. Nooit vereist voor normale toepassingen." "interne systeemstatus ophalen" "Hiermee kan een toepassing de interne status van het systeem ophalen. Schadelijke toepassingen kunnen privé- of veiligheidsgegevens ophalen die ze normaal niet nodig hebben." - "services op laag niveau publiceren" - "Hiermee kunnen toepassingen hun eigen systeemservices op laag niveau publiceren. Schadelijke toepassingen kunnen het systeem mogelijk kapen en willekeurige gegevens van het systeem stelen of beschadigen." "alle startende toepassingen bijhouden en beheren" "Hiermee kan een toepassing de manier waarop het systeem activiteiten start, bijhouden en beheren. Schadelijke toepassingen kunnen het systeem volledig in gevaar brengen. Deze machtiging is alleen voor ontwikkeling vereist, nooit voor normaal telefoongebruik." "melding verzenden dat pakket is verwijderd" @@ -181,8 +177,6 @@ "Hiermee kan een toepassing het maximum aantal processen bepalen dat wordt uitgevoerd. Nooit vereist voor normale toepassingen." "alle achtergrondtoepassingen sluiten" "Hiermee kan een toepassing bepalen of activiteiten altijd worden afgesloten zodra deze naar de achtergrond gaan. Nooit nodig voor normale toepassingen." - "systeemupdates automatisch installeren" - "Hiermee ontvangt een toepassing meldingen over beschikbare systeemupdates en kan hun installatie starten. Schadelijke toepassingen kunnen hiervan gebruik maken om het systeem met ongeautoriseerde updates te beschadigen of het updateproces in het algemeen te verstoren." "accustatistieken aanpassen" "Hiermee kunnen verzamelde accustatistieken worden gewijzigd. Niet voor gebruik door normale toepassingen." "niet-geautoriseerde vensters weergeven" @@ -418,9 +412,6 @@ "Wachtwoord" "Aanmelden" "Gebruikersnaam of wachtwoord ongeldig." - "h:mm AA" - "%-l:%M%P" - "%-l:%M%p" "%-l%P" "%-l%p" "Meldingen wissen" @@ -452,9 +443,6 @@ "invoeren" "verwijderen" "Zoeken" - "Vandaag" - "Gisteren" - "Morgen" "1 maand geleden" "Meer dan 1 maand geleden" @@ -536,13 +524,6 @@ "weken" "jaar" "jaren" - "Zondag" - "Maandag" - "Dinsdag" - "Woensdag" - "Donderdag" - "Vrijdag" - "Zaterdag" "Elke weekdag (ma-vr)" "Dagelijks" "Wekelijks op %s" @@ -552,137 +533,15 @@ "Deze video kan helaas niet worden gestreamd naar dit apparaat." "Deze video kan niet worden afgespeeld." "OK" - "am" - "pm" - "%d-%m-%Y" - "%1$s %2$s, %3$s%4$s %5$s, %6$s" - "%1$s %2$s%4$s %5$s" - "%2$s, %3$s%5$s, %6$s" - "%2$s%5$s" - "%1$s%2$s" - "%1$s, %2$s %3$s" - "%2$s %3$s" - "%1$s, %3$s" - "%1$s, %2$s" "%1$s, %2$s" - "%1$s, %2$s" - "d' 'MMMM' 'yyyy" - "d' 'MMMM' 'yyyy" - "d' 'MMM' 'yyyy" - "d' 'MMM' 'yyyy" - "h':'mm' 'a" - "HH':'mm" "twaalf uur \'s middags" "Twaalf uur \'s middags" "middernacht" "Middernacht" - "%-d %B" - "%-d %B %Y" - "%B %Y" - "%H:%M:%S" - "%H:%M:%S %-d %B %Y" - "%3$s %2$s%8$s %7$s" - "%1$s %3$s %2$s%6$s %8$s %7$s" - "%3$s %2$s%8$s %7$s %9$s" - "%1$s %3$s %2$s%6$s %8$s %7$s %9$s" - "%3$s %2$s, %5$s%8$s %7$s, %10$s" - "%1$s %3$s %2$s, %5$s%6$s %8$s %7$s, %10$s" - "%3$s %2$s %4$s, %5$s%8$s %7$s %9$s, %10$s" - "%1$s %3$s %2$s %4$s, %5$s%6$s %8$s %7$s %9$s, %10$s" - "%3$s-%2$s%8$s-%7$s" - "%1$s %3$s-%2$s%6$s %8$s-%7$s" - "%3$s-%2$s-%4$s%8$s-%7$s-%9$s" - "%1$s %3$s-%2$s-%4$s%6$s %8$s-%7$s-%9$s" - "%3$s-%2$s, %5$s%8$s-%7$s, %10$s" - "%1$s %3$s-%2$s, %5$s%6$s %8$s-%7$s, %10$s" - "%3$s-%2$s-%4$s, %5$s%8$s-%7$s-%9$s, %10$s" - "%1$s %3$s-%2$s-%4$s, %5$s%6$s %8$s-%7$s-%9$s, %10$s" - "%3$s%8$s %2$s" - "%1$s %3$s %2$s%6$s %8$s %7$s" - "%3$s%8$s %2$s %9$s" - "%1$s %3$s %2$s %4$s%6$s %8$s %7$s %9$s" - "%3$s %2$s, %5$s%8$s %7$s, %10$s" - "%1$s %3$s %2$s, %5$s%6$s %8$s %7$s, %10$s" - "%3$s %2$s %4$s, %5$s%8$s %7$s %9$s, %10$s" - "%1$s %3$s %2$s %4$s, %5$s%6$s %8$s %7$s %9$s, %10$s" - "%-d %b %Y" - "%b %Y" - "%-d %b" - "Zondag" - "Maandag" - "Dinsdag" - "Woensdag" - "Donderdag" - "Vrijdag" - "Zaterdag" - "Zo" - "Ma" - "Di" - "Wo" - "Do" - "Vr" - "Za" - "Zo" - "Ma" - "Di" - "Wo" - "Do" - "Vr" - "Za" - "Zo" - "M" - "Di" - "W" - "Do" - "V" - "Za" - "Z" - "M" - "D" - "W" - "D" - "V" - "Z" - "Januari" - "Februari" - "Maart" - "April" - "Mei" - "Juni" - "Juli" - "Augustus" - "September" - "Oktober" - "November" - "December" - "Jan" - "Feb" - "Mrt" - "Apr" - "Mei" - "Jun" - "Jul" - "Aug" - "Sep" - "Okt" - "Nov" - "Dec" - "J" - "V" - "M" - "A" - "M" - "J" - "J" - "A" - "S" - "O" - "N" - "D" "%1$02d:%2$02d" "%1$d:%2$02d:%3$02d" "Alles selecteren" diff --git a/core/res/res/values-pl-rPL/donottranslate-cldr.xml b/core/res/res/values-pl-rPL/donottranslate-cldr.xml new file mode 100644 index 0000000000000000000000000000000000000000..4ad17bf11c469ae6d2c315b64822323dbd7092e0 --- /dev/null +++ b/core/res/res/values-pl-rPL/donottranslate-cldr.xml @@ -0,0 +1,147 @@ + + + styczeń + luty + marzec + kwiecień + maj + czerwiec + lipiec + sierpień + wrzesień + październik + listopad + grudzień + + stycznia + lutego + marca + kwietnia + maja + czerwca + lipca + sierpnia + września + października + listopada + grudnia + + sty + lut + mar + kwi + maj + cze + lip + sie + wrz + paź + lis + gru + + s + l + m + k + m + c + l + s + w + p + l + g + + niedziela + poniedziałek + wtorek + środa + czwartek + piątek + sobota + + niedz. + pon. + wt. + śr. + czw. + pt. + sob. + + niedz. + pon. + wt. + śr. + czw. + pt. + sob. + + N + P + W + Ś + C + P + S + + AM + PM + Wczoraj + Dzisiaj + Jutro + + %-k:%M + %-l:%M %p + %-l:%M %^p + h:mm a + H:mm + %d-%m-%Y + dd-MM-yyyy + "%s-%s-%s" + %-e %B %Y + %H:%M:%S + %H:%M:%S %d-%m-%Y + %2$s %1$s + %1$s %3$s + %d-%m-%Y + %-e %B + %-B + %-B %Y + %b %-e + %-b + %Y %b + %1$s - %2$s + %2$s - %5$s + %3$s.%2$s-%8$s.%7$s + %1$s, %3$s.%2$s - %6$s, %8$s.%7$s + %3$s.%2$s.%4$s-%8$s.%7$s.%9$s + %1$s, %3$s-%2$s-%4$s-%6$s, %8$s-%7$s-%9$s + %5$s %1$s, %3$s.%2$s.%4$s - %10$s %6$s, %8$s.%7$s.%9$s + %5$s %3$s.%2$s - %10$s %8$s.%7$s + %5$s %1$s, %2$s-%3$s - %10$s %6$s, %7$s-%8$s + %5$s %3$s-%2$s-%4$s - %10$s %8$s-%7$s-%9$s + %3$s %1$s, %2$s - %6$s %4$s, %5$s + %1$s, %2$s - %4$s, %5$s + %3$s %2$s - %6$s %5$s + %1$s %2$s, %3$s + %2$s, %3$s + %1$s %2$s + %3$s %2$s - %8$s %7$s + %3$s %2$s %1$s - %8$s %7$s %6$s + %5$s %3$s %2$s - %10$s %8$s %7$s + %5$s %3$s %2$s - %10$s %8$s %7$s + %5$s %3$s %2$s %1$s - %10$s %8$s %7$s %6$s + %5$s %3$s %2$s %1$s - %10$s %8$s %7$s %6$s + %5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s + %5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s + %5$s %1$s, %3$s %2$s %4$s - %10$s %6$s, %8$s %7$s %9$s + %5$s %1$s, %3$s %2$s %4$s - %10$s %6$s, %8$s %7$s %9$s + %1$s, %3$s %2$s %4$s - %6$s, %8$s %7$s %9$s + %3$s-%8$s %2$s + %3$s %2$s %1$s - %8$s %7$s %6$s + %3$s %2$s - %8$s %7$s %9$s + %3$s-%8$s %2$s %9$s + %1$s, %3$s %2$s - %6$s, %8$s %7$s %9$s + %b + diff --git a/core/res/res/values-pl/donottranslate-cldr.xml b/core/res/res/values-pl/donottranslate-cldr.xml new file mode 100644 index 0000000000000000000000000000000000000000..4ad17bf11c469ae6d2c315b64822323dbd7092e0 --- /dev/null +++ b/core/res/res/values-pl/donottranslate-cldr.xml @@ -0,0 +1,147 @@ + + + styczeń + luty + marzec + kwiecień + maj + czerwiec + lipiec + sierpień + wrzesień + październik + listopad + grudzień + + stycznia + lutego + marca + kwietnia + maja + czerwca + lipca + sierpnia + września + października + listopada + grudnia + + sty + lut + mar + kwi + maj + cze + lip + sie + wrz + paź + lis + gru + + s + l + m + k + m + c + l + s + w + p + l + g + + niedziela + poniedziałek + wtorek + środa + czwartek + piątek + sobota + + niedz. + pon. + wt. + śr. + czw. + pt. + sob. + + niedz. + pon. + wt. + śr. + czw. + pt. + sob. + + N + P + W + Ś + C + P + S + + AM + PM + Wczoraj + Dzisiaj + Jutro + + %-k:%M + %-l:%M %p + %-l:%M %^p + h:mm a + H:mm + %d-%m-%Y + dd-MM-yyyy + "%s-%s-%s" + %-e %B %Y + %H:%M:%S + %H:%M:%S %d-%m-%Y + %2$s %1$s + %1$s %3$s + %d-%m-%Y + %-e %B + %-B + %-B %Y + %b %-e + %-b + %Y %b + %1$s - %2$s + %2$s - %5$s + %3$s.%2$s-%8$s.%7$s + %1$s, %3$s.%2$s - %6$s, %8$s.%7$s + %3$s.%2$s.%4$s-%8$s.%7$s.%9$s + %1$s, %3$s-%2$s-%4$s-%6$s, %8$s-%7$s-%9$s + %5$s %1$s, %3$s.%2$s.%4$s - %10$s %6$s, %8$s.%7$s.%9$s + %5$s %3$s.%2$s - %10$s %8$s.%7$s + %5$s %1$s, %2$s-%3$s - %10$s %6$s, %7$s-%8$s + %5$s %3$s-%2$s-%4$s - %10$s %8$s-%7$s-%9$s + %3$s %1$s, %2$s - %6$s %4$s, %5$s + %1$s, %2$s - %4$s, %5$s + %3$s %2$s - %6$s %5$s + %1$s %2$s, %3$s + %2$s, %3$s + %1$s %2$s + %3$s %2$s - %8$s %7$s + %3$s %2$s %1$s - %8$s %7$s %6$s + %5$s %3$s %2$s - %10$s %8$s %7$s + %5$s %3$s %2$s - %10$s %8$s %7$s + %5$s %3$s %2$s %1$s - %10$s %8$s %7$s %6$s + %5$s %3$s %2$s %1$s - %10$s %8$s %7$s %6$s + %5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s + %5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s + %5$s %1$s, %3$s %2$s %4$s - %10$s %6$s, %8$s %7$s %9$s + %5$s %1$s, %3$s %2$s %4$s - %10$s %6$s, %8$s %7$s %9$s + %1$s, %3$s %2$s %4$s - %6$s, %8$s %7$s %9$s + %3$s-%8$s %2$s + %3$s %2$s %1$s - %8$s %7$s %6$s + %3$s %2$s - %8$s %7$s %9$s + %3$s-%8$s %2$s %9$s + %1$s, %3$s %2$s - %6$s, %8$s %7$s %9$s + %b + diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml index 50d245b5d8e18670a2b0ef173bc5bbcdadb56566..c6c9bd07dec2f747f3c3d59536f4ceb23f075a78 100644 --- a/core/res/res/values-pl/strings.xml +++ b/core/res/res/values-pl/strings.xml @@ -161,14 +161,10 @@ "Pozwala aplikacji zmieniać bieżącą konfigurację, na przykład lokalny lub globalny rozmiar czcionki." "resetowanie innych aplikacji" "Pozwala aplikacji na wymuszenie ponownego uruchomienia innych aplikacji." - "zapobieganie zatrzymaniu" - "Pozwala aplikacji na uruchamianie dowolnego procesu na pierwszym planie tak, że nie można go wyłączyć. Nigdy nie powinno być potrzebne normalnym aplikacjom." "wymuszanie zamknięcia aplikacji" "Pozwala aplikacji na wymuszenie zamknięcia i cofnięcia dowolnej operacji działającej na pierwszym planie. Nigdy nie powinno być potrzebne normalnym aplikacjom." "pobieranie informacji o wewnętrznym stanie systemu" "Pozwala aplikacjom na pobieranie informacji o wewnętrznym stanie systemu. Szkodliwe aplikacje mogą pobrać szeroką gamę osobistych i zabezpieczonych informacji, które normalnie nie powinny im być nigdy potrzebne." - "publikowanie usług niskiego poziomu" - "Pozwala aplikacji na publikowanie własnych usług systemowych niskiego poziomu. Szkodliwe aplikacje mogą przejąć kontrolę nad systemem oraz wykraść lub uszkodzić znajdujące się w nim dane." "monitorowanie i kontrolowanie wszystkich uruchamianych aplikacji" "Pozwala aplikacji na monitorowanie i kontrolowanie sposobu, w jaki w systemie uruchamiane są różne działania. Szkodliwe aplikacje mogą całkowicie przejąć system. Te uprawnienia potrzebne są tylko programistom, nigdy w przypadku normalnego wykorzystywania telefonu." "wysyłanie transmisji informującej o usuniętym pakiecie" @@ -181,8 +177,6 @@ "Pozwala aplikacji na kontrolowanie maksymalnej liczby uruchamianych procesów. Nigdy nie wykorzystywane przez normalne aplikacje." "zamykanie wszystkich aplikacji działających w tle" "Pozwala aplikacji na kontrolowanie, czy czynności są zawsze kończone, kiedy zaczynają działać w tle. Nigdy nie jest potrzebne normalnym aplikacjom." - "automatyczne instalowanie aktualizacji systemu" - "Pozwala aplikacji na otrzymywanie powiadomień o oczekujących aktualizacjach systemu i uruchamianie ich instalacji. Szkodliwe aplikacje mogą to wykorzystać do uszkodzenia systemu za pomocą nieuwierzytelnionych aktualizacji lub ogólnie wpłynąć na proces aktualizowania." "zmienianie statystyk dotyczących baterii" "Pozwala na zmianę zebranych statystyk dotyczących baterii. Nie do wykorzystania przez normalne aplikacje." "wyświetlanie nieuwierzytelnionych okien" @@ -418,9 +412,6 @@ "Hasło" "Zaloguj" "Błędna nazwa użytkownika lub hasło." - "h:mm AA" - "%-l:%M%P" - "%-l:%M%p" "%-l%P" "%-l%p" "Wyczyść powiadomienia" @@ -452,9 +443,6 @@ "enter" "usuń" "Szukaj" - "Dzisiaj" - "Wczoraj" - "Jutro" "1 miesiąc temu" "Ponad 1 miesiąc temu" @@ -536,13 +524,6 @@ "tygodni" "rok" "lat" - "niedziela" - "poniedziałek" - "wtorek" - "środa" - "czwartek" - "piątek" - "sobota" "W każdy dzień roboczy (pon–pt)" "Codziennie" "Co tydzień w %s" @@ -552,137 +533,15 @@ "Przepraszamy, ten film wideo nie nadaje się do przesyłania strumieniowego do tego urządzenia." "Niestety, nie można odtworzyć tego filmu wideo." "OK" - "rano" - "po południu" - "%m/%d/%Y" - "%1$s, %2$s, %3$s%4$s, %5$s, %6$s" - "%1$s, %2$s%4$s, %5$s" - "%2$s, %3$s%5$s, %6$s" - "%2$s%5$s" - "%1$s%2$s" - "%1$s, %2$s, %3$s" - "%2$s, %3$s" - "%1$s, %3$s" - "%1$s, %2$s" "%1$s, %2$s" - "%1$s, %2$s" - "MMMM' 'd', 'yyyy" - "d' 'MMMM', 'yyyy" - "MMM' 'd', 'yyyy" - "d' 'MMM', 'yyyy" - "h':'mm' 'a" - "HH':'mm" "południe" "Południe" "północ" "Północ" - "%-d %B" - "%-d %B %Y" - "%B %Y" - "%H:%M:%S" - "%H:%M:%S %B %-d, %Y" - "%2$s %3$s%7$s %8$s" - "%1$s, %2$s %3$s%6$s, %7$s %8$s" - "%2$s %3$s%7$s %8$s, %9$s" - "%1$s, %2$s %3$s%6$s, %7$s %8$s, %9$s" - "%2$s %3$s, %5$s%7$s %8$s, %10$s" - "%1$s, %2$s %3$s, %5$s%6$s, %7$s %8$s, %10$s" - "%2$s %3$s, %4$s, %5$s%7$s %8$s, %9$s, %10$s" - "%1$s, %2$s %3$s, %4$s, %5$s%6$s, %7$s %8$s, %9$s, %10$s" - "%2$s/%3$s%7$s/%8$s" - "%1$s, %2$s/%3$s%6$s, %7$s/%8$s" - "%2$s/%3$s/%4$s%7$s/%8$s/%9$s" - "%1$s, %2$s/%3$s/%4$s%6$s, %7$s/%8$s/%9$s" - "%2$s/%3$s, %5$s%7$s/%8$s, %10$s" - "%1$s, %2$s/%3$s, %5$s%6$s, %7$s/%8$s, %10$s" - "%2$s/%3$s/%4$s, %5$s%7$s/%8$s/%9$s, %10$s" - "%1$s, %2$s/%3$s/%4$s, %5$s%6$s, %7$s/%8$s/%9$s, %10$s" - "%2$s %3$s%8$s" - "%1$s, %2$s %3$s%6$s, %7$s %8$s" - "%2$s %3$s%8$s, %9$s" - "%1$s, %2$s %3$s, %4$s%6$s, %7$s %8$s, %9$s" - "%2$s %3$s, %5$s%7$s %8$s, %10$s" - "%1$s, %2$s %3$s, %5$s%6$s, %7$s %8$s, %10$s" - "%2$s %3$s, %4$s, %5$s%7$s %8$s, %9$s, %10$s" - "%1$s, %2$s %3$s, %4$s, %5$s%6$s, %7$s %8$s, %9$s, %10$s" - "%-d %b %Y" - "%b %Y" - "%b %-d" - "niedziela" - "poniedziałek" - "wtorek" - "środa" - "czwartek" - "piątek" - "sobota" - "Nie" - "Pon" - "Wt" - "Śro" - "Czw" - "Pią" - "Sob" - "Nd" - "Pn" - "Wt" - "Śr" - "Czw" - "Pt" - "So" - "Nd" - "Pon" - "Wt" - "Śr" - "Czw" - "Pt" - "So" - "Nd" - "Pon" - "Czw" - "Śr" - "Czw" - "Pt" - "Sob" - "Styczeń" - "Luty" - "Marzec" - "Kwiecień" - "Maj" - "Czerwiec" - "Lipiec" - "Sierpień" - "Wrzesień" - "Październik" - "Listopad" - "Grudzień" - "Sty" - "Lut" - "Mar" - "Kwi" - "Maj" - "Cze" - "Lip" - "Sie" - "Wrz" - "Paź" - "Lis" - "Gru" - "Sty" - "Lut" - "Pon" - "Kwi" - "Maj" - "Cze" - "Lip" - "Sie" - "Wrz" - "Paź" - "Lis" - "Gru" "%1$02d:%2$02d" "%1$d:%2$02d:%3$02d" "Zaznacz wszystko" diff --git a/core/res/res/values-pt-rBR/donottranslate-cldr.xml b/core/res/res/values-pt-rBR/donottranslate-cldr.xml new file mode 100644 index 0000000000000000000000000000000000000000..47290552dd2e1c7267fe4ead602804646be70a19 --- /dev/null +++ b/core/res/res/values-pt-rBR/donottranslate-cldr.xml @@ -0,0 +1,147 @@ + + + janeiro + fevereiro + março + abril + maio + junho + julho + agosto + setembro + outubro + novembro + dezembro + + janeiro + fevereiro + março + abril + maio + junho + julho + agosto + setembro + outubro + novembro + dezembro + + jan + fev + mar + abr + mai + jun + jul + ago + set + out + nov + dez + + J + F + M + A + M + J + J + A + S + O + N + D + + domingo + segunda-feira + terça-feira + quarta-feira + quinta-feira + sexta-feira + sábado + + dom + seg + ter + qua + qui + sex + sáb + + dom + seg + ter + qua + qui + sex + sáb + + D + S + T + Q + Q + S + S + + AM + PM + Ontem + Hoje + Amanhã + + %-kh%M + %-l:%M %p + %-l:%M %^p + h:mm a + H'h'mm + %d/%m/%Y + dd/MM/yyyy + "%s/%s/%s" + %-e de %B de %Y + %H:%M:%S + %H:%M:%S %d/%m/%Y + %2$s %1$s + %1$s %3$s + %d/%m/%Y + %-e de %B + %-B + %B de %Y + %-e de %b + %-b + %b de %Y + %1$s - %2$s + %2$s - %5$s + %3$s/%2$s - %8$s/%7$s + %1$s, %3$s/%2$s - %6$s, %8$s/%7$s + %3$s/%2$s/%4$s - %8$s/%7$s/%9$s + %1$s, %3$s/%2$s/%4$s - %6$s, %8$s/%7$s/%9$s + %5$s %1$s, %3$s/%2$s/%4$s - %10$s %6$s, %8$s/%7$s/%9$s + %5$s %3$s/%2$s - %10$s %8$s/%7$s + %5$s %1$s, %3$s/%2$s - %10$s %6$s, %8$s/%7$s + %5$s %3$s/%2$s/%4$s - %10$s %8$s/%7$s/%9$s + %3$s %1$s, %2$s - %6$s %4$s, %5$s + %1$s, %2$s - %4$s, %5$s + %3$s %2$s - %6$s %5$s + %1$s %2$s, %3$s + %2$s, %3$s + %1$s %2$s + %3$s de %2$s - %8$s de %7$s + %1$s, %3$s de %2$s - %6$s, %8$s de %7$s + %5$s %3$s de %2$s - %10$s %8$s de %7$s + %5$s %3$s de %2$s - %10$s %8$s de %7$s + %5$s %1$s, %3$s de %2$s - %10$s %6$s, %8$s de %7$s + %5$s %1$s, %3$s de %2$s - %10$s %6$s, %8$s de %7$s + %5$s %3$s de %2$s de %4$s - %10$s %8$s de %7$s de %9$s + %5$s %3$s de %2$s de %4$s - %10$s %8$s de %7$s de %9$s + %5$s %1$s, %3$s de %2$s de %4$s - %10$s %6$s, %8$s de %7$s de %9$s + %5$s %1$s, %3$s de %2$s de %4$s - %10$s %6$s, %8$s de %7$s de %9$s + %1$s, %3$s de %2$s de %4$s - %6$s, %8$s de %7$s de %9$s + %3$s-%8$s de %2$s + %1$s, %3$s de %2$s - %6$s, %8$s de %7$s + %3$s de %2$s - %8$s de %7$s de %9$s + %3$s-%8$s de %2$s de %9$s + %1$s, %3$s de %2$s - %6$s, %8$s de %7$s de %9$s + %b + diff --git a/core/res/res/values-pt-rPT/donottranslate-cldr.xml b/core/res/res/values-pt-rPT/donottranslate-cldr.xml new file mode 100644 index 0000000000000000000000000000000000000000..f38a2d0499d79f748d9c9ba0c2762e263b058260 --- /dev/null +++ b/core/res/res/values-pt-rPT/donottranslate-cldr.xml @@ -0,0 +1,147 @@ + + + Janeiro + Fevereiro + Março + Abril + Maio + Junho + Julho + Agosto + Setembro + Outubro + Novembro + Dezembro + + Janeiro + Fevereiro + Março + Abril + Maio + Junho + Julho + Agosto + Setembro + Outubro + Novembro + Dezembro + + Jan + Fev + Mar + Abr + Mai + Jun + Jul + Ago + Set + Out + Nov + Dez + + J + F + M + A + M + J + J + A + S + O + N + D + + domingo + segunda-feira + terça-feira + quarta-feira + quinta-feira + sexta-feira + sábado + + dom + seg + ter + qua + qui + sex + sáb + + dom + seg + ter + qua + qui + sex + sáb + + D + S + T + Q + Q + S + S + + Antes do meio-dia + Depois do meio-dia + Ontem + Hoje + Amanhã + + %-kh%M + %-l:%M %p + %-l:%M %^p + h:mm a + H'h'mm + %d/%m/%Y + dd/MM/yyyy + "%s/%s/%s" + %-e de %B de %Y + %H:%M:%S + %H:%M:%S %-e de %b de %Y + %2$s %1$s + %1$s %3$s + %-e de %b de %Y + %-e de %B + %-B + %B de %Y + %-e de %b + %b + %b de %Y + %1$s - %2$s + %2$s - %5$s + %3$s/%2$s - %8$s/%7$s + %1$s, %3$s/%2$s - %6$s, %8$s/%7$s + %3$s/%2$s/%4$s - %8$s/%7$s/%9$s + %1$s, %3$s/%2$s/%4$s - %6$s, %8$s/%7$s/%9$s + %5$s %1$s, %3$s/%2$s/%4$s - %10$s %6$s, %8$s/%7$s/%9$s + %5$s %3$s/%2$s - %10$s %8$s/%7$s + %5$s %1$s, %3$s/%2$s - %10$s %6$s, %8$s/%7$s + %5$s %3$s/%2$s/%4$s - %10$s %8$s/%7$s/%9$s + %3$s %1$s, %2$s - %6$s %4$s, %5$s + %1$s, %2$s - %4$s, %5$s + %3$s %2$s - %6$s %5$s + %1$s %2$s, %3$s + %2$s, %3$s + %1$s %2$s + %3$s de %2$s - %8$s de %7$s + %1$s, %3$s de %2$s - %6$s, %8$s de %7$s + %5$s %3$s de %2$s - %10$s %8$s de %7$s + %5$s %3$s de %2$s - %10$s %8$s de %7$s + %5$s %1$s, %3$s de %2$s - %10$s %6$s, %8$s de %7$s + %5$s %1$s, %3$s de %2$s - %10$s %6$s, %8$s de %7$s + %5$s %3$s de %2$s de %4$s - %10$s %8$s de %7$s de %9$s + %5$s %3$s de %2$s de %4$s - %10$s %8$s de %7$s de %9$s + %5$s %1$s, %3$s de %2$s de %4$s - %10$s %6$s, %8$s de %7$s de %9$s + %5$s %1$s, %3$s de %2$s de %4$s - %10$s %6$s, %8$s de %7$s de %9$s + %1$s, %3$s de %2$s de %4$s - %6$s, %8$s de %7$s de %9$s + %3$s-%8$s de %2$s + %1$s, %3$s de %2$s - %6$s, %8$s de %7$s + %3$s de %2$s - %8$s de %7$s de %9$s + %3$s-%8$s de %2$s de %9$s + %1$s, %3$s de %2$s - %6$s, %8$s de %7$s de %9$s + %b + diff --git a/core/res/res/values-pt/donottranslate-cldr.xml b/core/res/res/values-pt/donottranslate-cldr.xml new file mode 100644 index 0000000000000000000000000000000000000000..47290552dd2e1c7267fe4ead602804646be70a19 --- /dev/null +++ b/core/res/res/values-pt/donottranslate-cldr.xml @@ -0,0 +1,147 @@ + + + janeiro + fevereiro + março + abril + maio + junho + julho + agosto + setembro + outubro + novembro + dezembro + + janeiro + fevereiro + março + abril + maio + junho + julho + agosto + setembro + outubro + novembro + dezembro + + jan + fev + mar + abr + mai + jun + jul + ago + set + out + nov + dez + + J + F + M + A + M + J + J + A + S + O + N + D + + domingo + segunda-feira + terça-feira + quarta-feira + quinta-feira + sexta-feira + sábado + + dom + seg + ter + qua + qui + sex + sáb + + dom + seg + ter + qua + qui + sex + sáb + + D + S + T + Q + Q + S + S + + AM + PM + Ontem + Hoje + Amanhã + + %-kh%M + %-l:%M %p + %-l:%M %^p + h:mm a + H'h'mm + %d/%m/%Y + dd/MM/yyyy + "%s/%s/%s" + %-e de %B de %Y + %H:%M:%S + %H:%M:%S %d/%m/%Y + %2$s %1$s + %1$s %3$s + %d/%m/%Y + %-e de %B + %-B + %B de %Y + %-e de %b + %-b + %b de %Y + %1$s - %2$s + %2$s - %5$s + %3$s/%2$s - %8$s/%7$s + %1$s, %3$s/%2$s - %6$s, %8$s/%7$s + %3$s/%2$s/%4$s - %8$s/%7$s/%9$s + %1$s, %3$s/%2$s/%4$s - %6$s, %8$s/%7$s/%9$s + %5$s %1$s, %3$s/%2$s/%4$s - %10$s %6$s, %8$s/%7$s/%9$s + %5$s %3$s/%2$s - %10$s %8$s/%7$s + %5$s %1$s, %3$s/%2$s - %10$s %6$s, %8$s/%7$s + %5$s %3$s/%2$s/%4$s - %10$s %8$s/%7$s/%9$s + %3$s %1$s, %2$s - %6$s %4$s, %5$s + %1$s, %2$s - %4$s, %5$s + %3$s %2$s - %6$s %5$s + %1$s %2$s, %3$s + %2$s, %3$s + %1$s %2$s + %3$s de %2$s - %8$s de %7$s + %1$s, %3$s de %2$s - %6$s, %8$s de %7$s + %5$s %3$s de %2$s - %10$s %8$s de %7$s + %5$s %3$s de %2$s - %10$s %8$s de %7$s + %5$s %1$s, %3$s de %2$s - %10$s %6$s, %8$s de %7$s + %5$s %1$s, %3$s de %2$s - %10$s %6$s, %8$s de %7$s + %5$s %3$s de %2$s de %4$s - %10$s %8$s de %7$s de %9$s + %5$s %3$s de %2$s de %4$s - %10$s %8$s de %7$s de %9$s + %5$s %1$s, %3$s de %2$s de %4$s - %10$s %6$s, %8$s de %7$s de %9$s + %5$s %1$s, %3$s de %2$s de %4$s - %10$s %6$s, %8$s de %7$s de %9$s + %1$s, %3$s de %2$s de %4$s - %6$s, %8$s de %7$s de %9$s + %3$s-%8$s de %2$s + %1$s, %3$s de %2$s - %6$s, %8$s de %7$s + %3$s de %2$s - %8$s de %7$s de %9$s + %3$s-%8$s de %2$s de %9$s + %1$s, %3$s de %2$s - %6$s, %8$s de %7$s de %9$s + %b + diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..c5c5bbbdf66e69fbf999d2f2b272108fff15c9a4 --- /dev/null +++ b/core/res/res/values-pt/strings.xml @@ -0,0 +1,793 @@ + + + + "B" + "KB" + "MB" + "GB" + "TB" + "PB" + "<sem título>" + "…" + "(Nenhum número de telefone)" + "(Desconhecido)" + "Correio de voz" + "MSISDN1" + "Problema de conexão ou código MMI inválido." + "O serviço foi ativado." + "O serviço foi ativado para:" + "O serviço foi desativado." + "O registro foi bem-sucedido." + "Exclusão bem-sucedida." + "Senha incorreta" + "MMI completo." + "O PIN antigo digitado não está correto." + "O PUK digitado não está correto." + "Os PINs digitados não correspondem." + "Digite um PIN com 4 a 8 números." + "Seu cartão SIM está bloqueado pelo código PUK. Digite o PUK para desbloqueá-lo." + "Digite PUK2 para desbloquear cartão SIM." + "ID do chamador" + "ID de quem realiza a chamada" + "Transferência de chamada" + "Chamada em espera" + "Bloqueio de chamada" + "Alteração da senha" + "Alteração de PIN" + + + + + + + + + + + + + "ID do chamador assume o padrão de restrito. Próxima chamada: restrita" + "ID do chamador assume o padrão de restrito. Próxima chamada: não restrita" + "ID do chamador assume o padrão de não restrito. Próxima chamada: restrita" + "ID do chamador assume o padrão de não restrito. Próxima chamada: não restrita" + "Serviço não fornecido" + "A configuração da ID do chamador não pode ser alterada." + + + + + + + + + + + "Voz" + "Dados" + "FAX" + "SMS" + "Assíncrono" + "Sincronizar" + "Pacote" + "PAD" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + "{0}: Não transferido" + "{0}: {1}" + "{0}: {1} após {2} segundos" + "{0}: Não transferido" + "{0}: Não transferido" + + + + + "OK" + "A página da web contém um erro." + "Não foi possível encontrar o URL." + "O esquema de autenticação não é suportado." + "Falha na autenticação." + "Falha na autenticação pelo servidor proxy." + "Falha na conexão com o servidor." + "Falha de comunicação com o servidor. Tente novamente mais tarde." + "Tempo limite da conexão com o servidor esgotado." + "A página contém muitos redirecionamentos do servidor." + "O protocolo não é suportado." + "Não foi possível estabelecer uma conexão segura." + "A página não pode ser aberta, pois o URL é inválido." + "Não foi possível acessar o arquivo." + "O arquivo solicitado não foi encontrado." + "Muitas solicitações sendo processadas. Tente novamente mais tarde." + "Sincronizar" + "Sincronizar" + "Muitas exclusões do %s." + "O armazenamento do telefone está cheio! Exclua alguns arquivos para liberar espaço." + "Eu" + "Opções do telefone" + "Modo silencioso" + "Ativar rede sem fio" + "Desativar a rede sem fio" + "Bloqueio de tela" + "Desligar" + "Desligando…" + "Seu telefone desligará" + "Nenhum aplicativo recente." + "Opções do telefone" + "Bloqueio de tela" + "Desligar" + "Modo silencioso" + "O som está DESLIGADO" + "O som está ATIVADO" + + + + + + + "Modo de segurança" + + + "Serviços que custam dinheiro" + "Permite que os aplicativos façam coisas que podem custar dinheiro." + "Suas mensagens" + "Ler e gravar suas mensagens SMS, e-mail e outras mensagens." + "Suas informações pessoais" + "Acesso direto aos seus contatos e calendário armazenados no telefone." + "Sua localização" + "Monitore seu local físico" + "Comunicação de rede" + "Permite que os aplicativos acessem diversos recursos de rede." + "Suas contas do Google" + "Acesse as contas do Google disponíveis." + "Controles de hardware" + "Acesso direto ao hardware no handset." + "Chamadas telefônicas" + "Monitorar, registrar e processar chamadas telefônicas." + "Ferramentas do sistema" + "Acesso de nível inferior e controle do sistema." + "Ferramentas de desenvolvimento" + "Recursos necessários apenas aos desenvolvedores de aplicativo." + + + + + "desativar ou modificar a barra de status" + "Permite que os aplicativos desativem a barra de status ou adicionem e removam ícones do sistema." + "expandir/recolher barra de status" + "Permite que um aplicativo expanda ou recolha a barra de status." + "Interceptar chamadas realizadas" + "Permite que aplicativos processem chamadas realizadas e alterem o número a ser discado. Aplicativos maliciosos podem monitorar, redirecionar ou impedir chamadas realizadas." + "receber SMS" + "Permite que o aplicativo receba e processe mensagens SMS. Aplicativos maliciosos podem monitorar suas mensagens ou excluí-las sem mostrá-las a você." + "receber MMS" + "Permite que o aplicativo receba e processe mensagens MMS. Aplicativos maliciosos podem monitorar suas mensagens ou excluí-las sem mostrá-las a você." + "enviar mensagens SMS" + "Permite que os aplicativos enviem mensagens SMS. Os aplicativos maliciosos podem causar prejuízo financeiro a você ao enviar mensagens sem a sua confirmação." + "ler SMS ou MMS" + "Permite que um aplicativo leia mensagens SMS armazenadas no seu telefone ou cartão SIM. Aplicativos maliciosos podem ler suas mensagens confidenciais." + "editar SMS ou MMS" + "Permite que um aplicativo grave mensagens SMS armazenadas no seu telefone ou cartão SIM. Aplicativos maliciosos podem excluir suas mensagens." + "receber WAP" + "Permite que o aplicativo receba e processe mensagens WAP. Aplicativos maliciosos podem monitorar suas mensagens ou excluí-las sem mostrá-las a você." + "recuperar aplicativos em execução" + "Permite que os aplicativos recuperem informações sobre as tarefas em execução no momento ou recentemente. Pode permitir que aplicativos maliciosos descubram informações particulares sobre outros aplicativos." + "reorganizar os aplicativos em execução" + "Permite que um aplicativo mova as tarefas para o primeiro ou segundo plano. Os aplicativos maliciosos podem forçar sua permanência no primeiro plano sem o seu controle." + "ativar depuração do aplicativo" + "Permite que um aplicativo ative a depuração de outro aplicativo. Aplicativos maliciosos podem usar isso para encerrar outros aplicativos." + "alterar as configurações da sua IU" + "Permite que um aplicativo mude a configuração atual, como a localidade ou o tamanho geral de fonte." + "reiniciar outros aplicativos" + "Permite que um aplicativo reinicie outros aplicativos forçosamente." + "forçar fechamento do aplicativo" + "Permite que um aplicativo force qualquer atividade que esteja em primeiro plano a fechar e voltar. Normalmente não é necessário para aplicativos normais." + "recuperar estado interno do sistema" + "Permite que um aplicativo recupere o estado interno do sistema. Aplicativos maliciosos podem recuperar um ampla variedade de informações privadas e seguras, as quais não deveriam precisar normalmente." + + + + + + + + + "monitorar e controle toda inicialização de aplicativo" + "Permite que um aplicativo monitore e controle como o sistema inicia as atividades. Os aplicativos maliciosos podem comprometer completamente o sistema. Esta permissão é necessária apenas para desenvolvimento, nunca para uso normal do telefone." + "enviar transmissão de pacote removido" + "Permite que um aplicativo transmita uma notificação de que o pacote de um aplicativo foi removido. Aplicativos maliciosos podem usar isso para encerrar outro aplicativo em execução." + "enviar transmissão de SMS recebido" + "Permite que um aplicativo transmita uma notificação de que uma mensagem SMS foi recebida. Aplicativos maliciosos podem usar isso para forjar o recebimento de mensagens SMS." + "enviar transmissão de WAP-PUSH recebido" + "Permite que um aplicativo transmita uma notificação de que uma mensagem WAP PUSH foi recebida. Aplicativos maliciosos podem usar isso para forjar o recebimento de uma mensagem MMS ou substituir silenciosamente o conteúdo de qualquer página da web por variantes maliciosas." + "limitar o número de processos em execução" + "Permite que um aplicativo controle o número máximo de processos que serão executados. Nunca é necessário para aplicativos normais." + "fazer todos os aplicativos em segundo plano fechar" + "Permite que um aplicativo controle se as atividades são sempre concluídas assim que vão para o segundo plano. Nunca é necessário para aplicativos normais." + "Modificar as estatísticas da bateria" + "Permite a modificação das estatísticas coletadas sobre a bateria. Não deve ser usado em aplicativos normais." + + + + + "exibir janelas não autorizadas" + "Permite a criação de janelas que devem ser usadas pela interface de usuário do sistema interno. Normalmente não é necessário para aplicativos normais." + "exibir alertas do nível do sistema" + "Permite que um aplicativo mostre janelas de alerta do sistema. Aplicativos maliciosos podem assumir o controle de toda a tela do telefone." + "modificar a velocidade de animação global" + "Permite que um aplicativo altere a velocidade de animação global (animações mais rápidas ou mais lentas) a qualquer momento." + "gerenciar os símbolos do aplicativo" + "Permite que um aplicativo crie e gerencie seus próprio símbolos, ignorando a ordem-Z (Z-ordering). Normalmente não é necessário para aplicativos normais." + "pressionar as teclas e os botões de controle" + "Permite que um aplicativo proporcione seus próprios eventos de entrada (pressionamentos de tecla etc.) a outros aplicativos. Aplicativos maliciosos podem usar isso para assumir o controle do telefone." + "registrar o que você digita e as ações que executa" + "Permite que os aplicativos observem as teclas que você pressiona ao interagir com outro aplicativo (como ao digitar uma senha). Normalmente não é necessário para aplicativos normais." + "aderir a um método de entrada" + "Permite que o portador se vincule à interface de nível superior de um método de entrada. Normalmente não é necessário em aplicativos normais." + "alterar orientação da tela" + "Permite que um aplicativo altere a rotação da tela a qualquer momento. Normalmente não é necessário para aplicativos normais." + "enviar sinais de Linux aos aplicativos" + "Permite que o aplicativo solicite que o sinal fornecido seja enviado a todos os processos persistentes." + "fazer com que o aplicativo execute sempre" + "Permite que um aplicativo torne partes dele mesmo persistentes, para que o sistema não possa usá-lo para outros aplicativos." + "excluir aplicativos" + "Permite que um aplicativo exclua pacotes do Android. Aplicativos maliciosos podem usar isso para excluir aplicativos importantes." + "excluir os dados de outros aplicativos" + "Permite que um aplicativo limpe os dados do usuário." + "excluir o cache de outros aplicativos" + "Permite que um aplicativo exclua arquivos armazenados em cache." + "medir o espaço de armazenamento do aplicativo" + "Permite que um aplicativo recupere seu código, dados e tamanho de cache" + "instalar os aplicativos diretamente" + "Permite que um aplicativo instale pacotes novos ou atualizados do Android. Aplicativos maliciosos podem usar isso para adicionar novos aplicativos com permissões aleatórias avançadas." + "excluir todos os dados do cache do aplicativo" + "Permite que um aplicativo libere espaço de armazenamento do telefone excluindo arquivos no diretório de cache do aplicativo. O acesso é normalmente restrito ao processo do sistema." + "ler arquivos do registro do sistema" + "Permite que um aplicativo leia os diversos arquivos de registro do sistema. Isso permite que ele descubra informações gerais sobre o que você está fazendo com o telefone, mas esses arquivos não devem conter informações pessoais ou privadas." + "ler/gravar em recursos que pertencem ao diagnóstico" + "Permite que um aplicativo leia e grave em qualquer recurso que pertença ao grupo diag; por exemplo, arquivos em /dev. Isso poderia afetar a estabilidade e a segurança do sistema. Por isso, SÓ deve ser usado para diagnósticos específicos do hardware pelo fabricante ou operador." + "ativar ou desativar componentes do aplicativo" + "Permite que um aplicativo altere a ativação ou desativação de um componente de outro aplicativo. Aplicativos maliciosos podem usar isso para desativar recursos importantes do telefone. É preciso ter permissão e cuidado no uso, pois é possível deixar os componentes do aplicativo em um estado inutilizável, inconsistente ou instável." + "definir aplicativos preferidos" + "Permite que um aplicativo modifique seus aplicativos preferidos. Isso pode permitir que aplicativos maliciosos alterem silenciosamente os aplicativos em execução, falsificando seus aplicativos existentes para coletar seus dados privados." + "modificar configurações globais do sistema" + "Permite que um aplicativo modifique os dados da configuração do sistema. Aplicativos maliciosos podem corromper a configuração do sistema." + "modificar configurações de segurança do sistema" + "Permite que um aplicativo modifique os dados das configurações de segurança dos sistemas. Não deve ser usado em aplicativos normais." + "modificar o mapa de serviços do Google" + "Permite que um aplicativo modifique o mapa de serviços do Google. Não deve ser usado em aplicativos normais." + "iniciar automaticamente na inicialização" + "Permite que um aplicativo se inicie assim que o sistema termina de inicializar. Isso pode causar uma demora na inicialização do telefone e faz com que todo o telefone fique mais lento pela execução contínua do aplicativo." + "enviar transmissão complexa" + "Permite que um aplicativo envie transmissões persistentes, as quais permanecem após o término da transmissão. Aplicativos maliciosos podem tornar o telefone lento ou instável fazendo com que use muita memória." + "ler dados de contato" + "Permite que um aplicativo leia todos os dados de contato (endereço) armazenados no telefone. Aplicativos maliciosos podem usar isso para enviar seus dados a outras pessoas." + "gravar dados de contato" + "Permite que um aplicativo modifique os dados de contato (endereço) armazenados no telefone. Aplicativos maliciosos podem usar isso para apagar ou modificar seus dados de contato." + "gravar dados do proprietário" + "Permite que um aplicativo modifique os dados do proprietário do telefone armazenados no seu telefone. Aplicativos maliciosos podem usar isso para apagar ou modificar os dados do proprietário." + "ler dados do proprietário" + "Permite que um aplicativo leia os dados do proprietário do telefone armazenados no seu telefone. Aplicativos maliciosos podem usar isso para ler os dados do proprietário." + "ler os dados do calendário" + "Permite que um aplicativo leia todos os eventos de calendário armazenados no seu telefone. Aplicativos maliciosos podem usar isso para enviar os eventos do seu calendário a outras pessoas." + "gravar dados do calendário" + "Permite que um aplicativo modifique os eventos do calendário armazenados no seu telefone. Aplicativos maliciosos podem usar isso para apagar ou modificar seus dados de contato." + "imitar fontes de localização para teste" + "Criar imitação de fontes de localização para teste. Os aplicativos maliciosos podem usar isso para sobrescrever o local e/ou status retornado pelas fontes de localização reais como GPS ou provedores de rede." + "acessar comandos extra do provedor de localização" + "Acessar comandos extra de fornecedor de localização. Aplicativos maliciosos podem usar isso para interferir com a operação do GPS ou com outras fontes de localização." + + + + + "Localização precisa (GPS)" + "Acesse fontes de localização precisa como o sistema GPS (Global Positioning System) no telefone, quando estiver disponível. Aplicativos maliciosos podem usar isso para determinar onde você está e também pode consumir energia da bateria." + "Local inadequado (com base na rede)" + "Acessar fontes de localização aproximada como o banco de dados de rede de celular para determinar a localização aproximada de um telefone, quando houver disponibilidade. Aplicativos maliciosos podem usar isso para determinar sua localização aproximada." + "acessar SurfaceFlinger" + "Permite que o aplicativo use os recursos de nível inferior do SurfaceFlinger." + "ler buffer do quadro" + "Permite que o aplicativo leia o conteúdo do buffer do quadro." + "alterar as configurações do seu áudio" + "Permite que o aplicativo modifique as configurações de áudio globais como volume e roteamento." + "gravar áudio" + "Permite que o aplicativo acesso o caminho do registro de áudio." + "tirar fotos" + "Permite que o aplicativo tire fotos com a câmera. Isso permite que o aplicativo colete imagens exibidas pela câmera a qualquer momento." + "desativar permanentemente o telefone" + "Permite que o aplicativo desative todo o telefone permanentemente. Isso é muito perigoso." + "forçar reinicializarão do telefone" + "Permite que o aplicativo force a reinicialização do telefone." + "montar e desmontar sistemas de arquivos" + "Permite que o aplicativo monte e desmonte sistemas de arquivos para armazenamento removível." + + + + + "controlar vibrador" + "Permite que o aplicativo controle o vibrador." + "controlar lanterna" + "Permite que o aplicativo controle a lanterna." + "testar hardware" + "Permite que o aplicativo controle diversos periféricos para teste de hardware." + "chamar números de telefone diretamente" + "Permite que o aplicativo chame números de telefone sem sua intervenção. Aplicativos maliciosos podem causar a aparição de chamadas inesperadas na conta do seu telefone. Observe que isso não permite que o aplicativo ligue para números de emergência." + "chamar quaisquer números de telefone diretamente" + "Permite que o aplicativo chame qualquer número de telefone, incluindo números de emergência, sem sua intervenção. Aplicativos maliciosos podem fazer chamadas desnecessárias e ilegais para serviços de emergência." + "controlar notificações de atualização de localização" + "Permite a ativação/desativação das notificações sobre atualização de localização pelo rádio. Não deve ser usado em aplicativos normais." + "acessar propriedades de verificação" + "Permite acesso de leitura/gravação às propriedades enviadas pelo serviço de verificação. Não deve ser usado em aplicativos normais." + + + + + "modificar estado do telefone" + "Permite que o aplicativo controle os recursos do telefone do dispositivo. Um aplicativo com essa permissão pode alternar entre redes, ligar e desligar o rádio e executar ações parecidas sem o notificar." + "ler estado do telefone" + "Permite que o aplicativo acesse os recursos do telefone do aparelho. Um aplicativo com essa permissão pode determinar o número deste telefone, se uma chamada está ativa, o número com o qual está chamada está conectada e outras coisas semelhantes." + "impedir que o telefone entre em repouso" + "Permite que um aplicativo impeça o telefone de entrar em repouso." + "ligar ou desligar o telefone" + "Permite que o aplicativo ligue ou desligue o telefone." + "executar no modo de teste de fábrica" + "Executar como um teste de fabricante de nível inferior, permitindo o acesso completo ao hardware do telefone. Disponível apenas quando um telefone está executando no modo de teste de fábrica." + "definir papel de parede" + "Permite que o aplicativo defina o papel de parede do sistema." + "definir dicas de tamanho de papel de parede" + "Permite que o aplicativo defina as dicas de tamanho do papel de parede do sistema." + "reiniciar o sistema com o padrão de fábrica" + "Permite que um aplicativo reinicie completamente o sistema com suas configurações de fábrica, apagando todos os dados, configuração e aplicativos instalados." + "definir fuso horário" + "Permite que um aplicativo altere o fuso horário do telefone." + "descobrir contas conhecidas" + "Permite que um aplicativo obtenha a lista de contas conhecidas pelo telefone." + "exibir estado da rede" + "Permite que um aplicativo exiba o estado de todas as redes." + "acesso total à Internet" + "Permite que um aplicativo crie soquetes de rede." + "gravar configurações de Nome do ponto de acesso" + "Permite que um aplicativo modifique as configurações de APN, como Proxy e a Porta de qualquer APN." + "alterar conectividade da rede" + "Permite que um aplicativo mude o estado da conectividade da rede." + + + + + "exibir estado da rede Wi-Fi" + "Permite que um aplicativo exiba as informações sobre o estado da rede Wi-Fi." + "Alterar estado de Wi-Fi" + "Permite que um aplicativo se conecte e desconecte dos pontos de acesso Wi-Fi e faça alterações nas redes Wi-Fi configuradas." + + + + + "administração do bluetooth" + "Permite que um aplicativo configure o telefone Bluetooth local, além de descobrir e parear com dispositivos remotos." + "criar conexões Bluetooth" + "Permite que um aplicativo exiba a configuração do telefone Bluetooth local e faça e aceite conexões com os dispositivos pareados." + "desativar bloqueio de teclado" + "Permite que um aplicativo desative o bloqueio do teclado e qualquer segurança de senha associada. Um exemplo legítimo disso é o telefone desativando o bloqueio do teclado ao receber uma chamada e reativando o bloqueio ao final da chamada." + "ler configurações de sincronização" + "Permite que um aplicativo leia as configurações de sincronização, por exemplo se a sincronização está ativada para Contatos." + "gravar configurações de sincronização" + "Permite que um aplicativo modifique as configurações de sincronização, por exemplo se a sincronização está ativada para Contatos." + "ler estatísticas de sincronização" + "Permite que um aplicativo leia as estatísticas de sincronização; por exemplo, o histórico de sincronizações realizadas." + "ler feeds inscritos" + "Permite que um aplicativo obtenha detalhes sobre os feeds sincronizados atualmente." + "gravar feeds inscritos" + "Permite que um aplicativo modifique seus feeds sincronizados recentemente. Isso poderia permitir que um aplicativo malicioso alterasse seus feeds sincronizados." + + + + + + + + + + + + + + "Página Inicial" + "Celular" + "Trabalho" + "Fax comercial" + "Fax doméstico" + "Pager" + "Outro" + "Personalizar" + + + "Página Inicial" + "Trabalho" + "Outro" + "Personalizar" + + + + + "Página Inicial" + "Trabalho" + "Outro" + "Personalizar" + + + "Página Inicial" + "Trabalho" + "Outro" + "Personalizar" + + + "Trabalho" + "Outro" + "Personalizar" + + + "AIM" + "Windows Live" + "Yahoo" + "Skype" + "QQ" + "Google Talk" + "ICQ" + "Jabber" + + "Digite o código PIN" + "Código PIN incorreto!" + "Para desbloquear, pressione Menu e 0." + "Número de emergência" + "(Sem serviço)" + "Tela bloqueada." + "Pressione Menu para desbloquear ou fazer chamada de emergência." + "Pressione Menu para desbloquear." + "Desenhar padrão para desbloqueio" + "Chamada de emergência" + "Correto!" + "Sentimos muito, tente novamente" + + + "Conecte o carregador." + "Sem cartão SIM." + "Não há um cartão SIM no telefone." + "Insira um cartão SIM." + "Rede bloqueada" + "O cartão SIM está bloqueado pelo PUK." + + + "O cartão SIM está bloqueado." + "Desbloqueando cartão SIM…" + "Você desenhou incorretamente seu padrão de desbloqueio %d vezes. "\n\n"Tente novamente em %d segundos." + "Você desenhou seu padrão de desbloqueio incorretamente %d vezes. Após mais %d tentativas sem êxito, você receberá uma solicitação para desbloquear o telefone usando seu login do Google."\n\n" Tente novamente em %d segundos." + "Tentar novamente em %d segundos." + "Esqueceu o padrão?" + "Muitas tentativas de padrão!" + + + "Nome de usuário (e-mail)" + "Senha" + "Fazer login" + "Nome de usuário ou senha inválida." + + + + + "Limpar notificações" + "Sem modificações" + "Em andamento" + "Notificações" + + + "Carregando..." + "Conecte o carregador" + "A carga da bateria está ficando baixa:" + "menos de %d%% restantes." + "Falha no teste de fábrica" + "A ação FACTORY_TEST é suportada apenas para pacotes instalados em /system/app." + "Nenhum pacote foi encontrado que forneça a ação FACTORY_TEST." + "Reiniciar" + + + + + + + "Confirmar" + "Deseja que o navegador se lembre desta senha?" + "Não agora" + "Lembre-se" + "Nunca" + "Você não tem permissão para abrir essa página." + "Texto copiado para a área de transferência." + "Mais" + "Menu+" + "espaço" + "enter" + "excluir" + "Procurar" + "1 mês atrás" + "Antes de 1 mês atrás" + + "1 segundo atrás" + "%d segundos atrás" + + + "1 minute atrás" + "%d minutos atrás" + + + "1 hora trás" + "%d horas atrás" + + + "ontem" + "%d dias atrás" + + + "em 1 segundo" + "em %d segundos" + + + "em 1 minuto" + "em %d minutos" + + + "em 1 hora" + "em %d horas" + + + "amanhã" + "em %d dias" + + + + + + + + + + + + + + + + + + "em %s" + "a %s" + "em %s" + "dia" + "dias" + "hora" + "horas" + "minuto" + "minutos" + "segundos" + "segundos" + "semana" + "semanas" + "ano" + "anos" + "Todo dia de semana (Seg–Sex)" + "Diariamente" + "Semanalmente em %s" + "Mensalmente" + "Anualmente" + "Não é possível reproduzir o vídeo" + + + "Sentimos muito, este vídeo não pode ser reproduzido." + "OK" + + + "meio-dia" + "Meio-dia" + "meia-noite" + "Meia-noite" + "%1$02d:%2$02d" + "%1$d:%2$02d:%3$02d" + "Selecionar tudo" + "Selecionar texto" + "Interromper seleção de texto" + "Recortar" + "Recortar tudo" + "Copiar" + "Copiar tudo" + "Colar" + "Copiar URL" + "Método de entrada" + + + "Editar texto" + "Pouco espaço" + "O espaço de armazenamento do telefone está diminuindo." + "OK" + "Cancelar" + "OK" + "Cancelar" + + + "LIGAR" + "DESLIGADO" + "Completar ação usando" + "Use por padrão para esta ação." + "Limpar o padrão nas Configurações da página inicial> Aplicativos > Gerenciar aplicativos." + "Selecione uma ação" + "Nenhum aplicativo pode executar essa ação." + "Sentimos muito." + "O aplicativo %1$s(processo %2$s) parou inesperadamente. Tente novamente." + "O processo %1$s parou inesperadamente. Tente novamente." + "Sentimos muito." + "A atividade %1$s (no aplicativo %2$s) não está respondendo." + "A atividade %1$s (%2$s em processamento) não está respondendo." + "O aplicativo %1$s (%2$s em processamento) não está respondendo." + "O processo %1$s não está respondendo." + "Forçar fechamento" + + + "Aguarde" + "Depuração" + "Selecione uma ação para texto" + "Volume da campainha" + "Volume da mídia" + "Reprodução usando Bluetooth" + "Volume da chamada recebida" + + + "Volume do alarme" + "Volume da notificação" + "Volume" + "Ringtone padrão" + "Ringtone padrão (%1$s)" + "Silencioso" + "Ringtones" + "Ringtone desconhecido" + + "Rede Wi-Fi disponível" + "Redes Wi-Fi disponíveis" + + + "Redes Wi-Fi abertas disponíveis" + "Redes Wi-Fi abertas disponíveis" + + "Inserir caractere" + "Aplicativo desconhecido" + "Envio de mensagens SMS" + "Uma grande quantidade de mensagens SMS está sendo enviada. Selecione \"OK\" para continuar ou \"Cancelar\" para parar de enviar." + "OK" + "Cancelar" + "Definir" + "Padrão" + "Nenhuma permissão é necessária" + "Ocultar" + "Mostrar tudo" + "Carregando…" + "Conectado via USB" + "Você conectou o telefone ao seu computador via USB. Selecione \"Montar\" se quiser copiar os arquivos entre seu computador e o cartão SD do telefone." + "Montar" + "Não montar" + "Há um problema com o uso do seu cartão SD para armazenamento USB." + "Conectado via USB" + "Selecione para copiar os arquivos para/do computador." + + + + + + + + + + + + + + + + + + + + + "Selecionar método de entrada" + " ABCDEFGHIJKLMNOPQRSTUVWXYZ" + " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/core/res/res/values-ro-rRO/donottranslate-cldr.xml b/core/res/res/values-ro-rRO/donottranslate-cldr.xml new file mode 100644 index 0000000000000000000000000000000000000000..4622445ca6c24c774aab4b3cfd03e659f6f81463 --- /dev/null +++ b/core/res/res/values-ro-rRO/donottranslate-cldr.xml @@ -0,0 +1,147 @@ + + + ianuarie + februarie + martie + aprilie + mai + iunie + iulie + august + septembrie + octombrie + noiembrie + decembrie + + ianuarie + februarie + martie + aprilie + mai + iunie + iulie + august + septembrie + octombrie + noiembrie + decembrie + + ian. + feb. + mar. + apr. + mai + iun. + iul. + aug. + sept. + oct. + nov. + dec. + + I + F + M + A + M + I + I + A + S + O + N + D + + duminică + luni + marți + miercuri + joi + vineri + sâmbătă + + Du + Lu + Ma + Mi + Jo + Vi + + + Du + Lu + Ma + Mi + Jo + Vi + + + D + L + M + M + J + V + S + + AM + PM + ieri + azi + mâine + + %-k:%M + %-l:%M %p + %-l:%M %^p + h:mm a + H:mm + %d.%m.%Y + dd.MM.yyyy + "%s.%s.%s" + %-e %B %Y + %H:%M:%S + %H:%M:%S, %d.%m.%Y + %2$s, %1$s + %1$s, %3$s + %d.%m.%Y + %-e %B + %-B + %B %Y + %-e %b + %-b + %b %Y + %1$s - %2$s + %2$s - %5$s + %3$s.%2$s - %8$s.%7$s + %1$s, %3$s.%2$s - %6$s, %8$s.%7$s + %3$s.%2$s.%4$s - %8$s.%7$s.%9$s + %1$s, %3$s.%2$s.%4$s - %6$s, %8$s.%7$s.%9$s + %5$s, %1$s, %3$s/%2$s/%4$s - %10$s, %6$s, %8$s/%7$s/%9$s + %5$s, %3$s.%2$s - %10$s, %8$s.%7$s + %5$s, %1$s, %3$s %2$s - %10$s, %6$s, %8$s %7$s + %5$s, %3$s.%2$s.%4$s - %10$s, %8$s.%7$s.%9$s + %3$s, %1$s, %2$s - %6$s, %4$s, %5$s + %1$s, %2$s - %4$s, %5$s + %3$s, %2$s - %6$s, %5$s + %1$s, %2$s, %3$s + %2$s, %3$s + %1$s, %2$s + %3$s %2$s - %8$s %7$s + %1$s, %3$s %2$s - %6$s, %8$s %7$s + %5$s, %3$s %2$s - %10$s, %8$s %7$s + %5$s, %3$s %2$s - %10$s, %8$s %7$s + %5$s, %1$s, %3$s %2$s - %10$s, %6$s, %8$s %7$s + %5$s, %1$s, %3$s %2$s - %10$s, %6$s, %8$s %7$s + %5$s, %3$s %2$s %4$s - %10$s, %8$s %7$s %9$s + %5$s, %3$s %2$s %4$s - %10$s, %8$s %7$s %9$s + %5$s, %1$s, %3$s %2$s %4$s - %10$s, %6$s, %8$s %7$s %9$s + %5$s, %1$s, %3$s %2$s %4$s - %10$s, %6$s, %8$s %7$s %9$s + %1$s, %3$s %2$s %4$s - %6$s, %8$s %7$s %9$s + %3$s-%8$s %2$s + %1$s, %3$s %2$s - %6$s, %8$s %7$s + %3$s %2$s - %8$s %7$s %9$s + %3$s-%8$s %2$s %9$s + %1$s, %3$s %2$s - %6$s, %8$s %7$s %9$s + %b + diff --git a/core/res/res/values-ru-rRU/donottranslate-cldr.xml b/core/res/res/values-ru-rRU/donottranslate-cldr.xml new file mode 100644 index 0000000000000000000000000000000000000000..21c06ff3a0abc64a0b6b3c5299a735978e922e07 --- /dev/null +++ b/core/res/res/values-ru-rRU/donottranslate-cldr.xml @@ -0,0 +1,147 @@ + + + Январь + Февраль + Март + Апрель + Май + Июнь + Июль + Август + Сентябрь + Октябрь + Ноябрь + Декабрь + + января + февраля + марта + апреля + мая + июня + июля + августа + сентября + октября + ноября + декабря + + янв. + февр. + марта + апр. + мая + июня + июля + авг. + сент. + окт. + нояб. + дек. + + Я + Ф + М + А + М + И + И + А + С + О + Н + Д + + воскресенье + понедельник + вторник + среда + четверг + пятница + суббота + + Вс + Пн + Вт + Ср + Чт + Пт + Сб + + Вс + Пн + Вт + Ср + Чт + Пт + Сб + + В + П + В + С + Ч + П + С + + AM + PM + Вчера + Сегодня + Завтра + + %-k:%M + %-l:%M %p + %-l:%M %^p + h:mm a + H:mm + %d.%m.%Y + dd.MM.yyyy + "%s.%s.%s" + %-e %B %Y г. + %-k:%M:%S + %-k:%M:%S %d.%m.%Y + %2$s %1$s + %1$s %3$s + %d.%m.%Y + %-e %B + %-B + %B %Y + %-e %b + %-b + %b %Y + %1$s - %2$s + %2$s - %5$s + %3$s.%2$s - %8$s.%7$s + %1$s, %2$s-%3$s – %6$s, %7$s-%8$s + %3$s.%2$s.%4$s - %8$s.%7$s.%9$s + %3$s.%2$s.%4$s - %8$s.%7$s.%9$s + %5$s %1$s, %4$s-%2$s-%3$s - %10$s %6$s, %9$s-%7$s-%8$s + %5$s %3$s.%2$s - %10$s %8$s.%7$s + %5$s %1$s, %2$s-%3$s - %10$s %6$s, %7$s-%8$s + %5$s %3$s.%2$s.%4$s - %10$s %8$s.%7$s.%9$s + %3$s %2$s - %6$s %5$s + %2$s - %5$s + %3$s %2$s - %6$s %5$s + %1$s %3$s + %3$s + %1$s %2$s + %3$s %2$s - %8$s %7$s + %1$s %2$s %3$s - %6$s %7$s %8$s + %5$s %3$s %2$s - %10$s %8$s %7$s + %5$s %3$s %2$s - %10$s %8$s %7$s + %5$s %1$s %2$s %3$s - %10$s %6$s %7$s %8$s + %5$s %1$s %2$s %3$s - %10$s %6$s %7$s %8$s + %5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s + %5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s + %5$s %1$s, %3$s %2$s %4$s - %10$s %6$s, %8$s %7$s %9$s + %5$s %1$s, %3$s %2$s %4$s - %10$s %6$s, %8$s %7$s %9$s + %1$s, %3$s %2$s %4$s - %6$s, %8$s %7$s %9$s + %3$s-%8$s %2$s + %1$s %2$s %3$s - %6$s %7$s %8$s + %3$s %2$s - %8$s %7$s %9$s г. + %3$s-%8$s %2$s %9$s г. + %3$s %2$s - %8$s %7$s %9$s г. + %b + diff --git a/core/res/res/values-ru/donottranslate-cldr.xml b/core/res/res/values-ru/donottranslate-cldr.xml new file mode 100644 index 0000000000000000000000000000000000000000..21c06ff3a0abc64a0b6b3c5299a735978e922e07 --- /dev/null +++ b/core/res/res/values-ru/donottranslate-cldr.xml @@ -0,0 +1,147 @@ + + + Январь + Февраль + Март + Апрель + Май + Июнь + Июль + Август + Сентябрь + Октябрь + Ноябрь + Декабрь + + января + февраля + марта + апреля + мая + июня + июля + августа + сентября + октября + ноября + декабря + + янв. + февр. + марта + апр. + мая + июня + июля + авг. + сент. + окт. + нояб. + дек. + + Я + Ф + М + А + М + И + И + А + С + О + Н + Д + + воскресенье + понедельник + вторник + среда + четверг + пятница + суббота + + Вс + Пн + Вт + Ср + Чт + Пт + Сб + + Вс + Пн + Вт + Ср + Чт + Пт + Сб + + В + П + В + С + Ч + П + С + + AM + PM + Вчера + Сегодня + Завтра + + %-k:%M + %-l:%M %p + %-l:%M %^p + h:mm a + H:mm + %d.%m.%Y + dd.MM.yyyy + "%s.%s.%s" + %-e %B %Y г. + %-k:%M:%S + %-k:%M:%S %d.%m.%Y + %2$s %1$s + %1$s %3$s + %d.%m.%Y + %-e %B + %-B + %B %Y + %-e %b + %-b + %b %Y + %1$s - %2$s + %2$s - %5$s + %3$s.%2$s - %8$s.%7$s + %1$s, %2$s-%3$s – %6$s, %7$s-%8$s + %3$s.%2$s.%4$s - %8$s.%7$s.%9$s + %3$s.%2$s.%4$s - %8$s.%7$s.%9$s + %5$s %1$s, %4$s-%2$s-%3$s - %10$s %6$s, %9$s-%7$s-%8$s + %5$s %3$s.%2$s - %10$s %8$s.%7$s + %5$s %1$s, %2$s-%3$s - %10$s %6$s, %7$s-%8$s + %5$s %3$s.%2$s.%4$s - %10$s %8$s.%7$s.%9$s + %3$s %2$s - %6$s %5$s + %2$s - %5$s + %3$s %2$s - %6$s %5$s + %1$s %3$s + %3$s + %1$s %2$s + %3$s %2$s - %8$s %7$s + %1$s %2$s %3$s - %6$s %7$s %8$s + %5$s %3$s %2$s - %10$s %8$s %7$s + %5$s %3$s %2$s - %10$s %8$s %7$s + %5$s %1$s %2$s %3$s - %10$s %6$s %7$s %8$s + %5$s %1$s %2$s %3$s - %10$s %6$s %7$s %8$s + %5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s + %5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s + %5$s %1$s, %3$s %2$s %4$s - %10$s %6$s, %8$s %7$s %9$s + %5$s %1$s, %3$s %2$s %4$s - %10$s %6$s, %8$s %7$s %9$s + %1$s, %3$s %2$s %4$s - %6$s, %8$s %7$s %9$s + %3$s-%8$s %2$s + %1$s %2$s %3$s - %6$s %7$s %8$s + %3$s %2$s - %8$s %7$s %9$s г. + %3$s-%8$s %2$s %9$s г. + %3$s %2$s - %8$s %7$s %9$s г. + %b + diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml index fac6cb7ea7538848a7fb88ea43739c16a3195f35..048f6b1c20d049b7d4bd34955551f848b5ba49ad 100644 --- a/core/res/res/values-ru/strings.xml +++ b/core/res/res/values-ru/strings.xml @@ -161,14 +161,10 @@ "Позволяет приложению изменять текущую конфигурацию, например локаль и общий размер шрифта." "перезапускать другие приложения" "Разрешает приложению принудительно перезапускать другие приложения." - "предотвращать остановку" - "Разрешает приложению запускать любые процессы на переднем плане так, что их нельзя прекратить. Не требуется обычным приложениям." "принудительно закрывать приложения" "Позволяет приложению принудительно закрывать и переводить в фоновый режим действия, работающие на переднем плане. Не требуется обычным приложениям." "получать внутреннее состояние системы" "Разрешает приложениям получать внутреннее состояние системы. Вредоносное ПО может получать множество личной и защищенной информации, которая обычно не была бы им доступна." - "публиковать службы низкого уровня" - "Разрешает приложению публиковать собственные системные службы низкого уровня. Вредоносное ПО может взломать систему и украсть или повредить данные в ней." "наблюдать и управлять запуском всех приложений" "Разрешает приложению следить и управлять тем, как система запускает действия. Вредоносное ПО может полностью нарушить работу системы. Это разрешение нужно только для разработки, но не при обычном использовании телефона." "отправлять оповещения об удалении пакетов" @@ -181,8 +177,6 @@ "Позволяет приложению контролировать максимальное количество выполняемых процессов. Не требуется обычным приложениям." "закрывать все фоновые приложения" "Разрешает приложению следить, чтобы действия всегда завершались после перехода в фоновый режим. Не требуется обычным приложениям." - "автоматически устанавливать системные обновления" - "Разрешает приложению получать уведомления о предстоящих обновлениях системы и запускать их установку. Это дает вредоносному ПО возможность повредить систему неавторизованными обновлениями или помешать выполнению обновления." "изменять данные о батарее" "Разрешает изменять данные о батарее. Не используется обычными приложениями." "отображать неавторизованные окна" @@ -418,9 +412,6 @@ "Пароль" "Войти" "Недействительное имя пользователя или пароль." - "h:mm AA" - "%-l:%M%P" - "%-l:%M%p" "%-l%P" "%-l%p" "Очистить уведомления" @@ -452,9 +443,6 @@ "ввод" "удалить" "Поиск" - "Сегодня" - "Вчера" - "Завтра" "1 месяц назад" "Больше 1 месяца назад" @@ -536,13 +524,6 @@ "недели" "год" "годы" - "воскресенье" - "понедельник" - "вторник" - "среда" - "четверг" - "пятница" - "суббота" "По рабочим дням (пн-пт)" "Ежедневно" "Еженедельно в: %s" @@ -552,137 +533,15 @@ "К сожалению, это видео не подходит для потокового воспроизведения на данном устройстве." "К сожалению, это видео нельзя воспроизвести." "ОК" - "AM" - "PM" - "%d/%m/%Y" - "%1$s, %2$s, %3$s%4$s, %5$s, %6$s" - "%1$s, %2$s%4$s, %5$s" - "%2$s, %3$s%5$s, %6$s" - "%2$s%5$s" - "%1$s%2$s" - "%1$s, %2$s, %3$s" - "%2$s, %3$s" - "%1$s, %3$s" - "%1$s, %2$s" "%1$s, %2$s" - "%1$s, %2$s" - "d' 'MMMM' 'yyyy" - "d' 'MMMM' 'yyyy" - "d' 'MMM' 'yyyy" - "d' 'MMM' 'yyyy" - "h':'mm' 'a" - "HH':'mm" "полдень" "Полдень" "полночь" "Полночь" - "%-d %B" - "%-d %B %Y" - "%Y %B г." - "%H:%M:%S" - "%H:%M:%S %-d %B %Y" - "%3$s %2$s%8$s %7$s" - "%1$s, %3$s %2$s%6$s, %8$s %7$s" - "%3$s %2$s%8$s %7$s %9$s" - "%1$s, %3$s %2$s%6$s, %8$s %7$s %9$s" - "%3$s %2$s, %5$s%8$s %7$s, %10$s" - "%1$s, %3$s %2$s, %5$s%6$s, %8$s %7$s, %10$s" - "%3$s %2$s %4$s, %5$s%8$s %7$s %9$s, %10$s" - "%1$s, %3$s %2$s %4$s, %5$s%6$s, %8$s %7$s %9$s, %10$s" - "%3$s/%2$s%8$s/%7$s" - "%1$s, %3$s/%2$s%6$s, %8$s/%7$s" - "%3$s/%2$s/%4$s%8$s/%7$s/%9$s" - "%1$s, %3$s/%2$s/%4$s%6$s, %8$s/%7$s/%9$s" - "%3$s/%2$s, %5$s%8$s/%7$s, %10$s" - "%1$s, %3$s/%2$s, %5$s%6$s, %8$s/%7$s, %10$s" - "%3$s/%2$s/%4$s, %5$s%8$s/%7$s/%9$s, %10$s" - "%1$s, %3$s/%2$s/%4$s, %5$s%6$s, %8$s/%7$s/%9$s, %10$s" - "%3$s%8$s %2$s" - "%1$s, %3$s %2$s%6$s, %8$s %7$s" - "%3$s%8$s %2$s %9$s" - "%1$s, %3$s %2$s %4$s%6$s, %8$s %7$s %9$s" - "%3$s %2$s, %5$s%8$s %7$s, %10$s" - "%1$s, %3$s %2$s, %5$s%6$s, %8$s %7$s, %10$s" - "%3$s %2$s %4$s, %5$s%8$s %7$s %9$s, %10$s" - "%1$s, %3$s %2$s %4$s, %5$s%6$s, %8$s %7$s %9$s, %10$s" - "%-d %b %Y" - "%b %Y г." - "%-d %b" - "воскресенье" - "понедельник" - "вторник" - "среда" - "четверг" - "пятница" - "суббота" - "вс" - "пн" - "вт" - "ср" - "чт" - "пт" - "сб" - "вс" - "пн" - "вт" - "ср" - "чт" - "пт" - "сб" - "вс" - "пн" - "вт" - "с" - "чт" - "пт" - "сб" - "в" - "п" - "в" - "с" - "ч" - "п" - "с" - "январь" - "февраль" - "март" - "апрель" - "май" - "июнь" - "июль" - "август" - "сентябрь" - "октябрь" - "ноябрь" - "декабрь" - "янв" - "фев" - "мар" - "апр" - "май" - "июн" - "июл" - "авг" - "сен" - "окт" - "ноя" - "дек" - "Я" - "ф" - "м" - "а" - "м" - "и" - "и" - "а" - "с" - "о" - "н" - "д" "%1$02d:%2$02d" "%1$d:%2$02d:%3$02d" "Выбрать все" diff --git a/core/res/res/values-sk-rSK/donottranslate-cldr.xml b/core/res/res/values-sk-rSK/donottranslate-cldr.xml new file mode 100644 index 0000000000000000000000000000000000000000..16239df8c04e6d34539e324e00c7019030976c31 --- /dev/null +++ b/core/res/res/values-sk-rSK/donottranslate-cldr.xml @@ -0,0 +1,147 @@ + + + január + február + marec + apríl + máj + jún + júl + august + september + október + november + december + + januára + februára + marca + apríla + mája + júna + júla + augusta + septembra + októbra + novembra + decembra + + jan + feb + mar + apr + máj + jún + júl + aug + sep + okt + nov + dec + + j + f + m + a + m + j + j + a + s + o + n + d + + nedeľa + pondelok + utorok + streda + štvrtok + piatok + sobota + + ne + po + ut + st + št + pi + so + + ne + po + ut + st + št + pi + so + + N + P + U + S + Š + P + S + + dopoludnia + popoludní + Včera + Dnes + Zajtra + + %H:%M + %-l:%M %p + %-l:%M %p + h:mm a + HH:mm + %-e.%-m.%Y + d.M.yyyy + "%s.%s.%s" + %-e. %B %Y + %-k:%M:%S + %-k:%M:%S %-e.%-m.%Y + %2$s %1$s + %1$s %3$s + %-e.%-m.%Y + %-e. %B + %-B + %B %Y + %-e. %b + %b + %b %Y + %1$s - %2$s + %2$s - %5$s + %3$s.%2$s. - %8$s.%7$s. + %1$s, %3$s.%2$s. - %6$s, %8$s.%7$s. + %3$s.%2$s.%4$s - %8$s.%7$s.%9$s + %1$s, %3$s.%2$s.%4$s - %6$s, %8$s.%7$s.%9$s + %5$s %1$s, %3$s.%2$s.%4$s - %10$s %6$s, %8$s.%7$s.%9$s + %5$s %3$s.%2$s. - %10$s %8$s.%7$s. + %5$s %1$s, %3$s.%2$s. - %10$s %6$s, %8$s.%7$s. + %5$s %3$s.%2$s.%4$s - %10$s %8$s.%7$s.%9$s + %3$s %1$s, %2$s - %6$s %4$s, %5$s + %1$s, %2$s - %4$s, %5$s + %3$s %2$s - %6$s %5$s + %1$s %2$s, %3$s + %2$s, %3$s + %1$s %2$s + %3$s. %2$s - %8$s. %7$s + %1$s, %3$s. %2$s - %6$s, %8$s. %7$s + %5$s %3$s. %2$s - %10$s %8$s. %7$s + %5$s %3$s. %2$s - %10$s %8$s. %7$s + %5$s %1$s, %3$s. %2$s - %10$s %6$s, %8$s. %7$s + %5$s %1$s, %3$s. %2$s - %10$s %6$s, %8$s. %7$s + %5$s %3$s. %2$s %4$s - %10$s %8$s. %7$s %9$s + %5$s %3$s. %2$s %4$s - %10$s %8$s. %7$s %9$s + %5$s %1$s, %3$s. %2$s %4$s - %10$s %6$s, %8$s. %7$s %9$s + %5$s %1$s, %3$s. %2$s %4$s - %10$s %6$s, %8$s. %7$s %9$s + %1$s, %3$s. %2$s %4$s - %6$s, %8$s. %7$s %9$s + %3$s. - %8$s. %2$s + %1$s, %3$s. %2$s - %6$s, %8$s. %7$s + %3$s. %2$s - %8$s. %7$s %9$s + %3$s. - %8$s. %2$s %9$s + %1$s, %3$s. %2$s - %6$s, %8$s. %7$s %9$s + %b + diff --git a/core/res/res/values-sl-rSI/donottranslate-cldr.xml b/core/res/res/values-sl-rSI/donottranslate-cldr.xml new file mode 100644 index 0000000000000000000000000000000000000000..b4ea32f37abdc6a4ac8170d081bde5efb71778b7 --- /dev/null +++ b/core/res/res/values-sl-rSI/donottranslate-cldr.xml @@ -0,0 +1,147 @@ + + + januar + februar + marec + april + maj + junij + julij + avgust + september + oktober + november + december + + januar + februar + marec + april + maj + junij + julij + avgust + september + oktober + november + december + + jan + feb + mar + apr + maj + jun + jul + avg + sep + okt + nov + dec + + j + f + m + a + m + j + j + a + s + o + n + d + + nedelja + ponedeljek + torek + sreda + četrtek + petek + sobota + + ned + pon + tor + sre + čet + pet + sob + + ned + pon + tor + sre + čet + pet + sob + + n + p + t + s + č + p + s + + dop. + pop. + Včeraj + Danes + Jutri + + %-k:%M + %-l:%M %p + %-l:%M %^p + h:mm a + H:mm + %-e. %m. %Y + d. MM. yyyy + "%s. %s. %s" + %d. %B %Y + %H:%M:%S + %H:%M:%S %-e. %b. %Y + %2$s %1$s + %1$s %3$s + %-e. %b. %Y + %-e. %B + %-B + %B %Y + %-e. %b. + %b + %b. %Y + %1$s – %2$s + %2$s – %5$s + %3$s. %2$s. – %8$s. %7$s. + %1$s., %3$s. %2$s. – %6$s., %8$s. %7$s. + %3$s. %2$s. %4$s – %8$s. %7$s. %9$s + %1$s., %3$s. %2$s. %4$s – %6$s., %8$s. %7$s. %9$s + %5$s %1$s., %3$s. %2$s. %4$s – %10$s %6$s., %8$s. %7$s. %9$s + %5$s %3$s. %2$s. – %10$s %8$s. %7$s. + %5$s %1$s., %3$s. %2$s. – %10$s %6$s., %8$s. %7$s. + %5$s %3$s. %2$s. %4$s – %10$s %8$s. %7$s. %9$s + %3$s %1$s., %2$s – %6$s %4$s., %5$s + %1$s., %2$s – %4$s., %5$s + %3$s %2$s – %6$s %5$s + %1$s %2$s., %3$s + %2$s., %3$s + %1$s %2$s + %3$s. %2$s – %8$s. %7$s + %1$s., %3$s. %2$s. – %6$s., %8$s. %7$s. + %5$s %3$s. %2$s – %10$s %8$s. %7$s + %5$s %3$s. %2$s – %10$s %8$s. %7$s + %5$s %1$s., %3$s. %2$s. – %10$s %6$s., %8$s. %7$s. + %5$s %1$s., %3$s. %2$s. – %10$s %6$s., %8$s. %7$s. + %5$s %3$s. %2$s. %4$s – %10$s %8$s. %7$s. %9$s + %5$s %3$s. %2$s. %4$s – %10$s %8$s. %7$s. %9$s + %5$s %1$s., %3$s. %2$s. %4$s – %10$s %6$s., %8$s. %7$s. %9$s + %5$s %1$s., %3$s. %2$s. %4$s – %10$s %6$s., %8$s. %7$s. %9$s + %1$s., %3$s. %2$s. %4$s – %6$s., %8$s. %7$s. %9$s + %3$s.–%8$s. %2$s. + %1$s., %3$s. %2$s. – %6$s., %8$s. %7$s. + %3$s. %2$s. – %8$s. %7$s. %9$s + %3$s.–%8$s. %2$s. %9$s + %1$s., %3$s. %2$s. – %6$s., %8$s. %7$s. %9$s + %b + diff --git a/core/res/res/values-sr-rRS/donottranslate-cldr.xml b/core/res/res/values-sr-rRS/donottranslate-cldr.xml new file mode 100644 index 0000000000000000000000000000000000000000..55ca96829f4e53c443027a728ff9471b97e3c090 --- /dev/null +++ b/core/res/res/values-sr-rRS/donottranslate-cldr.xml @@ -0,0 +1,147 @@ + + + јануар + фебруар + март + април + мај + јун + јул + август + септембар + октобар + новембар + децембар + + јануар + фебруар + март + април + мај + јун + јул + август + септембар + октобар + новембар + децембар + + јан + феб + мар + апр + мај + јун + јул + авг + сеп + окт + нов + дец + + ј + ф + м + а + м + ј + ј + а + с + о + н + д + + недеља + понедељак + уторак + среда + четвртак + петак + субота + + нед + пон + уто + сре + чет + пет + суб + + нед + пон + уто + сре + чет + пет + суб + + н + п + у + с + ч + п + с + + пре подне + поподне + јуче + данас + сутра + + %H.%M + %-l:%M %p + %-l:%M %p + h:mm a + HH.mm + %-e.%-m.%Y. + d.M.yyyy. + "%s.%s.%s." + %d. %B %Y. + %H.%M.%S + %H.%M.%S %d.%m.%Y. + %2$s %1$s + %1$s %3$s + %d.%m.%Y. + %B %-e. + %-B + %Y %B + %b %-e. + %-b + %b. %Y + %1$s - %2$s + %2$s - %5$s + %3$s.%2$s - %8$s.%7$s + %1$s, %3$s.%2$s - %6$s, %8$s.%7$s + %3$s.%2$s.%4$s. - %8$s.%7$s.%9$s. + %1$s, %3$s.%2$s.%4$s. - %6$s, %8$s.%7$s.%9$s. + %5$s %1$s, %3$s. %2$s. %4$s. - %10$s %6$s, %8$s. %7$s. %9$s. + %5$s %3$s/%2$s - %10$s %8$s/%7$s + %5$s %1$s, %2$s-%3$s - %10$s %6$s, %7$s-%8$s + %5$s %3$s.%2$s.%4$s. - %10$s %8$s.%7$s.%9$s. + %3$s %1$s, %2$s - %6$s %4$s, %5$s + %1$s, %2$s - %4$s, %5$s + %3$s %2$s - %6$s %5$s + %1$s %2$s, %3$s + %2$s, %3$s + %1$s %2$s + %2$s %3$s. - %7$s %8$s. + %1$s %3$s. %2$s - %6$s %8$s. %7$s + %5$s %2$s %3$s. - %10$s %7$s %8$s. + %5$s %2$s %3$s. - %10$s %7$s %8$s. + %5$s %1$s %3$s. %2$s - %10$s %6$s %8$s. %7$s + %5$s %1$s %3$s. %2$s - %10$s %6$s %8$s. %7$s + %5$s %3$s. %2$s %4$s. - %10$s %8$s. %7$s %9$s. + %5$s %3$s. %2$s %4$s. - %10$s %8$s. %7$s %9$s. + %5$s %1$s, %3$s. %2$s %4$s. - %10$s %6$s, %8$s. %7$s %9$s. + %5$s %1$s, %3$s. %2$s %4$s. - %10$s %6$s, %8$s. %7$s %9$s. + %1$s, %3$s. %2$s %4$s. - %6$s, %8$s. %7$s %9$s. + %3$s.-%8$s. %2$s + %1$s %3$s. %2$s - %6$s %8$s. %7$s + %3$s. %2$s - %8$s. %7$s %9$s. + %3$s.-%8$s. %2$s %9$s. + %1$s, %3$s. %2$s - %6$s, %8$s. %7$s %9$s. + %b + diff --git a/core/res/res/values-sv-rSE/donottranslate-cldr.xml b/core/res/res/values-sv-rSE/donottranslate-cldr.xml new file mode 100644 index 0000000000000000000000000000000000000000..a6ffc9aa77af6af769be658291db9b1cb54379d0 --- /dev/null +++ b/core/res/res/values-sv-rSE/donottranslate-cldr.xml @@ -0,0 +1,147 @@ + + + januari + februari + mars + april + maj + juni + juli + augusti + september + oktober + november + december + + januari + februari + mars + april + maj + juni + juli + augusti + september + oktober + november + december + + jan + feb + mar + apr + maj + jun + jul + aug + sep + okt + nov + dec + + J + F + M + A + M + J + J + A + S + O + N + D + + söndag + måndag + tisdag + onsdag + torsdag + fredag + lördag + + sön + mån + tis + ons + tors + fre + lör + + sön + mån + tis + ons + tors + fre + lör + + S + M + T + O + T + F + L + + f.m. + e.m. + igår + idag + imorgon + + %-k.%M + %-l:%M %p + %-l:%M %^p + h:mm a + H.mm + %Y-%m-%d + yyyy-MM-dd + "%s-%s-%s" + %-e %B %Y + %H:%M:%S + %H:%M:%S %-e %b %Y + %2$s %1$s + %1$s %3$s + %-e %b %Y + %-e %B + %-B + %Y %B + %-e %b + %-b + %Y %b + %1$s – %2$s + %2$s – %5$s + %3$s/%2$s – %8$s/%7$s + %1$s %3$s/%2$s – %6$s %8$s/%7$s + %4$s-%2$s-%3$s – %9$s-%7$s-%8$s + %1$s, %4$s-%2$s-%3$s – %6$s, %9$s-%7$s-%8$s + %5$s %1$s, %4$s-%2$s-%3$s – %10$s %6$s, %9$s-%7$s-%8$s + %5$s %3$s/%2$s – %10$s %8$s/%7$s + %5$s %1$s %3$s/%2$s – %10$s %6$s %8$s/%7$s + %5$s %4$s-%2$s-%3$s – %10$s %9$s-%7$s-%8$s + %3$s %1$s %2$s – %6$s %4$s %5$s + %1$s %2$s – %4$s %5$s + %3$s %2$s – %6$s %5$s + %1$s %2$s %3$s + %2$s %3$s + %1$s %2$s + %3$s %2$s – %8$s %7$s + %1$s %3$s %2$s – %6$s %8$s %7$s + %5$s %3$s %2$s – %10$s %8$s %7$s + %5$s %3$s %2$s – %10$s %8$s %7$s + %5$s %1$s %3$s %2$s – %10$s %6$s %8$s %7$s + %5$s %1$s %3$s %2$s – %10$s %6$s %8$s %7$s + %5$s %3$s %2$s %4$s – %10$s %8$s %7$s %9$s + %5$s %3$s %2$s %4$s – %10$s %8$s %7$s %9$s + %5$s %1$s %3$s %2$s %4$s – %10$s %6$s %8$s %7$s %9$s + %5$s %1$s %3$s %2$s %4$s – %10$s %6$s %8$s %7$s %9$s + %1$s %3$s %2$s %4$s – %6$s %8$s %7$s %9$s + %3$s–%8$s %2$s + %1$s %3$s %2$s – %6$s %8$s %7$s + %3$s %2$s–%8$s %7$s %9$s + %3$s–%8$s %2$s %9$s + %1$s %3$s %2$s–%6$s %8$s %7$s %9$s + %b + diff --git a/core/res/res/values-sv/donottranslate-cldr.xml b/core/res/res/values-sv/donottranslate-cldr.xml new file mode 100644 index 0000000000000000000000000000000000000000..a6ffc9aa77af6af769be658291db9b1cb54379d0 --- /dev/null +++ b/core/res/res/values-sv/donottranslate-cldr.xml @@ -0,0 +1,147 @@ + + + januari + februari + mars + april + maj + juni + juli + augusti + september + oktober + november + december + + januari + februari + mars + april + maj + juni + juli + augusti + september + oktober + november + december + + jan + feb + mar + apr + maj + jun + jul + aug + sep + okt + nov + dec + + J + F + M + A + M + J + J + A + S + O + N + D + + söndag + måndag + tisdag + onsdag + torsdag + fredag + lördag + + sön + mån + tis + ons + tors + fre + lör + + sön + mån + tis + ons + tors + fre + lör + + S + M + T + O + T + F + L + + f.m. + e.m. + igår + idag + imorgon + + %-k.%M + %-l:%M %p + %-l:%M %^p + h:mm a + H.mm + %Y-%m-%d + yyyy-MM-dd + "%s-%s-%s" + %-e %B %Y + %H:%M:%S + %H:%M:%S %-e %b %Y + %2$s %1$s + %1$s %3$s + %-e %b %Y + %-e %B + %-B + %Y %B + %-e %b + %-b + %Y %b + %1$s – %2$s + %2$s – %5$s + %3$s/%2$s – %8$s/%7$s + %1$s %3$s/%2$s – %6$s %8$s/%7$s + %4$s-%2$s-%3$s – %9$s-%7$s-%8$s + %1$s, %4$s-%2$s-%3$s – %6$s, %9$s-%7$s-%8$s + %5$s %1$s, %4$s-%2$s-%3$s – %10$s %6$s, %9$s-%7$s-%8$s + %5$s %3$s/%2$s – %10$s %8$s/%7$s + %5$s %1$s %3$s/%2$s – %10$s %6$s %8$s/%7$s + %5$s %4$s-%2$s-%3$s – %10$s %9$s-%7$s-%8$s + %3$s %1$s %2$s – %6$s %4$s %5$s + %1$s %2$s – %4$s %5$s + %3$s %2$s – %6$s %5$s + %1$s %2$s %3$s + %2$s %3$s + %1$s %2$s + %3$s %2$s – %8$s %7$s + %1$s %3$s %2$s – %6$s %8$s %7$s + %5$s %3$s %2$s – %10$s %8$s %7$s + %5$s %3$s %2$s – %10$s %8$s %7$s + %5$s %1$s %3$s %2$s – %10$s %6$s %8$s %7$s + %5$s %1$s %3$s %2$s – %10$s %6$s %8$s %7$s + %5$s %3$s %2$s %4$s – %10$s %8$s %7$s %9$s + %5$s %3$s %2$s %4$s – %10$s %8$s %7$s %9$s + %5$s %1$s %3$s %2$s %4$s – %10$s %6$s %8$s %7$s %9$s + %5$s %1$s %3$s %2$s %4$s – %10$s %6$s %8$s %7$s %9$s + %1$s %3$s %2$s %4$s – %6$s %8$s %7$s %9$s + %3$s–%8$s %2$s + %1$s %3$s %2$s – %6$s %8$s %7$s + %3$s %2$s–%8$s %7$s %9$s + %3$s–%8$s %2$s %9$s + %1$s %3$s %2$s–%6$s %8$s %7$s %9$s + %b + diff --git a/core/res/res/values-th-rTH/donottranslate-cldr.xml b/core/res/res/values-th-rTH/donottranslate-cldr.xml new file mode 100644 index 0000000000000000000000000000000000000000..b3c76a33abf5c72e144b3758f6543ea97e7b2f77 --- /dev/null +++ b/core/res/res/values-th-rTH/donottranslate-cldr.xml @@ -0,0 +1,147 @@ + + + มกราคม + กุมภาพันธ์ + มีนาคม + เมษายน + พฤษภาคม + มิถุนายน + กรกฎาคม + สิงหาคม + กันยายน + ตุลาคม + พฤศจิกายน + ธันวาคม + + มกราคม + กุมภาพันธ์ + มีนาคม + เมษายน + พฤษภาคม + มิถุนายน + กรกฎาคม + สิงหาคม + กันยายน + ตุลาคม + พฤศจิกายน + ธันวาคม + + ม.ค. + ก.พ. + มี.ค. + เม.ย. + พ.ค. + มิ.ย. + ก.ค. + ส.ค. + ก.ย. + ต.ค. + พ.ย. + ธ.ค. + + ม.ค. + ก.พ. + มี.ค. + เม.ย. + พ.ค. + มิ.ย. + ก.ค. + ส.ค. + ก.ย. + ต.ค. + พ.ย. + ธ.ค. + + วันอาทิตย์ + วันจันทร์ + วันอังคาร + วันพุธ + วันพฤหัสบดี + วันศุกร์ + วันเสาร์ + + อา. + จ. + อ. + พ. + พฤ. + ศ. + ส. + + อา. + จ. + อ. + พ. + พฤ. + ศ. + ส. + + + + + + + + + + ก่อนเที่ยง + หลังเที่ยง + เมื่อวาน + วันนี้ + พรุ่งนี้ + + %-k:%M + %-l:%M %p + %-l:%M %p + h:mm a + H:mm + %-e/%-m/%Y + d/M/yyyy + "%s/%s/%s" + %-e %B %Y + %-k:%M:%S + %-k:%M:%S, %-e %b %Y + %2$s, %1$s + %1$s, %3$s + %-e %b %Y + %-e %B + %-B + %B %Y + %-e %b + %-b + %b %Y + %1$s - %2$s + %2$s - %5$s + %3$s/%2$s - %8$s/%7$s + %1$s %3$s/%2$s – %6$s %8$s/%7$s + %3$s/%2$s/%4$s – %8$s/%7$s/%9$s + %1$s %3$s/%2$s/%4$s – %6$s %8$s/%7$s/%9$s + %5$s, %1$s %3$s/%2$s/%4$s - %10$s, %6$s %8$s/%7$s/%9$s + %5$s, %3$s/%2$s - %10$s, %8$s/%7$s + %5$s, %1$s, %3$s/%2$s - %10$s, %6$s, %8$s/%7$s + %5$s, %3$s/%2$s/%4$s - %10$s, %8$s/%7$s/%9$s + %3$s, %1$s %2$s - %6$s, %4$s %5$s + %1$s %2$s - %4$s %5$s + %3$s, %2$s - %6$s, %5$s + %1$s, %2$s %3$s + %2$s %3$s + %1$s, %2$s + %3$s %2$s - %8$s %7$s + %1$s %3$s %2$s - %6$s %8$s %7$s + %5$s, %3$s %2$s - %10$s, %8$s %7$s + %5$s, %3$s %2$s - %10$s, %8$s %7$s + %5$s, %1$s %3$s %2$s - %10$s, %6$s %8$s %7$s + %5$s, %1$s %3$s %2$s - %10$s, %6$s %8$s %7$s + %5$s, %3$s %2$s %4$s - %10$s, %8$s %7$s %9$s + %5$s, %3$s %2$s %4$s - %10$s, %8$s %7$s %9$s + %5$s, %1$s %3$s %2$s %4$s - %10$s, %6$s %8$s %7$s %9$s + %5$s, %1$s %3$s %2$s %4$s - %10$s, %6$s %8$s %7$s %9$s + %1$s %3$s %2$s %4$s - %6$s %8$s %7$s %9$s + %3$s – %8$s %2$s + %1$s %3$s %2$s - %6$s %8$s %7$s + %3$s %2$s – %8$s %7$s %9$s + %3$s–%8$s %2$s %9$s + %1$s %3$s %2$s – %6$s %8$s %7$s %9$s + %b + diff --git a/core/res/res/values-tr-rTR/donottranslate-cldr.xml b/core/res/res/values-tr-rTR/donottranslate-cldr.xml new file mode 100644 index 0000000000000000000000000000000000000000..d61230593437de89665d90a9f51a1914f8ca223d --- /dev/null +++ b/core/res/res/values-tr-rTR/donottranslate-cldr.xml @@ -0,0 +1,147 @@ + + + Ocak + Şubat + Mart + Nisan + Mayıs + Haziran + Temmuz + Ağustos + Eylül + Ekim + Kasım + Aralık + + Ocak + Şubat + Mart + Nisan + Mayıs + Haziran + Temmuz + Ağustos + Eylül + Ekim + Kasım + Aralık + + Oca + Şub + Mar + Nis + May + Haz + Tem + Ağu + Eyl + Eki + Kas + Ara + + O + Ş + M + N + M + H + T + A + E + E + K + A + + Pazar + Pazartesi + Salı + Çarşamba + Perşembe + Cuma + Cumartesi + + Paz + Pzt + Sal + Çar + Per + Cum + Cmt + + Paz + Pzt + Sal + Çar + Per + Cum + Cmt + + P + P + S + Ç + P + C + C + + AM + PM + Dün + Bugün + Yarın + + %H:%M + %-l:%M %p + %-l:%M %^p + h:mm a + HH:mm + %d %m %Y + dd MM yyyy + "%s %s %s" + %d %B %Y + %H:%M:%S + %H:%M:%S %d %b %Y + %2$s %1$s + %1$s %3$s + %d %b %Y + %d %B + %-B + %B %Y + %d %b + %-b + %b %Y + %1$s - %2$s + %2$s - %5$s + %3$s.%2$s - %8$s.%7$s + %3$s.%2$s %1$s - %8$s.%7$s %6$s + %3$s.%2$s.%4$s - %8$s.%7$s.%9$s + %3$s.%2$s.%4$s %1$s - %8$s.%7$s.%9$s %6$s + %5$s %3$s.%2$s.%4$s %1$s - %10$s %8$s.%7$s.%9$s %6$s + %5$s %3$s/%2$s - %10$s %8$s/%7$s + %5$s %3$s/%2$s %1$s - %10$s %8$s/%7$s %6$s + %5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s + %3$s %2$s %1$s - %6$s %5$s %4$s + %2$s %1$s - %5$s %4$s + %3$s %2$s - %6$s %5$s + %1$s %3$s %2$s + %3$s %2$s + %1$s %2$s + %3$s %2$s - %8$s %7$s + %3$s %2$s %1$s - %8$s %7$s %6$s + %5$s %3$s %2$s - %10$s %8$s %7$s + %5$s %3$s %2$s - %10$s %8$s %7$s + %5$s %3$s %2$s %1$s - %10$s %8$s %7$s %6$s + %5$s %3$s %2$s %1$s - %10$s %8$s %7$s %6$s + %5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s + %5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s + %5$s %3$s %2$s %4$s %1$s - %10$s %8$s %7$s %9$s %6$s + %5$s %3$s %2$s %4$s %1$s - %10$s %8$s %7$s %9$s %6$s + %3$s %2$s %4$s %1$s - %8$s %7$s %9$s %6$s + %3$s-%8$s %2$s + %3$s %2$s %1$s - %8$s %7$s %6$s + %3$s %2$s - %8$s %7$s %9$s + %3$s-%8$s %2$s %9$s + %3$s %2$s %9$s %1$s - %8$s %7$s y %6$s + %b + diff --git a/core/res/res/values-tr/donottranslate-cldr.xml b/core/res/res/values-tr/donottranslate-cldr.xml new file mode 100644 index 0000000000000000000000000000000000000000..d61230593437de89665d90a9f51a1914f8ca223d --- /dev/null +++ b/core/res/res/values-tr/donottranslate-cldr.xml @@ -0,0 +1,147 @@ + + + Ocak + Şubat + Mart + Nisan + Mayıs + Haziran + Temmuz + Ağustos + Eylül + Ekim + Kasım + Aralık + + Ocak + Şubat + Mart + Nisan + Mayıs + Haziran + Temmuz + Ağustos + Eylül + Ekim + Kasım + Aralık + + Oca + Şub + Mar + Nis + May + Haz + Tem + Ağu + Eyl + Eki + Kas + Ara + + O + Ş + M + N + M + H + T + A + E + E + K + A + + Pazar + Pazartesi + Salı + Çarşamba + Perşembe + Cuma + Cumartesi + + Paz + Pzt + Sal + Çar + Per + Cum + Cmt + + Paz + Pzt + Sal + Çar + Per + Cum + Cmt + + P + P + S + Ç + P + C + C + + AM + PM + Dün + Bugün + Yarın + + %H:%M + %-l:%M %p + %-l:%M %^p + h:mm a + HH:mm + %d %m %Y + dd MM yyyy + "%s %s %s" + %d %B %Y + %H:%M:%S + %H:%M:%S %d %b %Y + %2$s %1$s + %1$s %3$s + %d %b %Y + %d %B + %-B + %B %Y + %d %b + %-b + %b %Y + %1$s - %2$s + %2$s - %5$s + %3$s.%2$s - %8$s.%7$s + %3$s.%2$s %1$s - %8$s.%7$s %6$s + %3$s.%2$s.%4$s - %8$s.%7$s.%9$s + %3$s.%2$s.%4$s %1$s - %8$s.%7$s.%9$s %6$s + %5$s %3$s.%2$s.%4$s %1$s - %10$s %8$s.%7$s.%9$s %6$s + %5$s %3$s/%2$s - %10$s %8$s/%7$s + %5$s %3$s/%2$s %1$s - %10$s %8$s/%7$s %6$s + %5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s + %3$s %2$s %1$s - %6$s %5$s %4$s + %2$s %1$s - %5$s %4$s + %3$s %2$s - %6$s %5$s + %1$s %3$s %2$s + %3$s %2$s + %1$s %2$s + %3$s %2$s - %8$s %7$s + %3$s %2$s %1$s - %8$s %7$s %6$s + %5$s %3$s %2$s - %10$s %8$s %7$s + %5$s %3$s %2$s - %10$s %8$s %7$s + %5$s %3$s %2$s %1$s - %10$s %8$s %7$s %6$s + %5$s %3$s %2$s %1$s - %10$s %8$s %7$s %6$s + %5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s + %5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s + %5$s %3$s %2$s %4$s %1$s - %10$s %8$s %7$s %9$s %6$s + %5$s %3$s %2$s %4$s %1$s - %10$s %8$s %7$s %9$s %6$s + %3$s %2$s %4$s %1$s - %8$s %7$s %9$s %6$s + %3$s-%8$s %2$s + %3$s %2$s %1$s - %8$s %7$s %6$s + %3$s %2$s - %8$s %7$s %9$s + %3$s-%8$s %2$s %9$s + %3$s %2$s %9$s %1$s - %8$s %7$s y %6$s + %b + diff --git a/core/res/res/values-uk-rUA/donottranslate-cldr.xml b/core/res/res/values-uk-rUA/donottranslate-cldr.xml new file mode 100644 index 0000000000000000000000000000000000000000..c51f8d1eae067eeec27b8b4a9284c461c21650d2 --- /dev/null +++ b/core/res/res/values-uk-rUA/donottranslate-cldr.xml @@ -0,0 +1,147 @@ + + + Січень + Лютий + Березень + Квітень + Травень + Червень + Липень + Серпень + Вересень + Жовтень + Листопад + Грудень + + січня + лютого + березня + квітня + травня + червня + липня + серпня + вересня + жовтня + листопада + грудня + + січ. + лют. + бер. + квіт. + трав. + черв. + лип. + серп. + вер. + жовт. + лист. + груд. + + С + Л + Б + К + Т + Ч + Л + С + В + Ж + Л + Г + + Неділя + Понеділок + Вівторок + Середа + Четвер + Пʼятниця + Субота + + Нд + Пн + Вт + Ср + Чт + Пт + Сб + + Нд + Пн + Вт + Ср + Чт + Пт + Сб + + Н + П + В + С + Ч + П + С + + дп + пп + Вчора + Сьогодні + Завтра + + %-k:%M + %-l:%M %p + %-l:%M %p + h:mm a + H:mm + %d.%m.%Y + dd.MM.yyyy + "%s.%s.%s" + %-e %B %Y р. + %H:%M:%S + %H:%M:%S %-e %b %Y + %2$s %1$s + %1$s %3$s + %-e %b %Y + %-e %B + %-B + %-B %Y + %-e %b + %-b + %-b %Y + %1$s – %2$s + %2$s – %5$s + %3$s.%2$s – %8$s.%7$s + %1$s, %3$s.%2$s – %6$s, %8$s.%7$s + %3$s.%2$s.%4$s – %8$s.%7$s.%9$s + %1$s, %3$s.%2$s.%4$s – %6$s, %8$s.%7$s.%9$s + %5$s %1$s, %3$s.%2$s.%4$s – %10$s %6$s, %8$s.%7$s.%9$s + %5$s %3$s.%2$s – %10$s %8$s.%7$s + %5$s %1$s, %3$s.%2$s – %10$s %6$s, %8$s.%7$s + %5$s %3$s.%2$s.%4$s – %10$s %8$s.%7$s.%9$s + %3$s %1$s, %2$s – %6$s %4$s, %5$s + %1$s, %2$s – %4$s, %5$s + %3$s %2$s – %6$s %5$s + %1$s %2$s, %3$s + %2$s, %3$s + %1$s %2$s + %3$s %2$s – %8$s %7$s + %1$s, %3$s %2$s – %6$s, %8$s %7$s + %5$s %3$s %2$s – %10$s %8$s %7$s + %5$s %3$s %2$s – %10$s %8$s %7$s + %5$s %1$s, %3$s %2$s – %10$s %6$s, %8$s %7$s + %5$s %1$s, %3$s %2$s – %10$s %6$s, %8$s %7$s + %5$s %3$s %2$s %4$s – %10$s %8$s %7$s %9$s + %5$s %3$s %2$s %4$s – %10$s %8$s %7$s %9$s + %5$s %1$s, %3$s %2$s %4$s – %10$s %6$s, %8$s %7$s %9$s + %5$s %1$s, %3$s %2$s %4$s – %10$s %6$s, %8$s %7$s %9$s + %1$s, %3$s %2$s %4$s – %6$s, %8$s %7$s %9$s + %3$s–%8$s %2$s + %1$s, %3$s %2$s – %6$s, %8$s %7$s + %3$s %2$s – %8$s %7$s %9$s + %3$s–%8$s %2$s %9$s + %1$s, %3$s %2$s – %6$s, %8$s %7$s %9$s + %b + diff --git a/core/res/res/values-vi-rVN/donottranslate-cldr.xml b/core/res/res/values-vi-rVN/donottranslate-cldr.xml new file mode 100644 index 0000000000000000000000000000000000000000..72ff8b68999c33df882022e47356ee35683a05bb --- /dev/null +++ b/core/res/res/values-vi-rVN/donottranslate-cldr.xml @@ -0,0 +1,147 @@ + + + tháng một + tháng hai + tháng ba + tháng tư + tháng năm + tháng sáu + tháng bảy + tháng tám + tháng chín + tháng mười + tháng mười một + tháng mười hai + + tháng một + tháng hai + tháng ba + tháng tư + tháng năm + tháng sáu + tháng bảy + tháng tám + tháng chín + tháng mười + tháng mười một + tháng mười hai + + thg 1 + thg 2 + thg 3 + thg 4 + thg 5 + thg 6 + thg 7 + thg 8 + thg 9 + thg 10 + thg 11 + thg 12 + + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + + Chủ nhật + Thứ hai + Thứ ba + Thứ tư + Thứ năm + Thứ sáu + Thứ bảy + + CN + Th 2 + Th 3 + Th 4 + Th 5 + Th 6 + Th 7 + + CN + Th 2 + Th 3 + Th 4 + Th 5 + Th 6 + Th 7 + + 1 + 2 + 3 + 4 + 5 + 6 + 7 + + SA + CH + Yesterday + Today + Tomorrow + + %-k:%M + %-l:%M %p + %-l:%M %^p + h:mm a + H:mm + %d/%m/%Y + dd/MM/yyyy + "%s/%s/%s" + Ngày %d tháng %-m năm %Y + %H:%M:%S + %d-%m-%Y %H:%M:%S + %1$s %2$s + %3$s %1$s + %d-%m-%Y + %-e %B + %-B + %B %Y + %-e %b + %b + %b %Y + %1$s - %2$s + %2$s - %5$s + %3$s/%2$s - %8$s/%7$s + %1$s, %3$s/%2$s - %6$s, %8$s/%7$s + %3$s/%2$s/%4$s - %8$s/%7$s/%9$s + %1$s, %3$s/%2$s/%4$s - %6$s, %8$s/%7$s/%9$s + %1$s, %3$s-%2$s-%4$s %5$s - %6$s, %8$s-%7$s-%9$s %10$s + %3$s-%2$s %5$s - %8$s-%7$s %10$s + %1$s, %3$s-%2$s %5$s - %6$s, %8$s-%7$s %10$s + %3$s/%2$s/%4$s %5$s - %8$s/%7$s/%9$s %10$s + %1$s %2$s %3$s - %4$s %5$s %6$s + %1$s %2$s - %4$s %5$s + %2$s %3$s - %5$s %6$s + %2$s %3$s %1$s + %2$s %3$s + %2$s %1$s + %3$s %2$s - %8$s %7$s + %1$s %3$s %2$s - %6$s %8$s %7$s + %3$s %2$s %5$s - %8$s %7$s %10$s + %3$s %2$s %5$s - %8$s %7$s %10$s + %1$s %3$s %2$s %5$s - %6$s %8$s %7$s %10$s + %1$s %3$s %2$s %5$s - %6$s %8$s %7$s %10$s + Ngày %3$s tháng %2$s năm %4$s %5$s - 'Ngày %8$s tháng %7$s năm %9$s %10$s + Ngày %3$s tháng %2$s năm %4$s %5$s - 'Ngày %8$s tháng %7$s năm %9$s %10$s + %1$s, %3$s %2$s %4$s %5$s - %6$s, %8$s %7$s %9$s %10$s + %1$s, %3$s %2$s %4$s %5$s - %6$s, %8$s %7$s %9$s %10$s + %1$s, %3$s %2$s %4$s - %6$s, %8$s %7$s %9$s + Ngày %3$s tháng %2$s - Ngày %8$s tháng %7$s + %1$s %3$s %2$s - %6$s %8$s %7$s + Ngày %3$s tháng %2$s - Ngày %8$s tháng %7$s năm %9$s + Ngày %3$s tháng %2$s - Ngày %8$s tháng M năm %9$s + %1$s, ngày %3$s %2$s - %6$s, ngày %8$s %7$s năm %9$s + %b + diff --git a/core/res/res/values-zh-rCN/donottranslate-cldr.xml b/core/res/res/values-zh-rCN/donottranslate-cldr.xml new file mode 100644 index 0000000000000000000000000000000000000000..6d52d7046f99800bee0d3ca51aa0b33e8fe5db74 --- /dev/null +++ b/core/res/res/values-zh-rCN/donottranslate-cldr.xml @@ -0,0 +1,147 @@ + + + 一月 + 二月 + 三月 + 四月 + 五月 + 六月 + 七月 + 八月 + 九月 + 十月 + 十一月 + 十二月 + + 1月 + 2月 + 3月 + 4月 + 5月 + 6月 + 7月 + 8月 + 9月 + 10月 + 11月 + 12月 + + 1月 + 2月 + 3月 + 4月 + 5月 + 6月 + 7月 + 8月 + 9月 + 10月 + 11月 + 12月 + + 1月 + 2月 + 3月 + 4月 + 5月 + 6月 + 7月 + 8月 + 9月 + 10月 + 11月 + 12月 + + 星期日 + 星期一 + 星期二 + 星期三 + 星期四 + 星期五 + 星期六 + + 周日 + 周一 + 周二 + 周三 + 周四 + 周五 + 周六 + + 周日 + 周一 + 周二 + 周三 + 周四 + 周五 + 周六 + + + + + + + + + + 上午 + 下午 + 昨天 + 今天 + 明天 + + %-k:%M + %p%-l:%M + %p%-l:%M + ah:mm + H:mm + %Y-%-m-%-e + yyyy-M-d + "%s-%s-%s" + %Y年%-m月%-e日 + %p%I:%M:%S + %p%I:%M:%S %Y-%-m-%-e + %2$s %1$s + %1$s %3$s + %Y-%-m-%-e + %B%-e日 + %-B + %Y年%B + %b%-e日 + %-b + %Y年%b + %1$s–%2$s + %2$s–%5$s + %2$s-%3$s至%7$s-%8$s + %2$s-%3$s%1$s至%7$s-%8$s%6$s + %4$s-%2$s-%3$s至%9$s-%7$s-%8$s + %4$s-%2$s-%3$s%1$s至%9$s-%7$s-%8$s%6$s + %5$s %4$s年%2$s月%3$s日,%1$s–%10$s %9$s年%7$s月%8$s日,%6$s + %5$s %2$s-%3$s–%10$s %7$s-%8$s + %5$s %2$s-%3$s%1$s–%10$s %7$s-%8$s%6$s + %5$s %4$s-%2$s-%3$s–%10$s %9$s-%7$s-%8$s + %3$s %2$s%1$s–%6$s %5$s%4$s + %2$s%1$s–%5$s%4$s + %3$s %2$s–%6$s %5$s + %1$s %3$s%2$s + %3$s%2$s + %1$s %2$s + %2$s%3$s日–%7$s%8$s日 + %2$s%3$s日%1$s–%7$s%8$s日%6$s + %5$s %2$s%3$s日–%10$s %7$s%8$s日 + %5$s %2$s%3$s日–%10$s %7$s%8$s日 + %5$s %2$s%3$s日%1$s–%10$s %7$s%8$s日%6$s + %5$s %2$s%3$s日%1$s–%10$s %7$s%8$s日%6$s + %5$s %4$s年%2$s%3$s日–%10$s %9$s年%7$s%8$s日 + %5$s %4$s年%2$s%3$s日–%10$s %9$s年%7$s%8$s日 + %5$s %4$s年%2$s%3$s日%1$s–%10$s %9$s年%7$s%8$s日%6$s + %5$s %4$s年%2$s%3$s日%1$s–%10$s %9$s年%7$s%8$s日%6$s + %4$s年%2$s%3$s日%1$s–%9$s年%7$s%8$s日%6$s + %2$s%3$s日至%8$s日 + %2$s%3$s日%1$s–%7$s%8$s日%6$s + %9$s年%2$s%3$s日至%7$s%8$s日 + %9$s年%2$s%3$s日至%8$s日 + %9$s年%2$s%3$s日%1$s至%7$s%8$s日%6$s + %b + diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml index 54b0b97d9691e7ab982afcb495e8d4ed699d2d64..3be8aa09a5fe68e518faa4cfda7e3507cff4f339 100644 --- a/core/res/res/values-zh-rCN/strings.xml +++ b/core/res/res/values-zh-rCN/strings.xml @@ -166,14 +166,10 @@ "允许应用程序更改当前配置,例如语言设置或整体的字体大小。" "重新启动其他应用程序" "允许应用程序强制重新启动其他应用程序。" - "防止停止" - "允许应用程序在前台运行任何进程,因此该进程不能被终止。普通应用程序从不需要使用此权限。" "强制应用程序关闭" "允许应用程序强制前台的任何活动关闭和重新开始。普通应用程序从不需要使用此权限。" "检索系统内部状态" "允许应用程序检索系统的内部状态。恶意应用程序可能会借此检索通常它们本不需要的各种私有和安全信息。" - "发布低级服务" - "允许应用程序发布自己的低级系统服务。恶意应用程序可能会借此攻击系统,以及盗取或破坏系统中的任何数据。" "监视和控制所有应用程序启动" "允许应用程序监视和控制系统启动活动的方式。恶意应用程序可能会借此彻底损坏系统。此权限仅在开发时才需要,普通的手机应用不需要。" "发送包删除的广播" @@ -186,8 +182,6 @@ "允许应用程序控制运行的进程数上限。普通应用程序从不需要使用此权限。" "关闭所有后台应用程序" "允许应用程序控制活动是否始终是一转至后台就完成。普通应用程序从不需要使用此权限。" - "自动安装系统更新" - "允许应用程序接收有关未决系统更新的通知并安装这些更新。恶意应用程序可能会借此通过未经授权的更新破坏系统,通常情况下,它们会干扰更新过程。" "修改电池统计信息" "允许修改收集的电池统计信息。普通应用程序不能使用此权限。" "显示未授权的窗口" @@ -424,9 +418,6 @@ "密码" "登录" "用户名或密码无效。" - "h:mm AA" - "%-l:%M%P" - "%-l:%M%p" "%P%-l点" "%p%-l点" "清除通知" @@ -458,9 +449,6 @@ "Enter 键" "删除" "搜索" - "今天" - "昨天" - "明天" "1 个月前" "1 个月前" @@ -542,13 +530,6 @@ "周" "年" "年" - "周日" - "周一" - "周二" - "周三" - "周四" - "周五" - "周六" "每个工作日(周一到周五)" "每天" "每周的%s" @@ -558,137 +539,15 @@ "抱歉,该视频不适合在此设备上播放。" "很抱歉,此视频不能播放。" "确定" - "上午" - "下午" - "%Y%m%d 日" - "%2$s%1$s %3$s%5$s%4$s %6$s" - "%2$s%1$s%5$s%4$s" - "%2$s %3$s%5$s %6$s" - "%2$s%5$s" - "%1$s%2$s" - "%3$s%2$s %1$s" - "%3$s%2$s" - "%3$s %1$s" - "%1$s%2$s" "%1$s%2$s" - "%2$s %1$s" - "yyyy' 年 'MMMM' 月 'd' 日'" - "yyyy' 年 'MMMM' 月 'd' 日'" - "yyyy' 年 'MMM' 月 'd' 日'" - "yyyy' 年 'd' 月 'MMM' 日'" - "h':'mm' 'a" - "HH':'mm" "中午" "中午" "午夜" "午夜" - "%B%-d 日" - "%Y%B%-d 日" - "%Y%B 月" - "%H:%M:%S" - "%Y%B%-d%H:%M:%S" - "%2$s%3$s 日至 %7$s%8$s 日" - "%2$s%3$s%1$s%7$s%8$s%6$s" - "%9$s%2$s%3$s 日至 %7$s%8$s 日" - "%9$s%2$s%3$s%1$s%7$s%8$s%6$s" - "%2$s%3$s%5$s%7$s%8$s%10$s" - "%2$s%3$s%1$s %5$s%7$s%8$s%6$s %10$s" - "%4$s%2$s%3$s%5$s%9$s%7$s%8$s%10$s" - "%4$s%2$s%3$s%1$s %5$s 至 – %9$s%7$s%8$s%6$s %10$s" - "%2$s%3$s 日至 %7$s%8$s 日" - "%2$s%3$s%1$s%7$s%8$s%6$s" - "%4$s%2$s%3$s 日至 %9$s%7$s%8$s 日" - "%4$s%2$s%3$s%1$s%9$s%7$s%8$s%6$s" - "%2$s%3$s%5$s%7$s%8$s%10$s" - "%2$s%3$s%1$s %5$s%7$s%8$s%6$s %10$s" - "%4$s%2$s%3$s%5$s%9$s%7$s%8$s%10$s" - "%4$s%2$s%3$s%1$s %5$s%9$s%7$s%8$s%6$s %10$s" - "%2$s%3$s 日至 %8$s 日" - "%2$s%3$s%1$s%7$s%8$s%6$s" - "%9$s%2$s%3$s 日至 %8$s 日" - "%4$s%2$s%3$s%1$s%9$s%7$s%8$s%6$s" - "%2$s%3$s%5$s%7$s%8$s%10$s" - "%2$s%3$s%1$s %5$s%7$s%8$s%6$s %10$s" - "%4$s%2$s%3$s%5$s%9$s%7$s%8$s%10$s" - "%4$s%2$s%3$s%1$s %5$s%9$s%7$s%8$s%6$s %10$s" - "%Y%b%-d 日" - "%Y%b 月" - "%b%-d 日" - "周日" - "周一" - "周二" - "周三" - "周四" - "周五" - "周六" - "周日" - "周一" - "周二" - "周三" - "周四" - "周五" - "周六" - "周日" - "周一" - "周二" - "周三" - "周四" - "周五" - "周六" - "周日" - "周一" - "周二" - "周三" - "周四" - "周五" - "周六" - "周日" - "周一" - "周二" - "周三" - "周四" - "周五" - "周六" - "1 月" - "2 月" - "3 月" - "4 月" - "5 月" - "6 月" - "7 月" - "8 月" - "9 月" - "10 月" - "11 月" - "12 月" - "1 月" - "2 月" - "3 月" - "4 月" - "5 月" - "6 月" - "7 月" - "8 月" - "9 月" - "10 月" - "11 月" - "12 月" - "1 月" - "2 月" - "3 月" - "4 月" - "5 月" - "6 月" - "7 月" - "8 月" - "9 月" - "10 月" - "11 月" - "12 月" "%1$02d:%2$02d" "%1$d:%2$02d:%3$02d" "全选" diff --git a/core/res/res/values-zh-rTW/donottranslate-cldr.xml b/core/res/res/values-zh-rTW/donottranslate-cldr.xml new file mode 100644 index 0000000000000000000000000000000000000000..6d52d7046f99800bee0d3ca51aa0b33e8fe5db74 --- /dev/null +++ b/core/res/res/values-zh-rTW/donottranslate-cldr.xml @@ -0,0 +1,147 @@ + + + 一月 + 二月 + 三月 + 四月 + 五月 + 六月 + 七月 + 八月 + 九月 + 十月 + 十一月 + 十二月 + + 1月 + 2月 + 3月 + 4月 + 5月 + 6月 + 7月 + 8月 + 9月 + 10月 + 11月 + 12月 + + 1月 + 2月 + 3月 + 4月 + 5月 + 6月 + 7月 + 8月 + 9月 + 10月 + 11月 + 12月 + + 1月 + 2月 + 3月 + 4月 + 5月 + 6月 + 7月 + 8月 + 9月 + 10月 + 11月 + 12月 + + 星期日 + 星期一 + 星期二 + 星期三 + 星期四 + 星期五 + 星期六 + + 周日 + 周一 + 周二 + 周三 + 周四 + 周五 + 周六 + + 周日 + 周一 + 周二 + 周三 + 周四 + 周五 + 周六 + + + + + + + + + + 上午 + 下午 + 昨天 + 今天 + 明天 + + %-k:%M + %p%-l:%M + %p%-l:%M + ah:mm + H:mm + %Y-%-m-%-e + yyyy-M-d + "%s-%s-%s" + %Y年%-m月%-e日 + %p%I:%M:%S + %p%I:%M:%S %Y-%-m-%-e + %2$s %1$s + %1$s %3$s + %Y-%-m-%-e + %B%-e日 + %-B + %Y年%B + %b%-e日 + %-b + %Y年%b + %1$s–%2$s + %2$s–%5$s + %2$s-%3$s至%7$s-%8$s + %2$s-%3$s%1$s至%7$s-%8$s%6$s + %4$s-%2$s-%3$s至%9$s-%7$s-%8$s + %4$s-%2$s-%3$s%1$s至%9$s-%7$s-%8$s%6$s + %5$s %4$s年%2$s月%3$s日,%1$s–%10$s %9$s年%7$s月%8$s日,%6$s + %5$s %2$s-%3$s–%10$s %7$s-%8$s + %5$s %2$s-%3$s%1$s–%10$s %7$s-%8$s%6$s + %5$s %4$s-%2$s-%3$s–%10$s %9$s-%7$s-%8$s + %3$s %2$s%1$s–%6$s %5$s%4$s + %2$s%1$s–%5$s%4$s + %3$s %2$s–%6$s %5$s + %1$s %3$s%2$s + %3$s%2$s + %1$s %2$s + %2$s%3$s日–%7$s%8$s日 + %2$s%3$s日%1$s–%7$s%8$s日%6$s + %5$s %2$s%3$s日–%10$s %7$s%8$s日 + %5$s %2$s%3$s日–%10$s %7$s%8$s日 + %5$s %2$s%3$s日%1$s–%10$s %7$s%8$s日%6$s + %5$s %2$s%3$s日%1$s–%10$s %7$s%8$s日%6$s + %5$s %4$s年%2$s%3$s日–%10$s %9$s年%7$s%8$s日 + %5$s %4$s年%2$s%3$s日–%10$s %9$s年%7$s%8$s日 + %5$s %4$s年%2$s%3$s日%1$s–%10$s %9$s年%7$s%8$s日%6$s + %5$s %4$s年%2$s%3$s日%1$s–%10$s %9$s年%7$s%8$s日%6$s + %4$s年%2$s%3$s日%1$s–%9$s年%7$s%8$s日%6$s + %2$s%3$s日至%8$s日 + %2$s%3$s日%1$s–%7$s%8$s日%6$s + %9$s年%2$s%3$s日至%7$s%8$s日 + %9$s年%2$s%3$s日至%8$s日 + %9$s年%2$s%3$s日%1$s至%7$s%8$s日%6$s + %b + diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml index 514e304f1b258e579fe08b726515cd69be364b59..8cace6638b438c7509579a74e15ce651abbbe037 100644 --- a/core/res/res/values-zh-rTW/strings.xml +++ b/core/res/res/values-zh-rTW/strings.xml @@ -161,14 +161,10 @@ "允許應用程式變更目前設定,例如:地區設定或字型大小。" "重新啟動其他應用程式" "允許應用程式強制重新啟動其他應用程式。" - "保持已停止狀態" - "允許應用程式在前端執行任何程序 (無法中止)。一般應用程式不需要此功能。" "強制關閉應用程式" "允許應用程式強制關閉在前端運作的活動並返回。一般應用程式不需要此功能。" "接收系統內部狀態" "允許應用程式取得系統內部狀態。請注意:惡意程式可能利用此功能,不當取得私人或安全性資料。" - "發行低階服務" - "允許應用程式發行自有低階系統服務。請注意:惡意程式可能利用此功能綁架系統或偷取、竄改資料內容。" "監視控制所有應用程式啟動狀態。" "允許應用程式監控管理系統啟動活動。請注意:惡意程式可能因此癱瘓整個系統。此權限只在開發時需要,一般手機使用不需要此權限。" "傳送程式已移除廣播" @@ -181,8 +177,6 @@ "允許應用程式控制可使用的最大執行緒。一般應用程式不需要此功能。" "關閉所有背景程式" "允許應用程式控制哪些活動在被移到背景執行時,儘速結束。一般應用程式不需要此功能。" - "自動安裝系統更新" - "允許應用程式接收可安裝系統更新的通知,並啟動安裝。請注意:惡意程式可能透過未授權的更新竄改系統,或干擾更新程序。" "編輯電池狀態" "允許修改電池狀態。一般應用程式不會使用此功能。" "顯示未授權視窗" @@ -418,9 +412,6 @@ "密碼" "登入" "使用者名稱或密碼錯誤。" - "h:mm AA" - "%-l:%M%P" - "%-l:%M%p" "%-l%P" "%-l%p" "清除通知" @@ -452,9 +443,6 @@ "輸入" "刪除" "搜尋" - "今天" - "昨天" - "明天" "1 個月以前" "1 個月前" @@ -536,13 +524,6 @@ "週" "年" "年" - "星期日" - "星期一" - "星期二" - "星期三" - "星期四" - "星期五" - "星期六" "每天 (週一至週五)" "每天" "每週%s" @@ -552,137 +533,15 @@ "很抱歉,影片格式無效,裝置無法進行串流處理。" "很抱歉,此影片無法播放。" "確定" - "上午" - "下午" - "%m/%d/%Y" - "%2$s%1$s%3$s%5$s%4$s%6$s" - "%2$s%1$s%5$s%4$s" - "%2$s%3$s%5$s%6$s" - "%2$s%5$s" - "%1$s%2$s" - "%3$s%2$s%1$s" - "%3$s%2$s" - "%3$s%1$s" - "%1$s%2$s" "%1$s%2$s" - "%2$s%1$s" - "MMMM' 'd','yyyy" - "yyyy' 年 'MMMM' 'd' 日'" - "MMM' 'd','yyyy" - "yyyy' 年 'MMM' 'd' 日'" - "h':'mm' 'a" - "HH':'mm" "中午" "中午" "午夜" "午夜" - "%B %-d 日" - "%Y%B %-d 日" - "%Y%B" - "%H:%M:%S" - "%Y%B %-d 日,%H:%M:%S" - "%2$s %3$s%7$s %8$s" - "%2$s %3$s 日,%1$s%7$s %8$s 日,%6$s" - "%9$s%2$s %3$s 日 – %7$s %8$s 日" - "%9$s%2$s %3$s 日,%1$s%7$s %8$s日,%6$s" - "%2$s %3$s 日,%5$s%7$s %8$s 日,%10$s" - "%2$s %3$s%1$s%5$s%7$s %8$s%6$s%10$s" - "%4$s%2$s %3$s 日,%5$s%9$s%7$s %8$s 日,%10$s" - "%4$s%2$s %3$s%1$s%5$s%9$s%7$s %8$s%6$s%10$s" - "%2$s/%3$s%7$s/%8$s" - "%2$s/%3$s%1$s%7$s/%8$s%6$s" - "%4$s/%2$s/%3$s%9$s/%7$s/%8$s" - "%4$s/%2$s/%3$s%1$s%9$s/%7$s/%8$s%6$s" - "%2$s/%3$s%5$s%7$s/%8$s%10$s" - "%2$s/%3$s%1$s%5$s%7$s/%8$s%6$s%10$s" - "%4$s/%2$s/%3$s%5$s%9$s/%7$s/%8$s%10$s" - "%4$s/%2$s/%3$s%1$s%5$s%9$s/%7$s/%8$s%6$s%10$s" - "%2$s %3$s 日 – %8$s 日" - "%2$s %3$s 日,%1$s%7$s %8$s 日,%6$s" - "%9$s%2$s %3$s 日 – %8$s 日" - "%4$s%2$s %3$s 日,%1$s%9$s%7$s %8$s 日,%6$s" - "%2$s %3$s 日,%5$s%7$s %8$s 日,%10$s" - "%2$s %3$s 日,%1$s%5$s%7$s %8$s 日,%6$s%10$s" - "%4$s%2$s %3$s%5$s%9$s%7$s %8$s%10$s" - "%4$s%2$s %3$s 日,%1$s%5$s%9$s%7$s %8$s 日,%6$s%10$s" - "%Y%b %-d 日" - "%Y%b" - "%b %-d 日" - "星期日" - "星期一" - "星期二" - "星期三" - "星期四" - "星期五" - "星期六" - "週日" - "週一" - "週二" - "週三" - "週四" - "五" - "週六" - "日" - "一" - "二" - "三" - "週四" - "五" - "六" - "日" - "一" - "二" - "三" - "四" - "五" - "六" - "日" - "一" - "二" - "三" - "四" - "五" - "六" - "1 月" - "2 月" - "3 月" - "4 月" - "5 月" - "6 月" - "7 月" - "8 月" - "9 月" - "10 月" - "11 月" - "12 月" - "1 月" - "2 月" - "3 月" - "4 月" - "5 月" - "6 月" - "7 月" - "8 月" - "9 月" - "10 月" - "11 月" - "12 月" - "1" - "2" - "3" - "4" - "5" - "6" - "7" - "8" - "9" - "10" - "11" - "12" "%1$02d:%2$02d" "%1$d:%2$02d:%3$02d" "全部選取" diff --git a/core/res/res/values/arrays.xml b/core/res/res/values/arrays.xml index 7db73f0141408b3784a2daf843cbe7bcb14bb281..66f0e820cb382c7d72500fdd89bbf7722f2f800c 100644 --- a/core/res/res/values/arrays.xml +++ b/core/res/res/values/arrays.xml @@ -110,7 +110,10 @@ alarm_clock battery phone_signal + phone_evdo_signal data_connection + cdma_eri + tty volume mute speakerphone @@ -122,4 +125,14 @@ sync_failing ime + + + + com.google.android.providers.enhancedgooglesearch/.Launcher + com.android.googlesearch/.GoogleSearch + com.android.websearch/.Search.1 + diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 633a831f0b18484e5d6d93d9d5b3451ccf2b6210..6f2a5d3c6f8a3f2cbc987b3d13e390a2acc25285 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -4,9 +4,9 @@ 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. @@ -64,10 +64,13 @@ - + + + + @@ -80,6 +83,11 @@ + + + + + @@ -94,27 +102,28 @@ - - - + + + + - + - + - + @@ -139,7 +148,7 @@ - + @@ -152,27 +161,27 @@ - - + + + the indicator that will be shown to next to the item. --> + If this is not provided, it defaults to the expandableListPreferredItemPaddingLeft. --> + indicator, use expandableListPreferredChildIndicatorLeft. --> + indicator, use expandableListPreferredChildIndicatorRight. --> - + - + @@ -221,7 +230,7 @@ any of the attributes defined by {@link android.R.styleable#WindowAnimation}. --> - + @@ -243,7 +252,7 @@ - + - + - + - + @@ -320,6 +329,8 @@ + + @@ -342,6 +353,12 @@ + + + + + + @@ -381,7 +398,7 @@ - + @@ -402,7 +419,7 @@ - + @@ -420,7 +437,7 @@ --> - + @@ -428,26 +445,26 @@ - + - + - + - + - + - + @@ -456,7 +473,7 @@ - + - - + + @@ -721,8 +738,7 @@ + Defines how to place the view, both its x and y axis, within its parent view group. --> @@ -769,11 +785,11 @@ - - + @@ -906,7 +922,7 @@ - + @@ -950,7 +966,7 @@ allows you to later retrieve the view with findViewById(R.id.my_id). --> - + - + @@ -972,7 +988,7 @@ @@ -1044,14 +1060,14 @@ - + - + @@ -1113,10 +1129,10 @@ - + - + - + @@ -1142,16 +1158,16 @@ - + - + - + @@ -1165,6 +1181,11 @@ enabled for events such as long presses. --> + + + - + - + @@ -1330,7 +1351,7 @@ - @@ -1353,13 +1374,13 @@ already visible on screen. --> + are currently visible. --> - - @@ -1458,14 +1479,14 @@ + use childIndicatorLeft. --> + use childIndicatorRight. --> - + - + - - @@ -1573,9 +1594,9 @@ - @@ -1620,7 +1641,7 @@ - + @@ -1653,27 +1674,27 @@ - + - + - + - + - + - - - - - - - - + + + + + + + + - + @@ -1695,7 +1716,7 @@ indices are ignored. You can shrink all columns by using the value "*" instead. Note that a column can be marked stretchable and shrinkable at the same time. --> - + @@ -1796,7 +1817,7 @@ - @@ -1921,7 +1942,7 @@ - @@ -1986,6 +2007,16 @@ + + + + + + + @@ -2012,7 +2043,7 @@ - + @@ -2053,6 +2084,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -2136,7 +2205,7 @@ {@link android.graphics.drawable.Drawable#setVisible} --> - + - + @@ -2161,15 +2230,15 @@ restarting at the first frame after the last has finished. --> - + - + - + @@ -2192,18 +2261,18 @@ - + - + - + @@ -2214,18 +2283,18 @@ - + - + - + @@ -2233,14 +2302,14 @@ - + - + @@ -2249,7 +2318,7 @@ - + @@ -2257,7 +2326,7 @@ - + @@ -2267,6 +2336,15 @@ + + + + + + + + + @@ -2413,7 +2491,7 @@ - + @@ -2456,14 +2534,14 @@ - + - + @@ -2472,14 +2550,14 @@ - + - + @@ -2541,12 +2619,12 @@ - + - + @@ -2656,18 +2734,18 @@ - + - + - + - + @@ -2677,7 +2755,7 @@ @@ -2686,29 +2764,29 @@ plain text. This is a reference to a drawable (icon) resource. Optional attribute. --> - - - + - + @@ -2757,14 +2835,14 @@ - - - + - + + + + + + + - + - + - + - + - + - + - + - + @@ -2888,11 +2979,11 @@ - + - + @@ -2903,7 +2994,7 @@ this group. --> - + @@ -2917,7 +3008,7 @@ - + @@ -2928,15 +3019,15 @@ - + + the title should be sufficient in describing this item. --> - + @@ -2944,14 +3035,14 @@ - + - - - + @@ -2977,7 +3068,7 @@ with a View's attributes. Some subclasses (e.g., EditTextPreference) proxy all attributes to its EditText widget. --> - + @@ -3106,7 +3197,7 @@ it had previously been shown. --> - + @@ -3139,13 +3230,13 @@ - + - + - @@ -3228,7 +3319,7 @@ If not supplied, then no activity will be launched. --> - - + + diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index 7b48267045faaa0d129bdfe81e177e48f3f3f97f..7571e2453627933724eae98c4bcda00ec1b3499e 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -512,6 +512,9 @@ + + @@ -570,6 +573,15 @@ + + + + + + + {@link #AndroidManifestInstrumentation instrumentation}, + {@link #AndroidManifestUsesFeature uses-feature}. --> @@ -643,6 +656,8 @@ + + + + + + + - - - + {@link #AndroidManifest manifest} tag. --> + + + + + + + + + + + + + + + + + + + + + + + + - #ffffffff + #fff9f9f9 #ff1a1a1a #ff000000 #ff000000 @@ -37,7 +37,7 @@ #323232 #80323232 #808080 - #ffffffff + #fff9f9f9 #ff000000 #ffffffff #80000000 @@ -58,7 +58,7 @@ @drawable/editbox_dropdown_background_dark @drawable/editbox_dropdown_background - #ffffffff + #fff9f9f9 #ff0092f4 @@ -73,5 +73,9 @@ #eeeeee #c0c0c0 + + #7fa87f + @android:color/lighter_gray + diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 83ac8e27e146f443a1223cd6ea9745bbf7b2e97c..7215685a3d469e2e8af0675d33b95b3803a6b727 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -34,4 +34,8 @@ 300 + + + false diff --git a/core/res/res/values/donottranslate-cldr.xml b/core/res/res/values/donottranslate-cldr.xml new file mode 100644 index 0000000000000000000000000000000000000000..286cc0e554f293ae71b9a24c13db146e72bd44a9 --- /dev/null +++ b/core/res/res/values/donottranslate-cldr.xml @@ -0,0 +1,147 @@ + + + January + February + March + April + May + June + July + August + September + October + November + December + + January + February + March + April + May + June + July + August + September + October + November + December + + Jan + Feb + Mar + Apr + May + Jun + Jul + Aug + Sep + Oct + Nov + Dec + + J + F + M + A + M + J + J + A + S + O + N + D + + Sunday + Monday + Tuesday + Wednesday + Thursday + Friday + Saturday + + Sun + Mon + Tue + Wed + Thu + Fri + Sat + + Su + Mo + Tu + We + Th + Fr + Sa + + S + M + T + W + T + F + S + + am + pm + Yesterday + Today + Tomorrow + + %H:%M + %-l:%M%p + %-l:%M%^p + h:mm a + HH:mm + %-m/%-e/%Y + M/d/yyyy + "%s/%s/%s" + %B %-e, %Y + %-l:%M:%S %p + %b %-e, %Y, %-l:%M:%S %p + %1$s, %2$s + %1$s, %3$s + %b %-e, %Y + %B %-e + %-B + %B %Y + %b %-e + %-b + %b %Y + %1$s – %2$s + %2$s – %5$s + %2$s/%3$s – %7$s/%8$s + %1$s, %2$s/%3$s – %6$s, %7$s/%8$s + %2$s/%3$s/%4$s – %7$s/%8$s/%9$s + %1$s, %2$s/%3$s/%4$s – %6$s, %7$s/%8$s/%9$s + %1$s, %2$s/%3$s/%4$s, %5$s – %6$s, %7$s/%8$s/%9$s, %10$s + %2$s/%3$s, %5$s – %7$s/%8$s, %10$s + %1$s, %2$s/%3$s, %5$s – %6$s, %7$s/%8$s, %10$s + %2$s/%3$s/%4$s, %5$s – %7$s/%8$s/%9$s, %10$s + %1$s, %2$s, %3$s – %4$s, %5$s, %6$s + %1$s, %2$s – %4$s, %5$s + %2$s, %3$s – %5$s, %6$s + %1$s, %2$s, %3$s + %2$s, %3$s + %1$s, %2$s + %2$s %3$s – %7$s %8$s + %1$s, %2$s %3$s – %6$s, %7$s %8$s + %2$s %3$s, %5$s – %7$s %8$s, %10$s + %2$s %3$s, %5$s – %7$s %8$s, %10$s + %1$s, %2$s %3$s, %5$s – %6$s, %7$s %8$s, %10$s + %1$s, %2$s %3$s, %5$s – %6$s, %7$s %8$s, %10$s + %2$s %3$s, %4$s, %5$s – %7$s %8$s, %9$s, %10$s + %2$s %3$s, %4$s, %5$s – %7$s %8$s, %9$s, %10$s + %1$s, %2$s %3$s, %4$s, %5$s – %6$s, %7$s %8$s, %9$s, %10$s + %1$s, %2$s %3$s, %4$s, %5$s – %6$s, %7$s %8$s, %9$s, %10$s + %1$s, %2$s %3$s, %4$s – %6$s, %7$s %8$s, %9$s + %2$s %3$s – %8$s + %1$s, %2$s %3$s – %6$s, %7$s %8$s + %2$s %3$s – %7$s %8$s, %9$s + %2$s %3$s – %8$s, %9$s + %1$s, %2$s %3$s – %6$s, %7$s %8$s, %9$s + %b + diff --git a/core/res/res/values/donottranslate.xml b/core/res/res/values/donottranslate.xml new file mode 100644 index 0000000000000000000000000000000000000000..6def3bfdd1b000af3bb1c7b20863cb6c57d28a92 --- /dev/null +++ b/core/res/res/values/donottranslate.xml @@ -0,0 +1,23 @@ + + + + + Latin-1 + diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index f90c6b82edf9f4cdebec46e6fb96a24ca75f182a..32c693757dcf1d1fe85abd74d6c489425fb83218 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -1091,22 +1091,73 @@ =============================================================== --> - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 331ef1a0feaa6862bbcc87f1faad43598e9eb42a..9b9ba681713a9a02538c26116b6cdb25f4957d1b 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -30,6 +30,11 @@ TB PB + + %1$s%2$s @@ -148,6 +153,24 @@ PAD + + + Roaming Indicator On + Roaming Indicator Off + Roaming Indicator Flashing + Out of Neighborhood + Out of Building + Roaming - Preferred System + Roaming - Available System + Roaming - Alliance Partner + Roaming - Premium Partner + Roaming - Full Service Functionality + Roaming - Partial Service Functionality + Roaming Banner On + Roaming Banner Off + Searching for Service + + Too many requests are being processed. Try again later. + + The certificate is saved in the system\'s key store. @@ -437,13 +462,6 @@ Allows an application to forcibly restart other applications. - - keep from being stopped - - Allows an application to make - any process run in the foreground, so it can\'t be killed. - Should never be needed for normal applications. - force application to close @@ -459,19 +477,18 @@ a wide variety of private and secure information that they should never normally need. - - publish low-level services - - Allows application to publish - its own low-level system services. Malicious applications may hijack - the system, and steal or corrupt any data on it. - partial shutdown Puts the activity manager into a shutdown state. Does not perform a complete shutdown. + + prevent app switches + + Prevents the user from switching to + another application. + monitor and control all application launching @@ -518,21 +535,17 @@ to control whether activities are always finished as soon as they go to the background. Never needed for normal applications. - - automatically install system updates - - Allows an application to receive - notifications about pending system updates and trigger their - installation. Malicious applications may use this to corrupt the system - with unauthorized updates, or generally interfere with the update - process. - modify battery statistics Allows the modification of collected battery statistics. Not for use by normal applications. + + control system backup and restore + + Allows the application to control the system's backup and restore mechanism. Not for use by normal applications. + display unauthorized windows @@ -763,13 +776,7 @@ Create mock location sources for testing. Malicious applications can use this to override the location and/or status returned by real - location sources such as GPS or Network providers. - - - permission to install a location collector - - Create mock location sources for testing. - Malicious applications can use this to monitor and report your location to an external source. + location sources such as GPS or Network providers or monitor and report your location to an external source. fine (GPS) location @@ -887,7 +894,6 @@ properties uploaded by the checkin service. Not for use by normal applications. - choose widgets @@ -1005,6 +1011,15 @@ to and disconnect from Wi-Fi access points, and to make changes to configured Wi-Fi networks. + + allow Wi-Fi Multicast + reception + + Allows an application to + receive packets not directly addressed to your device. This can be + useful when discovering services offered near by. It uses more power + than the non-multicast mode. + bluetooth administration @@ -1070,7 +1085,7 @@ user dictionary. - write to SD card + modify/delete SD card contents Allows an application to write to the SD card. @@ -1096,6 +1111,9 @@ Custom + + Mobile + @@ -1174,9 +1192,12 @@ Sorry, try again - + Charging (%d%%) + + Charged. @@ -1245,15 +1266,6 @@ Invalid username or password. - - "h:mm AA" - - - "%-l:%M%P" - - - "%-l:%M%p" - "%-l%P" @@ -1262,7 +1274,7 @@ - Clear notifications + Clear @@ -1295,6 +1307,9 @@ less than %d%% remaining. + + Why? Factory test failed @@ -1321,7 +1336,23 @@ Confirm - + + + read Browser\'s history and bookmarks + + Allows the application to read all + the URLs that the Browser has visited, and all of the Browser\'s bookmarks. + + write Browser\'s history and bookmarks + + Allows an application to modify the + Browser\'s history or bookmarks stored on your phone. Malicious applications + can use this to erase or modify your Browser\'s data. + Do you want the browser to remember this password? @@ -1354,12 +1385,6 @@ It is also used by the home screen's search "widget". It should be short --> Search - - Today - - Yesterday - - Tomorrow 1 month ago @@ -1493,21 +1518,6 @@ years - - Sunday - - Monday - - Tuesday - - Wednesday - - Thursday - - Friday - - Saturday - "Every weekday (Mon\u2013Fri)" @@ -1530,95 +1540,12 @@ OK - - "AM" - - - "PM" - - - - "%m/%d/%Y" - - - "%1$s, %2$s, %3$s \u2013 %4$s, %5$s, %6$s" - - - "%1$s, %2$s \u2013 %4$s, %5$s" - - "%2$s, %3$s \u2013 %5$s, %6$s" - - - "%2$s \u2013 %5$s" - - - "%1$s \u2013 %2$s" - - - "%1$s, %2$s, %3$s" - - - "%2$s, %3$s" - - - "%1$s, %3$s" - - - "%1$s, %2$s" "%1$s, %2$s" - - "%1$s, %2$s" - - - MMMM d, yyyy - - - d MMMM, yyyy - - - MMM d, yyyy - - - d MMM, yyyy - - - h:mm a - - - HH:mm "noon" @@ -1629,433 +1556,15 @@ "Midnight" - - "%B %-d" - - - "%B" - - - "%B %-d, %Y" - - - "%B %Y" - - - "%H:%M:%S" - - - "%H:%M:%S %B %-d, %Y" - - - "%2$s %3$s \u2013 %7$s %8$s" - - - "%1$s, %2$s %3$s \u2013 %6$s, %7$s %8$s" - - - "%2$s %3$s \u2013 %7$s %8$s, %9$s" - - - "%1$s, %2$s %3$s \u2013 %6$s, %7$s %8$s, %9$s" - - - "%2$s %3$s, %5$s \u2013 %7$s %8$s, %10$s" - - - "%1$s, %2$s %3$s, %5$s \u2013 %6$s, %7$s %8$s, %10$s" - - - "%2$s %3$s, %4$s, %5$s \u2013 %7$s %8$s, %9$s, %10$s" - - - "%1$s, %2$s %3$s, %4$s, %5$s \u2013 %6$s, %7$s %8$s, %9$s, %10$s" - - - - "%2$s/%3$s \u2013 %7$s/%8$s" - - - "%1$s, %2$s/%3$s \u2013 %6$s, %7$s/%8$s" - - - "%2$s/%3$s/%4$s \u2013 %7$s/%8$s/%9$s" - - - "%1$s, %2$s/%3$s/%4$s \u2013 %6$s, %7$s/%8$s/%9$s" - - - "%2$s/%3$s, %5$s \u2013 %7$s/%8$s, %10$s" - - - "%1$s, %2$s/%3$s, %5$s \u2013 %6$s, %7$s/%8$s, %10$s" - - - "%2$s/%3$s/%4$s, %5$s \u2013 %7$s/%8$s/%9$s, %10$s" - - - "%1$s, %2$s/%3$s/%4$s, %5$s \u2013 %6$s, %7$s/%8$s/%9$s, %10$s" - - - - "%2$s %3$s \u2013 %8$s" - - - "%1$s, %2$s %3$s \u2013 %6$s, %7$s %8$s" - - - "%2$s %3$s \u2013 %8$s, %9$s" - - - "%1$s, %2$s %3$s, %4$s \u2013 %6$s, %7$s %8$s, %9$s" - - - "%2$s %3$s, %5$s \u2013 %7$s %8$s, %10$s" - - - "%1$s, %2$s %3$s, %5$s \u2013 %6$s, %7$s %8$s, %10$s" - - - "%2$s %3$s, %4$s, %5$s \u2013 %7$s %8$s, %9$s, %10$s" - - - "%1$s, %2$s %3$s, %4$s, %5$s \u2013 %6$s, %7$s %8$s, %9$s, %10$s" - - - "%b %-d, %Y" - - "%b %Y" - - "%b %-d" - - "%b" - - Sunday - - Monday - - Tuesday - - Wednesday - - Thursday - - Friday - - - Saturday - - - - Sun - - - Mon - - - Tue - - - Wed - - - Thu - - - Fri - - - Sat - - - - Su - - - Mo - - - Tu - - - We - - - Th - - - Fr - - - Sa - - - - Su - - - M - - - Tu - - - W - - - Th - - - F - - - Sa - - - - S - - - M - - - T - - - W - - - T - - - F - - - S - - - - January - - - February - - - March - - - April - - - May - - - June - - - July - - - August - - - September - - - October - - - November - - - December - - - - Jan - - - Feb - - - Mar - - - Apr - - - May - - - Jun - - - Jul - - - Aug - - - Sep - - - Oct - - - Nov - - - Dec - - - - J - - - F - - - M - - - A - - - M - - - J - - - J - - - A - - - S - - - O - - - N - - - D %1$02d:%2$02d @@ -2153,6 +1662,8 @@ Process %1$s is not responding. Force close + + Report Wait @@ -2278,6 +1789,11 @@ Format + + USB debugging connected + + A computer is connected to your phone. + @@ -2296,15 +1812,15 @@ Preparing SD card - Checking for errors + Checking for errors. Blank SD card - The SD card is blank or using an unsupported filesystem. + SD card blank or has unsupported filesystem. Damaged SD card - The SD card is damaged. You may have to reformat your card. + SD card damaged. You may have to reformat it. SD card unexpectedly removed @@ -2312,11 +1828,11 @@ SD card safe to remove - The SD card can now be safely removed. + You can safely remove SD card. Removed SD card - The SD has been removed. Insert a new SD card to increase your device storage. + SD card removed. Insert a new one. No matching activities found @@ -2366,10 +1882,12 @@ - - - - + + + checked + + not checked + diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml index 8160c9c9298f051110053291ef8dd199e1504e87..7d235ec8d4ef933e3d04f2a32c527cf77943cb67 100644 --- a/core/res/res/values/styles.xml +++ b/core/res/res/values/styles.xml @@ -124,6 +124,12 @@ @anim/shrink_fade_out_from_bottom + + + + + + + + + + + + + diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml index 9567523445e856378bf1e3309e519449328b94a8..be836ebec4e55e3e838ec2d7dfcc611bdb542794 100644 --- a/core/res/res/values/themes.xml +++ b/core/res/res/values/themes.xml @@ -44,12 +44,14 @@ @android:color/secondary_text_light @android:color/tertiary_text_light @android:color/primary_text_dark_disable_only + @android:color/primary_text_light_disable_only @android:color/primary_text_dark_nodisable @android:color/secondary_text_dark_nodisable @android:color/primary_text_light_nodisable @android:color/secondary_text_light_nodisable @android:color/hint_foreground_dark @android:color/hint_foreground_light + @android:color/search_url_text @android:style/TextAppearance.Large @android:style/TextAppearance.Medium @@ -141,7 +143,8 @@ @android:style/Widget.EditText @android:style/Widget.ExpandableListView @android:style/Widget.Gallery - @android:style/Widget.GridView + @android:style/Widget.GestureOverlayView + @android:style/Widget.GridView @android:style/Widget.ImageButton @android:style/Widget.ImageWell @android:style/Widget.ListView @@ -152,6 +155,9 @@ @android:style/Widget.ProgressBar.Small @android:style/Widget.ProgressBar.Small.Title @android:style/Widget.ProgressBar.Large + @android:style/Widget.ProgressBar.Inverse + @android:style/Widget.ProgressBar.Small.Inverse + @android:style/Widget.ProgressBar.Large.Inverse @android:style/Widget.SeekBar @android:style/Widget.RatingBar @android:style/Widget.RatingBar.Indicator @@ -181,6 +187,9 @@ @android:style/Preference.DialogPreference.EditTextPreference @android:style/Preference.RingtonePreference @android:layout/preference_child + + + @android:color/search_widget_corpus_item_background @@ -212,6 +221,7 @@ @android:color/secondary_text_dark @android:color/tertiary_text_dark @android:color/primary_text_light_disable_only + @android:color/primary_text_dark_disable_only @android:color/primary_text_light_nodisable @android:color/secondary_text_light_nodisable @android:color/primary_text_dark_nodisable @@ -224,9 +234,17 @@ @android:drawable/indicator_check_mark_light @android:drawable/indicator_check_mark_dark + @android:style/Widget.GestureOverlayView.White @android:style/Widget.ListView.White @drawable/divider_horizontal_bright @android:style/Widget.TextView.ListSeparator.White + + @android:style/Widget.ProgressBar.Inverse + @android:style/Widget.ProgressBar.Small.Inverse + @android:style/Widget.ProgressBar.Large.Inverse + @android:style/Widget.ProgressBar + @android:style/Widget.ProgressBar.Small + @android:style/Widget.ProgressBar.Large @@ -365,6 +383,12 @@ true @null + + + diff --git a/core/res/res/xml/eri.xml b/core/res/res/xml/eri.xml new file mode 100644 index 0000000000000000000000000000000000000000..cd66f1447a59809c2f65ac3d7f3afcd4571fbb62 --- /dev/null +++ b/core/res/res/xml/eri.xml @@ -0,0 +1,118 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/core/res/res/xml/power_profile.xml b/core/res/res/xml/power_profile.xml new file mode 100644 index 0000000000000000000000000000000000000000..859902e73d9eda4f06e22250d9b934f9320f6a4f --- /dev/null +++ b/core/res/res/xml/power_profile.xml @@ -0,0 +1,40 @@ + + + + + 0 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.2 + 1 + 0.1 + 0.1 + 1 + 1 + + 1 + 0.1 + + diff --git a/data/etc/platform.xml b/data/etc/platform.xml index f80bd6b00cdc4fd0058cc9b9580a2fbe56ff9f73..0bd32767cba98282e67e25d6445af8d4231dd88d 100644 --- a/data/etc/platform.xml +++ b/data/etc/platform.xml @@ -54,7 +54,7 @@ - + @@ -84,6 +84,24 @@ others should have a fairly open environment in which to interact with the system. --> + + + + + + + + + + + + + + + + + + @@ -114,11 +132,14 @@ + + + diff --git a/data/fonts/DroidSansJapanese.ttf b/data/fonts/DroidSansJapanese.ttf index ca7922122ab536ba681800615c3c818a7983b69d..412fa3de05e84bc5cdac712997583dbcedddbe64 100755 Binary files a/data/fonts/DroidSansJapanese.ttf and b/data/fonts/DroidSansJapanese.ttf differ diff --git a/data/sounds/AudioPackage2.mk b/data/sounds/AudioPackage2.mk index f24d05c4f4258df3fbe010928e02af13b8a41a33..aea3f0b3007bd93428c752bacc233f94475f2d00 100644 --- a/data/sounds/AudioPackage2.mk +++ b/data/sounds/AudioPackage2.mk @@ -7,90 +7,91 @@ # that have larger internal flash. # -local_path:= frameworks/base/data/sounds +LOCAL_PATH:= frameworks/base/data/sounds PRODUCT_COPY_FILES += \ - $(local_path)/F1_MissedCall.ogg:system/media/audio/notifications/F1_MissedCall.ogg \ - $(local_path)/F1_New_MMS.ogg:system/media/audio/notifications/F1_New_MMS.ogg \ - $(local_path)/F1_New_SMS.ogg:system/media/audio/notifications/F1_New_SMS.ogg \ - $(local_path)/Alarm_Buzzer.ogg:system/media/audio/alarms/Alarm_Buzzer.ogg \ - $(local_path)/Alarm_Beep_01.ogg:system/media/audio/alarms/Alarm_Beep_01.ogg \ - $(local_path)/Alarm_Beep_02.ogg:system/media/audio/alarms/Alarm_Beep_02.ogg \ - $(local_path)/Alarm_Classic.ogg:system/media/audio/alarms/Alarm_Classic.ogg \ - $(local_path)/Alarm_Beep_03.ogg:system/media/audio/alarms/Alarm_Beep_03.ogg \ - $(local_path)/Alarm_Rooster_02.ogg:system/media/audio/alarms/Alarm_Rooster_02.ogg \ - $(local_path)/Ring_Classic_02.ogg:system/media/audio/ringtones/Ring_Classic_02.ogg \ - $(local_path)/Ring_Digital_02.ogg:system/media/audio/ringtones/Ring_Digital_02.ogg \ - $(local_path)/Ring_Synth_04.ogg:system/media/audio/ringtones/Ring_Synth_04.ogg \ - $(local_path)/Ring_Synth_02.ogg:system/media/audio/ringtones/Ring_Synth_02.ogg \ - $(local_path)/newwavelabs/BeatPlucker.ogg:system/media/audio/ringtones/BeatPlucker.ogg \ - $(local_path)/newwavelabs/BentleyDubs.ogg:system/media/audio/ringtones/BentleyDubs.ogg \ - $(local_path)/newwavelabs/BirdLoop.ogg:system/media/audio/ringtones/BirdLoop.ogg \ - $(local_path)/newwavelabs/CaribbeanIce.ogg:system/media/audio/ringtones/CaribbeanIce.ogg \ - $(local_path)/newwavelabs/CurveBall.ogg:system/media/audio/ringtones/CurveBall.ogg \ - $(local_path)/newwavelabs/EtherShake.ogg:system/media/audio/ringtones/EtherShake.ogg \ - $(local_path)/newwavelabs/FriendlyGhost.ogg:system/media/audio/ringtones/FriendlyGhost.ogg \ - $(local_path)/newwavelabs/GameOverGuitar.ogg:system/media/audio/ringtones/GameOverGuitar.ogg \ - $(local_path)/newwavelabs/Growl.ogg:system/media/audio/ringtones/Growl.ogg \ - $(local_path)/newwavelabs/InsertCoin.ogg:system/media/audio/ringtones/InsertCoin.ogg \ - $(local_path)/newwavelabs/LoopyLounge.ogg:system/media/audio/ringtones/LoopyLounge.ogg \ - $(local_path)/newwavelabs/LoveFlute.ogg:system/media/audio/ringtones/LoveFlute.ogg \ - $(local_path)/newwavelabs/MidEvilJaunt.ogg:system/media/audio/ringtones/MidEvilJaunt.ogg \ - $(local_path)/newwavelabs/MildlyAlarming.ogg:system/media/audio/ringtones/MildlyAlarming.ogg \ - $(local_path)/newwavelabs/NewPlayer.ogg:system/media/audio/ringtones/NewPlayer.ogg \ - $(local_path)/newwavelabs/Noises1.ogg:system/media/audio/ringtones/Noises1.ogg \ - $(local_path)/newwavelabs/Noises2.ogg:system/media/audio/ringtones/Noises2.ogg \ - $(local_path)/newwavelabs/Noises3.ogg:system/media/audio/ringtones/Noises3.ogg \ - $(local_path)/newwavelabs/OrganDub.ogg:system/media/audio/ringtones/OrganDub.ogg \ - $(local_path)/newwavelabs/RomancingTheTone.ogg:system/media/audio/ringtones/RomancingTheTone.ogg \ - $(local_path)/newwavelabs/SitarVsSitar.ogg:system/media/audio/ringtones/SitarVsSitar.ogg \ - $(local_path)/newwavelabs/SpringyJalopy.ogg:system/media/audio/ringtones/SpringyJalopy.ogg \ - $(local_path)/newwavelabs/Terminated.ogg:system/media/audio/ringtones/Terminated.ogg \ - $(local_path)/newwavelabs/TwirlAway.ogg:system/media/audio/ringtones/TwirlAway.ogg \ - $(local_path)/newwavelabs/VeryAlarmed.ogg:system/media/audio/ringtones/VeryAlarmed.ogg \ - $(local_path)/newwavelabs/World.ogg:system/media/audio/ringtones/World.ogg \ - $(local_path)/newwavelabs/CaffeineSnake.ogg:system/media/audio/notifications/CaffeineSnake.ogg \ - $(local_path)/newwavelabs/DearDeer.ogg:system/media/audio/notifications/DearDeer.ogg \ - $(local_path)/newwavelabs/DontPanic.ogg:system/media/audio/notifications/DontPanic.ogg \ - $(local_path)/newwavelabs/Highwire.ogg:system/media/audio/notifications/Highwire.ogg \ - $(local_path)/newwavelabs/KzurbSonar.ogg:system/media/audio/notifications/KzurbSonar.ogg \ - $(local_path)/newwavelabs/OnTheHunt.ogg:system/media/audio/notifications/OnTheHunt.ogg \ - $(local_path)/newwavelabs/Voila.ogg:system/media/audio/notifications/Voila.ogg \ - $(local_path)/notifications/Beat_Box_Android.ogg:system/media/audio/notifications/Beat_Box_Android.ogg \ - $(local_path)/notifications/Heaven.ogg:system/media/audio/notifications/Heaven.ogg \ - $(local_path)/notifications/TaDa.ogg:system/media/audio/notifications/TaDa.ogg \ - $(local_path)/notifications/Tinkerbell.ogg:system/media/audio/notifications/Tinkerbell.ogg \ - $(local_path)/effects/Effect_Tick.ogg:system/media/audio/ui/Effect_Tick.ogg \ - $(local_path)/effects/KeypressStandard.ogg:system/media/audio/ui/KeypressStandard.ogg \ - $(local_path)/effects/KeypressSpacebar.ogg:system/media/audio/ui/KeypressSpacebar.ogg \ - $(local_path)/effects/KeypressDelete.ogg:system/media/audio/ui/KeypressDelete.ogg \ - $(local_path)/effects/KeypressReturn.ogg:system/media/audio/ui/KeypressReturn.ogg \ - $(local_path)/effects/VideoRecord.ogg:system/media/audio/ui/VideoRecord.ogg \ - $(local_path)/effects/camera_click.ogg:system/media/audio/ui/camera_click.ogg \ - $(local_path)/newwavelabs/CrazyDream.ogg:system/media/audio/ringtones/CrazyDream.ogg \ - $(local_path)/newwavelabs/DreamTheme.ogg:system/media/audio/ringtones/DreamTheme.ogg \ - $(local_path)/newwavelabs/Big_Easy.ogg:system/media/audio/ringtones/Big_Easy.ogg \ - $(local_path)/newwavelabs/Bollywood.ogg:system/media/audio/ringtones/Bollywood.ogg \ - $(local_path)/newwavelabs/Cairo.ogg:system/media/audio/ringtones/Cairo.ogg \ - $(local_path)/newwavelabs/Calypso_Steel.ogg:system/media/audio/ringtones/Calypso_Steel.ogg \ - $(local_path)/newwavelabs/Champagne_Edition.ogg:system/media/audio/ringtones/Champagne_Edition.ogg \ - $(local_path)/newwavelabs/Club_Cubano.ogg:system/media/audio/ringtones/Club_Cubano.ogg \ - $(local_path)/newwavelabs/Eastern_Sky.ogg:system/media/audio/ringtones/Eastern_Sky.ogg \ - $(local_path)/newwavelabs/Funk_Yall.ogg:system/media/audio/ringtones/Funk_Yall.ogg \ - $(local_path)/newwavelabs/Savannah.ogg:system/media/audio/ringtones/Savannah.ogg \ - $(local_path)/newwavelabs/Gimme_Mo_Town.ogg:system/media/audio/ringtones/Gimme_Mo_Town.ogg \ - $(local_path)/newwavelabs/Glacial_Groove.ogg:system/media/audio/ringtones/Glacial_Groove.ogg \ - $(local_path)/newwavelabs/Seville.ogg:system/media/audio/ringtones/Seville.ogg \ - $(local_path)/newwavelabs/No_Limits.ogg:system/media/audio/ringtones/No_Limits.ogg \ - $(local_path)/newwavelabs/Revelation.ogg:system/media/audio/ringtones/Revelation.ogg \ - $(local_path)/newwavelabs/Paradise_Island.ogg:system/media/audio/ringtones/Paradise_Island.ogg \ - $(local_path)/newwavelabs/Road_Trip.ogg:system/media/audio/ringtones/Road_Trip.ogg \ - $(local_path)/newwavelabs/Shes_All_That.ogg:system/media/audio/ringtones/Shes_All_That.ogg \ - $(local_path)/newwavelabs/Steppin_Out.ogg:system/media/audio/ringtones/Steppin_Out.ogg \ - $(local_path)/newwavelabs/Third_Eye.ogg:system/media/audio/ringtones/Third_Eye.ogg \ - $(local_path)/newwavelabs/Thunderfoot.ogg:system/media/audio/ringtones/Thunderfoot.ogg \ - $(local_path)/notifications/moonbeam.ogg:system/media/audio/notifications/moonbeam.ogg \ - $(local_path)/notifications/pixiedust.ogg:system/media/audio/notifications/pixiedust.ogg \ - $(local_path)/notifications/pizzicato.ogg:system/media/audio/notifications/pizzicato.ogg \ - $(local_path)/notifications/tweeters.ogg:system/media/audio/notifications/tweeters.ogg + $(LOCAL_PATH)/F1_MissedCall.ogg:system/media/audio/notifications/F1_MissedCall.ogg \ + $(LOCAL_PATH)/F1_New_MMS.ogg:system/media/audio/notifications/F1_New_MMS.ogg \ + $(LOCAL_PATH)/F1_New_SMS.ogg:system/media/audio/notifications/F1_New_SMS.ogg \ + $(LOCAL_PATH)/Alarm_Buzzer.ogg:system/media/audio/alarms/Alarm_Buzzer.ogg \ + $(LOCAL_PATH)/Alarm_Beep_01.ogg:system/media/audio/alarms/Alarm_Beep_01.ogg \ + $(LOCAL_PATH)/Alarm_Beep_02.ogg:system/media/audio/alarms/Alarm_Beep_02.ogg \ + $(LOCAL_PATH)/Alarm_Classic.ogg:system/media/audio/alarms/Alarm_Classic.ogg \ + $(LOCAL_PATH)/Alarm_Beep_03.ogg:system/media/audio/alarms/Alarm_Beep_03.ogg \ + $(LOCAL_PATH)/Alarm_Rooster_02.ogg:system/media/audio/alarms/Alarm_Rooster_02.ogg \ + $(LOCAL_PATH)/Ring_Classic_02.ogg:system/media/audio/ringtones/Ring_Classic_02.ogg \ + $(LOCAL_PATH)/Ring_Digital_02.ogg:system/media/audio/ringtones/Ring_Digital_02.ogg \ + $(LOCAL_PATH)/Ring_Synth_04.ogg:system/media/audio/ringtones/Ring_Synth_04.ogg \ + $(LOCAL_PATH)/Ring_Synth_02.ogg:system/media/audio/ringtones/Ring_Synth_02.ogg \ + $(LOCAL_PATH)/Silence.ogg:system/media/audio/ringtones/Silence.ogg \ + $(LOCAL_PATH)/newwavelabs/BeatPlucker.ogg:system/media/audio/ringtones/BeatPlucker.ogg \ + $(LOCAL_PATH)/newwavelabs/BentleyDubs.ogg:system/media/audio/ringtones/BentleyDubs.ogg \ + $(LOCAL_PATH)/newwavelabs/BirdLoop.ogg:system/media/audio/ringtones/BirdLoop.ogg \ + $(LOCAL_PATH)/newwavelabs/CaribbeanIce.ogg:system/media/audio/ringtones/CaribbeanIce.ogg \ + $(LOCAL_PATH)/newwavelabs/CurveBall.ogg:system/media/audio/ringtones/CurveBall.ogg \ + $(LOCAL_PATH)/newwavelabs/EtherShake.ogg:system/media/audio/ringtones/EtherShake.ogg \ + $(LOCAL_PATH)/newwavelabs/FriendlyGhost.ogg:system/media/audio/ringtones/FriendlyGhost.ogg \ + $(LOCAL_PATH)/newwavelabs/GameOverGuitar.ogg:system/media/audio/ringtones/GameOverGuitar.ogg \ + $(LOCAL_PATH)/newwavelabs/Growl.ogg:system/media/audio/ringtones/Growl.ogg \ + $(LOCAL_PATH)/newwavelabs/InsertCoin.ogg:system/media/audio/ringtones/InsertCoin.ogg \ + $(LOCAL_PATH)/newwavelabs/LoopyLounge.ogg:system/media/audio/ringtones/LoopyLounge.ogg \ + $(LOCAL_PATH)/newwavelabs/LoveFlute.ogg:system/media/audio/ringtones/LoveFlute.ogg \ + $(LOCAL_PATH)/newwavelabs/MidEvilJaunt.ogg:system/media/audio/ringtones/MidEvilJaunt.ogg \ + $(LOCAL_PATH)/newwavelabs/MildlyAlarming.ogg:system/media/audio/ringtones/MildlyAlarming.ogg \ + $(LOCAL_PATH)/newwavelabs/NewPlayer.ogg:system/media/audio/ringtones/NewPlayer.ogg \ + $(LOCAL_PATH)/newwavelabs/Noises1.ogg:system/media/audio/ringtones/Noises1.ogg \ + $(LOCAL_PATH)/newwavelabs/Noises2.ogg:system/media/audio/ringtones/Noises2.ogg \ + $(LOCAL_PATH)/newwavelabs/Noises3.ogg:system/media/audio/ringtones/Noises3.ogg \ + $(LOCAL_PATH)/newwavelabs/OrganDub.ogg:system/media/audio/ringtones/OrganDub.ogg \ + $(LOCAL_PATH)/newwavelabs/RomancingTheTone.ogg:system/media/audio/ringtones/RomancingTheTone.ogg \ + $(LOCAL_PATH)/newwavelabs/SitarVsSitar.ogg:system/media/audio/ringtones/SitarVsSitar.ogg \ + $(LOCAL_PATH)/newwavelabs/SpringyJalopy.ogg:system/media/audio/ringtones/SpringyJalopy.ogg \ + $(LOCAL_PATH)/newwavelabs/Terminated.ogg:system/media/audio/ringtones/Terminated.ogg \ + $(LOCAL_PATH)/newwavelabs/TwirlAway.ogg:system/media/audio/ringtones/TwirlAway.ogg \ + $(LOCAL_PATH)/newwavelabs/VeryAlarmed.ogg:system/media/audio/ringtones/VeryAlarmed.ogg \ + $(LOCAL_PATH)/newwavelabs/World.ogg:system/media/audio/ringtones/World.ogg \ + $(LOCAL_PATH)/newwavelabs/CaffeineSnake.ogg:system/media/audio/notifications/CaffeineSnake.ogg \ + $(LOCAL_PATH)/newwavelabs/DearDeer.ogg:system/media/audio/notifications/DearDeer.ogg \ + $(LOCAL_PATH)/newwavelabs/DontPanic.ogg:system/media/audio/notifications/DontPanic.ogg \ + $(LOCAL_PATH)/newwavelabs/Highwire.ogg:system/media/audio/notifications/Highwire.ogg \ + $(LOCAL_PATH)/newwavelabs/KzurbSonar.ogg:system/media/audio/notifications/KzurbSonar.ogg \ + $(LOCAL_PATH)/newwavelabs/OnTheHunt.ogg:system/media/audio/notifications/OnTheHunt.ogg \ + $(LOCAL_PATH)/newwavelabs/Voila.ogg:system/media/audio/notifications/Voila.ogg \ + $(LOCAL_PATH)/notifications/Beat_Box_Android.ogg:system/media/audio/notifications/Beat_Box_Android.ogg \ + $(LOCAL_PATH)/notifications/Heaven.ogg:system/media/audio/notifications/Heaven.ogg \ + $(LOCAL_PATH)/notifications/TaDa.ogg:system/media/audio/notifications/TaDa.ogg \ + $(LOCAL_PATH)/notifications/Tinkerbell.ogg:system/media/audio/notifications/Tinkerbell.ogg \ + $(LOCAL_PATH)/effects/Effect_Tick.ogg:system/media/audio/ui/Effect_Tick.ogg \ + $(LOCAL_PATH)/effects/KeypressStandard.ogg:system/media/audio/ui/KeypressStandard.ogg \ + $(LOCAL_PATH)/effects/KeypressSpacebar.ogg:system/media/audio/ui/KeypressSpacebar.ogg \ + $(LOCAL_PATH)/effects/KeypressDelete.ogg:system/media/audio/ui/KeypressDelete.ogg \ + $(LOCAL_PATH)/effects/KeypressReturn.ogg:system/media/audio/ui/KeypressReturn.ogg \ + $(LOCAL_PATH)/effects/VideoRecord.ogg:system/media/audio/ui/VideoRecord.ogg \ + $(LOCAL_PATH)/effects/camera_click.ogg:system/media/audio/ui/camera_click.ogg \ + $(LOCAL_PATH)/newwavelabs/CrazyDream.ogg:system/media/audio/ringtones/CrazyDream.ogg \ + $(LOCAL_PATH)/newwavelabs/DreamTheme.ogg:system/media/audio/ringtones/DreamTheme.ogg \ + $(LOCAL_PATH)/newwavelabs/Big_Easy.ogg:system/media/audio/ringtones/Big_Easy.ogg \ + $(LOCAL_PATH)/newwavelabs/Bollywood.ogg:system/media/audio/ringtones/Bollywood.ogg \ + $(LOCAL_PATH)/newwavelabs/Cairo.ogg:system/media/audio/ringtones/Cairo.ogg \ + $(LOCAL_PATH)/newwavelabs/Calypso_Steel.ogg:system/media/audio/ringtones/Calypso_Steel.ogg \ + $(LOCAL_PATH)/newwavelabs/Champagne_Edition.ogg:system/media/audio/ringtones/Champagne_Edition.ogg \ + $(LOCAL_PATH)/newwavelabs/Club_Cubano.ogg:system/media/audio/ringtones/Club_Cubano.ogg \ + $(LOCAL_PATH)/newwavelabs/Eastern_Sky.ogg:system/media/audio/ringtones/Eastern_Sky.ogg \ + $(LOCAL_PATH)/newwavelabs/Funk_Yall.ogg:system/media/audio/ringtones/Funk_Yall.ogg \ + $(LOCAL_PATH)/newwavelabs/Savannah.ogg:system/media/audio/ringtones/Savannah.ogg \ + $(LOCAL_PATH)/newwavelabs/Gimme_Mo_Town.ogg:system/media/audio/ringtones/Gimme_Mo_Town.ogg \ + $(LOCAL_PATH)/newwavelabs/Glacial_Groove.ogg:system/media/audio/ringtones/Glacial_Groove.ogg \ + $(LOCAL_PATH)/newwavelabs/Seville.ogg:system/media/audio/ringtones/Seville.ogg \ + $(LOCAL_PATH)/newwavelabs/No_Limits.ogg:system/media/audio/ringtones/No_Limits.ogg \ + $(LOCAL_PATH)/newwavelabs/Revelation.ogg:system/media/audio/ringtones/Revelation.ogg \ + $(LOCAL_PATH)/newwavelabs/Paradise_Island.ogg:system/media/audio/ringtones/Paradise_Island.ogg \ + $(LOCAL_PATH)/newwavelabs/Road_Trip.ogg:system/media/audio/ringtones/Road_Trip.ogg \ + $(LOCAL_PATH)/newwavelabs/Shes_All_That.ogg:system/media/audio/ringtones/Shes_All_That.ogg \ + $(LOCAL_PATH)/newwavelabs/Steppin_Out.ogg:system/media/audio/ringtones/Steppin_Out.ogg \ + $(LOCAL_PATH)/newwavelabs/Third_Eye.ogg:system/media/audio/ringtones/Third_Eye.ogg \ + $(LOCAL_PATH)/newwavelabs/Thunderfoot.ogg:system/media/audio/ringtones/Thunderfoot.ogg \ + $(LOCAL_PATH)/notifications/moonbeam.ogg:system/media/audio/notifications/moonbeam.ogg \ + $(LOCAL_PATH)/notifications/pixiedust.ogg:system/media/audio/notifications/pixiedust.ogg \ + $(LOCAL_PATH)/notifications/pizzicato.ogg:system/media/audio/notifications/pizzicato.ogg \ + $(LOCAL_PATH)/notifications/tweeters.ogg:system/media/audio/notifications/tweeters.ogg diff --git a/data/sounds/OriginalAudio.mk b/data/sounds/OriginalAudio.mk index 291f0b6b997e15071b6ac5cc460102570d9f045e..8c8fc329c43085f3aaffaea7538ab0a34517353f 100644 --- a/data/sounds/OriginalAudio.mk +++ b/data/sounds/OriginalAudio.mk @@ -22,6 +22,7 @@ PRODUCT_COPY_FILES += \ $(LOCAL_PATH)/Ring_Digital_02.ogg:system/media/audio/ringtones/Ring_Digital_02.ogg \ $(LOCAL_PATH)/Ring_Synth_04.ogg:system/media/audio/ringtones/Ring_Synth_04.ogg \ $(LOCAL_PATH)/Ring_Synth_02.ogg:system/media/audio/ringtones/Ring_Synth_02.ogg \ + $(LOCAL_PATH)/Silence.ogg:system/media/audio/ringtones/Silence.ogg \ $(LOCAL_PATH)/newwavelabs/BeatPlucker.ogg:system/media/audio/ringtones/BeatPlucker.ogg \ $(LOCAL_PATH)/newwavelabs/BentleyDubs.ogg:system/media/audio/ringtones/BentleyDubs.ogg \ $(LOCAL_PATH)/newwavelabs/BirdLoop.ogg:system/media/audio/ringtones/BirdLoop.ogg \ diff --git a/data/sounds/Silence.ogg b/data/sounds/Silence.ogg new file mode 100644 index 0000000000000000000000000000000000000000..6d789071a525fd1a43d947e8cf87ecd3945d35d2 Binary files /dev/null and b/data/sounds/Silence.ogg differ diff --git a/docs/html/guide/appendix/media-formats.jd b/docs/html/guide/appendix/media-formats.jd index a3cc0ff09692f07508bad68b0821cd92267b4f30..db5a15e10126a814cbf5b38b443be935ce1adaa9 100644 --- a/docs/html/guide/appendix/media-formats.jd +++ b/docs/html/guide/appendix/media-formats.jd @@ -2,8 +2,9 @@ page.title=Android Supported Media Formats @jd:body

        The Core Media Formats table below describes the media format support built into the Android platform. Note that any given mobile device may provide support for additional formats or file types not listed in the table.

        -

        For your convenience, the table T-Mobile G1 Media Formats describes additional media format details for the T-Mobile G1 device.

        +

        For your convenience, the table T-Mobile G1 Media Formats describes additional media formats and characteristics provided by the T-Mobile G1 device. Other devices may support additional formats not listed on this page.

        +

        As an application developer, you are free to make use of any media codec that is available on any Android-powered device, including those provided by the Android platform and those that are device-specific.

        Core Media Formats

        @@ -130,7 +131,7 @@ page.title=Android Supported Media Formats X X   -3GPP (.3gp) +3GPP (.3gp) and MPEG-4 (.mp4) @@ -151,9 +152,9 @@ page.title=Android Supported Media Formats -

        T-Mobile G1 Media Formats

        +

        T-Mobile G1 Media Formats and Characteristics

        -

        In addition to the core media formats supported in the Android platform, the T-Mobile G1 also supports the formats listed below.

        +

        The table below lists media formats supported by the T-Mobile G1 in addition to those provided as part of the Android platform. This table also details G1-specific performance characteristics of some Android core media formats.

        @@ -163,7 +164,7 @@ page.title=Android Supported Media Formats - + @@ -178,13 +179,13 @@ page.title=Android Supported Media Formats
      4. L2: <=161 kbps <=48 kHz
      5. L3: <385 kbps <=48 kHz
      6. -Mono and stereo profiles with 16-bits per sample. Decoder does not support WMA Pro, Lossless, or Speech codecs. +Mono and stereo profiles with 16-bits per sample. Decoder does not support WMA Pro, Lossless, or Speech codecs. - + @@ -192,23 +193,17 @@ Mono and stereo profiles with 16-bits per sample. Decoder does not support WMA P - - - - - - - - - +
        Format Encoder DecoderDetailsComment File Type(s) Supported
        Windows Media Audio (.wma)
        VideoVideo WMV   XWindows Media Video (.wmv)
        H.263XX 3GPP (.3gp) and MPEG-4 (.mp4)
        H.264 AVC   XLimited to baseline profile up to 480x320, and 600 kbps average bitrate for the video stream.On the G1, this decoder is limited to baseline profile up to 480x320, and 600 kbps average bitrate for the video stream. 3GPP (.3gp) and MPEG-4 (.mp4)
        -

        Note that Windows Media codecs are not part of the Android platform and require special licensing from Microsoft or an authorized developer such as Packet Video.

        + + + diff --git a/docs/html/guide/developing/eclipse-adt.jd b/docs/html/guide/developing/eclipse-adt.jd index 75f3d78f48097a96349ea4987a4d6fa228797124..3b3bb38a476d5b84c00655b853985854beab3fcb 100644 --- a/docs/html/guide/developing/eclipse-adt.jd +++ b/docs/html/guide/developing/eclipse-adt.jd @@ -38,15 +38,15 @@ manifest and resource files.

        To begin developing Android applications in the Eclipse IDE with ADT, you first need to download the Eclipse IDE and then download and install the ADT plugin. To do so, follow the -steps given in Installing +steps given in Installing the ADT Plugin.

        If you are already developing applications using a version of ADT earlier than 0.9, make sure to upgrade to the latest version before continuing. See the guide to -Update Your Eclipse ADT Plugin.

        +Updating Your Eclipse ADT Plugin.

        Note: This guide assumes you are using the latest version of -the ADT plugin (0.9). While most of the information covered also applies to previous +the ADT plugin. While most of the information covered also applies to previous versions, if you are using an older version, you may want to consult this document from the set of documentation included in your SDK package (instead of the online version).

        @@ -138,9 +138,9 @@ folders and files in your new project:

        Wait! Before you can run your application on the Android Emulator, you must create an Android Virtual Device (AVD). An AVD is a configuration that specifies the Android platform to be used on the emulator. -You can read more about AVDs in the Developing -Overview, but if you just want to get started, follow the simple guide below to create -an AVD.

        +You can read more in the Android Virtual +Devices document, but if you just want to get started, follow the simple guide below to +create an AVD.

        If you will be running your applications only on actual device hardware, you do not need an AVD — see diff --git a/docs/html/guide/guide_toc.cs b/docs/html/guide/guide_toc.cs index a044ceaecd3b9d8fbefe4359ed9d7e69e4ee4c8b..da4a2c33c5cfeb7cab75e27cc4060b24114545e9 100644 --- a/docs/html/guide/guide_toc.cs +++ b/docs/html/guide/guide_toc.cs @@ -146,7 +146,9 @@

      7. Designing for Performance
      8. diff --git a/docs/html/guide/practices/ui_guidelines/activity_task_design.jd b/docs/html/guide/practices/ui_guidelines/activity_task_design.jd new file mode 100644 index 0000000000000000000000000000000000000000..e2fc89c6ddba27a4f8fbc8668b969797324be266 --- /dev/null +++ b/docs/html/guide/practices/ui_guidelines/activity_task_design.jd @@ -0,0 +1,1238 @@ +page.title=Activity and Task Design Guidelines +@jd:body + +
        +
        + +

        Activity and task design quickview

        + +
          +
        • Activities are the main building blocks of Android applications.
        • +
        • In addition to writing your own activities, you are free to re-use activities from many other applications through intents.
        • +
        • You can enable activities in your application to be started from intents in other applications.
        • +
        • In nearly all cases, the activity stack just works as expected.
        • + In a couple of cases you might need to ensure the right thing happens by setting a string or flag. +
        + +

        In this document

        + +
          +
        1. Applications, Activities, Activity Stack and Tasks +
        2. +
        3. A Tour of Activities and Tasks +
            +
          1. Starting an Activity from Home
          2. +
          3. Navigating Away from an Activity
          4. +
          5. Re-using an Activity
          6. +
          7. Replacing an Activity
          8. +
          9. Multitasking
          10. +
          11. Launching from Two Entry Points
          12. +
          13. Intents
          14. +
          15. Switching Between Tasks
          16. +
          +
        4. +
        5. Design Tips +
            +
          1. Don't specify intent filters in an activity that won't be re-used
          2. + +
          3. Handle case where no activity matches
          4. +
          5. Consider how to launch your activities
          6. +
          7. Allow activities to add to current task
          8. +
          9. Notifications should let user easily get back
          10. +
          11. Use the notification system
          12. +
          13. Don't take over BACK key unless you absolutely need to
          14. +
          +
        6. +
        + +

        See also

        + +
          +
        1. Application Fundamentals
        2. +
        + +
        +
        + +

        + This document describes core principles of the Android application + framework, from a high-level, user-centric perspective useful to + interaction and application designers as well as application + developers. +

        + +

        + It illustrates activities and tasks with examples, and describes some + of their underlying principles and mechanisms, such as navigation, + multitasking, activity re-use, intents, and the activity stack. + The document also highlights design decisions that are available to you + and what control they give you over the UI of your application. +

        + +

        + This document draws examples from several Android applications, + including default applications (such as Dialer) and Google + applications (such as Maps). You can try out the examples yourself in + the Android emulator or on an Android-powered device. If you are using + a device, note that your device may not offer all of the example + applications used in this document. +

        + +

        + Be sure to look at the Design Tips section + for guidelines, tips, and things to avoid. This document is a + complement to Application Fundamentals, + which covers the underlying mechanics for programmers. +

        + +

        Applications, Activities, Activity Stack and Tasks

        + +

        + Four fundamental concepts in the Android system that are helpful for you to understand are: +

        + +
          +
        • Applications +
        • Activities +
        • Activity Stack +
        • Tasks +
        + +

        Applications

        + +

        + An Android application typically consists of one or more + related, loosely bound activities for the user to interact with, typically bundled up + in a single file (with an .apk suffix). Android ships with a rich set + of applications that may include email, calendar, browser, maps, text + messaging, contacts, camera, dialer, music player, settings and + others. +

        + +

        + Android has an application launcher available at the Home screen, + typically in a sliding drawer which displays applications as icons, + which the user can pick to start an application. +

        + + +

        Activities

        + +

        + Activities are the main building blocks of Android + applications. When you create an application, you can assemble it from + activities that you create and from activities you re-use from other + applications. These activities are bound at runtime, so that newly + installed applications can take advantage of already installed + activities. Once assembled, activities work together to form a + cohesive user interface. An activity has a distinct visual user + interface designed around a single, well-bounded purpose, such as + viewing, editing, dialing the phone, taking a photo, searching, + sending data, starting a voice command, or performing some other type + of user action. Any application that presents anything on the display + must have at least one activity responsible for that display. +

        + +

        + When using an Android device, as the user moves through the user + interface they start activities one after the other, totally oblivious + to the underlying behavior — to them the experience should be + seamless, activity after activity, task after + task. +

        + +

        + An activity handles a particular type of content (data) and accepts a + set of related user actions. In general, each activity has a + lifecycle that is independent of the other + activities in its application or task — each activity is + launched (started) independently, and the user or system can start, + run, pause, resume, stop and restart it as needed. Because of this + independence, activities can be re-used and replaced by other + activities in a variety of ways. +

        + +

        + The Dialer application is an example of an application that consists + basically of four activities: dialer, contacts list, view contact, and + new contact, as shown in the following screenshots: +

        + + + + + + + + + + +
        + +
        + Dialer +
        +
        + +
        + Contacts +
        +
        + +
        + View Contact +
        +
        + +
        + New Contact +
        +
        + +

        + Here are other examples of applications and the activities they might contain: +

        + +
          +
        • + Email - activities to view folders, view list of messages, + view a message, compose a message, and set up an account +
        • +
        • + Calendar - activities to view day, view week, view month, view + agenda, edit an event, edit preferences, and view an alert +
        • +
        • + Camera - activities for running the camera, viewing the list + of pictures, viewing a picture, cropping a picture, running + the camcorder, viewing the list of movies, and viewing a movie +
        • +
        • + Game - one activity to play the game, typically another for setup +
        • +
        • + Maps - one activity to view a location on a map, a second for lists + (such as turn list or friend list), and a third for details + (friend location, status, photo) +
        • +
        + + +

        + An activity is the most prominent of four components of an + application. The other components are service, content provider and + broadcast receiver. For more details on activities, see Activity in + Application Components. +

        + + +

        Activity Stack

        + +

        + As the user moves from activity to activity, across applications, the + Android system keeps a linear navigation history of activities the + user has visited. This is the activity stack, also known as the + back stack. In general, when a user starts a new activity, it is added + to the activity stack, so that pressing BACK displays the previous + activity on the stack. However, the user cannot use the BACK key to go + back further than the last visit to Home. The adding of an activity to + the current stack happens whether or not that activity begins a new + task (as long as that task was started + without going Home), so going back can let the user go back to + activities in previous tasks. The user can get to tasks earlier than + the most recent Home by selecting its root activity from the + application launcher, a shortcut, or the "Recent tasks" screen. +

        + +

        + Activities are the only things that can be added to the activity stack + — views, windows, menus, and dialogs cannot. That is, when + designing the navigation, if you have screen A and you want the user + to be able go to a subsequent screen B and then use the BACK key to go + back to screen A, then the screen A needs to be implemented as an + activity. The one exception to this rule is if your application + and manages the navigation itself. +

        + + + +

        Tasks

        + +

        + A task is the sequence of activities the user follows to + accomplish an objective, regardless of which applications the + activities belong to. Until a new task is explicitly specified (see + "Interrupting the Task"), all activities the user starts are + considered to be part of the current task. It's notable that these + activities can be in any application — that is, all in the same + application or in different ones. That is, a task that starts out in + contacts can continue, by choosing an email address, to an email + activity and then, by attaching a file, to a picture gallery to pick + from. Contacts, email and picture gallery are all separate + applications. +

        + +

        + The activity that starts a task is called the root activity. + It is often, but not necessarily, started from the application + launcher, Home screen shortcut or "Recent tasks" switcher (a long + press on Home on some devices). The user can return to a task by + choosing the icon for its root activity the same way they started the + task. Once inside a task, the BACK key goes to previous activities in + that task. The activity stack is made up of one or more tasks. +

        + +

        + Here are some examples of tasks: +

        + +
          +
        • + Send a text message with an attachment +
        • +
        • + View a YouTube video and share it by email with someone else +
        • +
        + +

        + Interrupting the Task - An important property of a task is that + the user can interrupt what they're doing (their task) to perform a + different task, then are able to return to where they left off to + complete the original task. The idea is that users can run multiple + tasks simultaneously and switch between them. There are two primary + ways to jump off to that other task — in both cases the user + should be able to return to where they were before the interruption: +

        + + +
          +
        • + User is interrupted by a notification – a notification appears and the user wants to act on it +
        • +
        • + User deciding to perform another task – user just presses Home and starts an application +
        • +
        + +

        + Of course, there are exceptions to the rules. Beyond the two ways just + mentioned, there is a third way to start a task, and that is by + starting an activity that defines itself as a new task. Maps and + Browser are two applications that do this. For example, choosing an + address in an email starts the Maps activity as a new task, and + choosing a link in an email starts the Browser activity as a new + task. In these cases, the BACK key will return to the previous + activity in a different task (Email), because it was not started from + Home. +

        + + +

        A Tour of Activities and Tasks

        + +

        + The following examples illustrate basic principles for applications, + activities, the activity stack, the BACK key, tasks and intents. It + shows how the system responds to user actions such as starting + activities and switching between tasks. With most of these examples + you can follow along, launching activities on your device as + indicated. +

        + + +

        Starting an Activity from Home

        + +

        + Home is the starting place for most applications. (Some applications + can be launched only from other applications.) When the user touches + an icon in the application launcher (or a shortcut on the Home + screen), the main activity for that application is launched into the + foreground where it has user focus. As shown in the following figure, + the user action of going Home and touching the Email icon launches the + List Messages activity of the Email application. The Home activity + remains stopped in the background, ready to restart when called on by + the user. +

        + +

        + +

        + + + +

        + An activity can keep or lose its state depending on how the user + leaves the activity — by the HOME or BACK key. +

        + +

        + By default, pressing the BACK key finishes (destroys) the current + activity and displays the previous activity to the user. In the + following figure, the user starts email by touching the Email icon in + the Home screen, which displays a list of email messages. The user + scrolls down the list (changing its initial state). Pressing BACK + destroys the List Messages activity and returns to the previous + activity, which is Home. If the user re-launches Email, it would + re-load the messages and display its initial, non-scrolled state. +

        + +

        + +

        + +

        + In the above example, pressing BACK goes to Home because it was the + last activity the user was viewing. But if the user had gotten to List + Message from some other activity, then pressing BACK would have + returned there. +

        + +

        + By contrast, the next figure shows the user leaving List Messages by + pressing HOME instead of BACK — the List Messages activity is + stopped and moved to the background rather than being + destroyed. Starting Email again from its icon would simply bring the + List Messages activity to the foreground (changing it from stopped to + running) in the same scrolled state the user last left it. +

        + +

        + +

        + +

        + Exceptions. Some background activities return to their initial + screen (they lose any state, such as scrolling) when they are brought + to the foreground. This is true for Contacts and Gallery. If the user + chooses Home > Contacts then chooses a contact, they are viewing + the details of a contact. If they start again by choosing Home > + Contacts, they are presented with the initial list of contacts rather + than the contact they were last viewing. Contacts is designed this way + because this initial screen is the main entry point for the + application with four tabs for accessing the full range of features. +

        + +

        + In addition, not all activities have the behavior that they are + destroyed when BACK is pressed. When the user starts playing music in + the Music application and then presses BACK, the application overrides + the normal back behavior, preventing the player activity from being + destroyed, and continues playing music, even though its activity is no + longer visible — as a visual substitute, the Music application + places a notification in the status bar so the user still has an easy + way to get to the application to stop or control the music. Note that + you can write an activity to stop when its screen is no longer + visible, or to continue running in the background — the latter + was chosen for the music player. +

        + + +

        Re-using an Activity

        + +

        + When activity A starts activity B in a different application, activity + B is said to be re-used. This use case normally takes place + because activity A is lacking a capability and can find it in activity B. +

        + +

        + Contacts Re-Uses Gallery to Get a Picture - The Contacts + activity has a field for a picture of a contact, but the Gallery is + normally where pictures are kept. So Contacts can re-use the Gallery + activity to get a picture. This is a good example of re-use of the + Gallery activity. The following figure illustrates the sequence of + activities to do this (up to crop). This is how it's done: The user + chooses Contacts, selects the contact for viewing, chooses MENU > + Edit contact and touches the picture field, which launches the Gallery + activity. The user then chooses the picture they want, crops and saves + it. Saving it causes the picture to be inserted into the picture field + in the contact. +

        + +

        + Notice the Gallery returns a picture to the Contacts application that + started it. The next example illustrates re-use of an activity that + does not return a result. Also notice that the following figure is + illustrates the navigation history through the activities, or the + activity stack — the user can back up through each activity all + the way to Home. +

        + +

        + When designing an application, it's good to think about how it can + re-use activities in other applications, and how your activities might + be re-used by other applications. If you add an activity with the same + intent filter as an + exisiting activity, then the system presents the user with a choice + between the activities. +

        + +

        + +

        + +

        + Gallery Re-Uses Messaging for Sharing a Picture - Sharing is + another good example of one application re-using an activity from a + different application. As shown in the following figure, the user + starts Gallery, picks a picture to view, chooses MENU > Share, and + picks "Messaging". This starts the Messaging activity, creates a new + message and attaches the original picture to it. The user then fills + in the "To" field, writes a short message and sends it. User focus + remains in the Messaging program. If the user wants to go back to the + Gallery, they must press the BACK key. (The user can back up through + each activity all the way to Home.) +

        + +

        + In contrast to the previous example, this re-use of the Messaging + activity does not return anything to the Gallery activity that started it. +

        + +

        + +

        + +

        + Both of these examples illustrate tasks — a sequence of + activities that accomplish an objective. Each case uses activities + from two different applications to get the job done. +

        + + +

        Replacing an Activity

        + +

        + This is the use case where activity A replaces activity B in a + different application. This situation normally happens because + activity A is better at doing the job than activity B. In other words, + A and B are equivalent enough that A can replace B. This case stands + in contrast with re-using an activity, where A and B are quite + different activities and supplement each other. +

        + +

        + In this example, the user has downloaded a replacement for the Phone + Ringtone activity, called Rings Extended. Now when they go to + Settings, Sound & Display, Phone Ringtone, the system presents + them with a choice between the Android System's ringtone activity and + the new one. This dialog box has an option to remember their choice + "Use by default for this action". When they choose "Rings Extended", + that activity loads, replacing the original Android ringtone activity. +

        + +

        + +

        + +

        Multitasking

        + +

        + As previously noted, when an activity has been launched, the user can + go to Home and launch a second activity without destroying the first + activity. This scenario demonstrates launching the Maps application. +

        + +
          +
        • + State 1 - The user launches the View Map activity and searches + for a map location. Let's say the network is slow, so the map is + taking an unusually long taking time to draw. +
        • +
        +
          +
        • + State 2 - The user wants to do something else while they're + waiting, so they press HOME, which does not interrupt the map's + network connection and allows the map to continue loading in the + background. + +

          + Note that when you write an activity, you can make it stop or + continue running when it is moved to the background (see + onStop() in Activity Lifecycle). + For activities that download data from the network, it's recommended + to let them continue downloading so the user can multi-task. +

          + +
        • + +
        • + State 3 - The map activity is now running in the background, + with Home in the foreground. The user then launches the Calendar + activity, which launches into the foreground, taking user focus, + where they view today's calendar (as indicated by the heavy + outline). +
        • +
        + +

        + +

        +
          +
        • + State 4 - The user presses Home, then Maps to return to the map, which by now has fully loaded. +
        • +
        + +

        + +

        + +

        + The application launcher at Home has launched "View Map" and "Day + View" activities into separate tasks, hence the system is + multitasking — running multiple tasks. +

        + + +

        Launching from Two Entry Points

        + +

        + Every application must have at least one entry point — a way + for the user or system to access activities inside the + application. Each icon in the application launcher at home + represents an entry point. Applications can also from another + application. Each activity is a potential entry point into the + application.  +

        + +

        + The phone application has two entry points: Contacts and Dialer. A + user entering from Contacts can choose a phone number to launch the + Dialer. As shown in the following figure, a user could choose the + Contacts icon to launch the Contacts activity, then pick a phone + number to launch the Dialer activity and dial the phone. +

        + +

        + Once the user is inside the application, they can access other + activities, such as New Contact and Edit Contact, through tabs, menu + items, list items, onscreen buttons, or other user interface + controls. +

        + +

        + +

        + +

        Intents

        + +

        + When the user takes an action on some data, such as touching a + mailto:info@example.com link, they are actually initiating an Intent + object, or just an intent, which then gets resolved to a + particular component (we consider only activity components here). + So, the result of a user touching a mailto: link is an Intent object + that the system tries to match to an activity. If that Intent object was + written explicitly naming an activity (an explicit intent), + then the system immediately launches that activity in response to the user + action. However, if that Intent object was written without naming an + activity (an implicit intent), the system compares the Intent + object to the intent filters of available activities. If more + than one activity can handle the action and data, the system + displays an activity chooser for the user to choose from. +

        + +

        + This example of touching the mailto: link is shown in the following + figure. If the device has two email applications set up, when a user + touches a mailto: email address on a web page, the result is an + Intent object which displays a dialog box with a choice between the + two activities to compose an email (Gmail and Email). +

        + +

        + +

        + +

        + Here are some examples of Intent objects and the activities they resolve to: +

        + +
          +
        • + View the list of contacts - resolves to a contact list viewer activity +
        • + +
        • + View a particular contact - resolves to a contact viewer activity +
        • + +
        • + Edit a particular contact - resolves to a contact editor activity +
        • + +
        • + Send to a particular email - resolves to an email activity +
        • + +
        • + Dial a phone number - resolves to a phone dialer activity +
        • + +
        • + View the list of images - resolves to an image list viewer activity +
        • + +
        • + View a particular image - resolves to an image viewer activity +
        • + +
        • + Crop a particular image - resolves to an image cropper activity +
        • +
        + +

        + Notice that an Intent object specifies two things, an action and data: +

        + +
          +
        • + A generic action to be performed. In these examples: view, edit, dial or crop +
        • + +
        • + The specific data to be acted on. In these examples: the list of contacts, a particular contact, a phone number, the list of images, or a particular image +
        • +
        + +

        + Note that any user action to start an activity from the + application launcher at Home is an explicit intent to a specific + activity. Likewise, some activities launch private activities + within their application as explicit intents so no other activity + can access them. +

        + +

        + For more on intents, see {@link android.content.Intent Intent class} and + intent filters. +

        + + +

        Switching Between Tasks

        + +

        + This scenario shows how the user can switch between two tasks. In + this example, the user writes a text message, attaches a picture, + but before they are done they glance at their calendar. They then + return to where they left off, attaching the picture and sending the + message. +

        + +
          +
        1. + Start first task. You want to send a text message and attach a photo. You would choose: + +

          + Home > Messaging > New message > MENU > Attach + > Pictures. This last step launches the picture gallery + for picking a photo. Notice that picture gallery is an + activity in a separate application. +

          + + + + + + + + + + +
          + + + + + +
          + +

          + At this point, before you have picked a picture, you decide + to stop and glance at your calendar, which is a separate + task. Because the current activity has no button to go + directly to the Calendar, you need to start from Home. +

          + +
        2. +
        3. + Start second task. You choose Home > Calendar to + look at a calendar event. Calendar launches from Home as a new + task because the application launcher creates a new task for + each application it launches. + +

          + +

          +
        4. + +
        5. + Switch to first task and complete it. When done looking + at the Calendar, you can return to attaching the picture by + starting the root activity again for that task: choose Home + > Messaging, which takes you not to Messaging, but directly + to the Picture gallery, where you left off. You can then pick + a photo, which is added to the message, you send the message + and you're done with the first task. + + + + + + + + + + + + + +
          + + + + +         +
          +
        6. +
        + + +

        Design Tips

        + +

        + The following are tips and guidelines for application designers and developers. +

        + +

        When writing an activity that won't be re-used, don't specify intent filters — use explicit intents

        + +

        + If you're writing an activity that you don't want other activities + to use, be sure not to add any intent filters to that activity. This + applies to an activity that will be launched only from the + application launcher or from other activities inside your + application. Instead, just create intents specifying the explicit + component to launch — that is, explicit intents. In this case, + there's just no need for intent filters. Intent filters are + published to all other applications, so if you make an intent + filter, what you're doing is publishing access to your activity, + which means you can cause unintentional security holes. +

        + + + +

        When reusing an activity owned by others, handle the case where no activity matches

        + +

        + Your applications can re-use activities made available from other + applications. In doing so, you cannot presume your intent will always + be resolved to a matching external activity — you must handle the case + where no application installed on the device can handle the intent. +

        + +

        + You can either test that an activity matches the intent, which you can do + before starting the activity, or catch an exception if starting the + activity fails. Both approaches are descibed in the blog posting + Can + I use this Intent?. +

        + +

        + To test whether an intent can be resolved, your code can query the package manager. + The blog post provides an example in the isIntentAvailable() helper method. + You can perform this test when initializing the user interface. + For instance, you could disable the user control that initiates + the Intent object, or display a message to the user that lets them go + to a location, such as the Market, to download its application. + In this way, your code can start the activity (using either startActivity() + or startActivityForResult()) only if the intent has tested to resolve + to an activity that is actually present. +

        + +

        Consider how you want your activities to be launched or used by other applications

        + +

        + As a designer or developer, it's up to you to determine how users + start your application and the activities in it. As an application + is a set of activities, the user can start these activities from + Home or from another application. +

        + +
          +
        • + Launch your main activity from an icon at Home - If + your application can run standalone, it should probably be + started by the user touching an icon in application + launcher (typically implemented as a sliding drawer on the + Home screen), or from a shortcut icon on the Home screen, or + from the task switcher. (The mechanism for this is for the + activity to have an + intent filter with action MAIN and + category LAUNCHER.) +
        • +
        + +
          +
        • + Launch your activity from within another application - + Perhaps your activities are meant for re-use. For example, + many applications have data they want to share with other + users. Activities that can share data with other users include + email, text messaging and uploading to a public website.

          + If one or more of your activities can be an alternative to an + existing activity in another application, you can make it + available to users at the point they request that + activity. For example, if your activity can send data to + others (such as by email, text messaging, or uploading), + consider setting up that activity to appear as a choice to the + user. To give a specific example, Gallery enables a user to + view and share pictures. When the user chooses "Share" from + the menus, the system compares the "Share" request (an Intent + object) to available activities (by looking at their intent + filters) and displays choices to share. In this case, it + matches Email, Gmail, Messaging and Picassa. If your activity + can send a picture or upload it to a website, all it needs to + do is make itself available for sharing (by setting its intent + filter). +

          +

          + Another activity can start your activity either with or without expecting a result back.  +

          +
        • + +
            +
          • + Start an activity expecting a result - This approach + is closed loop, where the activity being started must either + return a valid result or be canceled. In the previous + examples of sharing a photo from a Gallery, the user ends up + back in the Gallery after completing the send or upload + procedure. These are examples of starting an activity + external to the Gallery. (Such an activity is started with + {@link + android.app.Activity#startActivityForResult(android.content.Intent, + int) startActivityForResult()}.) +
          • + +
          • + Start an activity not expecting a result - This + approach is open-ended. An example is choosing an house + address in an email message (or web page), where the Maps + activity is started to map the location. No result from maps + is expected to be returned to the email message; the user + can return by pressing the BACK key. (Such an activity is + started with {@link + android.content.Context#startActivity(android.content.Intent) + startActivity()}.) +
          • +
          + +
        • + Launch your activity only from within another + application - The previous cases of sharing by way of + Email, Gmail, Messaging and Picassa (from within Gallery) are + all activities that can also be started from icons in the + application launcher at Home. In contrast, the activities for + cropping a picture and attaching a file cannot be started from + Home, because they do not stand alone and require a + context.  +
        • + +

          + In fact, not all applications have icons and can be started from + Home. Take for example a small app that is infrequently used and + replaces existing functionality, that already has a natural entry + point inside an existing application. For example, an Android phone + typically has a built-in ringtone picker that can be selected from + the sound settings of the Settings application. A custom ringtone + picker application that you write could be launched by an intent + identical to the built-in ringtone picker. At the point where the + user chooses "Phone ringtone", they are presented with a dialog + letting them choose between "Android System" and your ringtone + picker (and letting them save their choice) as shown in the + following figure. A ringtone is something you set infrequently, and + already has a well-defined starting point, so probably does not need + an application icon at Home. +

          + +

          + +

          + +
        • + Launch two or more main activities within a single + application from separate icon at Home - As we have + defined it, all the code in a single .apk file is considered + to be one application. You can write an application + that contains two main activities launchable from Home. +
        • + +

          + The Camera.apk application is a good example of an application that + contains two independent main activities — Camera and + Camcorder — that each have their own icons in application + launcher, that can be launched separately, and so appear to the user + as separate applications. They both share use of the same lens, and + both store their images (still and moving) in the Gallery.  +

          + +

          + In order for your application to contain two different, independent + activities launchable from Home, you must define them to be + associated with different tasks. (This means setting the main + activity for each task to a different task affinity — in this case, + "com.android.camera" and "com.android.videocamera".) +

          + +

          + Contacts and Dialer are another example of two main activities + launchable from Home that reside in the same application. +

          + +
        • + Making your application available as a widget - An + application can also display a portion of itself as an app widget, embedded in Home or another + application, and receive periodic updates. +
        • + +
        + + +

        Allow your activities to be added to the current task

        + +

        + If your activities can be started from another application, allow + them to be added to the current task + (or an existing task it has an affinity with). Having activities + added to a task enables the user to switch between a task that + contains your activities and other tasks. Exceptions are + your activities that have only one instance.  +

        + +

        + For this behavior, your activity should have a launch + mode of standard or singleTop rather than singleTask or + singleInstance. These modes also enable multiple instances of your + activity to be run. +

        + + +

        Notifications should let the user easily get back to the previous activity

        +

        + Applications that are in the background or not running can have + services that send out notifications to the user letting them know about + events of interest. Two examples are Calendar, which can send out notifications of + upcoming events, and Email, which can send out notifications when new + messages arrive. One of the user interface guidelines is that when the + user is in activity A, gets a notification for activity B and + picks that notification, when they press the BACK key, they should + go back to activity A.  +

        + +

        + The following scenario shows how the activity stack should work + when the user responds to a notification. +

        + +
          +
        1. + User is creating a new event in Calendar. They realize they + need to copy part of an email message into this event +
        2. +
        3. + The user chooses Home > Gmail +
        4. +
        5. + While in Gmail, they receive a notification from Calendar for an upcoming meeting +
        6. +
        7. + So they choose that notification, which takes them to a + dedicated Calendar activity that displays brief details of the + upcoming meeting +
        8. +
        9. + The user chooses this short notice to view further details +
        10. +
        11. + When done viewing the event, the user presses the BACK + key. They should be taken to Gmail, which is where they were + when they took the notification +
        12. +
        + +

        +This behavior doesn't necessarily happen by default. +

        + +

        +Notifications generally happen primarily in one of two ways: +

        + +
          +
        • + The chosen activity is dedicated for notification only - + For example, when the user receives a + Calendar notification, choosing that + notification starts a special activity that displays a list + of upcoming calendar events — this view is available only + from the notification, not through the Calendar's own user + interface. After viewing this upcoming event, to ensure that + the user pressing the BACK key will return to the activity + the user was in when they picked the notification, you would + make sure this dedicated activity does not have the same + task affinity as the Calendar or any other activity. (You do + this by setting task affinity to the empty string, which + means it has no affinity to anything.) The explanation for + this follows. + +

          + Because of the way tasks work, if the taskAffinity of the + dedicated activity is kept as its default, then pressing the + BACK key (in step 6, above) would go to Calendar, rather + than Gmail. The reason is that, by default, all activities + in a given application have the same task + affinity. Therefore, the task affinity of the dedicated + activity matches the Calendar task, which is already running + in step 1. This means in step 4, choosing the notification + brings the existing Calendar event (in step 1) forward and + starts the dedicated activity on top of it. This is not + what you want to have happen. Setting the dedicated + activity's taskAffinity to empty string fixes this. +

          +
        • + +
        • + The chosen activity is not dedicated, but always comes to + the foreground in its initial state - For example, in + response to a notification, when the Gmail application comes + to the foreground, it always presents the list of conversations. + You can ensure this happens by setting a "clear top" flag in the + intent that the notification triggers. This ensures that when the + activity is launched, it displays its initial activity, preventing + Gmail from coming to the foreground in whatever state the user last + happened to be viewing it. (To do this, you put {@link + android.content.Intent#FLAG_ACTIVITY_CLEAR_TOP + FLAG_ACTIVITY_CLEAR_TOP} in the intent you pass to startActivity()). +
        • +
        + +

        + There are other ways to handle notifications, such as bringing the + activity to the foreground, set to display specific data, such as + displaying the text message thread for the person who just sent a + new text message. +

        + +

        + A notification always starts an activity as a new task (that is, it + puts FLAG_ACTIVITY_NEW_TASK in the intent it + passes to startActivity()). This is done because interruptions to a + task should not become part of that task. +

        + +

        Use the notification system — don't use dialog boxes in place of notifications

        + +

        + If your background service needs to notify a user, use the standard + notification system — don't use a dialog or toast to notify + them. A dialog or toast would immediately take focus and interrupt + the user, taking focus away from what they were doing: the user + could be in the middle of typing text the moment the dialog appears + and could accidentally act on the dialog. Users are used to dealing + with notifications and can pull down the notification shade at their + convenience to respond to your message. +

        + +

        Don't take over the BACK key unless you absolutely need to

        + +

        + As a user navigates from one activity to the next, the system adds + them to the activity stack. This forms a navigation history that is + accessible with the BACK key. Most activities are relatively limited + in scope, with just one set of data, such as viewing a list of + contacts, composing an email, or taking a photo. But what if your + application is one big activity with several pages of content and + needs finer-grained control of the BACK key? Examples of such Google + applications are the Browser, which can have several web pages open + at once, and Maps, which can have several layers of geographic data + to switch between. Both of these applications take control of the + BACK key and maintain their own internal back stacks that operate + only when these applications have focus. +

        + +

        + For example, Maps uses layers to present different + information on a map to the user: displaying the location of a + search result, displaying locations of friends, and displaying a + line for a street path providing direction between points. Maps + stores these layers in its own history so the BACK key can return to + a previous layer. +

        + +

        + Similarly, Browser uses browser windows to present different + web pages to the user. Each window has its own navigation history, + equivalent to tabs in a browser in a desktop operating system (such + as Windows, Macintosh or Linux). For example, if you did a Google + web search in one window of the Android Browser, clicking on a link + in the search results displays a web page in that same window, and + then pressing BACK would to the search results page. Pressing + BACK goes to a previous window only if the current window was + launched from that previous window. If the user keeps pressing + back, they will eventually leave the browser activity and return + Home. +

        + diff --git a/docs/html/guide/practices/ui_guidelines/icon_design.jd b/docs/html/guide/practices/ui_guidelines/icon_design.jd new file mode 100644 index 0000000000000000000000000000000000000000..155684a25b93506f53d72d0e0f1a08c3a4605505 --- /dev/null +++ b/docs/html/guide/practices/ui_guidelines/icon_design.jd @@ -0,0 +1,1406 @@ +page.title=Icon Design Guidelines +@jd:body + +
        +
        + +

        Icon design quickview

        + +
          +
        • You can use several types of icons in an Android application.
        • +
        • Your icons should follow the specification in this document.
        • +
        • A set of standard icons is provided by the Android platform. Your +application can use the standard icons by referencing them as resources.
        • +
        + +

        In this document

        + +
          +
        1. Launcher icon
        2. +
        3. Menu icon
        4. +
        5. Status bar icon
        6. +
        7. Tab icon
        8. +
        9. Dialog icon
        10. +
        11. List view icon
        12. + +
        13. General guidelines
        14. +
        15. Using the Icon Templates Pack
        16. +
        17. Icon appendix +
            +
          1. Launcher icons
          2. +
          3. Menu icons
          4. +
          5. Status bar icons
          6. +
          +
        18. + +
        + +

        See also

        + +
          +
        1. Android Icon +Templates Pack »
        2. +
        + +
        +
        + +

        Creating a unified look and feel throughout a user interface adds value to +your product. Streamlining the graphic style will also make the UI seem more +professional to the user.

        + +

        This document shows you how to create icons for various parts +of your application’s user interface that fit the style set by the Android UI +team. Following these guidelines will help you to create a polished and unified +experience for the user.

        + +

        To get started creating conforming icons more quickly, you can download +the Android Icon Templates Pack. For more information, see +Using the Android Icon Template Pack.

        + +

        Launcher icon

        + +

        A launcher icon is the graphic that represents your application on an Android +device’s Home screen. It is a simplified 3D icon with a fixed perspective. The +required perspective is shown in Figure 1.

        + +

        Structure

        + +
          +
        • The base of a launcher icon can face either the top view or the front +view.
        • + +
        • The majority of a launcher icon’s surface should be created using the +launcher icon color palette. To add emphasis, use +one or more bright accent colors to highlight specific characteristics.
        • + +
        • All launcher icons must be created with rounded corners to make them look +friendly and simple—as shown in Figure 2.
        • + +
        • All dimensions specified are based on a 250x250 pixel artboard size +in a vector graphics editor like Adobe Illustrator, where the icon fits within +the artboard boundaries.
        • + +
        • Final art must be scaled down and exported as a transparent 48x48 px +PNG file using a raster image editor such as Adobe Photoshop.
        • + +
        • Templates for creating launcher icons in Adobe Illustrator and Photoshop are +available in the Icon Templates Pack.
        • +
        + + + + + + +
        + A view of
+launcher icon corners and perspective angles + +
        +

        Figure 1. Perspective angles for launcher icons (90° is +vertical).

        +
        + + + + + + + + + + + + +
        1.92°
        2.92°
        3.173°
        4.171°
        5.49°
        6.171°
        7.64°
        8.97°
        9.75°
        10.93°
        11.169°
        +
        +
        +
        +

        Figure 2. Rounded corners for launcher icons.

        +
        +
        + +

        Light, effects, and shadows

        + +

        Launcher icons are simplified 3D icons using light and shadows for +definition. A light source is placed slightly to the left in front of the icon, +and therefore the shadow expands to the right and back.

        + + + + + + +
        + A view of
+light, effects, and shadows for launcher icons. + +
        +

        Figure 3. Light, effects, and shadows for launcher icons.

        +
        + + + + + + +
        1.Edge highlight:white
        2.Icon shadow:black | 20px blur
        50% opacity | angle 67°
        3.Front part:Use light gradient from color palette
        4.Detail shadow:black | 10px blur
        75% opacity
        5. Side part:Use medium gradient from color palette
        +
        +
        +
        + + + + + + + +
        + +

        Launcher icon color palette

        + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        Color palette, whiteWhite
        r 0 | g 0 | b 0
        Used for highlights on edges.
        Color palette, light gradientLight gradient
        1:  r 0 | g 0 | b 0
        2:  r 217 | g 217 | b 217
        Used on the front (lit) part of the icon.
        Color palette, medium gradienMedium gradient
        1:  r 190 | g 190 | b 190
        2:  r 115 | g 115 | b 115
        Used on the side (shaded) part of the icon.
        Color palette, dark gradientDark gradient
        1:  r 100 | g 100 | b 100
        2:  r 25 | g 25 | b 25
        Used on details and parts in the shade of the icon.
        Color palette, blackBlack
        r 255 | g 255 | b 255
        Used as base color in shadows.
        + +
        + +

        Step by step

        + +
          +
        1. Create the basic shapes with a tool like Adobe Illustrator, using the +angles described in Launcher icon: structure. +The shapes and effects must fit within a 250x250 pixel artboard.
        2. +
        3. Add depth to shapes by extruding them and create the rounded corners as +described for the launcher icon structure.
        4. +
        5. Add details and colors. Gradients should be treated as if there is a light +source placed slightly to the left in front of the icon.
        6. +
        7. Create the shadows with the correct angle and blur effect.
        8. +
        9. Import the icon into a tool like Adobe Photoshop and scale to fit an image +size of 48x48 px on a transparent background.
        10. +
        11. Export the icon at 48x48 as a PNG file with transparency enabled.
        12. +
        + +
        + + + +

        Menu icons are graphical elements placed in the pop-up menu shown to users +when they press the Menu button. They are drawn in a flat-front perspective. +Elements in a menu icon must not be visualized in 3D or perspective.

        + +

        Structure

        + +
          +
        • In order to maintain consistency, all menu icons must use the same +primary palette and the same effects. For more information, see the +menu icon color palette.
        • + +
        • Menu icons should include rounded corners, but only when logically +appropriate. For example, in Figure 3 the logical place for rounded corners is +the roof and not the rest of the building.
        • + +
        • All dimensions specified on this page are based on a 48x48 pixel artboard +size with a 6 pixel safeframe.
        • + +
        • The menu icon effect (the outer glow) described in Light, effects, and shadows can overlap the 6px safeframe, +but only when necessary. The base shape must always stay inside the +safeframe.
        • + +
        • Final art must be exported as a transparent PNG file.
        • + +
        • Templates for creating menu icons in Adobe Photoshop are available in the +Icon Templates Pack.
        • +
        + + + + + + +
        + A view of menu
+icon structure. + +
        +

        Figure 4. Safeframe and corner-rounding for menu +icons. Icon size is 48x48.

        +
        +
        + + + + +

        Menu icons are flat and pictured face on. A slight deboss and some other +effects, which are shown below, are used to create depth.

        + + + + + + +
        + A view of light, effects, and shadows for launcher icons. + +
        +

        Figure 5. Light, effects, and shadows for launcher icons.

        +
        + + + + + +
        1.Front part:Use fill gradient from primary color palette
        2.Inner shadow:black | 20 % opacity
        angle 90° | distance 2px
        size 2px
        3.Outer glow:white | 55% opacity
        spread 10% | size 3px
        5.Inner bevel:depth 1% | direction down size 0px
        angle 90° | altitude 10°
        highlight white 70% opacity
        shadow black 25% opacity
        +
        +
        +
        + + + + + + + +
        + + + + + + + + + + + + + + + + + + + +
        Color palette, whiteWhite
        r 0 | g 0 | b 0
        Used for outer glow and bevel highlight.
        Color palette, medium gradientFill gradient
        1:  r 163 | g 163 | b 163
        2:  r 120 | g 120 | b 120
        Used as color fill.
        Color palette, blackBlack
        r 255 | g 255 | b 255
        Used for inner shadow and bevel shadow.
        + +
        + + + +
          +
        1. Create the basic shapes using a tool like Adobe Illustrator.
        2. +
        3. Import the shape into a tool like Adobe Photoshop and scale to fit an image +of 48x48 px on a transparent background. Mind the safeframe.
        4. +
        5. Add the effects seen as described in Figure 5.
        6. +
        7. Export the icon at 48x48 as a PNG file with transparency enabled.
        8. +
        + +
        + + +

        Status bar icon

        + +

        Status bar icons are used to represent notifications from your application in +the status bar. Graphically, they are very similar to menu icons, but are +smaller and higher in contrast.

        + +

        Structure

        + +
          +
        • Rounded corners must always be applied to the base shape and to the details +of a status bar icon shown Figure 7.
        • + +
        • All dimensions specified are based on a 25x25 pixel artboard size with a 2 +pixel safeframe.
        • + +
        • Status bar icons can overlap the safeframe to the left and right when +necessary, but must not overlap the safeframe at the top and bottom.
        • + +
        • Final art must be exported as a transparent PNG file.
        • + +
        • Templates for creating status bar icons using Adobe Photoshop are available +in the Icon Templates Pack.
        • +
        + + + + + + +
        + A view of
+status bar icon structure. + +
        +

        Figure 6. Safeframe and corner-rounding for status bar +icons. Icon size is 25x25.

        +
        +
        + + +

        Light, effects, and shadows

        + +

        Status bar icons are slightly debossed, high in contrast, and pictured +face-on to enhance clarity at small sizes.

        + + + + + + +
        + A view of
+light, effects, and shadows for launcher icons. + +
        +

        Figure 7. Light, effects, and shadows for launcher icons.

        +
        + + + + + +
        1.Front part:Use fill gradient from primary color palette
        2.Inner bevel:depth 100% | direction down
        size 0px | angle 90° |
        altitude 30°
        highlight white 75% opacity
        shadow black 75% opacity
        3.Detail:white
        4.Disabled detail:grey gradient from palette
        + inner bevel: smooth | depth 1% | direction down | size 0px | angle 117° |
        altitude 42° | highlight white 70% | no shadow
        +
        +
        +
        + + + + + + + +
        + + + +

        Only status bar icons related to the phone function use full color; all other status bar icons should remain monochromatic.

        + + + + + + + + + + + + + + + + + + + + + + +
        Color palette, whiteWhite
        r 0 | g 0 | b 0
        Used for details within the icons and bevel highlight.
        Color palette, grey gradientGrey gradient
        1:  r 169 | g 169 | b 169
        2:  r 126 | g 126 | b 126
        Used for disabled details within the icon.
        Color palette, fill gradientFill gradient
        1:  1 r 105 | g 105 | b 105
        2:  r 10 | g 10 | b 10
        Used as color fill.
        Color palette, blackBlack
        r 255 | g 255 | b 255
        Used for bevel shadow.
        + +
        + + + +
          +
        1. In a tool like Adobe Photoshop, create the base shape within a 25x25 px +image on a transparent background. Mind the safeframe, and keep the upper and +lower 2 pixels free.
        2. +
        3. Add rounded corners as specified in Figure 6.
        4. +
        5. Add light, effects, and shadows as specified in Figure 7.
        6. +
        7. Export the icon at 25x25 as a PNG file with transparency enabled.
        8. +
        + +
        + + +

        Tab icon

        + +

        Tab icons are graphical elements used to represent individual tabs in a +multi-tab interface. Each tab icon has two states: unselected and selected.

        + +

        Structure

        + +
          +
        • Unselected tab icons have the same fill gradient and effects as menu icons, +but with no outer glow.
        • + +
        • Selected tab icons look just like unselected tab icons, but with a fainter +inner shadow, and have the same front part gradient as dialog icons.
        • + +
        • Tab icons have a 1 px safeframe which should only be overlapped for the edge +of the anti-alias of a round shape.
        • + +
        • All dimensions specified on this page are based on a 32x32 px artboard size. +Keep 1 px of padding around the bounding box inside the Photoshop template.
        • + +
        • Final art must be exported as a 32x32 px transparent PNG +file.
        • + +
        • Templates for creating tab icons in Adobe Photoshop are available in the +Icon Templates Pack.
        • +
        + + + + + + + + + + +
        + A view of
+unselected tab icon structure. + +
        +

        Figure 8. Safeframe and fill gradient for unselected tab +icons. Icon size is 32x32.

        +
        +
        + A view of
+selected tab icon structure. + +
        +

        Figure 9. Safeframe and fill gradient for tab icons in +selected state. Icon size is 32x32.

        +
        +
        + +

        Unselected tab icon

        + +

        Light, effects, and shadows

        + +

        Unselected tab icons look just like the selected tab icons, but with a +fainter inner shadow, and the same front part gradient as the dialog icons.

        + + + + + + +
        + A view
+of light, effects, and shadows for unselected tab icons. + +
        +

        Figure 10. Light, effects, and shadows for unselected +tab icons.

        +
        + + + + +
        1.Front part:gradient overlay | angle 90°
        bottom color: r 223 | g 223 | b 223
        top color: r 249 | g 249 | b 249
        bottom color location: 0%
        top color location: 75%
        2.Inner shadow:black | 10 % opacity | angle 90° distance 2px | size 2px
        3.Inner bevel:depth 1% | direction down | size 0px | angle 90° | altitude 10°
        highlight white 70% opacity
        shadow black 25% opacity
        +
        +
        +
        + + + + + + +
        + + + +
          +
        1. Create the basic shapes using a tool like Adobe Illustrator.
        2. +
        3. Import the shape to a tool like Adobe Photoshop and scale to fit an image of +32x32 px on a transparent background.
        4. +
        5. Add the effects seen in Figure 10 for the unselected state filter.
        6. +
        7. Export the icon at 32x32 as a PNG file with transparency enabled.
        8. +
        + +
        + +

        Selected tab icon

        + +

        The selected tab icons have the same fill gradient and effects as the menu +icon, but with no outer glow.

        + + + + + + +
        + A view of
+light, effects, and shadows for selected tab icons. + +
        +

        Figure 11. Light, effects, and shadows for selected tab +icons.

        +
        + + + + +
        1.Front part:Use fill gradient from color palette.
        2.Inner shadow:black | 20% opacity |
        angle 90° | distance 2px |
        size 2px
        3.Inner bevel:depth 1% | direction down | size 0px | angle 90° |
        altitude 10°
        highlight white 70% opacity
        shadow black 25% opacity
        +
        +
        +
        + + + + + + + +
        + + + + + + + + + +
        Color palette, fill gradientFill gradient
        1:  r 163 | g 163 | b 163
        2:  r 120 | g 120 | b 120
        Used as color fill on unselected tab icons.
        + +
        + + + +
          +
        1. Create the basic shape using a tool like Adobe Illustrator.
        2. +
        3. Import the shape into a tool like Adobe Photoshop and scale to fit a 32x32 +px artboard with a transparent background.
        4. +
        5. Add the effects seen in Figure 11 for the selected state filter.
        6. +
        7. Export the icon at 32x32 as a PNG file with transparency enabled.
        8. +
        + +
        + + +

        Dialog icon

        + +

        Dialog icons are shown in pop-up dialog boxes that prompt the user for +interaction. They use a light gradient and inner +shadow in order to stand out against a dark background.

        + +

        Structure

        + +
          +
        • Dialog icons have a 1 pixel safeframe. The base shape must fit within the +safeframe, but the anti-alias of a round shape can overlap the safeframe.
        • + +
        • All dimensions specified on this page are based on a 32x32 pixel artboard size +in Adobe Photoshop. Keep 1 pixel of padding around the bounding box inside the +Photoshop template.
        • + +
        • Final art must be exported as a transparent PNG file.
        • + +
        • Templates for creating dialog icons in Adobe Photoshop are available in the +Icon Templates Pack.
        • +
        + + + + + + +
        + A view of dialog
+icon structure. + +
        +

        Figure 12. Safeframe and fill gradient for dialog icons. +Icon size is 32x32.

        +
        +
        + + +

        Light, effects, and shadows

        + +

        Dialog icons are flat and pictured face-on. In order to stand out against a +dark background, they are built up using a light gradient and inner shadow.

        + + + + + + +
        + A view of light,
+effects, and shadows for dialog icons. + +
        +

        Figure 13. Light, effects, and shadows for dialog +icons.

        +
        + + + +
        1.Front part:gradient overlay | angle 90°
        bottom: r 223 | g 223 | b 223
        top: r 249 | g 249 | b 249
        bottom color location: 0%
        top color location: 75%
        2.Inner shadow:black | 25% opacity |
        angle -90° | distance 1px | size 0px
        +
        +
        +
        + + + + + + +
        + + + +
          +
        1. Create the basic shapes using a tool like Adobe Illustrator.
        2. +
        3. Import the shape into a tool like Adobe Photoshop and scale to fit an image +of 32x32 px on a transparent background.
        4. +
        5. Add the effects seen in Figure 13 for the proper filter.
        6. +
        7. Export the icon at 32x32 as a PNG file with transparency enabled.
        8. +
        + +
        + + +

        List view icon

        + +

        List view icons look a lot like dialog icons, but they use an inner shadow +effect where the light source is above the object. They are also designed to be +used only in a list view. Examples include the Android Market application home +screen and the driving directions screen in the Maps application.

        + +

        Structure

        + +
          +
        • A list view icon normally has a 1 px safeframe, but it is OK to use the +safeframe area for the edge of the anti-alias of a round shape.
        • + +
        • All dimensions specified are based on a 32x32 pixel artboard size in +Photoshop. Keep 1 pixel of padding around the bounding box inside the template. +
        • + +
        • Final art must be exported as a transparent PNG file.
        • + +
        • Templates for creating list view icons in Adobe Photoshop are available in +the Icon Templates Pack.
        • +
        + + + + + + +
        + A view of list
+view icon structure. + +
        +

        Figure 14. Safeframe and fill gradient for list view +icons. Icon size is 32x32.

        +
        +
        + +

        Light, effects, and shadows

        + +

        List view icons are flat and pictured face-on with an inner shadow. Built up +by a light gradient and inner shadow, they stand out well on a dark +background.

        + + + + + + +
        + A view
+of light, effects, and shadows for list view icons. + +
        +

        Figure 15. Light, effects, and shadows for list view +icons.

        +
        + + + + +
        1.Inner shadow:black | 57 % opacity | angle 120° | blend mode normal | distance 1px | size 1px
        2.Background:black | standard system color
        These icons are displayed in list views only.
        Note: The list view icon sits on 32x32 px artboard in Photoshop, without a safeframe.
        +
        +
        +
        + + + + + +
        + + + +
          +
        1. Add the effects seen in Figure 15 for the proper filter.
        2. +
        3. Export the icon at 32x32 as a PNG file with transparency enabled.
        4. +
        5. Create the basic shapes using a tool like Adobe Illustrator.
        6. +
        7. Import the shape into a tool like Adobe Photoshop and scale to fit an image +of 32x32 px on a transparent background.
        8. +
        + +
        + + +

        General guidelines

        + +

        Below are some "do and don't" guidelines to consider when creating icons for +your application. By following the guidelines, you can ensure that your icons +will work well with other parts of the Android platform UI and will meet the +expectations of your application's users.

        + + + + + + + + +
        + +

        Do...

        + +
          +
        • Use a normal perspective. The depth of an object should be realistic.
        • +
        • Keep it simple! By overdoing an icon, it loses it purpose and +readability.
        • +
        • Use colors only when necessary. Mind that the base of a launcher icon should +be grey and feel solid.
        • +
        • Use the correct angles for the specific icon types.
        • +
        +
        + +

        Don’t...

        + +
          +
        • Use open elements like text alone as icons. Instead place those elements on +a base shape.
        • +
        • Use colors for your status bar notifications. Those are reserved for +specific phone-only functions.
        • +
        +
        +Side-by-side examples
+of good/bad icon design. +
        + +

        Using the Android Icon Templates Pack

        + +

        The Android Icon Templates Pack is a collection of template designs, filters, +and settings that make it easier for you to create icons that conform to the +general specifications given in this document. We recommend downloading the +template pack archive before you get started with your icon design.

        + +

        The icon templates are provided in Adobe Photoshop and Adobe Illustrator file +formats, which preserves the layers and design treatments we used when creating the +standard icons for the Android platform. You can load the template files into any +compatible image-editing program, although your ability to work directly with the +layers and treatments may vary based on the program you are using.

        + +

        You can obtain the Icon Templates Pack archive using the link below:

        + +

        Download the Icon Templates +Pack » + + +

        Icon appendix

        + +

        Standard launcher icons

        + +

        Shown below are examples of launcher icons used by Android applications. The +icons are provided for your reference only — please do not reuse these +icons in your applications.. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        + Android asset +
        Alarm Clock
        + Android asset +
        Browser
        + Android asset +
        Calculator
        + Android asset +
        Calendar
        + Android asset +
        Camcorder
        + Android asset +
        Camera
        + Android asset +
        Contacts
        + Android asset +
        Dialer
        + Android asset +
        Email
        + Android asset +
        Gallery
        + Android asset +
        Generic application
        + Android asset +
        Gmail
        + Android asset +
        Google Talk
        + Android asset +
        IM
        + Android asset +
        Maps
        + Android asset +
        Market
        + Android asset +
        Messaging
        + Android asset +
        Music
        + Android asset +
        Settings
        + Android asset +
        Voice Dialer
        + Android asset +
        Voice Search
        + Android asset +
        YouTube
        + +

        + +

        Shown below are standard menu icons that are included in the Android platform +(as of Android 1.5). You can reference any of these icon resources from your +application as needed, but make sure that the action you assign to the icon is +consistent with that listed. Note that this is not a complete list of icons and +that the actual appearance of standard icons may change across platform +versions.

        + +

        To reference one of the icons from your code, use +android.R.drawable.<icon_resource_identifier>. For example, +you can call a menu item's {@link android.view.MenuItem#setIcon(android.graphics.drawable.Drawable) setIcon()} +method and pass the resource name:

        + +

        .setIcon(android.R.drawable.ic_menu_more);. + +

        You could reference the same icon from a layout file using +android:icon="@android:drawable/ic_menu_more">.

        + +

        To determine the resource ID for an icon listed below, hover over the icon or +simply look at image filenames, which use the format +"<icon_resource_identifier>.png".

        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        + Android asset +
        Add
        + Android asset +
        Archive
        + Android asset +
        Attach
        + Android asset +
        Back
        + Android asset +
        Call
        + Android asset +
        Camera
        + Android asset +
        Clear / Close / Cancel / Discard
        + Android asset +
        Compass
        + Android asset +
        Delete
        + Android asset +
        Directions
        + Android asset +
        Edit
        + Android asset +
        Favorite
        + Android asset +
        Forward
        + Android asset +
        Gallery
        + Android asset +
        Go to
        + Android asset +
        Help
        + Android asset +
        History
        + Android asset +
        Home
        + Android asset +
        Info / details
        + Android asset +
        Map mode
        + Android asset +
        Mark
        + Android asset +
        My Location
        + Android asset +
        More
        + Android asset +
        Play
        + Android asset +
        Preferences
        + Android asset +
        Refresh
        + Android asset +
        Rotate
        + Android asset +
        Save
        + Android asset +
        Send
        + Android asset +
        Search
        + Android asset +
        Share
        + Android asset +
        Shuffle
        + Android asset +
        Upload
        + Android asset +
        View
        + Android asset +
        Video
        + Android asset +
        Zoom
        + + +

        Standard status bar icons

        + +

        Shown below are standard status bar icons included in the Android platform +(as of Android 1.5). You can reference any of these icon resources from your +application as needed, but make sure that the meaning of the icon is consistent +with the standard meaning listed. Note that this is not a complete list of icons +and that the actual appearance of standard icons may change across platform +versions.

        + +

        To reference one of the icons from your code, use +android.R.drawable.<icon_resource_identifier>. For example, +you can construct a simple notification that references one of the icons like +this:

        + +

        new Notification(R.drawable.stat_notify_calendar, +"sample text", System.currentTimeMillis());

        + +

        To determine the resource ID for an icon listed below, hover over the icon +or simply look at the image filename, which use the format +"<icon_resource_identifier>.png".

        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        + Android asset +
        3G
        + Android asset +
        Airplane mode
        + Android asset +
        Alarm
        + Android asset +
        Bluetooth
        + Android asset +
        Bluetooth connected
        + Android asset +
        Calendar
        + Android asset +
        Disk full
        + Android asset +
        EDGE
        + Android asset +
        Email
        + Android asset +
        Gmail
        + Android asset +
        GPRS
        + Android asset +
        IM
        + Android asset +
        Installation complete
        + Android asset +
        Music
        + Android asset +
        Roaming
        + Android asset +
        Signal
        + Android asset +
        Signal unavailable
        + Android asset +
        Silent mode
        + Android asset +
        SMS/MMS
        + Android asset +
        Speaker phone
        + Android asset +
        Sync
        + Android asset +
        Sync error
        + Android asset +
        USB connected
        + Android asset +
        Vibrate
        + Android asset +
        Voicemail
        + Android asset +
        Warning
        + Android asset +
        WiFi
        + Android asset +
        WiFi network available
        + Android asset +
        WiFi unavailable
        + Android asset +
        Battery 100%
        + Android asset +
        Battery empty
        + Android asset +
        Call
        + Android asset +
        Call forward
        + Android asset +
        Call on hold
        + Android asset +
        GPS on
        + Android asset +
        Missed call
        + + diff --git a/docs/html/guide/practices/ui_guidelines/index.jd b/docs/html/guide/practices/ui_guidelines/index.jd index e19d5b49f3ff8b4c7fbeb71ccfd708fa5e2d6aff..0b9d2754d21a3516374023619016aed32699ebc4 100644 --- a/docs/html/guide/practices/ui_guidelines/index.jd +++ b/docs/html/guide/practices/ui_guidelines/index.jd @@ -6,18 +6,39 @@ page.title=User Interface Guidelines

        The Android UI team has begun developing guidelines for the interaction and -design of Android applications. Look here for articles that describe these -visual guidelines as we release them.

        +visual design of Android applications. Look here for articles that describe +these guidelines as we release them.

        -
        -
        Widget Design Guidelines
        -
        Widgets are a new feature introduced in Cupcake. A widget displays -an application's most important or timely information at a glance, on a user's -Home screen. These design guidelines describe how to design widgets that fit -with others on the Home screen. They include links to graphics files and -templates that will make your designer's life easier.
        - +
        Icon +Design Guidelines and Android Icon Templates Pack +»
        +
        Your applications need a wide variety of icons, from a launcher icon to +icons in menus, dialogs, tabs, the status bar, and lists. The Icon Guidelines +describe each kind of icon in detail, with specifications for the size, color, +shading, and other details for making all your icons fit in the Android system. +The Icon Templates Pack is an archive of Photoshop and Illustrator templates and +filters that make it much simpler to create conforming icons.
        +
        +
        +
        Widget Design Guidelines
        +
        A widget displays an application's most important or timely information +at a glance, on a user's Home screen. These design guidelines describe how to +design widgets that fit with others on the Home screen. They include links to +graphics files and templates that will make your designer's life easier.
        +
        +
        +
        Activity and Task Design Guidelines
        +
        Activities are the basic, independent building blocks of applications. + As you design your application's UI and feature set, you are free to + re-use activities from other applications as if they were yours, + to enrich and extend your application. These guidelines + describe how activities work, illustrates them with examples, and + describes important underlying principles and mechanisms, such as + multitasking, activity reuse, intents, the activity stack, and + tasks. It covers this all from a high-level design perspective. +
        diff --git a/docs/html/guide/topics/resources/res-selection-flowchart.png b/docs/html/guide/topics/resources/res-selection-flowchart.png new file mode 100755 index 0000000000000000000000000000000000000000..d738b3f0c866ef39f9b756dc08dd615798ec5400 Binary files /dev/null and b/docs/html/guide/topics/resources/res-selection-flowchart.png differ diff --git a/docs/html/guide/topics/resources/resources-i18n.jd b/docs/html/guide/topics/resources/resources-i18n.jd old mode 100644 new mode 100755 index 4bbb44ac5e5a26fdf9489720b36120a460d41fc6..2bcc5e33deeaa884825253efe88d4e5667d9d6c5 --- a/docs/html/guide/topics/resources/resources-i18n.jd +++ b/docs/html/guide/topics/resources/resources-i18n.jd @@ -37,7 +37,7 @@ PNG, and JPEG files. The XML files have very different formats depending on what they describe. This document describes what kinds of files are supported, and the syntax or format of each.

        Resources are externalized from source code, and XML files are compiled into -a binary, fast loading format for efficiency reasons. Strings, likewise are compressed +a binary, fast loading format for efficiency reasons. Strings, likewise, are compressed into a more efficient storage form. It is for these reasons that we have these different resource types in the Android platform.

        @@ -88,12 +88,12 @@ of any XML files.

        You will create and store your resource files under the appropriate subdirectory under the res/ directory in your project. Android has a resource compiler (aapt) that compiles resources according to which -subfolder they are in, and the format of the file. Here is a list of the file +subfolder they are in, and the format of the file. Table 1 shows a list of the file types for each resource. See the Available Resources for descriptions of each type of object, the syntax, and the format or syntax of the containing file.

        - +

        Table 1

        @@ -410,7 +410,7 @@ public class MyActivity extends Activity

        Alternate Resources (for alternate languages and configurations)

        -

        You can supply different resources for your product according to the UI +

        You can supply different resources for your application to use depending on the UI language or hardware configuration on the device. Note that although you can include different string, layout, and other resources, the SDK does not expose methods to let you specify which alternate resource set to load. Android @@ -436,7 +436,7 @@ MyApp/ Append these to the end of the resource folder name, separated by dashes. You can add multiple qualifiers to each folder name, but they must appear in the order they are listed here. For example, a folder containing drawable -resources for a fully specified configuration would look like:

        +resources for a fully specified configuration would look like this:

         MyApp/
        @@ -444,8 +444,7 @@ MyApp/
                 drawable-en-rUS-port-160dpi-finger-keysexposed-qwerty-dpad-480x320/
         
        -

        More typically, you will only specify a few specific configuration options -that a resource is defined for. You may drop any of the values from the +

        More typically, you will only specify a few specific configuration options. You may drop any of the values from the complete list, as long as the remaining values are still in the same order:

        @@ -457,41 +456,46 @@ MyApp/ drawable-port-160dpi/ drawable-qwerty/ - +

        Table 2 lists the valid folder-name qualifiers, in order of precedence. Qualifiers that are listed higher in the table take precedence over those listed lower, as described in How Android finds the best matching directory.

        +

        Table 2

        Directory
        - - - + + + - - - + + + - - + + - - + + @@ -506,6 +510,14 @@ MyApp/ + + + + + + + +
        Qualifier Values
        LanguageThe two letter ISO - 639-1 language code in lowercase. For example: - en, fr, es
        MCC and MNCThe mobile country code and mobile network code from the SIM in the device. For example mcc310-mnc004 (U.S., Verizon brand); mcc208-mnc00 (France, Orange brand); mcc234-mnc00 (U.K., BT brand).
        +
        + If the device uses a radio connection (GSM phone), the MCC will come from the SIM, and the MNC will come from the network to which the device is attached. You might sometimes use the MCC alone, for example to include country-specific legal resources in your application. If your application specifies resources for a MCC/MNC combination, those resources can only be used if both the MCC and the MNC match.
        RegionThe two letter - ISO - 3166-1-alpha-2 language code in uppercase preceded by a lowercase - "r". For example: rUS, rFR, rES
        Language and regionThe two letter ISO + 639-1 language code and two letter + ISO + 3166-1-alpha-2 region code (preceded by lowercase "r"). For example + en-rUS, fr-rFR, es-rES.
        +
        + The codes are case-sensitive: The language code is lowercase, and the country code is uppercase. You cannot specify a region alone, but you can specify a language alone, for example en, fr, es.
        Screen orientation port, land, square
        Screen pixel density92dpi, 108dpi, etc.
        92dpi, 108dpi, etc. When Android selects which resource files to use, it handles screen density differently than the other qualifiers. In step 1 of How Android finds the best matching directory (below), screen density is always considered to be a match. In step 4, if the qualifier being considered is screen density, Android will select the best final match at that point, without any need to move on to step 5.
        Touchscreen type notouch, stylus, finger
        Whether the keyboard is available to the userkeysexposed, keyshidden
        keysexposed, keyshidden, keyssoft
        + If your application has specific resources that should only be used with a soft keyboard, use the keyssoft value. If no keyssoft resources are available (only keysexposed and keyshidden) and the device shows a soft keyboard, the system will use keysexposed resources.
        Primary text input method nokeys, qwerty, 12key 320x240, 640x480, etc. The larger dimension must be specified first.
        SDK versionThe SDK version supported by the device, for example v3. The Android 1.0 SDK is v1, the 1.1 SDK is v2, and the 1.5 SDK is v3.
        (Minor version)(You cannot currently specify minor version. It is always set to 0.)

        This list does not include device-specific parameters such as carrier, @@ -513,92 +525,84 @@ branding, device/hardware, or manufacturer. Everything that an application needs to know about the device that it is running on is encoded via the resource qualifiers in the table above.

        -

        Here are some general guidelines on qualified resource directory names:

        +

        All resource directories, qualified and unqualified, live under the res/ folder. Here are some guidelines on qualified resource directory names:

          -
        • Values are separated by a dash (as well as a dash after the base directory - name)
        • -
        • Values are case-sensitive (even though they must be unique across all folder - names in a case-insensitive way)
          For example,
        • -
            -
          • A portrait-specific drawable directory must be named - drawable-port, not drawable-PORT.
          • -
          • You may not have two directories named drawable-port - and drawable-PORT, even if you had intended "port" and - "PORT" to refer to different parameter values.
          • -
          -
        • Only one value for each qualifier type is supported (that is, you cannot - specify drawable-rEN-rFR/)
        • -
        • You can specify multiple parameters to define specific configurations, - but they must always be in the order listed above. - For example, drawable-en-rUS-land will apply to landscape view, - US-English devices.
        • -
        • Android will try to find the most specific matching directory for the current - configuration, as described below
        • -
        • The order of parameters listed in this table is used to break a tie in case - of multiple qualified directories (see the example given below)
        • -
        • All directories, both qualified and unqualified, live under the res/ folder. - Qualified directories cannot be nested (you cannot have res/drawable/drawable-en)
        • -
        • All resources will be referenced in code or resource reference syntax by - their simple, undecorated name. So if a resource is named this:
          -       MyApp/res/drawable-port-92dp/myimage.png
          - It would be referenced as this:
          -       R.drawable.myimage (code)
          -       @drawable/myimage (XML)
        • +
        • You can specify multiple qualifiers, separated by dashes. For example, drawable-en-rUS-land will apply to US-English + devices in landscape orientation.
        • +
        • The qualifiers must be in the order listed in Table 2 above. For example: +
            +
          • Correct: values-mcc460-nokeys/
          • +
          • Incorrect: values-nokeys-mcc460/
          • +
          +
        • +
        • Values are case-sensitive. For example, a portrait-specific drawable directory must be named + drawable-port, not drawable-PORT or drawable-Port.
        • +
        • Only one value for each qualifier type is supported. For example, if you want to use exactly the same drawable files for Spain and France, you will need two resource directories, such as drawable-rES/ and drawable-rFR/, containing identical files. You cannot + have a directory named drawable-rES-rFR/.
        • +
        • Qualified directories cannot be nested. For example, you cannot have res/drawable/drawable-en.
        -

        How Android finds the best matching directory

        +

        How resources are referenced in code

        +

        All resources will be referenced in code or resource reference syntax by + their simple, undecorated names. So if a resource were named this:
        +      MyApp/res/drawable-port-92dpi/myimage.png
        + It would be referenced as this:
        +      R.drawable.myimage (code)
        +      @drawable/myimage (XML)

        +

        If several drawable directories are available, Android will select one of them (as described below) and load myimage.png from it.

        +

        How Android finds the best matching directory

        Android will pick which of the various underlying resource files should be -used at runtime, depending on the current configuration. The selection process -is as follows:

        - +used at runtime, depending on the current configuration of the device. The example used here assumes the following device configuration:

        +
        +

        Locale = en-GB
        + Screen orientation = port
        + Screen pixel density = 108dpi
        + Touchscreen type = notouch
        + Primary text input method = 12key
        +

        +
        +

        Here is how Android makes the selection:

        1. - Eliminate any resources whose configuration does not match the current - device configuration. For example, if the screen pixel density is 108dpi, - this would eliminate only MyApp/res/drawable-port-92dpi/. -
          -
          -MyApp/res/drawable/myimage.png
          -MyApp/res/drawable-en/myimage.png
          -MyApp/res/drawable-port/myimage.png
          -MyApp/res/drawable-port-92dpi/myimage.png
          -
          -
          -
        2. -
        3. - Pick the resources with the highest number of matching configurations. - For example, if our locale is en-GB and orientation is port, then we - have two candidates with one matching configuration each: - MyApp/res/drawable-en/ and MyApp/res/drawable-port/. - The directory MyApp/res/drawable/ is eliminated because - it has zero matching configurations, while the others have one matching - configuration. -
          -
          -MyApp/res/drawable/myimage.png
          -MyApp/res/drawable-en/myimage.png
          -MyApp/res/drawable-port/myimage.png
          -
          -
          -
        4. -
        5. - Pick the final matching file based on configuration precedence, which - is the order of parameters listed in the table above. That is, it is - more important to match the language than the orientation, so we break - the tie by picking the language-specific file, MyApp/res/drawable-en/. -
          -
          MyApp/res/drawable-en/myimage.png
          -MyApp/res/drawable-port/myimage.png
          -
          -
          -
        6. + Eliminate resource files that contradict the + device configuration. For example, assume that the following resource directories are available for drawables. The drawable-fr-rCA/ directory will be eliminated, because it contradicts the locale of the device.
          +
          MyApp/res/drawable/
          +MyApp/res/drawable-en/
          +MyApp/res/drawable-fr-rCA/
          +MyApp/res/drawable-en-port/
          +MyApp/res/drawable-en-notouch-12key/
          +MyApp/res/drawable-port-92dpi/
          +MyApp/res/drawable-port-notouch-12key
          + Exception: Screen pixel density is the one qualifier that is not used to eliminate files. Even though the screen density of the device is 108 dpi, drawable-port-92dpi/ is not eliminated from the list, because every screen density is considered to be a + match at this point. +
        7. From Table 2, pick the highest-precedence qualifier that remains in the list. (Start with MCC, then move down through the list.)
        8. +
        9. Do any of the available resource directories include this qualifier?
        10. +
            +
          • If No, return to step 2 and look at the next qualifier listed in Table 2. In our example, the answer is "no" until we reach Language.
          • +
          • If Yes, move on to step 4.
          • +
          +
        11. Eliminate resource directories that do not include this qualifier. In our example, we eliminate all the directories that do not include a language qualifier.
        12. +
          MyApp/res/drawable/
          +MyApp/res/drawable-en/
          +MyApp/res/drawable-en-port/
          +MyApp/res/drawable-en-notouch-12key/
          +MyApp/res/drawable-port-92dpi/
          +MyApp/res/drawable-port-notouch-12key
          + Exception: If the qualifier in question is screen pixel density, Android will select the option that most closely matches the device, and the selection process will be complete. In general, Android will prefer scaling down a larger original image to scaling up a smaller original image.

          + +
        13. Go back and repeat steps 2, 3, and 4 until only one choice remains. In the example, screen orientation is the next qualifier in the table for which we have any matches. + Eliminate resources that do not specify a screen orientation.

          +
          MyApp/res/drawable-en/
          +MyApp/res/drawable-en-port/
          +MyApp/res/drawable-en-notouch-12key/
          + Only one choice remains, so that's it. When drawables are called for in this example application, the Android system will load resources from the MyApp/res/drawable-en-port/ directory.
        - - -

        Terminology

        - +

        Tip: The precedence of the qualifiers is more important than the number of qualifiers that exactly match the device. For example, in step 4 above, the last choice on the list includes three qualifiers that exactly match the device (orientation, touchscreen type, and input method), while drawable-en has only one parameter that matches (language). However, language has a higher precedence, so drawable-port-notouch-12key is out.

        +

        This flowchart summarizes how Android selects resource directories to load.

        +

        resource-selection

        +

        Terminology

        The resource system brings a number of different pieces together to form the final complete resource functionality. To help understand the overall system, here are some brief definitions of the core concepts and @@ -671,7 +675,7 @@ information is applied as approriate) and load them into its instance.

        Configuration: For any particular resource identifier, there may be multiple different available values depending on the current configuration. The configuration includes the locale (language and country), screen -orientation, screen density, etc. The current configuration is used to +orientation, etc. The current configuration is used to select which resource values are in effect when the resource table is loaded.

        @@ -710,4 +714,3 @@ SDK matures, this section will contain information on the Internationalization and Localization features of the Android platform. In the meantime, it is a good idea to start by externalizing all strings, and practicing good structure in creating and using resources.

        - diff --git a/docs/html/guide/tutorials/hello-world.jd b/docs/html/guide/tutorials/hello-world.jd index 4d1e9cdf5b6752ea4d992453020f5ceb707f09e1..79b723d162d6e78cde3aa2a62a620df07f9c2d36 100644 --- a/docs/html/guide/tutorials/hello-world.jd +++ b/docs/html/guide/tutorials/hello-world.jd @@ -29,7 +29,7 @@ You can then return to this tutorial and ignore anything about Eclipse.

        Before you start, you should already have the very latest SDK installed, and if you're using Eclipse, you should have installed the ADT plugin as well. If you have not installed these, see -Installing the Android SDK and return +Installing the Android SDK and return here when you've completed the installation.

        Create an AVD

        @@ -80,7 +80,7 @@ Android project in Eclipse.

        "Android XML File" will also be available.)

        -
      9. Selected "Android Project" and click Next. +
      10. Select "Android Project" and click Next.
      11. @@ -147,7 +147,7 @@ Android project in Eclipse.

        built against the 1.1 platform library will run normally on the 1.5 platform. The reverse is not true.

        - +

        Your Android project is now ready. It should be visible in the Package Explorer on the left. diff --git a/docs/html/images/activity_task_design/ActivityChooser.png b/docs/html/images/activity_task_design/ActivityChooser.png new file mode 100644 index 0000000000000000000000000000000000000000..6c20afbbd21cb0b24b626ea786d5c5a3e6c38071 Binary files /dev/null and b/docs/html/images/activity_task_design/ActivityChooser.png differ diff --git a/docs/html/images/activity_task_design/ContactNew.png b/docs/html/images/activity_task_design/ContactNew.png new file mode 100644 index 0000000000000000000000000000000000000000..decaaeb0b1e498d5a19bcc3057b98b3053413f76 Binary files /dev/null and b/docs/html/images/activity_task_design/ContactNew.png differ diff --git a/docs/html/images/activity_task_design/ContactView.png b/docs/html/images/activity_task_design/ContactView.png new file mode 100644 index 0000000000000000000000000000000000000000..5eff2ba6d1fc256e9770eca542f2a3ea7ecbaaa2 Binary files /dev/null and b/docs/html/images/activity_task_design/ContactView.png differ diff --git a/docs/html/images/activity_task_design/ContactsDialer.png b/docs/html/images/activity_task_design/ContactsDialer.png new file mode 100644 index 0000000000000000000000000000000000000000..28794b7f2576abf0b0c22131cfd56a827705fa4a Binary files /dev/null and b/docs/html/images/activity_task_design/ContactsDialer.png differ diff --git a/docs/html/images/activity_task_design/ContactsList.png b/docs/html/images/activity_task_design/ContactsList.png new file mode 100644 index 0000000000000000000000000000000000000000..ef1b83f48db19ba96b7576b0f1965abd616c1381 Binary files /dev/null and b/docs/html/images/activity_task_design/ContactsList.png differ diff --git a/docs/html/images/activity_task_design/HomeTaskBasics1a.png b/docs/html/images/activity_task_design/HomeTaskBasics1a.png new file mode 100644 index 0000000000000000000000000000000000000000..eca480705b12acb70f279b98ab3e1f79c91a71ea Binary files /dev/null and b/docs/html/images/activity_task_design/HomeTaskBasics1a.png differ diff --git a/docs/html/images/activity_task_design/HomeTaskBasics1b.png b/docs/html/images/activity_task_design/HomeTaskBasics1b.png new file mode 100644 index 0000000000000000000000000000000000000000..ce76d634f3419cdc1d112a4ac1addea2d571b201 Binary files /dev/null and b/docs/html/images/activity_task_design/HomeTaskBasics1b.png differ diff --git a/docs/html/images/activity_task_design/HomeTaskBasics1c.png b/docs/html/images/activity_task_design/HomeTaskBasics1c.png new file mode 100644 index 0000000000000000000000000000000000000000..95f48c1084b40a40ca203e1e6f5fb0961ec88971 Binary files /dev/null and b/docs/html/images/activity_task_design/HomeTaskBasics1c.png differ diff --git a/docs/html/images/activity_task_design/HomeTaskBasics1d.png b/docs/html/images/activity_task_design/HomeTaskBasics1d.png new file mode 100644 index 0000000000000000000000000000000000000000..bbb96d9b33d96ab0f9d3ff541ef25fc68fe2369b Binary files /dev/null and b/docs/html/images/activity_task_design/HomeTaskBasics1d.png differ diff --git a/docs/html/images/activity_task_design/HomeTaskBasics1e.png b/docs/html/images/activity_task_design/HomeTaskBasics1e.png new file mode 100644 index 0000000000000000000000000000000000000000..09dd491cf3357bd23d47712d79057146c364abcf Binary files /dev/null and b/docs/html/images/activity_task_design/HomeTaskBasics1e.png differ diff --git a/docs/html/images/activity_task_design/HomeTaskSwitching1a.png b/docs/html/images/activity_task_design/HomeTaskSwitching1a.png new file mode 100644 index 0000000000000000000000000000000000000000..de79aaf953eaa2c513eebacc6fb9913e6d7c8928 Binary files /dev/null and b/docs/html/images/activity_task_design/HomeTaskSwitching1a.png differ diff --git a/docs/html/images/activity_task_design/HomeTaskSwitching1b.png b/docs/html/images/activity_task_design/HomeTaskSwitching1b.png new file mode 100644 index 0000000000000000000000000000000000000000..bce77724a8be4d7a15db8ea56db395c678a7ac97 Binary files /dev/null and b/docs/html/images/activity_task_design/HomeTaskSwitching1b.png differ diff --git a/docs/html/images/activity_task_design/HomeTaskSwitching1c.png b/docs/html/images/activity_task_design/HomeTaskSwitching1c.png new file mode 100644 index 0000000000000000000000000000000000000000..8209f2fbab0b6bef483aaebf9c23a2297fe72356 Binary files /dev/null and b/docs/html/images/activity_task_design/HomeTaskSwitching1c.png differ diff --git a/docs/html/images/activity_task_design/HomeTaskSwitching2.png b/docs/html/images/activity_task_design/HomeTaskSwitching2.png new file mode 100644 index 0000000000000000000000000000000000000000..dee58a397a94e6ef72a03a3ccdef05ea63157dab Binary files /dev/null and b/docs/html/images/activity_task_design/HomeTaskSwitching2.png differ diff --git a/docs/html/images/activity_task_design/HomeTaskSwitching3a.png b/docs/html/images/activity_task_design/HomeTaskSwitching3a.png new file mode 100644 index 0000000000000000000000000000000000000000..0c90a86a1ddb82ae9c2baff63a017cbbc95f969e Binary files /dev/null and b/docs/html/images/activity_task_design/HomeTaskSwitching3a.png differ diff --git a/docs/html/images/activity_task_design/HomeTaskSwitching3b.png b/docs/html/images/activity_task_design/HomeTaskSwitching3b.png new file mode 100644 index 0000000000000000000000000000000000000000..4a16e69ddb460fe791ff3725658288ce3bb55f29 Binary files /dev/null and b/docs/html/images/activity_task_design/HomeTaskSwitching3b.png differ diff --git a/docs/html/images/activity_task_design/HomeTaskSwitching3c.png b/docs/html/images/activity_task_design/HomeTaskSwitching3c.png new file mode 100644 index 0000000000000000000000000000000000000000..d7789aa8f31fcfe550373a3fa144873f61741d55 Binary files /dev/null and b/docs/html/images/activity_task_design/HomeTaskSwitching3c.png differ diff --git a/docs/html/images/activity_task_design/IntentsDiagram.png b/docs/html/images/activity_task_design/IntentsDiagram.png new file mode 100644 index 0000000000000000000000000000000000000000..0ed366ff721d90519229797ec0494262e33c1b36 Binary files /dev/null and b/docs/html/images/activity_task_design/IntentsDiagram.png differ diff --git a/docs/html/images/activity_task_design/PhoneActivitiesDiagram.png b/docs/html/images/activity_task_design/PhoneActivitiesDiagram.png new file mode 100644 index 0000000000000000000000000000000000000000..8d346c17886908c8c8054d7a22f7ce2db13e56a7 Binary files /dev/null and b/docs/html/images/activity_task_design/PhoneActivitiesDiagram.png differ diff --git a/docs/html/images/activity_task_design/ReplacingAnActivity.png b/docs/html/images/activity_task_design/ReplacingAnActivity.png new file mode 100644 index 0000000000000000000000000000000000000000..03b4d929a7471a504a6c17bb526ef04177b83995 Binary files /dev/null and b/docs/html/images/activity_task_design/ReplacingAnActivity.png differ diff --git a/docs/html/images/activity_task_design/ReusingAnActivity1.png b/docs/html/images/activity_task_design/ReusingAnActivity1.png new file mode 100644 index 0000000000000000000000000000000000000000..01c1729bcbc86e1252a5ed3d5242c879407dad2c Binary files /dev/null and b/docs/html/images/activity_task_design/ReusingAnActivity1.png differ diff --git a/docs/html/images/activity_task_design/ReusingAnActivity2.png b/docs/html/images/activity_task_design/ReusingAnActivity2.png new file mode 100644 index 0000000000000000000000000000000000000000..288d2da757d0c06a03cca6134c3d7bb50191af66 Binary files /dev/null and b/docs/html/images/activity_task_design/ReusingAnActivity2.png differ diff --git a/docs/html/images/icon_design/dialog_icon.png b/docs/html/images/icon_design/dialog_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..9f924223db05398e45a7b651f9e68025312f7c20 Binary files /dev/null and b/docs/html/images/icon_design/dialog_icon.png differ diff --git a/docs/html/images/icon_design/dialog_light.png b/docs/html/images/icon_design/dialog_light.png new file mode 100644 index 0000000000000000000000000000000000000000..85056a9ff7a3761500ee496d99ac627c5f36bc35 Binary files /dev/null and b/docs/html/images/icon_design/dialog_light.png differ diff --git a/docs/html/images/icon_design/do_dont.png b/docs/html/images/icon_design/do_dont.png new file mode 100644 index 0000000000000000000000000000000000000000..bc6d649c2c99a4d4aa10a494f1a4246083342c27 Binary files /dev/null and b/docs/html/images/icon_design/do_dont.png differ diff --git a/docs/html/images/icon_design/ic_launcher_IM.png b/docs/html/images/icon_design/ic_launcher_IM.png new file mode 100644 index 0000000000000000000000000000000000000000..afc35a2977fb731de888449e1ec3029cff3e10df Binary files /dev/null and b/docs/html/images/icon_design/ic_launcher_IM.png differ diff --git a/docs/html/images/icon_design/ic_launcher_alarmclock.png b/docs/html/images/icon_design/ic_launcher_alarmclock.png new file mode 100644 index 0000000000000000000000000000000000000000..30ff2671a2f6d50c2742b8b099e315397deb2f1f Binary files /dev/null and b/docs/html/images/icon_design/ic_launcher_alarmclock.png differ diff --git a/docs/html/images/icon_design/ic_launcher_browser.png b/docs/html/images/icon_design/ic_launcher_browser.png new file mode 100644 index 0000000000000000000000000000000000000000..f58b84a0bc132d90af162c665014ee0f4ecd0b07 Binary files /dev/null and b/docs/html/images/icon_design/ic_launcher_browser.png differ diff --git a/docs/html/images/icon_design/ic_launcher_calculator.png b/docs/html/images/icon_design/ic_launcher_calculator.png new file mode 100644 index 0000000000000000000000000000000000000000..298c267ace33f269b2e706970204dbba17f30d1c Binary files /dev/null and b/docs/html/images/icon_design/ic_launcher_calculator.png differ diff --git a/docs/html/images/icon_design/ic_launcher_calendar.png b/docs/html/images/icon_design/ic_launcher_calendar.png new file mode 100644 index 0000000000000000000000000000000000000000..92410907d70b0075d0974e8fd1c76be0fb4777c1 Binary files /dev/null and b/docs/html/images/icon_design/ic_launcher_calendar.png differ diff --git a/docs/html/images/icon_design/ic_launcher_camera.png b/docs/html/images/icon_design/ic_launcher_camera.png new file mode 100644 index 0000000000000000000000000000000000000000..c2d760642613089d2815012d4abe9e1fee53d30d Binary files /dev/null and b/docs/html/images/icon_design/ic_launcher_camera.png differ diff --git a/docs/html/images/icon_design/ic_launcher_contacts.png b/docs/html/images/icon_design/ic_launcher_contacts.png new file mode 100644 index 0000000000000000000000000000000000000000..826656ffb528ec69a68580673a5bb9351829e2f9 Binary files /dev/null and b/docs/html/images/icon_design/ic_launcher_contacts.png differ diff --git a/docs/html/images/icon_design/ic_launcher_email.png b/docs/html/images/icon_design/ic_launcher_email.png new file mode 100644 index 0000000000000000000000000000000000000000..2fb263787ee25144b99c90db8ba14b7bfd26f070 Binary files /dev/null and b/docs/html/images/icon_design/ic_launcher_email.png differ diff --git a/docs/html/images/icon_design/ic_launcher_email_generic.png b/docs/html/images/icon_design/ic_launcher_email_generic.png new file mode 100644 index 0000000000000000000000000000000000000000..590ed705d3d3dffe21d7234f38a82aebaee692df Binary files /dev/null and b/docs/html/images/icon_design/ic_launcher_email_generic.png differ diff --git a/docs/html/images/icon_design/ic_launcher_gallery.png b/docs/html/images/icon_design/ic_launcher_gallery.png new file mode 100644 index 0000000000000000000000000000000000000000..965fb714309c98cc6bdfc2eabfe972561c93525e Binary files /dev/null and b/docs/html/images/icon_design/ic_launcher_gallery.png differ diff --git a/docs/html/images/icon_design/ic_launcher_generic_application.png b/docs/html/images/icon_design/ic_launcher_generic_application.png new file mode 100644 index 0000000000000000000000000000000000000000..75024841d327c4fbaefef7c8e9c8d0e892895f93 Binary files /dev/null and b/docs/html/images/icon_design/ic_launcher_generic_application.png differ diff --git a/docs/html/images/icon_design/ic_launcher_google_talk.png b/docs/html/images/icon_design/ic_launcher_google_talk.png new file mode 100644 index 0000000000000000000000000000000000000000..1618eb3dca8b24cfd1c9271d77d17f3eed63c06c Binary files /dev/null and b/docs/html/images/icon_design/ic_launcher_google_talk.png differ diff --git a/docs/html/images/icon_design/ic_launcher_maps.png b/docs/html/images/icon_design/ic_launcher_maps.png new file mode 100644 index 0000000000000000000000000000000000000000..f436b56d0a615c1fed1fe8d6888a069aaf0a17c9 Binary files /dev/null and b/docs/html/images/icon_design/ic_launcher_maps.png differ diff --git a/docs/html/images/icon_design/ic_launcher_marketplace.png b/docs/html/images/icon_design/ic_launcher_marketplace.png new file mode 100644 index 0000000000000000000000000000000000000000..f1f578ddfe4ebcbcaf8fa8deb3cec02bcb09d819 Binary files /dev/null and b/docs/html/images/icon_design/ic_launcher_marketplace.png differ diff --git a/docs/html/images/icon_design/ic_launcher_musicplayer_2.png b/docs/html/images/icon_design/ic_launcher_musicplayer_2.png new file mode 100644 index 0000000000000000000000000000000000000000..0353b9115f423b9686a259ec05ba03f37bbf3e6d Binary files /dev/null and b/docs/html/images/icon_design/ic_launcher_musicplayer_2.png differ diff --git a/docs/html/images/icon_design/ic_launcher_phone_dialer.png b/docs/html/images/icon_design/ic_launcher_phone_dialer.png new file mode 100644 index 0000000000000000000000000000000000000000..4e613ecce9e9f1f2274f6c2767eb1ee566835443 Binary files /dev/null and b/docs/html/images/icon_design/ic_launcher_phone_dialer.png differ diff --git a/docs/html/images/icon_design/ic_launcher_settings.png b/docs/html/images/icon_design/ic_launcher_settings.png new file mode 100644 index 0000000000000000000000000000000000000000..16db056f788ea81a1c52a89dab5c92fd836b6e1c Binary files /dev/null and b/docs/html/images/icon_design/ic_launcher_settings.png differ diff --git a/docs/html/images/icon_design/ic_launcher_sms_mms.png b/docs/html/images/icon_design/ic_launcher_sms_mms.png new file mode 100644 index 0000000000000000000000000000000000000000..e2ac7843aebaf59cb9e03a2ba8deb1a9b82e83d4 Binary files /dev/null and b/docs/html/images/icon_design/ic_launcher_sms_mms.png differ diff --git a/docs/html/images/icon_design/ic_launcher_video_camera.png b/docs/html/images/icon_design/ic_launcher_video_camera.png new file mode 100644 index 0000000000000000000000000000000000000000..e80255a5d6f9b09e34740f431fa2d7c7bd282c56 Binary files /dev/null and b/docs/html/images/icon_design/ic_launcher_video_camera.png differ diff --git a/docs/html/images/icon_design/ic_launcher_voicedial.png b/docs/html/images/icon_design/ic_launcher_voicedial.png new file mode 100644 index 0000000000000000000000000000000000000000..0c84fbac8dc3ff8d3cdfeffacbc73844dfdb92ff Binary files /dev/null and b/docs/html/images/icon_design/ic_launcher_voicedial.png differ diff --git a/docs/html/images/icon_design/ic_launcher_voicesearch.png b/docs/html/images/icon_design/ic_launcher_voicesearch.png new file mode 100644 index 0000000000000000000000000000000000000000..09d51995a15c45a37151b37a6604c95a0c2954fa Binary files /dev/null and b/docs/html/images/icon_design/ic_launcher_voicesearch.png differ diff --git a/docs/html/images/icon_design/ic_launcher_youtube.png b/docs/html/images/icon_design/ic_launcher_youtube.png new file mode 100644 index 0000000000000000000000000000000000000000..48d268da5d1bbcd7c2195f87972a751f74f08775 Binary files /dev/null and b/docs/html/images/icon_design/ic_launcher_youtube.png differ diff --git a/docs/html/images/icon_design/ic_menu_add.png b/docs/html/images/icon_design/ic_menu_add.png new file mode 100644 index 0000000000000000000000000000000000000000..6752bfd1007293ca6340f91501f86c71b76684ce Binary files /dev/null and b/docs/html/images/icon_design/ic_menu_add.png differ diff --git a/docs/html/images/icon_design/ic_menu_archive.png b/docs/html/images/icon_design/ic_menu_archive.png new file mode 100644 index 0000000000000000000000000000000000000000..a4599e37a063394c74fd83c3d255918431941641 Binary files /dev/null and b/docs/html/images/icon_design/ic_menu_archive.png differ diff --git a/docs/html/images/icon_design/ic_menu_attachment.png b/docs/html/images/icon_design/ic_menu_attachment.png new file mode 100644 index 0000000000000000000000000000000000000000..89d626f6cd6844fa176a844b47d0c73b169d9473 Binary files /dev/null and b/docs/html/images/icon_design/ic_menu_attachment.png differ diff --git a/docs/html/images/icon_design/ic_menu_back.png b/docs/html/images/icon_design/ic_menu_back.png new file mode 100644 index 0000000000000000000000000000000000000000..5ce50ebf179f40d28760da5c5cfb1ff43e636851 Binary files /dev/null and b/docs/html/images/icon_design/ic_menu_back.png differ diff --git a/docs/html/images/icon_design/ic_menu_call.png b/docs/html/images/icon_design/ic_menu_call.png new file mode 100644 index 0000000000000000000000000000000000000000..a63f86b16e5639ee8335f162b90bdcc5f0ae7bf4 Binary files /dev/null and b/docs/html/images/icon_design/ic_menu_call.png differ diff --git a/docs/html/images/icon_design/ic_menu_camera.png b/docs/html/images/icon_design/ic_menu_camera.png new file mode 100644 index 0000000000000000000000000000000000000000..cdf7ca31b737ffec3b38bc700cc662d2a326ecfd Binary files /dev/null and b/docs/html/images/icon_design/ic_menu_camera.png differ diff --git a/docs/html/images/icon_design/ic_menu_camera_video_view.png b/docs/html/images/icon_design/ic_menu_camera_video_view.png new file mode 100644 index 0000000000000000000000000000000000000000..f7e52c25bb60db01d48f85333daf4bffe6f539cf Binary files /dev/null and b/docs/html/images/icon_design/ic_menu_camera_video_view.png differ diff --git a/docs/html/images/icon_design/ic_menu_close_clear_cancel.png b/docs/html/images/icon_design/ic_menu_close_clear_cancel.png new file mode 100644 index 0000000000000000000000000000000000000000..619858c2bacea47b25846f18306c60498d0ae1fb Binary files /dev/null and b/docs/html/images/icon_design/ic_menu_close_clear_cancel.png differ diff --git a/docs/html/images/icon_design/ic_menu_compass.png b/docs/html/images/icon_design/ic_menu_compass.png new file mode 100644 index 0000000000000000000000000000000000000000..7717dde51db12424875f4b99e6476c3dfbf1b738 Binary files /dev/null and b/docs/html/images/icon_design/ic_menu_compass.png differ diff --git a/docs/html/images/icon_design/ic_menu_delete.png b/docs/html/images/icon_design/ic_menu_delete.png new file mode 100644 index 0000000000000000000000000000000000000000..7d954943d5daf3ebb8e19e1f4adb79e1c533658b Binary files /dev/null and b/docs/html/images/icon_design/ic_menu_delete.png differ diff --git a/docs/html/images/icon_design/ic_menu_directions.png b/docs/html/images/icon_design/ic_menu_directions.png new file mode 100644 index 0000000000000000000000000000000000000000..67d3ff21fceb9cf3ad45c3853ccc77229c3e5b83 Binary files /dev/null and b/docs/html/images/icon_design/ic_menu_directions.png differ diff --git a/docs/html/images/icon_design/ic_menu_edit.png b/docs/html/images/icon_design/ic_menu_edit.png new file mode 100644 index 0000000000000000000000000000000000000000..41a9c2e20b247b22a443e8f3ddcc2ea07aaff793 Binary files /dev/null and b/docs/html/images/icon_design/ic_menu_edit.png differ diff --git a/docs/html/images/icon_design/ic_menu_favorite.png b/docs/html/images/icon_design/ic_menu_favorite.png new file mode 100644 index 0000000000000000000000000000000000000000..527d74ac670a64e216f5c1993e3b62ca77dcea55 Binary files /dev/null and b/docs/html/images/icon_design/ic_menu_favorite.png differ diff --git a/docs/html/images/icon_design/ic_menu_forward.png b/docs/html/images/icon_design/ic_menu_forward.png new file mode 100644 index 0000000000000000000000000000000000000000..0936fac4e49aa7fc5879b869ec189c38ac509245 Binary files /dev/null and b/docs/html/images/icon_design/ic_menu_forward.png differ diff --git a/docs/html/images/icon_design/ic_menu_gallery.png b/docs/html/images/icon_design/ic_menu_gallery.png new file mode 100644 index 0000000000000000000000000000000000000000..f61bbd8bae606d2f108f3e093fa1fbf4b61204c0 Binary files /dev/null and b/docs/html/images/icon_design/ic_menu_gallery.png differ diff --git a/docs/html/images/icon_design/ic_menu_goto.png b/docs/html/images/icon_design/ic_menu_goto.png new file mode 100644 index 0000000000000000000000000000000000000000..40183ebc2f7a3018fb837329bd39d776f523fece Binary files /dev/null and b/docs/html/images/icon_design/ic_menu_goto.png differ diff --git a/docs/html/images/icon_design/ic_menu_help.png b/docs/html/images/icon_design/ic_menu_help.png new file mode 100644 index 0000000000000000000000000000000000000000..7c55dfd6936bd69671861792af2809b325c8d72a Binary files /dev/null and b/docs/html/images/icon_design/ic_menu_help.png differ diff --git a/docs/html/images/icon_design/ic_menu_home.png b/docs/html/images/icon_design/ic_menu_home.png new file mode 100644 index 0000000000000000000000000000000000000000..34943f6607f5a96f827af51bbf9591a3c5b8cc33 Binary files /dev/null and b/docs/html/images/icon_design/ic_menu_home.png differ diff --git a/docs/html/images/icon_design/ic_menu_info_details.png b/docs/html/images/icon_design/ic_menu_info_details.png new file mode 100644 index 0000000000000000000000000000000000000000..1786d1e2280bca40739030f470e50d980d3cdd4d Binary files /dev/null and b/docs/html/images/icon_design/ic_menu_info_details.png differ diff --git a/docs/html/images/icon_design/ic_menu_mapmode.png b/docs/html/images/icon_design/ic_menu_mapmode.png new file mode 100644 index 0000000000000000000000000000000000000000..d85cab5d6b1d14a2e490d917712761ffd6832447 Binary files /dev/null and b/docs/html/images/icon_design/ic_menu_mapmode.png differ diff --git a/docs/html/images/icon_design/ic_menu_mark.png b/docs/html/images/icon_design/ic_menu_mark.png new file mode 100644 index 0000000000000000000000000000000000000000..5e95da75a6940b3a777d2aa2b5749630192cdad7 Binary files /dev/null and b/docs/html/images/icon_design/ic_menu_mark.png differ diff --git a/docs/html/images/icon_design/ic_menu_more.png b/docs/html/images/icon_design/ic_menu_more.png new file mode 100644 index 0000000000000000000000000000000000000000..20915277cbf3766bd5d378fda9ba27b557c59ffb Binary files /dev/null and b/docs/html/images/icon_design/ic_menu_more.png differ diff --git a/docs/html/images/icon_design/ic_menu_mylocation.png b/docs/html/images/icon_design/ic_menu_mylocation.png new file mode 100644 index 0000000000000000000000000000000000000000..14b0af882b44ba67dbc3ba92a2612b02d6096449 Binary files /dev/null and b/docs/html/images/icon_design/ic_menu_mylocation.png differ diff --git a/docs/html/images/icon_design/ic_menu_play_clip.png b/docs/html/images/icon_design/ic_menu_play_clip.png new file mode 100644 index 0000000000000000000000000000000000000000..466994744c9d60fdcc1697daa63b37879fda80ae Binary files /dev/null and b/docs/html/images/icon_design/ic_menu_play_clip.png differ diff --git a/docs/html/images/icon_design/ic_menu_preferences.png b/docs/html/images/icon_design/ic_menu_preferences.png new file mode 100644 index 0000000000000000000000000000000000000000..b8e71412d8abd068d6c02fb237ada60b2919598f Binary files /dev/null and b/docs/html/images/icon_design/ic_menu_preferences.png differ diff --git a/docs/html/images/icon_design/ic_menu_recent_history.png b/docs/html/images/icon_design/ic_menu_recent_history.png new file mode 100644 index 0000000000000000000000000000000000000000..4ccae5d12fae77c3a120b5d443f9319a9171529d Binary files /dev/null and b/docs/html/images/icon_design/ic_menu_recent_history.png differ diff --git a/docs/html/images/icon_design/ic_menu_refresh.png b/docs/html/images/icon_design/ic_menu_refresh.png new file mode 100644 index 0000000000000000000000000000000000000000..77d70dd4f0534271b71ef4eb87f5a7a917d944fa Binary files /dev/null and b/docs/html/images/icon_design/ic_menu_refresh.png differ diff --git a/docs/html/images/icon_design/ic_menu_rotate.png b/docs/html/images/icon_design/ic_menu_rotate.png new file mode 100644 index 0000000000000000000000000000000000000000..27368b2454f5d2746d43edd90deddd42ddef7e6f Binary files /dev/null and b/docs/html/images/icon_design/ic_menu_rotate.png differ diff --git a/docs/html/images/icon_design/ic_menu_save.png b/docs/html/images/icon_design/ic_menu_save.png new file mode 100644 index 0000000000000000000000000000000000000000..36d50b38766f219dc7e259388641d0a8e47176af Binary files /dev/null and b/docs/html/images/icon_design/ic_menu_save.png differ diff --git a/docs/html/images/icon_design/ic_menu_search.png b/docs/html/images/icon_design/ic_menu_search.png new file mode 100644 index 0000000000000000000000000000000000000000..94446db976cfb4a0e317e94981ff9877be4dd0d2 Binary files /dev/null and b/docs/html/images/icon_design/ic_menu_search.png differ diff --git a/docs/html/images/icon_design/ic_menu_send.png b/docs/html/images/icon_design/ic_menu_send.png new file mode 100644 index 0000000000000000000000000000000000000000..74c096dc949dff33ceda0020ce48572b9d3dc892 Binary files /dev/null and b/docs/html/images/icon_design/ic_menu_send.png differ diff --git a/docs/html/images/icon_design/ic_menu_share.png b/docs/html/images/icon_design/ic_menu_share.png new file mode 100644 index 0000000000000000000000000000000000000000..44db9b16e24582ac61006501eca50f5dff167797 Binary files /dev/null and b/docs/html/images/icon_design/ic_menu_share.png differ diff --git a/docs/html/images/icon_design/ic_menu_shuffle.png b/docs/html/images/icon_design/ic_menu_shuffle.png new file mode 100644 index 0000000000000000000000000000000000000000..cb7009deaa2c13f10987565fad4c9e1ebe66c5aa Binary files /dev/null and b/docs/html/images/icon_design/ic_menu_shuffle.png differ diff --git a/docs/html/images/icon_design/ic_menu_upload.png b/docs/html/images/icon_design/ic_menu_upload.png new file mode 100644 index 0000000000000000000000000000000000000000..1c0dd3f671781ea3f52bf3620d5c0e1035f969cd Binary files /dev/null and b/docs/html/images/icon_design/ic_menu_upload.png differ diff --git a/docs/html/images/icon_design/ic_menu_view.png b/docs/html/images/icon_design/ic_menu_view.png new file mode 100644 index 0000000000000000000000000000000000000000..69828a9ebb28fac32a9e6155f823820802185ba3 Binary files /dev/null and b/docs/html/images/icon_design/ic_menu_view.png differ diff --git a/docs/html/images/icon_design/ic_menu_zoom.png b/docs/html/images/icon_design/ic_menu_zoom.png new file mode 100644 index 0000000000000000000000000000000000000000..0b8c4e8f99ab406208fd8b65732371c0381e7d1d Binary files /dev/null and b/docs/html/images/icon_design/ic_menu_zoom.png differ diff --git a/docs/html/images/icon_design/icon_guidelines_logo.png b/docs/html/images/icon_design/icon_guidelines_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..9362c8f5263af9028a601e36704bcbbc040dfaa3 Binary files /dev/null and b/docs/html/images/icon_design/icon_guidelines_logo.png differ diff --git a/docs/html/images/icon_design/launcher_light.png b/docs/html/images/icon_design/launcher_light.png new file mode 100644 index 0000000000000000000000000000000000000000..8a94e1dcb59d582513f104c1b099045d45850232 Binary files /dev/null and b/docs/html/images/icon_design/launcher_light.png differ diff --git a/docs/html/images/icon_design/launcher_palette_black.png b/docs/html/images/icon_design/launcher_palette_black.png new file mode 100644 index 0000000000000000000000000000000000000000..fba096f301f9b55744bcca8247b41d6d7ac98380 Binary files /dev/null and b/docs/html/images/icon_design/launcher_palette_black.png differ diff --git a/docs/html/images/icon_design/launcher_palette_dark.png b/docs/html/images/icon_design/launcher_palette_dark.png new file mode 100644 index 0000000000000000000000000000000000000000..37355427e872b088c73c89d18f928369c8fbd26d Binary files /dev/null and b/docs/html/images/icon_design/launcher_palette_dark.png differ diff --git a/docs/html/images/icon_design/launcher_palette_gradient_dark.png b/docs/html/images/icon_design/launcher_palette_gradient_dark.png new file mode 100644 index 0000000000000000000000000000000000000000..37355427e872b088c73c89d18f928369c8fbd26d Binary files /dev/null and b/docs/html/images/icon_design/launcher_palette_gradient_dark.png differ diff --git a/docs/html/images/icon_design/launcher_palette_gradient_light.png b/docs/html/images/icon_design/launcher_palette_gradient_light.png new file mode 100644 index 0000000000000000000000000000000000000000..f1121ebf87b8e4be2b58332961c6aa093386a09f Binary files /dev/null and b/docs/html/images/icon_design/launcher_palette_gradient_light.png differ diff --git a/docs/html/images/icon_design/launcher_palette_gradient_medium.png b/docs/html/images/icon_design/launcher_palette_gradient_medium.png new file mode 100644 index 0000000000000000000000000000000000000000..1442b17b73938acd69f64c14625ba55efa7128cb Binary files /dev/null and b/docs/html/images/icon_design/launcher_palette_gradient_medium.png differ diff --git a/docs/html/images/icon_design/launcher_palette_light.png b/docs/html/images/icon_design/launcher_palette_light.png new file mode 100644 index 0000000000000000000000000000000000000000..f1121ebf87b8e4be2b58332961c6aa093386a09f Binary files /dev/null and b/docs/html/images/icon_design/launcher_palette_light.png differ diff --git a/docs/html/images/icon_design/launcher_palette_medium.png b/docs/html/images/icon_design/launcher_palette_medium.png new file mode 100644 index 0000000000000000000000000000000000000000..1442b17b73938acd69f64c14625ba55efa7128cb Binary files /dev/null and b/docs/html/images/icon_design/launcher_palette_medium.png differ diff --git a/docs/html/images/icon_design/launcher_palette_white.png b/docs/html/images/icon_design/launcher_palette_white.png new file mode 100644 index 0000000000000000000000000000000000000000..8d7ac41e5efc110a65d7ba0c6ec1c1987813a907 Binary files /dev/null and b/docs/html/images/icon_design/launcher_palette_white.png differ diff --git a/docs/html/images/icon_design/launcher_structure.png b/docs/html/images/icon_design/launcher_structure.png new file mode 100644 index 0000000000000000000000000000000000000000..53e4d9a971ca7ab6b2939be1bec2d1158025c3f7 Binary files /dev/null and b/docs/html/images/icon_design/launcher_structure.png differ diff --git a/docs/html/images/icon_design/listview_icon.png b/docs/html/images/icon_design/listview_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..5711d88b2b635170978eea238514a98ec7c0ba97 Binary files /dev/null and b/docs/html/images/icon_design/listview_icon.png differ diff --git a/docs/html/images/icon_design/listview_icon_details.png b/docs/html/images/icon_design/listview_icon_details.png new file mode 100644 index 0000000000000000000000000000000000000000..5a684162f7bf0cf22969c6283d8c46c4e4b34ee8 Binary files /dev/null and b/docs/html/images/icon_design/listview_icon_details.png differ diff --git a/docs/html/images/icon_design/menu_light.png b/docs/html/images/icon_design/menu_light.png new file mode 100644 index 0000000000000000000000000000000000000000..93ed38bf6785a9f9013cc2ed5fb75523093a7eac Binary files /dev/null and b/docs/html/images/icon_design/menu_light.png differ diff --git a/docs/html/images/icon_design/menu_palette_black.png b/docs/html/images/icon_design/menu_palette_black.png new file mode 100644 index 0000000000000000000000000000000000000000..fba096f301f9b55744bcca8247b41d6d7ac98380 Binary files /dev/null and b/docs/html/images/icon_design/menu_palette_black.png differ diff --git a/docs/html/images/icon_design/menu_palette_fill.png b/docs/html/images/icon_design/menu_palette_fill.png new file mode 100644 index 0000000000000000000000000000000000000000..7079bda6f7785dafe49f1732cf6edba2f1764d23 Binary files /dev/null and b/docs/html/images/icon_design/menu_palette_fill.png differ diff --git a/docs/html/images/icon_design/menu_palette_gradient_medium.png b/docs/html/images/icon_design/menu_palette_gradient_medium.png new file mode 100644 index 0000000000000000000000000000000000000000..a806adb09dbb454937bc8e8be847cb6abd0ff104 Binary files /dev/null and b/docs/html/images/icon_design/menu_palette_gradient_medium.png differ diff --git a/docs/html/images/icon_design/menu_palette_white.png b/docs/html/images/icon_design/menu_palette_white.png new file mode 100644 index 0000000000000000000000000000000000000000..8d7ac41e5efc110a65d7ba0c6ec1c1987813a907 Binary files /dev/null and b/docs/html/images/icon_design/menu_palette_white.png differ diff --git a/docs/html/images/icon_design/menu_structure.png b/docs/html/images/icon_design/menu_structure.png new file mode 100644 index 0000000000000000000000000000000000000000..ab140154698ad4b9f0e438e1d0deb9e296f0c454 Binary files /dev/null and b/docs/html/images/icon_design/menu_structure.png differ diff --git a/docs/html/images/icon_design/stat_notify_alarm.png b/docs/html/images/icon_design/stat_notify_alarm.png new file mode 100644 index 0000000000000000000000000000000000000000..1b01b850619d2030e952203949d29eaf22dedfa6 Binary files /dev/null and b/docs/html/images/icon_design/stat_notify_alarm.png differ diff --git a/docs/html/images/icon_design/stat_notify_calendar.png b/docs/html/images/icon_design/stat_notify_calendar.png new file mode 100644 index 0000000000000000000000000000000000000000..4433a16ae5b5c8e83ea9d3751b28b59718e30233 Binary files /dev/null and b/docs/html/images/icon_design/stat_notify_calendar.png differ diff --git a/docs/html/images/icon_design/stat_notify_chat.png b/docs/html/images/icon_design/stat_notify_chat.png new file mode 100644 index 0000000000000000000000000000000000000000..238f0437ed5baf14f7dfad7d77a204e07cf0f4fb Binary files /dev/null and b/docs/html/images/icon_design/stat_notify_chat.png differ diff --git a/docs/html/images/icon_design/stat_notify_disk_full.png b/docs/html/images/icon_design/stat_notify_disk_full.png new file mode 100644 index 0000000000000000000000000000000000000000..9120f00375095442f0bce4c97321d1c84273de11 Binary files /dev/null and b/docs/html/images/icon_design/stat_notify_disk_full.png differ diff --git a/docs/html/images/icon_design/stat_notify_email.png b/docs/html/images/icon_design/stat_notify_email.png new file mode 100644 index 0000000000000000000000000000000000000000..d84a2471c172a3b6a90ec9586dce5acbbe3cefe9 Binary files /dev/null and b/docs/html/images/icon_design/stat_notify_email.png differ diff --git a/docs/html/images/icon_design/stat_notify_email_generic.png b/docs/html/images/icon_design/stat_notify_email_generic.png new file mode 100644 index 0000000000000000000000000000000000000000..686033f8db0e13a45458e19916e73cb59747ac89 Binary files /dev/null and b/docs/html/images/icon_design/stat_notify_email_generic.png differ diff --git a/docs/html/images/icon_design/stat_notify_missed_call.png b/docs/html/images/icon_design/stat_notify_missed_call.png new file mode 100644 index 0000000000000000000000000000000000000000..fe746b3b10cb7832009d56b8a3debe2b467ba18b Binary files /dev/null and b/docs/html/images/icon_design/stat_notify_missed_call.png differ diff --git a/docs/html/images/icon_design/stat_notify_musicplayer.png b/docs/html/images/icon_design/stat_notify_musicplayer.png new file mode 100644 index 0000000000000000000000000000000000000000..fd92c1888ec4496b58e3ef938d0dc9b074fe885f Binary files /dev/null and b/docs/html/images/icon_design/stat_notify_musicplayer.png differ diff --git a/docs/html/images/icon_design/stat_notify_sms.png b/docs/html/images/icon_design/stat_notify_sms.png new file mode 100644 index 0000000000000000000000000000000000000000..b437d5b7720642a57e35a2da8f5cf363ebc61921 Binary files /dev/null and b/docs/html/images/icon_design/stat_notify_sms.png differ diff --git a/docs/html/images/icon_design/stat_notify_sync_anim0.png b/docs/html/images/icon_design/stat_notify_sync_anim0.png new file mode 100644 index 0000000000000000000000000000000000000000..0edf69200c680ca7dbe892e9c7bcd0cdcd31f496 Binary files /dev/null and b/docs/html/images/icon_design/stat_notify_sync_anim0.png differ diff --git a/docs/html/images/icon_design/stat_notify_sync_error.png b/docs/html/images/icon_design/stat_notify_sync_error.png new file mode 100644 index 0000000000000000000000000000000000000000..3078b8c56af578651845274e0204360e0f311858 Binary files /dev/null and b/docs/html/images/icon_design/stat_notify_sync_error.png differ diff --git a/docs/html/images/icon_design/stat_notify_voicemail.png b/docs/html/images/icon_design/stat_notify_voicemail.png new file mode 100644 index 0000000000000000000000000000000000000000..658fa0520cae598a61005f6c3f33b11e4e2e1d7f Binary files /dev/null and b/docs/html/images/icon_design/stat_notify_voicemail.png differ diff --git a/docs/html/images/icon_design/stat_notify_wifi_in_range.png b/docs/html/images/icon_design/stat_notify_wifi_in_range.png new file mode 100644 index 0000000000000000000000000000000000000000..e9c74b481b05dc0f21ae4af960137fa4dd42a7e9 Binary files /dev/null and b/docs/html/images/icon_design/stat_notify_wifi_in_range.png differ diff --git a/docs/html/images/icon_design/stat_sys_battery_100.png b/docs/html/images/icon_design/stat_sys_battery_100.png new file mode 100644 index 0000000000000000000000000000000000000000..d280aebb08bd18f8279394b150780faafee795a2 Binary files /dev/null and b/docs/html/images/icon_design/stat_sys_battery_100.png differ diff --git a/docs/html/images/icon_design/stat_sys_battery_empty.png b/docs/html/images/icon_design/stat_sys_battery_empty.png new file mode 100644 index 0000000000000000000000000000000000000000..4a5e99e75cc6ceb68dbb258e424e3cf5ba7aa9df Binary files /dev/null and b/docs/html/images/icon_design/stat_sys_battery_empty.png differ diff --git a/docs/html/images/icon_design/stat_sys_data_bluetooth.png b/docs/html/images/icon_design/stat_sys_data_bluetooth.png new file mode 100644 index 0000000000000000000000000000000000000000..7a8b78f6e8db33e65477570c895510f4fc8c61d0 Binary files /dev/null and b/docs/html/images/icon_design/stat_sys_data_bluetooth.png differ diff --git a/docs/html/images/icon_design/stat_sys_data_bluetooth_connected.png b/docs/html/images/icon_design/stat_sys_data_bluetooth_connected.png new file mode 100644 index 0000000000000000000000000000000000000000..f09b83bfad670b4b6ce3b610033e520ba5aadbff Binary files /dev/null and b/docs/html/images/icon_design/stat_sys_data_bluetooth_connected.png differ diff --git a/docs/html/images/icon_design/stat_sys_data_connected_3g.png b/docs/html/images/icon_design/stat_sys_data_connected_3g.png new file mode 100644 index 0000000000000000000000000000000000000000..a1092807a9cf60ca5b73bad605c1b1f684a6b073 Binary files /dev/null and b/docs/html/images/icon_design/stat_sys_data_connected_3g.png differ diff --git a/docs/html/images/icon_design/stat_sys_data_connected_e.png b/docs/html/images/icon_design/stat_sys_data_connected_e.png new file mode 100644 index 0000000000000000000000000000000000000000..c55264447967c596b6ce6816b6de47090a244c88 Binary files /dev/null and b/docs/html/images/icon_design/stat_sys_data_connected_e.png differ diff --git a/docs/html/images/icon_design/stat_sys_data_connected_g.png b/docs/html/images/icon_design/stat_sys_data_connected_g.png new file mode 100644 index 0000000000000000000000000000000000000000..f7edb49954c417478b925c95a91f957a3fee3684 Binary files /dev/null and b/docs/html/images/icon_design/stat_sys_data_connected_g.png differ diff --git a/docs/html/images/icon_design/stat_sys_data_usb.png b/docs/html/images/icon_design/stat_sys_data_usb.png new file mode 100644 index 0000000000000000000000000000000000000000..2d0da4c8ec1b35742584997bb6239721939cd3fb Binary files /dev/null and b/docs/html/images/icon_design/stat_sys_data_usb.png differ diff --git a/docs/html/images/icon_design/stat_sys_gps_on.png b/docs/html/images/icon_design/stat_sys_gps_on.png new file mode 100644 index 0000000000000000000000000000000000000000..a2c677d9ec39ea738710686b77b30f670aa9c73a Binary files /dev/null and b/docs/html/images/icon_design/stat_sys_gps_on.png differ diff --git a/docs/html/images/icon_design/stat_sys_install_complete.png b/docs/html/images/icon_design/stat_sys_install_complete.png new file mode 100644 index 0000000000000000000000000000000000000000..62dba5ba7de40a430657954af24ff2cdc86b8707 Binary files /dev/null and b/docs/html/images/icon_design/stat_sys_install_complete.png differ diff --git a/docs/html/images/icon_design/stat_sys_phone_call.png b/docs/html/images/icon_design/stat_sys_phone_call.png new file mode 100644 index 0000000000000000000000000000000000000000..ad5369399221da11993a047a12ba4f1e17fe1683 Binary files /dev/null and b/docs/html/images/icon_design/stat_sys_phone_call.png differ diff --git a/docs/html/images/icon_design/stat_sys_phone_call_forward.png b/docs/html/images/icon_design/stat_sys_phone_call_forward.png new file mode 100644 index 0000000000000000000000000000000000000000..ed4b6ec1289f8e079cb405d722c1c4c8e33baacb Binary files /dev/null and b/docs/html/images/icon_design/stat_sys_phone_call_forward.png differ diff --git a/docs/html/images/icon_design/stat_sys_phone_call_on_hold.png b/docs/html/images/icon_design/stat_sys_phone_call_on_hold.png new file mode 100644 index 0000000000000000000000000000000000000000..921644765daf815f3d2620d7e71709c6b935340a Binary files /dev/null and b/docs/html/images/icon_design/stat_sys_phone_call_on_hold.png differ diff --git a/docs/html/images/icon_design/stat_sys_r_signal_4.png b/docs/html/images/icon_design/stat_sys_r_signal_4.png new file mode 100644 index 0000000000000000000000000000000000000000..f04fb11b4e73cd816495696d53b298fa3466e4d7 Binary files /dev/null and b/docs/html/images/icon_design/stat_sys_r_signal_4.png differ diff --git a/docs/html/images/icon_design/stat_sys_ringer_silent_old.png b/docs/html/images/icon_design/stat_sys_ringer_silent_old.png new file mode 100644 index 0000000000000000000000000000000000000000..d125ce5bef362cfcdedfa7effb532ad822694d92 Binary files /dev/null and b/docs/html/images/icon_design/stat_sys_ringer_silent_old.png differ diff --git a/docs/html/images/icon_design/stat_sys_ringer_vibrate.png b/docs/html/images/icon_design/stat_sys_ringer_vibrate.png new file mode 100644 index 0000000000000000000000000000000000000000..665ca38fefbdc56981b97d88067684f9d47c28b0 Binary files /dev/null and b/docs/html/images/icon_design/stat_sys_ringer_vibrate.png differ diff --git a/docs/html/images/icon_design/stat_sys_signal_4.png b/docs/html/images/icon_design/stat_sys_signal_4.png new file mode 100644 index 0000000000000000000000000000000000000000..a3320cbb4fd216a1215364d7a59856dee0e1fd32 Binary files /dev/null and b/docs/html/images/icon_design/stat_sys_signal_4.png differ diff --git a/docs/html/images/icon_design/stat_sys_signal_flightmode.png b/docs/html/images/icon_design/stat_sys_signal_flightmode.png new file mode 100644 index 0000000000000000000000000000000000000000..516ec2f4258ef5c5356f2b5a3984c5be369c8bbf Binary files /dev/null and b/docs/html/images/icon_design/stat_sys_signal_flightmode.png differ diff --git a/docs/html/images/icon_design/stat_sys_signal_null.png b/docs/html/images/icon_design/stat_sys_signal_null.png new file mode 100644 index 0000000000000000000000000000000000000000..5aa23f6c7348bb667d8ddde6d88340ede768288a Binary files /dev/null and b/docs/html/images/icon_design/stat_sys_signal_null.png differ diff --git a/docs/html/images/icon_design/stat_sys_speakerphone.png b/docs/html/images/icon_design/stat_sys_speakerphone.png new file mode 100644 index 0000000000000000000000000000000000000000..642dfd4891539131dc8023b169aaa075e6c4ddaf Binary files /dev/null and b/docs/html/images/icon_design/stat_sys_speakerphone.png differ diff --git a/docs/html/images/icon_design/stat_sys_warning.png b/docs/html/images/icon_design/stat_sys_warning.png new file mode 100644 index 0000000000000000000000000000000000000000..be00f470ad6a596d0054a6f95b112b835d1c21e0 Binary files /dev/null and b/docs/html/images/icon_design/stat_sys_warning.png differ diff --git a/docs/html/images/icon_design/stat_sys_wifi_signal_4.png b/docs/html/images/icon_design/stat_sys_wifi_signal_4.png new file mode 100644 index 0000000000000000000000000000000000000000..2062aada3e3ea4aae859a5f5bd117df10c0e50ec Binary files /dev/null and b/docs/html/images/icon_design/stat_sys_wifi_signal_4.png differ diff --git a/docs/html/images/icon_design/stat_sys_wifi_unavailable.png b/docs/html/images/icon_design/stat_sys_wifi_unavailable.png new file mode 100644 index 0000000000000000000000000000000000000000..53dd45b31c55fa8cd66fb23310a34e658e43b59c Binary files /dev/null and b/docs/html/images/icon_design/stat_sys_wifi_unavailable.png differ diff --git a/docs/html/images/icon_design/statusbar_light.png b/docs/html/images/icon_design/statusbar_light.png new file mode 100644 index 0000000000000000000000000000000000000000..ddebc2dcb1fa8a31e65f41b65c6cb63306f22226 Binary files /dev/null and b/docs/html/images/icon_design/statusbar_light.png differ diff --git a/docs/html/images/icon_design/statusbar_palette_black.png b/docs/html/images/icon_design/statusbar_palette_black.png new file mode 100644 index 0000000000000000000000000000000000000000..fba096f301f9b55744bcca8247b41d6d7ac98380 Binary files /dev/null and b/docs/html/images/icon_design/statusbar_palette_black.png differ diff --git a/docs/html/images/icon_design/statusbar_palette_fill.png b/docs/html/images/icon_design/statusbar_palette_fill.png new file mode 100644 index 0000000000000000000000000000000000000000..bbf652c5af522ddcc8d54aa0dfe91e8adb690df0 Binary files /dev/null and b/docs/html/images/icon_design/statusbar_palette_fill.png differ diff --git a/docs/html/images/icon_design/statusbar_palette_grey.png b/docs/html/images/icon_design/statusbar_palette_grey.png new file mode 100644 index 0000000000000000000000000000000000000000..0abb7f4b454a5e1f0b4fff8de4f92495319b26d5 Binary files /dev/null and b/docs/html/images/icon_design/statusbar_palette_grey.png differ diff --git a/docs/html/images/icon_design/statusbar_palette_white.png b/docs/html/images/icon_design/statusbar_palette_white.png new file mode 100644 index 0000000000000000000000000000000000000000..8d7ac41e5efc110a65d7ba0c6ec1c1987813a907 Binary files /dev/null and b/docs/html/images/icon_design/statusbar_palette_white.png differ diff --git a/docs/html/images/icon_design/statusbar_structure.png b/docs/html/images/icon_design/statusbar_structure.png new file mode 100644 index 0000000000000000000000000000000000000000..e7243ee6713f104dd6a2d858078f6be9aee192f9 Binary files /dev/null and b/docs/html/images/icon_design/statusbar_structure.png differ diff --git a/docs/html/images/icon_design/tab_icon_selected.png b/docs/html/images/icon_design/tab_icon_selected.png new file mode 100644 index 0000000000000000000000000000000000000000..66a8475073528637fe9a26429cb90706a03d2feb Binary files /dev/null and b/docs/html/images/icon_design/tab_icon_selected.png differ diff --git a/docs/html/images/icon_design/tab_icon_unselected.png b/docs/html/images/icon_design/tab_icon_unselected.png new file mode 100644 index 0000000000000000000000000000000000000000..80ae9c1c96393f904239b128d19235d98ba72df2 Binary files /dev/null and b/docs/html/images/icon_design/tab_icon_unselected.png differ diff --git a/docs/html/images/icon_design/tab_palette_selected_fill.png b/docs/html/images/icon_design/tab_palette_selected_fill.png new file mode 100644 index 0000000000000000000000000000000000000000..7079bda6f7785dafe49f1732cf6edba2f1764d23 Binary files /dev/null and b/docs/html/images/icon_design/tab_palette_selected_fill.png differ diff --git a/docs/html/images/icon_design/tab_selected_light.png b/docs/html/images/icon_design/tab_selected_light.png new file mode 100644 index 0000000000000000000000000000000000000000..3a87c5b1ed630ef9ce7ba693e5c13443b566e884 Binary files /dev/null and b/docs/html/images/icon_design/tab_selected_light.png differ diff --git a/docs/html/images/icon_design/tab_unselected_light.png b/docs/html/images/icon_design/tab_unselected_light.png new file mode 100644 index 0000000000000000000000000000000000000000..f888161875c3b19c4891041b2541a055eae97e2f Binary files /dev/null and b/docs/html/images/icon_design/tab_unselected_light.png differ diff --git a/docs/html/index.jd b/docs/html/index.jd index 883170a3aa87913ce7fdbab5f991cac2129c482a..07d0abe7f6e460e108febf19b3c154f58add6d5b 100644 --- a/docs/html/index.jd +++ b/docs/html/index.jd @@ -5,32 +5,36 @@ home=true

        -
        -

        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. The agenda includes a number of great sessions on Android topics by team engineers and other developers.

        -

        Learn more »

        -
        -
        -
        -
        -
        -
        -
        -
        - -
        -
        - -
        -
        - +
        +
        +

        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. The agenda includes a number of great sessions on Android topics by team engineers and other developers.

        +

        Learn more »

        +
        +
        +
        + +
        + +
        +
        + +
        +
        + +
        +
        +
         
        @@ -116,10 +120,10 @@ home=true 'sdk': { 'layout':"imgLeft", 'icon':"sdk-small.png", - 'name':"SDK 1.5 r1", + 'name':"SDK 1.5 r2", 'img':"sdk-large.png", - 'title':"Android 1.5 SDK r1", - 'desc': "

        The final version of the Android 1.5 SDK is now available. It includes new APIs for Android 1.5, updated developer tools, multiple platform versions, and a Google APIs add-on.

        Download Android 1.5 SDK

        " + 'title':"Android 1.5 SDK", + 'desc': "

        Android 1.5 SDK is now available. It includes new APIs for Android 1.5, updated developer tools, multiple platform versions, and a Google APIs add-on.

        Download Android 1.5 SDK

        " }, 'mapskey': { diff --git a/docs/html/robots.txt b/docs/html/robots.txt index 085b79dc1b68ff7cef912bff1b07f714d5c0a7ca..7046373e9570490b24e49000b5f385386695fee8 100644 --- a/docs/html/robots.txt +++ b/docs/html/robots.txt @@ -1,7 +1,8 @@ -User-Agent: * -Allow: / -Disallow: /gae_shell/ -Disallow: /assets/ -Disallow: /images/ -Disallow: /sdk/preview/ -Sitemap: http://developer.android.com/sitemap.txt +User-Agent: * +Allow: / +Disallow: /gae_shell/ +Disallow: /assets/ +Disallow: /images/ +Disallow: /sdk/preview/ +Disallow: /shareables/ +Sitemap: http://developer.android.com/sitemap.txt diff --git a/docs/html/sdk/1.5_r1/index.jd b/docs/html/sdk/1.5_r1/index.jd index 438ee4bb80ce195d85b20255066366a52e387354..405f56ce960aa1805b4e75cf4f08ea1b8a19b703 100644 --- a/docs/html/sdk/1.5_r1/index.jd +++ b/docs/html/sdk/1.5_r1/index.jd @@ -1,6 +1,7 @@ sdk.version=1.5 sdk.rel.id=1 sdk.date=April 2009 +sdk.not_latest_version=true sdk.win_download=android-sdk-windows-1.5_r1.zip sdk.win_bytes=176263368 diff --git a/docs/html/sdk/1.5_r2/index.jd b/docs/html/sdk/1.5_r2/index.jd new file mode 100644 index 0000000000000000000000000000000000000000..15342a4339923a93c5653191e5054580fab4f956 --- /dev/null +++ b/docs/html/sdk/1.5_r2/index.jd @@ -0,0 +1,87 @@ +sdk.version=1.5 +sdk.rel.id=2 +sdk.date=May 2009 + +sdk.win_download=android-sdk-windows-1.5_r2.zip +sdk.win_bytes=178346828 +sdk.win_checksum=ba54ac6bda45921d442b74b6de6ff6a9 + +sdk.mac_download=android-sdk-mac_x86-1.5_r2.zip +sdk.mac_bytes=169945128 +sdk.mac_checksum=f4e06a5194410243f213d0177713d6c9 + +sdk.linux_download=android-sdk-linux_x86-1.5_r2.zip +sdk.linux_bytes=165035130 +sdk.linux_checksum=1d3c3d099e95a31c43a7b3e6ae307ed3 + +page.title=Android 1.5 SDK, Release 2 +@jd:body + +

        For more information on this SDK release, read the +Release Notes.

        + +

        SDK Contents

        + +

        Development tools

        + +

        The SDK includes a full set 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 in this SDK includes updates from those provided in the previous SDK. The tools also require a different project structure. To use the new tools, you need to migrate your applications to the new development environment. For more information about how to migrate, see Upgrading the SDK. + +

        For more information about the new tools features, see the SDK Release Notes. + +

        Android Platforms

        + +

        This SDK includes multiple Android platform versions that you use to develop applications. For each version, both a fully compliant Android library and system image are provided. The table below lists the platform versions included in this SDK. For more information about a platform version — features, applications included, localizations, API changes, and so on — see its Version Notes.

        + + + + + + + + + + + + + + + + + + +
        PlatformAPI LevelNotesDescription
        Android 1.53Version NotesIncludes a standard Android 1.5 library and system image with a set of development applications. Does not include any external libraries (such as the Maps external library).
        Android 1.12Version NotesIncludes a compliant Android 1.1 library and system image with a set of development applications. Also includes the Maps external library (due to legacy build system issues).
        + +

        SDK Add-Ons

        + +

        An SDK add-on provides a development environment for an Android external library or a customized (but fully compliant) Android system image. This SDK includes the SDK add-on listed below. The Android system API Level required by the add-on is noted.

        + + + + + + + + + + + +
        Add-OnAPI LevelNotesDescription
        Google APIs3 Includes the com.google.android.maps external library, a compliant +system image, a {@link android.location.Geocoder Geocoder} +backend service implementation, documentation, and sample code.
        + +

        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>/platforms/android-1.5/samples/ directory of the SDK package. Note the new location — the SDK now includes multiple platform versions that you can develop against and each has its own sample code directory.

        + +

        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.5_r2/installing.jd b/docs/html/sdk/1.5_r2/installing.jd new file mode 100644 index 0000000000000000000000000000000000000000..69b2c1bef53355ffd04b0853bc9a18c137aeeae8 --- /dev/null +++ b/docs/html/sdk/1.5_r2/installing.jd @@ -0,0 +1,332 @@ +sdk.version=1.5 +sdk.rel.id=2 +sdk.date=April 2009 + +page.title=Installing the Android 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, you can +do so from the +Download page. Once you've downloaded +the SDK, return here.

        + +

        If you encounter any problems during installation, see the +Installation Notes at the bottom of +this page.

        + +

        Upgrading?

        +

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

        + + +

        Preparing for Installation

        + +

        Before you begin, take a moment to confirm that your development machine meets the +System Requirements. +

        + +

        If you will be developing on Eclipse with the Android Development +Tools (ADT) Plugin — the recommended path if you are new to +Android — make sure that you have a suitable version of Eclipse +installed on your computer (3.3 or newer). If you need to install Eclipse, you can +download it from this location:

        + +

        http://www.eclipse.org/downloads/

        + +

        A Java or RCP version of Eclipse is recommended.

        + +

        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>. +The directory contains a local copy of the documentation (accessible by opening +documentation.html in your browser) and the subdirectories +tools/, add-ons/, platforms/, and others. Inside +each subdirectory of platforms/ you'll find samples/, which includes +code samples that are specific to each version of the platform.

        + +

        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 when +using the SDK tools.

        + +

        Optionally, you may want to add the location of the SDK's primary tools directory +to your system PATH. The primary tools/ directory is located at the root of the +SDK folder. 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.

        +
          +
        • 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.
        • +
        + +

        Note that, if you update your SDK in the future, you +should remember to update your PATH settings to point to the new location, if different.

        + +

        If you will be using the Eclipse IDE as your development environment, +the next section describes how to install the Android Development Tools plugin and set up Eclipse. +If you choose not to use Eclipse, you can +develop Android applications in an IDE of your choice and then compile, debug and deploy using +the tools included in the SDK (skip to Next Steps).

        + + +

        Installing the ADT Plugin for Eclipse

        + +

        Android offers a custom plugin for the Eclipse IDE, called Android +Development Tools (ADT), that is designed to give you a powerful, +integrated environment in which to build Android applications. It +extends the capabilites of Eclipse to let you quickly set up new Android +projects, create an application UI, add components based on the Android +Framework API, debug your applications using the Android SDK tools, and even export +signed (or unsigned) APKs in order to distribute your application.

        + +

        In general, using Eclipse with ADT is a highly recommended +approach to Android development and is the fastest way to get started. +(If you prefer to work in an IDE other than Eclipse, +you do not need to install Eclipse or ADT, instead, you can directly +use the SDK tools to build and debug your application.)

        + +

        Once you have Eclipse installed, as described in Preparing for +Installation, follow the steps below to +download the ADT plugin and install it in your respective Eclipse +environment.

        + + + + + + + +
        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 the URL: +
          https://dl-ssl.google.com/android/eclipse/
          +

          If you have trouble aqcuiring the plugin, try using "http" in the URL, + instead of "https" (https is preferred for security reasons).

          +

          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 the + "Android Plugin". + This will select the nested tools: "Android DDMS" and "Android Development Tools". + Click Next.
        12. +
        13. Read and accept the license agreement, then click Next.
        14. +
        15. On the following Installation window, click Finish.
        16. +
        17. The ADT plugin is not digitally signed. 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 the Location: +
          https://dl-ssl.google.com/android/eclipse/
          +

          If you have trouble aqcuiring the plugin, try using "http" in the Location URL, + instead of "https" (https is preferred for security reasons).

          +

          Click OK.

        8. +
        9. Back in the Available Software view, you should see the plugin listed by the URL, + with "Developer Tools" nested within it. Select the checkbox next to + Developer Tools and click Install...
        10. +
        11. On the subsequent Install window, "Android DDMS" and "Android Development Tools" + should both be checked. Click Next.
        12. +
        13. Read and accept the license agreement, then click Finish.
        14. +
        15. Restart Eclipse.
        16. +
        + +
        + +

        Now modify your Eclipse preferences to point to the Android SDK directory:

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

        Done! If you haven't encountered any problems, then you're ready to +begin developing Android applications. See the +Next Steps section for suggestions on how to start.

        + + +

        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 zip file to your local machine and manually install the it: +

        +
          +
        1. Download the ADT zip file (do not unpack it).
        2. +
        3. Follow steps 1 and 2 in the default install instructions (above).
        4. +
        5. In Eclipse 3.3, click New Archive Site....
          + In Eclipse 3.4, click Add Site..., then Archive...
        6. +
        7. Browse and select the downloaded zip file.
        8. +
        9. Follow the remaining procedures, above, starting from steps 5.
        10. +
        +

        To update your plugin once you've installed using the zip file, you will have to +follow these steps again instead of the default update instructions.

        + +

        Other install errors

        + +

        Note that there are features of ADT that require some optional +Eclipse components (for example, WST). If you encounter an error when +installing ADT, your Eclipse installion might not include these 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.

        + + +

        Next Steps

        +

        Once you have completed installation, you are ready to +begin developing applications. Here are a few ways you can get started:

        + +

        Learn about Android

        +
          +
        • Take a look at the Dev + Guide and the types of information it provides
        • +
        • Read an introduction to Android as a platform in What is + Android?
        • +
        • Learn about the Android framework and how applications run on it in + Application + Fundamentals
        • +
        • Take a look at the Android framework API specification in the Reference tab
        • +
        + +

        Explore the SDK

        + + +

        Explore some code

        +
          +
        • Set up a Hello + World application (highly recommended, especially for Eclipse users)
        • +
        • Follow the + Notepad Tutorial to build a full Android application
        • +
        • Create a new project for one of the other sample applications + included in <sdk>/platforms/<platfrom>/samples, + then compile and run it in your development environment
        • +
        + +

        Visit the Android developer groups

        +
          +
        • Take a look at the Community tab to see a list of + Android developers groups. In particular, you might want to look at the + Android + Developers group to get a sense for what the Android developer + community is like.
        • +
        + + +

        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 + in the System Requirements. + 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.5_r2/requirements.jd b/docs/html/sdk/1.5_r2/requirements.jd new file mode 100644 index 0000000000000000000000000000000000000000..4ed38a741261c58ca680afbd35b0399ecfee3719 --- /dev/null +++ b/docs/html/sdk/1.5_r2/requirements.jd @@ -0,0 +1,39 @@ +page.title=System Requirements +@jd:body + +

        The sections below describe the system and software requirements for developing Android applications using the Android SDK tools included in Android SDK, Release .

        + +

        Supported Operating Systems

        +
          +
        • 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

        +
          +
        • Eclipse IDE +
            +
          • Eclipse 3.3 (Europa), 3.4 (Ganymede) +
              +
            • Recommended Eclipse IDE packages: Eclipse IDE for Java EE Developers, Eclipse IDE for Java Developers, Eclipse for RCP/Plug-in Developers
            • +
            • Eclipse JDT plugin (included in most Eclipse IDE packages)
            • +
            • Eclipse Classic IDE package is not supported.
            • +
            +
          • +
          • JDK 5 or JDK 6 (JRE alone is not sufficient)
          • +
          • Android Development Tools plugin (optional)
          • +
          • Not compatible with Gnu Compiler for Java (gcj)
          • +
          +
        • +
        • 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.5_r2/upgrading.jd b/docs/html/sdk/1.5_r2/upgrading.jd new file mode 100644 index 0000000000000000000000000000000000000000..bb5fc60e67f8ab2ad74cd9d62635660da63f0609 --- /dev/null +++ b/docs/html/sdk/1.5_r2/upgrading.jd @@ -0,0 +1,395 @@ +page.title=Upgrading the SDK +sdk.version=1.5_r2 +@jd:body + + +
        +
        + +

        Upgrading the SDK

        +
          +
        • The Android 1.5 SDK uses a new project structure and a new ADT plugin (ADT 0.9).
        • +
        • To move existing projects into the SDK, you must make some minor changes in your + development environment.
        • +
        • The new ADT plugin (ADT 0.9) is not compatible with projects created in previous SDKs.
        • +
        • You need to uninstall your existing ADT plugin, before installing ADT 0.9.
        • +
        + +

        In this document

        +
          +
        1. Install the SDK
        2. +
        3. Update Your Eclipse ADT Plugin
        4. +
        5. Update Your Projects +
            +
          1. Eclipse Users
          2. +
          3. Ant Users
          4. +
          +
        6. +
        7. Migrate Your Applications +
          1. Future-proof your apps
          +
        8. +
        + +

        Migrating references

        +
          +
        1. Android 1.5 API Differences
        2. +
        3. Future-Proofing +Your Apps »
        4. +
        5. UI +framework changes in Android 1.5 »
        6. +
        + +
        +
        + +

        This document describes how to move your development environment and existing +Android applications from an Android 1.0 or 1.1 SDK to the Android 1.5 SDK. +If you are migrating applications from an SDK older than 1.0, please also read the upgrading +document available in the Android 1.0 SDK package.

        + +

        There are several compelling reasons to upgrade, such as new SDK tools +that make developing more efficient and new APIs that allow you to expand the feature-set +of your applications. However, even if you or your applications don't require these enhancements, +it's important that you upgrade to ensure that your applications run properly on the +Android 1.5 platform.

        + +

        The Android 1.5 platform will soon be deployable to devices around the world. +If you have already released Android applications to the public, you should +test the forward-compatibility of your applications on the latest version of the platform +as soon as possible. It's unlikely that you'll encounter breakage in your applications, but +in the interest of maintaining the best user experience, you should take no risks. +So, please install the new Android SDK and test your applications on Android 1.5.

        + +

        For more information on new SDK features and system changes, +see the Android 1.5 Version Notes.

        + + +

        Install the SDK

        + +

        If you haven't yet downloaded the SDK, download from here +and unpack it into a safe location.

        + +

        Before you begin: +If you had previously setup your PATH variable to point to the SDK tools directory, +then you need to update it to point to the new SDK. For example, for a +.bashrc or .bash_profile file:

        +
        export PATH=$PATH:<your_sdk_dir>/tools
        + +

        If you don't use Eclipse for development, +skip to Update Your Projects.

        + + +

        Update Your Eclipse ADT Plugin

        + +

        If you installed ADT-0.9_pre with the early look 1.5 SDK, there have been +additional changes, so please continue with this guide and update to the final ADT 0.9.

        + +

        A new ADT plugin (version 0.9) is required for the Android 1.5 SDK. +Because the component structure has been changed since Android 1.1, +the Android 1.5 SDK does not work with ADT 0.8 (or older) and previously installed SDKs will not +work with ADT 0.9. However, the Android 1.5 SDK includes an Android 1.1 SDK image that you +can build against while using ADT 0.9.

        + +

        For information about using different system images (such as Android 1.1) +while running this SDK, see Developing +In Eclipse, with ADT or In +Other IDEs, as appropriate for your development environment.

        + +

        In order to upgrade your Eclipse IDE to use the new 0.9 ADT, follow the steps below +for your respective version of Eclipse.

        + +

        Uninstall your previous ADT plugin

        + +

        You must uninstall your existing ADT plugin (0.8 or older). If you do not uninstall it, +you will get a conflict with the Android Editors when installing the new ADT. +(If you have already installed ADT-0.9_pre with the early look 1.5 SDK, you can skip this +uninstall procedure and continue to Install the 0.9 ADT plugin).

        + + + + + + + +
        Eclipse 3.3 (Europa)Eclipse 3.4 (Ganymede)
        + +
          +
        1. Select Help > Software Updates > + Manage Configuration.
        2. +
        3. Expand the list in the left panel to reveal the installed tools.
        4. +
        5. Right-click "Android Editors" and click Uninstall. Click OK + to confirm.
        6. +
        7. Restart Eclipse. +

          (Do not uninstall "Android Development Tools".)

        8. +
        +
        + +
          +
        1. Select Help > Software Updates.
        2. +
        3. Select the Installed Software tab.
        4. +
        5. Select "Android Editors". Click Uninstall.
        6. +
        7. In the next window, be sure "Android Editors" is checked, then click Finish + to uninstall.
        8. +
        9. Restart Eclipse. +

          (Do not uninstall "Android Development Tools".)

        10. +
        +
        + + +

        Install the 0.9 ADT plugin

        + +

        Only install the new plugin once you've completed the procedure to +Uninstall your previous ADT plugin.

        + + + + + + + +
        Eclipse 3.3 (Europa)Eclipse 3.4 (Ganymede)
        + +
          +
        1. Select Help > Software Updates > + Find and Install.
        2. +
        3. Select Search for new features to install.
        4. +
        5. Select the Android plugin entry by checking the box next to it, + then click Finish. +

          (Your original entry for the plugin should still be here. If not, see the guide + to Installing the ADT Plugin.) +

        6. +
        7. In the results, expand the entry for the Android plugin and + be sure that "Developer Tools" is checked, then click Next. + (This will install "Android DDMS" and "Android Development Tools".)
        8. +
        9. Read and accept the license agreement, then click Next. +
        10. In the next window, click Finish to start installation.
        11. +
        12. The ADT plugin is not digitally signed. Accept the installation anyway by clicking + Install All.
        13. +
        14. Restart Eclipse.
        15. +
        +
        + +
          +
        1. Select Help > Software Updates.
        2. +
        3. Select the Available Software tab.
        4. +
        5. Expand the entry for the Andriod plugin (may be listed as the location URL) + and select "Developer Tools" by checking the box next to it, then click + Install.
        6. +
        7. On the next window, "Android DDMS" and "Android Development Tools" + should both be checked. Click Finish.
        8. +
        9. Restart Eclipse.
        10. +
        +
        + +

        If you encounter problems, ensure your ADT is fully uninstalled and then +follow the guide to +Installing the ADT Plugin +for Eclipse.

        + +

        Update your Eclipse SDK Preferences

        + +

        The last step is to update your Eclipse preferences to point to the new SDK directory:

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

        Update Your Projects

        + +

        You will now need to update any and all Android projects that you have +developed using a previous version of the Android SDK.

        + + +

        Eclipse users

        + +

        If you use Eclipse to develop applications, use the following procedure to +update each project:

        + +
          +
        1. Right-click on the individual project (in the Package Explorer) + and select Properties.
        2. +
        3. In the properties, open the Android panel and select a "build target" to compile + against. This SDK offers the Android 1.1 and Android 1.5 platforms to choose from. When + you are initially updating your projects to the new SDK, we recommend that you select a build + target with the Android 1.1 platform. Click Apply, then + OK.
        4. +
        + +

        The new plugin creates a gen/ folder in your project, in which it puts the +R.java file +and all automatically generated AIDL java files. If you get an error such as +The type R is already defined, +then you probably need to delete your old R.java or your old auto-generated +AIDL Java files in the src/ folder. +(This does not apply to your own hand-crafted parcelable AIDL java files.)

        + +

        Note that, with the Android 1.5 SDK, there is a new process for running +applications in the Android Emulator. +Specifically, you must create an Android Virtual Device (AVD) before you can launch an instance +of the Emulator. Before attempting to run your applications with the new SDK, +please continue with the section below to +Migrate Your Applications.

        + + +

        Ant users

        + +

        If you build your projects using the Ant tool (rather than with Eclipse), note the +following changes with the new SDK tools.

        + +

        build.xml has changed

        + +

        You must re-create your build.xml file.

        + +

        If you had customized your build.xml, first make a copy of it:

        + +
        +$ cd my-project
        +$ cp build.xml build.xml.old
        +
        + +

        Now use the new android tool (located in your_sdk/tools/) +to create a new build.xml that references +a specific platform target:

        + +
        $ android update project --path /path/to/my-project --target 1
        + +

        The "target" corresponds to an Android platform library (including any add-ons, such as +Google APIs) that you would like to build your project against. You can view a list of available +targets (and their corresponding integer ID) with the command, android list targets. +When you are initially updating your projects to the new SDK, we recommend that you select the +first target ("1"), which uses the Android 1.1 platform library.

        + +

        A gen/ folder will be created the first time you build and your R.java and +your AIDL Java files will be generated in here. You must remove +the old R.java and old auto-generated AIDL java files from the +src/ folder. (This +does not apply to your own hand-crafted parcelabe AIDL java files.)

        + +

        Note: The "activitycreator" tool has been replaced +by the new "android" tool. For information on creating new projects with the android tool, +see the documentation about Developing +In Other IDEs.

        + +

        Note that, with the Android 1.5 SDK, there is a new process for running +applications in the Android Emulator. +Specifically, you must create an Android Virtual Device (AVD) before you can launch an instance +of the Emulator. Before attempting to run your applications with the new SDK, +please continue with the section below to +Migrate Your Applications.

        + + +

        Migrate Your Applications

        + +

        After you have completed the process above to Update Your +Projects, you are strongly encouraged to run each of your applications in an instance +of the emulator running the Android 1.5 system image. It's possible (however, unlikely) +that you'll encounter some breakage in your application when you run your applications on +the Android 1.5 system image. Whether you believe your application will be affected by +platform changes or not, it's very important that you test the application's +forward-compatibility on Android 1.5.

        + +

        To test forward-compatibility, simply run your existing application (as-is) on an Android +Emulator that's running the Android 1.5 system image. The following procedure will guide +you through the process to running your existing applications on an emulator. Please read +the following guide completely before you begin.

        + +

        To test your application on an emulator running Android 1.5:

        +
          +
        1. Update Your Project (you should have done this + already, in the section above).
        2. +
        3. Run your existing project, as-is, on an emulator running the Android 1.5 system image. +

          As mentioned in the guide to Update Your Projects, + you should have selected a "build + target" of "1", which compiles your application against the Android 1.1 system image, so there + should be no new errors in your code.

          +

          Eclipse users: follow the + Eclipse guide to + Running Your Application.

          +

          Ant users: follow the + Ant guide to + Running Your Application +

          During the procedure to Running Your Application, select a "deployment target" + for the AVD that includes the Android 1.5 platform. + If your application utilizes the Google Maps APIs (i.e., + MapView), be certain to select a target that includes the Google APIs.

          +

          Once you complete the procedures to run your application in your respective environment, + linked above, return here.

          +
        4. +
        5. With your application running in the emulator, perform all regular testing on the application + to ensure that it functions normally (in both landscape and portrait orientations).
        6. +
        + +

        Chances are, your application runs just fine on the Android 1.5 platform — +new devices will be able to safely install and run your application and +current users who update their devices will be able to continue using your application as usual. +However, if something doesn't work the way you expect, then you might need to revisit +your project and make any necessary changes to your code.

        + +

        You can check for code breakages caused by API changes by opening your project +in Eclipse, changing the "build target" to one using the Android 1.5 platform, +and see where the ADT identifies errors in your code.

        + + +

        Future-proof your apps

        + +

        There have been several API additions made for this release, but there have been +very few actual API changes. Only a couple (relatively unused) elements +have been removed and a few have been deprecated, so your applications written with the +Android 1.1 system library should work just fine. However, +your application is more likely to encounter problems on Android 1.5 +if it performs any of the following:

        + +
          +
        • Uses internal APIs. That is, APIs that are not officially supported + and not available in the reference documentation. Any un-official APIs are always subject + to change (which is why they're un-official) and some have indeed changed. +
        • +
        • Directly manipulates system settings. There are some settings (such as + GPS, data roaming, bluetooth and others) that used to be writable by + applications but have been changed so that they can only be explicitly modified by the user + through the system settings. Refer to {@link android.provider.Settings.Secure} + to see which settings are now secured and cannot be directly changed by your application. +
        • +
        • Uses View hierarchies that are unreasonably deep (more than 10 or so levels) or + broad (more than 30 total). View hierarchies this big have always been troublesome, but + Android 1.5 is much more efficient at exposing this and your application may crash. +
        • +
        • Makes assumptions about the available hardware. With new support for soft keyboards, + not all devices will have full QWERTY keyboards on the hardware. So if your application + listens for special keypress events that only occur on a keypad, then your application + should degrade gracefully when there is no keyboard available. +
        • +
        • Performs its own layout orientation changes based on the acceletometer (or via other + sensors). Some devices running Android 1.5 will automatically rotate the orientation + (and all devices have the option to turn on auto-rotation), so if your application also + attempts to rotate the orientation, it can result in strange behavior. In addition, if your + application uses the accelerometer to detect shaking and you do not want to rotate the + orientation, then you should lock the current orientation with + android:screenOrientation. +
        • +
        + +

        Please read our blog post on Future-Proofing +Your Apps for more information on the issues mentioned above.

        + +

        For information +about other changes made to Android 1.5, refer to the following documents:

        + + +

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

        diff --git a/docs/html/sdk/RELEASENOTES.jd b/docs/html/sdk/RELEASENOTES.jd index c44cef3a55fba62b0aab489b48950721899ac342..f3a1951f71fbc15e21a8ea153fc5e6d3dccbc229 100644 --- a/docs/html/sdk/RELEASENOTES.jd +++ b/docs/html/sdk/RELEASENOTES.jd @@ -3,8 +3,16 @@ page.title=SDK Release Notes

        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.

        +page at http://developer.android.com/sdk/RELEASENOTES.html.

        + + +

        Android 1.5 SDK, Release 2

        + +

        This SDK release provides the same developer tools as the Android 1.5 SDK, +Release 1, but provides an updated Android 1.5 system image that includes a +security patch for the issue described in the oCert advisory below:

        + +

        http://www.ocert.org/advisories/ocert-2009-006.html

        Android 1.5 SDK, Release 1

        diff --git a/docs/html/sdk/android-1.5-highlights.jd b/docs/html/sdk/android-1.5-highlights.jd index e6c4f88e4d3227af8490decdfc84c4e8d472f52d..ff64e8c28cf9f3a56d9fbda6f72a32c97e991d35 100644 --- a/docs/html/sdk/android-1.5-highlights.jd +++ b/docs/html/sdk/android-1.5-highlights.jd @@ -1,5 +1,4 @@ page.title=Android 1.5 Platform Highlights -sdk.version=1.5_r1 @jd:body

        diff --git a/docs/html/sdk/older_releases.jd b/docs/html/sdk/older_releases.jd index ff57a36ec392233f794cd235bfeeb3f3dc7c15e7..3c2bbd403129d1c10a75dfadaac389b2cbf0f5c5 100644 --- a/docs/html/sdk/older_releases.jd +++ b/docs/html/sdk/older_releases.jd @@ -1,37 +1,67 @@ -page.title=Older Releases +page.title=Other SDK Releases @jd:body -

        - NOTICE: -

        The SDKs listed on this page are "early-look" versions that were released in +

        This page provides a full list of older, obsolete SDK releases, including +non-current versions of active releases and "early look" versions that were +released before Android 1.0. The list is provided for informational purposes +only.

        + +

        If you are just getting started developing on Android, make sure that you +are using the most current SDK available, +to ensure that your applications will be compatible with the latest +Android-powered devices.

        + +

        Obsolete Releases

        + +

        The table below lists Android SDK releases that have been superceded by an +active release and that are now obsolete. If you are using one of these +releases, please upgrade to the current SDK +release.

        + + + + + + + + + + + + + + + + + +
        Release + Platform(s)Date + Description +
        Android 1.5 SDK, Release 1Android 1.5
        Android 1.1
        April 2009Replaced by Android 1.5 SDK, Release 2. Release notes
        Android 1.0 SDK, Release 1Android 1.0September 2008Replaced by Android 1.0 SDK, Release 2. Release notes
        + +

        Non-Compatible Releases

        + + +

        The SDKs listed below are "early-look" versions that were released in the year preceding the full release of Android 1.0 in September 2008. Because these early-look SDKs were released before the Android 1.0 API specification was finalized, they do not provide a compliant Android execution environment. Consequently, applications that you develop in these SDKs will not be able to run on any Android-powered devices.

        -

        If you have an older application that you built in one of the early-look SDKs, - you must migrate it to the Android - 1.0 SDK (or later release) before you will be able to deploy it to - an Android-powered device. To help with this migration, each SDK package below - provides information about API changes from the previous version. You can find - the migration information in the documentation included in each SDK package.

        - -

        If you are just getting started developing on Android, do not use one of these early-look - SDKs. Instead, develop using the most current - SDK release available, to ensure that your applications will be compatible - with Android-powered devices.

        -
        - +

        If you have an older application that you built in one of the early-look +SDKs, you must migrate it to the Android 1.0 SDK (or later release) before you +will be able to deploy it to an Android-powered device. To help with this +migration, each SDK package below provides information about API changes from +the previous version. You can find the migration information in the +documentation included in each SDK package.

        + - - -

        Android 0.9 SDK beta

        -

        August 18, 2008 - Release Notes

        +

        Android SDK m5-rc15

        +

        August 18, 2008 - Release Notes

        - - + @@ -58,15 +88,11 @@ page.title=Older Releases
        PlatformPackagePackage Size MD5 Checksum
        - - - -

        Version m5-rc15

        +

        Version m5-rc15

        March 3, 2008 - Release Notes

        - - + @@ -93,15 +119,11 @@ page.title=Older Releases
        PlatformPackagePackage Size MD5 Checksum
        - - - -

        Version m5-rc14

        +

        Version m5-rc14

        February 12, 2008 - Release Notes

        - - + @@ -131,12 +153,11 @@ page.title=Older Releases -

        Version m3-rc37a

        +

        Version m3-rc37a

        December 14, 2007 - Release Notes

        PlatformPackagePackage Size MD5 Checksum
        - - + @@ -166,12 +187,11 @@ page.title=Older Releases -

        Version m3-rc22a

        +

        Version m3-rc22a

        November 16, 2007 - Release Notes

        PlatformPackagePackage Size MD5 Checksum
        - - + @@ -201,12 +221,11 @@ page.title=Older Releases -

        Version m3-rc20a

        +

        Version m3-rc20a

        November 12, 2007 - Release Notes

        PlatformPackagePackage Size MD5 Checksum
        - - + diff --git a/docs/html/sdk/preview/features.html b/docs/html/sdk/preview/features.html index 392c0895ec2590a3f736de87cc5e80331a9ea601..a2f085cdce34293c001427e2fec406ee6e6a3856 100644 --- a/docs/html/sdk/preview/features.html +++ b/docs/html/sdk/preview/features.html @@ -133,10 +133,10 @@
      12. Current SDK Release

        @@ -185,16 +184,6 @@ - - - diff --git a/docs/html/sdk/sdk_toc.cs b/docs/html/sdk/sdk_toc.cs index 4b55b56177ce1ca91691bb277b6644396f4e12a8..2079dd8a62f36f3ebdd71385e4995e42579b13cc 100644 --- a/docs/html/sdk/sdk_toc.cs +++ b/docs/html/sdk/sdk_toc.cs @@ -26,19 +26,12 @@
      13. Android 1.1 Version Notes
      14. -
      15. -

        Native Development Tools

        - -
      16. Previous SDK Releases

      17. diff --git a/docs/html/search.jd b/docs/html/search.jd index 0a802a6bfcbe76107b55efd7293e66175751f0e3..defba3087af340ae0a850b5b3815d758372e2db6 100644 --- a/docs/html/search.jd +++ b/docs/html/search.jd @@ -2,7 +2,7 @@ page.title=Search Results @jd:body - +
        PlatformPackagePackage Size MD5 Checksum