summaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/config/README.txt5
-rw-r--r--core/java/android/accounts/AccountAuthenticatorActivity.java2
-rw-r--r--core/java/android/accounts/AccountManager.java3
-rw-r--r--core/java/android/accounts/AccountManagerService.java12
-rw-r--r--core/java/android/accounts/ChooseAccountActivity.java8
-rw-r--r--core/java/android/accounts/GrantCredentialsPermissionActivity.java17
-rw-r--r--core/java/android/animation/AnimatorInflater.java6
-rw-r--r--core/java/android/animation/LayoutTransition.java12
-rwxr-xr-xcore/java/android/animation/ValueAnimator.java8
-rw-r--r--core/java/android/app/ActionBar.java87
-rw-r--r--core/java/android/app/Activity.java6
-rw-r--r--core/java/android/app/ActivityGroup.java2
-rw-r--r--core/java/android/app/ActivityManager.java147
-rw-r--r--core/java/android/app/ActivityManagerNative.java127
-rw-r--r--core/java/android/app/ActivityThread.java82
-rw-r--r--core/java/android/app/ApplicationPackageManager.java59
-rw-r--r--core/java/android/app/ApplicationThreadNative.java6
-rw-r--r--core/java/android/app/FullBackupAgent.java76
-rw-r--r--core/java/android/app/IActivityManager.java19
-rw-r--r--core/java/android/app/IApplicationThread.java4
-rw-r--r--core/java/android/app/IBackupAgent.aidl1
-rw-r--r--core/java/android/app/IProcessObserver.aidl25
-rw-r--r--core/java/android/app/IThumbnailRetriever.aidl24
-rw-r--r--core/java/android/app/Instrumentation.java1
-rw-r--r--core/java/android/app/NotificationManager.java3
-rw-r--r--core/java/android/app/Service.java20
-rw-r--r--core/java/android/app/WallpaperManager.java8
-rw-r--r--core/java/android/app/admin/DeviceAdminReceiver.java3
-rw-r--r--core/java/android/app/backup/BackupAgent.java15
-rw-r--r--core/java/android/app/backup/FullBackup.java40
-rw-r--r--core/java/android/app/backup/FullBackupAgent.java183
-rw-r--r--core/java/android/app/backup/IBackupManager.aidl38
-rw-r--r--core/java/android/app/backup/IFullBackupRestoreObserver.aidl69
-rw-r--r--core/java/android/bluetooth/BluetoothHeadset.java9
-rw-r--r--core/java/android/content/ContentResolver.java5
-rw-r--r--core/java/android/content/ContentService.java5
-rw-r--r--core/java/android/content/Context.java3
-rw-r--r--core/java/android/content/Intent.java17
-rw-r--r--core/java/android/content/IntentFilter.java9
-rw-r--r--core/java/android/content/pm/ApplicationInfo.java13
-rw-r--r--core/java/android/content/pm/IPackageManager.aidl4
-rw-r--r--core/java/android/content/pm/PackageManager.java204
-rw-r--r--core/java/android/content/pm/PackageParser.java27
-rw-r--r--core/java/android/content/pm/ServiceInfo.java20
-rw-r--r--core/java/android/content/pm/UserInfo.aidl20
-rw-r--r--core/java/android/content/pm/UserInfo.java105
-rw-r--r--core/java/android/content/res/AssetManager.java3
-rw-r--r--core/java/android/content/res/StringBlock.java3
-rw-r--r--core/java/android/database/AbstractCursor.java3
-rw-r--r--core/java/android/database/CursorToBulkCursorAdaptor.java3
-rw-r--r--core/java/android/database/DatabaseUtils.java3
-rw-r--r--core/java/android/database/sqlite/SQLiteCursor.java19
-rw-r--r--core/java/android/database/sqlite/SQLiteDatabase.java86
-rw-r--r--core/java/android/database/sqlite/SQLiteDirectCursorDriver.java2
-rw-r--r--core/java/android/database/sqlite/SQLiteProgram.java12
-rw-r--r--core/java/android/database/sqlite/SQLiteQuery.java3
-rw-r--r--core/java/android/database/sqlite/SQLiteStatement.java23
-rw-r--r--core/java/android/ddm/DdmHandleAppName.java3
-rw-r--r--core/java/android/ddm/DdmHandleExit.java3
-rw-r--r--core/java/android/ddm/DdmHandleHeap.java17
-rw-r--r--core/java/android/ddm/DdmHandleHello.java11
-rw-r--r--core/java/android/ddm/DdmHandleProfiling.java9
-rw-r--r--core/java/android/ddm/DdmHandleThread.java3
-rw-r--r--core/java/android/ddm/DdmRegister.java3
-rwxr-xr-xcore/java/android/gesture/Gesture.java2
-rw-r--r--core/java/android/hardware/Camera.java432
-rw-r--r--core/java/android/hardware/Sensor.java12
-rw-r--r--core/java/android/hardware/SensorEvent.java31
-rw-r--r--core/java/android/inputmethodservice/KeyboardView.java33
-rw-r--r--core/java/android/inputmethodservice/SoftInputWindow.java57
-rw-r--r--core/java/android/net/ConnectivityManager.java11
-rw-r--r--core/java/android/net/INetworkPolicyManager.aidl (renamed from core/config/sdk/android/util/ConfigBuildFlags.java)23
-rw-r--r--core/java/android/net/NetworkPolicyManager.java73
-rw-r--r--core/java/android/net/NetworkStats.aidl19
-rw-r--r--core/java/android/net/NetworkStats.java198
-rw-r--r--core/java/android/net/SSLCertificateSocketFactory.java16
-rw-r--r--core/java/android/net/SntpClient.java7
-rw-r--r--core/java/android/net/TrafficStats.java136
-rw-r--r--core/java/android/net/http/Headers.java5
-rw-r--r--core/java/android/net/http/HttpLog.java3
-rw-r--r--core/java/android/net/http/HttpsConnection.java6
-rw-r--r--core/java/android/os/AsyncTask.java11
-rw-r--r--core/java/android/os/BatteryStats.java497
-rw-r--r--core/java/android/os/Binder.java5
-rw-r--r--core/java/android/os/Build.java5
-rw-r--r--core/java/android/os/Debug.java9
-rw-r--r--core/java/android/os/Environment.java22
-rw-r--r--core/java/android/os/INetworkManagementService.aidl24
-rw-r--r--core/java/android/os/Looper.java1
-rw-r--r--core/java/android/os/MemoryFile.java7
-rw-r--r--core/java/android/os/MessageQueue.java3
-rw-r--r--core/java/android/os/ParcelFileDescriptor.java80
-rw-r--r--core/java/android/os/PowerManager.java43
-rw-r--r--core/java/android/os/Process.java6
-rw-r--r--core/java/android/os/RecoverySystem.java7
-rw-r--r--core/java/android/os/StrictMode.java7
-rw-r--r--core/java/android/pim/ICalendar.java3
-rw-r--r--core/java/android/pim/RecurrenceSet.java5
-rw-r--r--core/java/android/preference/MultiSelectListPreference.java6
-rw-r--r--core/java/android/preference/Preference.java15
-rw-r--r--core/java/android/preference/PreferenceActivity.java123
-rw-r--r--core/java/android/preference/PreferenceFragment.java11
-rw-r--r--core/java/android/provider/BrowserContract.java7
-rw-r--r--core/java/android/provider/Calendar.java233
-rw-r--r--core/java/android/provider/ContactsContract.java21
-rw-r--r--core/java/android/provider/MediaStore.java10
-rw-r--r--core/java/android/provider/Settings.java38
-rw-r--r--core/java/android/provider/Telephony.java19
-rw-r--r--core/java/android/server/BluetoothA2dpService.java14
-rw-r--r--core/java/android/server/BluetoothAdapterProperties.java3
-rw-r--r--core/java/android/server/BluetoothBondState.java86
-rw-r--r--core/java/android/server/BluetoothInputProfileHandler.java26
-rw-r--r--core/java/android/server/BluetoothPanProfileHandler.java28
-rwxr-xr-xcore/java/android/server/BluetoothService.java129
-rw-r--r--core/java/android/service/wallpaper/WallpaperService.java4
-rw-r--r--core/java/android/speech/RecognitionListener.java3
-rw-r--r--core/java/android/speech/RecognizerIntent.java39
-rw-r--r--core/java/android/speech/SpeechRecognizer.java16
-rw-r--r--core/java/android/speech/srec/Recognizer.java1
-rw-r--r--core/java/android/speech/tts/BlockingMediaPlayer.java146
-rw-r--r--core/java/android/speech/tts/FileSynthesisRequest.java252
-rwxr-xr-xcore/java/android/speech/tts/ITextToSpeechCallback.aidl (renamed from core/java/android/speech/tts/ITtsCallback.aidl)8
-rw-r--r--core/java/android/speech/tts/ITextToSpeechService.aidl140
-rwxr-xr-xcore/java/android/speech/tts/ITts.aidl69
-rw-r--r--core/java/android/speech/tts/PlaybackSynthesisRequest.java324
-rw-r--r--core/java/android/speech/tts/SynthesisRequest.java200
-rwxr-xr-xcore/java/android/speech/tts/TextToSpeech.java1460
-rw-r--r--core/java/android/speech/tts/TextToSpeechService.java713
-rw-r--r--core/java/android/text/BoringLayout.java44
-rw-r--r--core/java/android/text/CharSequenceIterator.java100
-rw-r--r--core/java/android/text/GraphicsOperations.java7
-rw-r--r--core/java/android/text/Selection.java46
-rw-r--r--core/java/android/text/SpannableStringBuilder.java84
-rw-r--r--core/java/android/text/StaticLayout.java2
-rw-r--r--core/java/android/text/TextLine.java28
-rw-r--r--core/java/android/text/TextUtils.java76
-rw-r--r--core/java/android/text/method/ArrowKeyMovementMethod.java20
-rw-r--r--core/java/android/text/method/BaseMovementMethod.java22
-rw-r--r--core/java/android/text/method/WordIterator.java220
-rw-r--r--core/java/android/text/style/SuggestionSpan.aidl (renamed from core/config/debug/android/util/ConfigBuildFlags.java)11
-rw-r--r--core/java/android/text/style/SuggestionSpan.java157
-rw-r--r--core/java/android/text/style/TextAppearanceSpan.java5
-rw-r--r--core/java/android/util/Config.java60
-rw-r--r--core/java/android/util/JsonReader.java19
-rw-r--r--core/java/android/util/LruCache.java3
-rw-r--r--core/java/android/util/NtpTrustedTime.java97
-rw-r--r--core/java/android/util/TimeUtils.java2
-rw-r--r--core/java/android/util/TrustedTime.java55
-rw-r--r--core/java/android/util/Xml.java40
-rw-r--r--core/java/android/view/GLES20Canvas.java156
-rw-r--r--core/java/android/view/GLES20Layer.java81
-rw-r--r--core/java/android/view/GLES20RenderLayer.java91
-rw-r--r--core/java/android/view/GLES20TextureLayer.java76
-rw-r--r--[-rwxr-xr-x]core/java/android/view/GestureDetector.java28
-rw-r--r--core/java/android/view/HardwareCanvas.java8
-rw-r--r--core/java/android/view/HardwareLayer.java12
-rw-r--r--core/java/android/view/HardwareRenderer.java257
-rwxr-xr-xcore/java/android/view/InputDevice.java10
-rwxr-xr-xcore/java/android/view/InputEvent.java46
-rw-r--r--core/java/android/view/InputEventConsistencyVerifier.java730
-rwxr-xr-xcore/java/android/view/KeyEvent.java122
-rw-r--r--core/java/android/view/LayoutInflater.java82
-rw-r--r--core/java/android/view/MotionEvent.java1087
-rwxr-xr-xcore/java/android/view/OrientationEventListener.java3
-rw-r--r--core/java/android/view/PointerIcon.aidl (renamed from core/config/ndebug/android/util/ConfigBuildFlags.java)11
-rw-r--r--core/java/android/view/PointerIcon.java435
-rw-r--r--core/java/android/view/ScaleGestureDetector.java23
-rw-r--r--core/java/android/view/SurfaceView.java5
-rw-r--r--core/java/android/view/TextureView.java334
-rw-r--r--core/java/android/view/VelocityTracker.java335
-rw-r--r--core/java/android/view/View.java916
-rw-r--r--core/java/android/view/ViewAncestor.java (renamed from core/java/android/view/ViewRoot.java)243
-rw-r--r--[-rwxr-xr-x]core/java/android/view/ViewConfiguration.java41
-rw-r--r--core/java/android/view/ViewDebug.java231
-rw-r--r--core/java/android/view/ViewGroup.java802
-rw-r--r--core/java/android/view/ViewParent.java19
-rw-r--r--core/java/android/view/WindowManager.java6
-rw-r--r--core/java/android/view/WindowManagerImpl.java33
-rw-r--r--core/java/android/view/WindowManagerPolicy.java2
-rwxr-xr-xcore/java/android/view/WindowOrientationListener.java3
-rw-r--r--core/java/android/view/accessibility/AccessibilityEvent.java485
-rw-r--r--core/java/android/view/accessibility/AccessibilityManager.java38
-rw-r--r--core/java/android/view/accessibility/AccessibilityRecord.java417
-rw-r--r--core/java/android/view/accessibility/IAccessibilityManager.aidl2
-rw-r--r--core/java/android/view/inputmethod/BaseInputConnection.java6
-rw-r--r--core/java/android/view/inputmethod/InputConnectionWrapper.java3
-rw-r--r--core/java/android/view/inputmethod/InputMethodInfo.java7
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java60
-rw-r--r--core/java/android/view/inputmethod/InputMethodSubtype.java35
-rw-r--r--core/java/android/webkit/BrowserFrame.java21
-rw-r--r--core/java/android/webkit/HTML5VideoFullScreen.java5
-rw-r--r--core/java/android/webkit/HTML5VideoViewProxy.java14
-rw-r--r--core/java/android/webkit/JniUtil.java6
-rw-r--r--core/java/android/webkit/WebSettings.java31
-rw-r--r--core/java/android/webkit/WebTextView.java3
-rw-r--r--core/java/android/webkit/WebView.java249
-rw-r--r--core/java/android/webkit/WebViewCore.java46
-rw-r--r--core/java/android/webkit/webdriver/By.java252
-rw-r--r--core/java/android/webkit/webdriver/WebDriver.java843
-rw-r--r--core/java/android/webkit/webdriver/WebDriverException.java38
-rw-r--r--core/java/android/webkit/webdriver/WebElement.java388
-rw-r--r--core/java/android/webkit/webdriver/WebElementNotFoundException.java41
-rw-r--r--core/java/android/webkit/webdriver/WebElementStaleException.java42
-rw-r--r--core/java/android/webkit/webdriver/WebViewClient.java125
-rw-r--r--core/java/android/webkit/webdriver/WebchromeClientWrapper.java193
-rw-r--r--core/java/android/widget/AbsListView.java42
-rw-r--r--core/java/android/widget/AbsSeekBar.java3
-rw-r--r--core/java/android/widget/AbsoluteLayout.java5
-rw-r--r--core/java/android/widget/AdapterView.java30
-rw-r--r--core/java/android/widget/AdapterViewAnimator.java66
-rw-r--r--core/java/android/widget/CheckedTextView.java9
-rw-r--r--core/java/android/widget/CompoundButton.java19
-rw-r--r--core/java/android/widget/CursorAdapter.java3
-rw-r--r--core/java/android/widget/CursorTreeAdapter.java3
-rw-r--r--core/java/android/widget/DatePicker.java89
-rw-r--r--core/java/android/widget/ExpandableListView.java27
-rw-r--r--core/java/android/widget/FrameLayout.java8
-rw-r--r--core/java/android/widget/ImageView.java34
-rw-r--r--core/java/android/widget/LinearLayout.java236
-rw-r--r--core/java/android/widget/ListView.java62
-rw-r--r--core/java/android/widget/MultiAutoCompleteTextView.java6
-rw-r--r--core/java/android/widget/PopupWindow.java8
-rw-r--r--core/java/android/widget/ProgressBar.java54
-rw-r--r--core/java/android/widget/RelativeLayout.java5
-rw-r--r--core/java/android/widget/RemoteViews.java106
-rw-r--r--core/java/android/widget/RemoteViewsAdapter.java21
-rw-r--r--core/java/android/widget/RemoteViewsService.java71
-rw-r--r--core/java/android/widget/ScrollView.java5
-rw-r--r--core/java/android/widget/SimpleCursorAdapter.java14
-rw-r--r--core/java/android/widget/StackView.java44
-rw-r--r--core/java/android/widget/TabWidget.java15
-rw-r--r--core/java/android/widget/TextView.java1482
-rw-r--r--core/java/android/widget/TimePicker.java5
-rw-r--r--core/java/android/widget/ZoomButtonsController.java10
-rw-r--r--core/java/com/android/internal/app/ActionBarImpl.java281
-rwxr-xr-xcore/java/com/android/internal/app/IMediaContainerService.aidl5
-rw-r--r--core/java/com/android/internal/app/ResolverActivity.java3
-rw-r--r--core/java/com/android/internal/content/NativeLibraryHelper.java3
-rw-r--r--core/java/com/android/internal/content/PackageHelper.java11
-rw-r--r--core/java/com/android/internal/net/DomainNameValidator.java3
-rw-r--r--core/java/com/android/internal/os/BatteryStatsImpl.java243
-rw-r--r--core/java/com/android/internal/os/BinderInternal.java1
-rw-r--r--core/java/com/android/internal/os/RuntimeInit.java7
-rw-r--r--core/java/com/android/internal/os/SamplingProfilerIntegration.java57
-rw-r--r--core/java/com/android/internal/os/ZygoteInit.java92
-rw-r--r--core/java/com/android/internal/statusbar/IStatusBar.aidl1
-rw-r--r--core/java/com/android/internal/statusbar/IStatusBarService.aidl1
-rw-r--r--core/java/com/android/internal/view/IInputConnectionWrapper.java2
-rw-r--r--core/java/com/android/internal/view/IInputContext.aidl1
-rw-r--r--core/java/com/android/internal/view/IInputMethodManager.aidl1
-rw-r--r--core/java/com/android/internal/view/InputConnectionWrapper.java2
-rw-r--r--core/java/com/android/internal/view/StandaloneActionMode.java2
-rw-r--r--core/java/com/android/internal/view/menu/ActionMenuItemView.java11
-rw-r--r--core/java/com/android/internal/view/menu/ActionMenuPresenter.java459
-rw-r--r--core/java/com/android/internal/view/menu/ActionMenuView.java433
-rw-r--r--core/java/com/android/internal/view/menu/BaseMenuPresenter.java195
-rw-r--r--core/java/com/android/internal/view/menu/ExpandedMenuView.java25
-rw-r--r--core/java/com/android/internal/view/menu/IconMenuItemView.java4
-rw-r--r--core/java/com/android/internal/view/menu/IconMenuPresenter.java141
-rw-r--r--core/java/com/android/internal/view/menu/IconMenuView.java99
-rw-r--r--core/java/com/android/internal/view/menu/ListMenuItemView.java16
-rw-r--r--core/java/com/android/internal/view/menu/ListMenuPresenter.java202
-rw-r--r--core/java/com/android/internal/view/menu/MenuBuilder.java761
-rw-r--r--core/java/com/android/internal/view/menu/MenuDialogHelper.java59
-rw-r--r--core/java/com/android/internal/view/menu/MenuItemImpl.java189
-rw-r--r--core/java/com/android/internal/view/menu/MenuPopupHelper.java148
-rw-r--r--core/java/com/android/internal/view/menu/MenuPresenter.java110
-rw-r--r--core/java/com/android/internal/view/menu/MenuView.java14
-rw-r--r--core/java/com/android/internal/view/menu/SubMenuBuilder.java12
-rw-r--r--core/java/com/android/internal/widget/AbsActionBarView.java213
-rw-r--r--core/java/com/android/internal/widget/ActionBarContainer.java47
-rw-r--r--core/java/com/android/internal/widget/ActionBarContextView.java97
-rw-r--r--core/java/com/android/internal/widget/ActionBarView.java335
-rw-r--r--core/java/com/android/internal/widget/EditableInputConnection.java1
-rw-r--r--core/java/com/android/internal/widget/IRemoteViewsFactory.aidl2
-rw-r--r--core/java/com/android/internal/widget/PasswordEntryKeyboardHelper.java8
-rw-r--r--core/java/com/android/internal/widget/PointerLocationView.java40
-rw-r--r--core/java/com/google/android/mms/pdu/EncodedStringValue.java3
-rwxr-xr-xcore/java/com/google/android/mms/pdu/PduParser.java3
-rw-r--r--core/java/com/google/android/mms/pdu/PduPersister.java3
-rw-r--r--core/java/com/google/android/mms/util/AbstractCache.java3
-rw-r--r--core/java/com/google/android/mms/util/PduCache.java3
-rw-r--r--core/jni/Android.mk14
-rw-r--r--core/jni/AndroidRuntime.cpp216
-rw-r--r--core/jni/android/graphics/Bitmap.cpp4
-rw-r--r--core/jni/android/graphics/BitmapFactory.cpp131
-rw-r--r--core/jni/android/graphics/BitmapRegionDecoder.cpp24
-rw-r--r--core/jni/android/graphics/Camera.cpp2
-rw-r--r--core/jni/android/graphics/Canvas.cpp124
-rw-r--r--core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp63
-rw-r--r--core/jni/android/graphics/Graphics.cpp90
-rw-r--r--core/jni/android/graphics/GraphicsJNI.h33
-rw-r--r--core/jni/android/graphics/HarfbuzzSkia.cpp232
-rw-r--r--core/jni/android/graphics/HarfbuzzSkia.h64
-rw-r--r--core/jni/android/graphics/Interpolator.cpp5
-rw-r--r--core/jni/android/graphics/LayerRasterizer.cpp4
-rw-r--r--core/jni/android/graphics/MaskFilter.cpp8
-rw-r--r--core/jni/android/graphics/Movie.cpp16
-rw-r--r--core/jni/android/graphics/NinePatch.cpp28
-rw-r--r--core/jni/android/graphics/NinePatchImpl.cpp6
-rw-r--r--core/jni/android/graphics/Paint.cpp285
-rw-r--r--core/jni/android/graphics/PathEffect.cpp20
-rw-r--r--core/jni/android/graphics/Region.cpp31
-rw-r--r--core/jni/android/graphics/RtlProperties.h58
-rw-r--r--core/jni/android/graphics/Shader.cpp22
-rw-r--r--core/jni/android/graphics/SurfaceTexture.cpp9
-rw-r--r--core/jni/android/graphics/TextLayout.cpp114
-rw-r--r--core/jni/android/graphics/TextLayout.h80
-rw-r--r--core/jni/android/graphics/TextLayoutCache.cpp698
-rw-r--r--core/jni/android/graphics/TextLayoutCache.h266
-rw-r--r--core/jni/android/graphics/Typeface.cpp27
-rw-r--r--core/jni/android/graphics/YuvToJpegEncoder.cpp1
-rw-r--r--core/jni/android/opengl/util.cpp79
-rw-r--r--core/jni/android_app_NativeActivity.cpp23
-rw-r--r--core/jni/android_app_backup_FullBackup.cpp130
-rw-r--r--core/jni/android_backup_BackupDataInput.cpp17
-rw-r--r--core/jni/android_backup_BackupDataOutput.cpp15
-rw-r--r--core/jni/android_backup_BackupHelperDispatcher.cpp29
-rw-r--r--core/jni/android_backup_FileBackupHelperBase.cpp17
-rwxr-xr-xcore/jni/android_bluetooth_BluetoothAudioGateway.cpp22
-rw-r--r--core/jni/android_bluetooth_HeadsetBase.cpp30
-rw-r--r--core/jni/android_bluetooth_common.cpp4
-rw-r--r--core/jni/android_content_res_Configuration.cpp38
-rw-r--r--core/jni/android_content_res_ObbScanner.cpp32
-rw-r--r--core/jni/android_database_SQLiteStatement.cpp2
-rw-r--r--core/jni/android_emoji_EmojiFactory.cpp31
-rw-r--r--core/jni/android_graphics_PixelFormat.cpp11
-rw-r--r--core/jni/android_hardware_Camera.cpp49
-rw-r--r--core/jni/android_hardware_UsbDevice.cpp7
-rw-r--r--core/jni/android_hardware_UsbDeviceConnection.cpp7
-rw-r--r--core/jni/android_media_AudioRecord.cpp26
-rw-r--r--core/jni/android_media_AudioSystem.cpp34
-rw-r--r--core/jni/android_media_AudioTrack.cpp67
-rw-r--r--core/jni/android_media_JetPlayer.cpp49
-rw-r--r--core/jni/android_media_ToneGenerator.cpp6
-rw-r--r--core/jni/android_net_LocalSocketImpl.cpp88
-rw-r--r--core/jni/android_net_NetUtils.cpp41
-rw-r--r--core/jni/android_net_TrafficStats.cpp14
-rw-r--r--core/jni/android_net_wifi_Wifi.cpp415
-rw-r--r--core/jni/android_nio_utils.cpp9
-rw-r--r--core/jni/android_opengl_GLES10.cpp186
-rw-r--r--core/jni/android_opengl_GLES10Ext.cpp65
-rw-r--r--core/jni/android_opengl_GLES11.cpp289
-rw-r--r--core/jni/android_opengl_GLES11Ext.cpp602
-rw-r--r--core/jni/android_opengl_GLES20.cpp470
-rw-r--r--core/jni/android_os_FileUtils.cpp58
-rw-r--r--core/jni/android_os_MemoryFile.cpp9
-rw-r--r--core/jni/android_os_MessageQueue.cpp10
-rw-r--r--core/jni/android_os_ParcelFileDescriptor.cpp90
-rw-r--r--core/jni/android_os_Power.cpp11
-rw-r--r--core/jni/android_os_SystemProperties.cpp46
-rw-r--r--core/jni/android_pim_EventRecurrence.cpp34
-rw-r--r--core/jni/android_server_BluetoothEventLoop.cpp1
-rw-r--r--core/jni/android_server_BluetoothService.cpp7
-rw-r--r--core/jni/android_server_Watchdog.cpp4
-rw-r--r--core/jni/android_text_AndroidBidi.cpp23
-rw-r--r--core/jni/android_text_AndroidCharacter.cpp78
-rw-r--r--core/jni/android_util_AssetManager.cpp312
-rw-r--r--core/jni/android_util_Binder.cpp103
-rw-r--r--core/jni/android_util_Binder.h17
-rw-r--r--core/jni/android_util_EventLog.cpp5
-rw-r--r--core/jni/android_util_Log.cpp47
-rw-r--r--core/jni/android_util_Process.cpp184
-rw-r--r--core/jni/android_util_StringBlock.cpp47
-rw-r--r--core/jni/android_util_XmlBlock.cpp82
-rw-r--r--core/jni/android_view_Display.cpp9
-rw-r--r--core/jni/android_view_GLES20Canvas.cpp127
-rw-r--r--core/jni/android_view_InputQueue.cpp2
-rw-r--r--core/jni/android_view_KeyCharacterMap.cpp13
-rw-r--r--core/jni/android_view_MotionEvent.cpp219
-rw-r--r--core/jni/android_view_MotionEvent.h9
-rw-r--r--core/jni/android_view_PointerIcon.cpp149
-rw-r--r--core/jni/android_view_PointerIcon.h80
-rw-r--r--core/jni/android_view_Surface.cpp127
-rw-r--r--core/jni/android_view_TextureView.cpp50
-rw-r--r--core/jni/android_view_VelocityTracker.cpp208
-rw-r--r--core/jni/android_view_ViewAncestor.cpp (renamed from core/jni/android_view_ViewRoot.cpp)8
-rw-r--r--core/jni/com_android_internal_graphics_NativeUtils.cpp9
-rw-r--r--core/jni/com_android_internal_os_ZygoteInit.cpp28
-rw-r--r--core/jni/com_google_android_gles_jni_EGLImpl.cpp122
-rw-r--r--core/jni/com_google_android_gles_jni_GLImpl.cpp558
-rw-r--r--core/res/AndroidManifest.xml28
-rw-r--r--core/res/res/drawable-hdpi/btn_cab_done_default_holo.9.pngbin2247 -> 1115 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_cab_done_focused_holo.9.pngbin2660 -> 1540 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_cab_done_pressed_holo.9.pngbin1561 -> 1578 bytes
-rw-r--r--core/res/res/drawable-hdpi/keyboard_textfield_selected.9.pngbin955 -> 0 bytes
-rw-r--r--core/res/res/drawable-hdpi/password_keyboard_background_holo.9.pngbin0 -> 1108 bytes
-rwxr-xr-xcore/res/res/drawable-hdpi/stat_sys_adb.pngbin703 -> 1302 bytes
-rw-r--r--core/res/res/drawable-hdpi/text_edit_suggestions_bottom_window.9.pngbin0 -> 929 bytes
-rw-r--r--core/res/res/drawable-hdpi/text_edit_suggestions_top_window.9.pngbin0 -> 943 bytes
-rw-r--r--core/res/res/drawable-hdpi/textfield_bg_activated_holo_dark.9.pngbin0 -> 213 bytes
-rw-r--r--core/res/res/drawable-hdpi/textfield_bg_default_holo_dark.9.pngbin0 -> 214 bytes
-rw-r--r--core/res/res/drawable-hdpi/textfield_bg_disabled_focused_holo_dark.9.pngbin0 -> 210 bytes
-rw-r--r--core/res/res/drawable-hdpi/textfield_bg_disabled_holo_dark.9.pngbin0 -> 212 bytes
-rw-r--r--core/res/res/drawable-hdpi/textfield_bg_focused_holo_dark.9.pngbin0 -> 209 bytes
-rw-r--r--core/res/res/drawable-ldpi/keyboard_textfield_selected.9.pngbin574 -> 0 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_cab_done_default_holo.9.pngbin1347 -> 762 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_cab_done_focused_holo.9.pngbin1594 -> 1024 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_cab_done_pressed_holo.9.pngbin1008 -> 854 bytes
-rw-r--r--core/res/res/drawable-mdpi/keyboard_textfield_selected.9.pngbin782 -> 0 bytes
-rw-r--r--core/res/res/drawable-mdpi/pointer_spot_anchor.pngbin0 -> 6817 bytes
-rw-r--r--core/res/res/drawable-mdpi/pointer_spot_anchor_icon.xml5
-rw-r--r--core/res/res/drawable-mdpi/pointer_spot_hover.pngbin0 -> 9669 bytes
-rw-r--r--core/res/res/drawable-mdpi/pointer_spot_hover_icon.xml5
-rw-r--r--core/res/res/drawable-mdpi/pointer_spot_touch.pngbin0 -> 2880 bytes
-rw-r--r--core/res/res/drawable-mdpi/pointer_spot_touch_icon.xml5
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_adb.pngbin604 -> 806 bytes
-rw-r--r--core/res/res/drawable-mdpi/text_edit_suggestions_bottom_window.9.pngbin0 -> 605 bytes
-rw-r--r--core/res/res/drawable-mdpi/text_edit_suggestions_top_window.9.pngbin0 -> 585 bytes
-rw-r--r--core/res/res/drawable-nodpi/platlogo.pngbin83490 -> 65254 bytes
-rw-r--r--core/res/res/drawable/btn_cab_done.xml4
-rw-r--r--core/res/res/layout-large/action_bar_home.xml39
-rw-r--r--core/res/res/layout-large/action_mode_close_item.xml38
-rw-r--r--core/res/res/layout-xlarge/status_bar_latest_event_content.xml48
-rw-r--r--core/res/res/layout/action_bar_home.xml13
-rw-r--r--core/res/res/layout/action_bar_title_item.xml3
-rw-r--r--core/res/res/layout/action_menu_item_layout.xml17
-rw-r--r--core/res/res/layout/action_menu_layout.xml5
-rw-r--r--core/res/res/layout/action_mode_close_item.xml14
-rw-r--r--core/res/res/layout/input_method_extract_view.xml1
-rw-r--r--core/res/res/layout/keyguard_screen_password_landscape.xml99
-rw-r--r--core/res/res/layout/keyguard_screen_password_portrait.xml92
-rw-r--r--core/res/res/layout/keyguard_screen_status_land.xml148
-rw-r--r--core/res/res/layout/keyguard_screen_status_port.xml145
-rw-r--r--core/res/res/layout/preference_list_content.xml26
-rw-r--r--core/res/res/layout/preference_list_content_large.xml140
-rw-r--r--core/res/res/layout/preference_list_content_single_large.xml (renamed from core/res/res/layout-xlarge/preference_list_content_single.xml)0
-rw-r--r--core/res/res/layout/preference_list_fragment.xml4
-rw-r--r--core/res/res/layout/preference_list_fragment_large.xml80
-rw-r--r--core/res/res/layout/screen_action_bar.xml5
-rw-r--r--core/res/res/layout/screen_action_bar_overlay.xml7
-rw-r--r--core/res/res/layout/status_bar_latest_event_content.xml60
-rw-r--r--core/res/res/layout/status_bar_latest_event_content_large_icon.xml (renamed from core/res/res/layout-xlarge/status_bar_latest_event_content_large_icon.xml)0
-rw-r--r--core/res/res/layout/tab_indicator_holo.xml28
-rw-r--r--core/res/res/layout/tab_indicator_holo_large.xml46
-rw-r--r--core/res/res/layout/text_edit_suggestion_item.xml27
-rw-r--r--core/res/res/layout/text_edit_suggestions_bottom_window.xml23
-rw-r--r--core/res/res/layout/text_edit_suggestions_top_window.xml (renamed from core/res/res/drawable/extract_edit_text.xml)12
-rw-r--r--core/res/res/raw/execute_script_android.js8
-rw-r--r--core/res/res/raw/find_element_android.js27
-rw-r--r--core/res/res/raw/find_elements_android.js27
-rw-r--r--core/res/res/raw/get_attribute_value_android.js26
-rw-r--r--core/res/res/raw/get_size_android.js11
-rw-r--r--core/res/res/raw/get_text_android.js21
-rw-r--r--core/res/res/raw/get_top_left_coordinates_android.js18
-rw-r--r--core/res/res/raw/get_value_of_css_property_android.js10
-rw-r--r--core/res/res/raw/is_displayed_android.js20
-rw-r--r--core/res/res/raw/is_enabled_android.js12
-rw-r--r--core/res/res/raw/is_selected_android.js10
-rw-r--r--core/res/res/raw/set_selected_android.js27
-rw-r--r--core/res/res/raw/submit_android.js29
-rw-r--r--core/res/res/raw/toggle_android.js30
-rw-r--r--core/res/res/raw/webdriver_readme.txt53
-rw-r--r--core/res/res/values-ar/strings.xml15
-rw-r--r--core/res/res/values-bg/strings.xml15
-rw-r--r--core/res/res/values-ca/strings.xml15
-rw-r--r--core/res/res/values-cs/strings.xml15
-rw-r--r--core/res/res/values-da/strings.xml15
-rw-r--r--core/res/res/values-de/strings.xml15
-rw-r--r--core/res/res/values-el/strings.xml15
-rw-r--r--core/res/res/values-en-rGB/strings.xml13
-rw-r--r--core/res/res/values-es-rUS/strings.xml15
-rw-r--r--core/res/res/values-es/strings.xml15
-rw-r--r--core/res/res/values-fa/strings.xml15
-rw-r--r--core/res/res/values-fi/strings.xml15
-rw-r--r--core/res/res/values-fr/strings.xml15
-rw-r--r--core/res/res/values-hr/strings.xml15
-rw-r--r--core/res/res/values-hu/strings.xml15
-rw-r--r--core/res/res/values-in/strings.xml15
-rw-r--r--core/res/res/values-it/strings.xml13
-rw-r--r--core/res/res/values-iw/strings.xml15
-rw-r--r--core/res/res/values-ja/strings.xml15
-rw-r--r--core/res/res/values-ko/strings.xml15
-rw-r--r--core/res/res/values-land/dimens.xml28
-rw-r--r--core/res/res/values-large/dimens.xml9
-rw-r--r--core/res/res/values-large/styles.xml (renamed from core/res/res/values-w720dp/dimens.xml)12
-rw-r--r--core/res/res/values-lt/strings.xml15
-rw-r--r--core/res/res/values-lv/strings.xml15
-rw-r--r--core/res/res/values-nb/strings.xml15
-rw-r--r--core/res/res/values-nl/strings.xml15
-rw-r--r--core/res/res/values-pl/strings.xml15
-rw-r--r--core/res/res/values-port/dimens.xml24
-rw-r--r--core/res/res/values-pt-rPT/strings.xml15
-rw-r--r--core/res/res/values-pt/strings.xml15
-rw-r--r--core/res/res/values-rm/strings.xml20
-rw-r--r--core/res/res/values-ro/strings.xml15
-rw-r--r--core/res/res/values-ru/strings.xml15
-rw-r--r--core/res/res/values-sk/strings.xml15
-rw-r--r--core/res/res/values-sl/strings.xml15
-rw-r--r--core/res/res/values-sr/strings.xml15
-rw-r--r--core/res/res/values-sv/strings.xml15
-rw-r--r--core/res/res/values-th/strings.xml15
-rw-r--r--core/res/res/values-tl/strings.xml15
-rw-r--r--core/res/res/values-tr/strings.xml15
-rw-r--r--core/res/res/values-uk/strings.xml15
-rw-r--r--core/res/res/values-vi/strings.xml13
-rw-r--r--core/res/res/values-w480dp/bools.xml23
-rw-r--r--core/res/res/values-xlarge/styles.xml16
-rw-r--r--core/res/res/values-zh-rCN/strings.xml15
-rw-r--r--core/res/res/values-zh-rTW/strings.xml15
-rwxr-xr-xcore/res/res/values/attrs.xml94
-rw-r--r--core/res/res/values/attrs_manifest.xml14
-rw-r--r--core/res/res/values/bools.xml23
-rw-r--r--core/res/res/values/colors.xml2
-rwxr-xr-xcore/res/res/values/config.xml16
-rw-r--r--core/res/res/values/dimens.xml22
-rw-r--r--core/res/res/values/public.xml23
-rwxr-xr-xcore/res/res/values/strings.xml35
-rw-r--r--core/res/res/values/styles.xml67
-rw-r--r--core/res/res/values/themes.xml39
-rw-r--r--core/tests/coretests/Android.mk2
-rw-r--r--core/tests/coretests/src/android/app/activity/LocalProvider.java5
-rw-r--r--core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java45
-rw-r--r--core/tests/coretests/src/android/net/NetworkStatsTest.java97
-rw-r--r--core/tests/coretests/src/android/os/MemoryFileTest.java23
-rw-r--r--core/tests/coretests/src/android/text/TextUtilsTest.java119
-rw-r--r--core/tests/coretests/src/android/view/menu/MenuLayoutLandscapeTest.java3
-rw-r--r--core/tests/coretests/src/android/view/menu/MenuLayoutPortraitTest.java3
-rw-r--r--core/tests/coretests/src/android/view/menu/MenuScenario.java40
-rw-r--r--core/tests/coretests/src/android/view/menu/MenuWith1ItemTest.java4
-rw-r--r--core/tests/coretests/src/android/widget/focus/RequestFocusTest.java2
-rw-r--r--core/tests/coretests/src/android/widget/listview/ListViewHeight.java2
-rw-r--r--core/tests/systemproperties/Android.mk2
-rw-r--r--core/tests/systemproperties/AndroidManifest.xml4
-rwxr-xr-xcore/tests/systemproperties/run_core_systemproperties_test.sh3
525 files changed, 26069 insertions, 10528 deletions
diff --git a/core/config/README.txt b/core/config/README.txt
deleted file mode 100644
index 5f48fb3..0000000
--- a/core/config/README.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-One of the subdirectories {debug, ndebug} is included in framework.jar
-by ../../Android.mk depending on the value of $(TARGET_BUILD_TYPE).
-
-The sdk/ directory contains the files that are passed to the doc/API
-tools regardless of $(TARGET_BUILD_TYPE).
diff --git a/core/java/android/accounts/AccountAuthenticatorActivity.java b/core/java/android/accounts/AccountAuthenticatorActivity.java
index 5cce6da..6a55ddf 100644
--- a/core/java/android/accounts/AccountAuthenticatorActivity.java
+++ b/core/java/android/accounts/AccountAuthenticatorActivity.java
@@ -26,7 +26,7 @@ import android.os.Bundle;
* to handle the request then it can have the activity extend AccountAuthenticatorActivity.
* The AbstractAccountAuthenticator passes in the response to the intent using the following:
* <pre>
- * intent.putExtra(Constants.ACCOUNT_AUTHENTICATOR_RESPONSE_KEY, response);
+ * intent.putExtra({@link AccountManager#KEY_ACCOUNT_AUTHENTICATOR_RESPONSE}, response);
* </pre>
* The activity then sets the result that is to be handed to the response via
* {@link #setAccountAuthenticatorResult(android.os.Bundle)}.
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index 5bdc79d..2156425 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -179,6 +179,7 @@ public class AccountManager {
public static final String KEY_PASSWORD = "password";
public static final String KEY_ACCOUNTS = "accounts";
+
public static final String KEY_ACCOUNT_AUTHENTICATOR_RESPONSE = "accountAuthenticatorResponse";
public static final String KEY_ACCOUNT_MANAGER_RESPONSE = "accountManagerResponse";
public static final String KEY_AUTHENTICATOR_TYPES = "authenticator_types";
@@ -1269,7 +1270,7 @@ public class AccountManager {
/** Handles the responses from the AccountManager */
private class Response extends IAccountManagerResponse.Stub {
public void onResult(Bundle bundle) {
- Intent intent = bundle.getParcelable("intent");
+ Intent intent = bundle.getParcelable(KEY_INTENT);
if (intent != null && mActivity != null) {
// since the user provided an Activity we will silently start intents
// that we see
diff --git a/core/java/android/accounts/AccountManagerService.java b/core/java/android/accounts/AccountManagerService.java
index 93983a6..20d5b96 100644
--- a/core/java/android/accounts/AccountManagerService.java
+++ b/core/java/android/accounts/AccountManagerService.java
@@ -37,6 +37,7 @@ import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.RegisteredServicesCache;
import android.content.pm.RegisteredServicesCacheListener;
+import android.content.res.Resources;
import android.database.Cursor;
import android.database.DatabaseUtils;
import android.database.sqlite.SQLiteDatabase;
@@ -73,8 +74,7 @@ import java.util.concurrent.atomic.AtomicReference;
* accounts on the device. Some of these calls are implemented with the help of the corresponding
* {@link IAccountAuthenticator} services. This service is not accessed by users directly,
* instead one uses an instance of {@link AccountManager}, which can be accessed as follows:
- * AccountManager accountManager =
- * (AccountManager)context.getSystemService(Context.ACCOUNT_SERVICE)
+ * AccountManager accountManager = AccountManager.get(context);
* @hide
*/
public class AccountManagerService
@@ -1064,14 +1064,18 @@ public class AccountManagerService
} catch (PackageManager.NameNotFoundException e) {
throw new IllegalArgumentException("unknown account type: " + accountType);
}
- return authContext.getString(serviceInfo.type.labelId);
+ try {
+ return authContext.getString(serviceInfo.type.labelId);
+ } catch (Resources.NotFoundException e) {
+ throw new IllegalArgumentException("unknown account type: " + accountType);
+ }
}
private Intent newGrantCredentialsPermissionIntent(Account account, int uid,
AccountAuthenticatorResponse response, String authTokenType, String authTokenLabel) {
Intent intent = new Intent(mContext, GrantCredentialsPermissionActivity.class);
- // See FLAT_ACTIVITY_NEW_TASK docs for limitations and benefits of the flag.
+ // See FLAG_ACTIVITY_NEW_TASK docs for limitations and benefits of the flag.
// Since it was set in Eclair+ we can't change it without breaking apps using
// the intent from a non-Activity context.
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
diff --git a/core/java/android/accounts/ChooseAccountActivity.java b/core/java/android/accounts/ChooseAccountActivity.java
index 293df78..bfbae24 100644
--- a/core/java/android/accounts/ChooseAccountActivity.java
+++ b/core/java/android/accounts/ChooseAccountActivity.java
@@ -18,6 +18,7 @@ package android.accounts;
import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Parcelable;
@@ -103,7 +104,12 @@ public class ChooseAccountActivity extends Activity {
} catch (PackageManager.NameNotFoundException e) {
// Nothing we can do much here, just log
if (Log.isLoggable(TAG, Log.WARN)) {
- Log.w(TAG, "No icon for account type " + accountType);
+ Log.w(TAG, "No icon name for account type " + accountType);
+ }
+ } catch (Resources.NotFoundException e) {
+ // Nothing we can do much here, just log
+ if (Log.isLoggable(TAG, Log.WARN)) {
+ Log.w(TAG, "No icon resource for account type " + accountType);
}
}
}
diff --git a/core/java/android/accounts/GrantCredentialsPermissionActivity.java b/core/java/android/accounts/GrantCredentialsPermissionActivity.java
index 89eee6d..0ee683c 100644
--- a/core/java/android/accounts/GrantCredentialsPermissionActivity.java
+++ b/core/java/android/accounts/GrantCredentialsPermissionActivity.java
@@ -58,6 +58,12 @@ public class GrantCredentialsPermissionActivity extends Activity implements View
mInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
final Bundle extras = getIntent().getExtras();
+ if (extras == null) {
+ // we were somehow started with bad parameters. abort the activity.
+ setResult(Activity.RESULT_CANCELED);
+ finish();
+ return;
+ }
// Grant 'account'/'type' to mUID
mAccount = extras.getParcelable(EXTRAS_ACCOUNT);
@@ -73,8 +79,15 @@ public class GrantCredentialsPermissionActivity extends Activity implements View
return;
}
- final String accountTypeLabel = accountManagerService.getAccountLabel(mAccount.type);
-
+ String accountTypeLabel;
+ try {
+ accountTypeLabel = accountManagerService.getAccountLabel(mAccount.type);
+ } catch (IllegalArgumentException e) {
+ // label or resource was missing. abort the activity.
+ setResult(Activity.RESULT_CANCELED);
+ finish();
+ return;
+ }
final TextView authTokenTypeView = (TextView) findViewById(R.id.authtoken_type);
authTokenTypeView.setVisibility(View.GONE);
diff --git a/core/java/android/animation/AnimatorInflater.java b/core/java/android/animation/AnimatorInflater.java
index bcab66e..ed4036d 100644
--- a/core/java/android/animation/AnimatorInflater.java
+++ b/core/java/android/animation/AnimatorInflater.java
@@ -31,11 +31,11 @@ import java.io.IOException;
import java.util.ArrayList;
/**
- * This class is used to instantiate menu XML files into Animator objects.
+ * This class is used to instantiate animator XML files into Animator objects.
* <p>
- * For performance reasons, menu inflation relies heavily on pre-processing of
+ * For performance reasons, inflation relies heavily on pre-processing of
* XML files that is done at build time. Therefore, it is not currently possible
- * to use MenuInflater with an XmlPullParser over a plain XML file at runtime;
+ * to use this inflater with an XmlPullParser over a plain XML file at runtime;
* it only works with an XmlPullParser returned from a compiled resource (R.
* <em>something</em> file.)
*/
diff --git a/core/java/android/animation/LayoutTransition.java b/core/java/android/animation/LayoutTransition.java
index 22dd3c7..adfda8e 100644
--- a/core/java/android/animation/LayoutTransition.java
+++ b/core/java/android/animation/LayoutTransition.java
@@ -48,6 +48,18 @@ import java.util.List;
* with setting up the basic properties of the animations used in these four situations,
* or with setting up custom animations for any or all of the four.</p>
*
+ * <p>By default, the DISAPPEARING animation begins immediately, as does the CHANGE_APPEARING
+ * animation. The other animations begin after a delay that is set to the default duration
+ * of the animations. This behavior facilitates a sequence of animations in transitions as
+ * follows: when an item is being added to a layout, the other children of that container will
+ * move first (thus creating space for the new item), then the appearing animation will run to
+ * animate the item being added. Conversely, when an item is removed from a container, the
+ * animation to remove it will run first, then the animations of the other children in the
+ * layout will run (closing the gap created in the layout when the item was removed). If this
+ * default choreography behavior is not desired, the {@link #setDuration(int, long)} and
+ * {@link #setStartDelay(int, long)} of any or all of the animations can be changed as
+ * appropriate.</p>
+ *
* <p>The animations specified for the transition, both the defaults and any custom animations
* set on the transition object, are templates only. That is, these animations exist to hold the
* basic animation properties, such as the duration, start delay, and properties being animated.
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index f562851..1dcaa04 100755
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -1173,13 +1173,11 @@ public class ValueAnimator extends Animator {
if (oldValues != null) {
int numValues = oldValues.length;
anim.mValues = new PropertyValuesHolder[numValues];
- for (int i = 0; i < numValues; ++i) {
- anim.mValues[i] = oldValues[i].clone();
- }
anim.mValuesMap = new HashMap<String, PropertyValuesHolder>(numValues);
for (int i = 0; i < numValues; ++i) {
- PropertyValuesHolder valuesHolder = mValues[i];
- anim.mValuesMap.put(valuesHolder.getPropertyName(), valuesHolder);
+ PropertyValuesHolder newValuesHolder = oldValues[i].clone();
+ anim.mValues[i] = newValuesHolder;
+ anim.mValuesMap.put(newValuesHolder.getPropertyName(), newValuesHolder);
}
}
return anim;
diff --git a/core/java/android/app/ActionBar.java b/core/java/android/app/ActionBar.java
index fc5fac6..cac06ec 100644
--- a/core/java/android/app/ActionBar.java
+++ b/core/java/android/app/ActionBar.java
@@ -107,6 +107,18 @@ public abstract class ActionBar {
public static final int DISPLAY_SHOW_CUSTOM = 0x10;
/**
+ * Disable the 'home' element. This may be combined with
+ * {@link #DISPLAY_SHOW_HOME} to create a non-focusable/non-clickable
+ * 'home' element. Useful for a level of your app's navigation hierarchy
+ * where clicking 'home' doesn't do anything.
+ *
+ * @see #setDisplayOptions(int)
+ * @see #setDisplayOptions(int, int)
+ * @see #setDisplayDisableHomeEnabled(boolean)
+ */
+ public static final int DISPLAY_DISABLE_HOME = 0x20;
+
+ /**
* Set the action bar into custom navigation mode, supplying a view
* for custom navigation.
*
@@ -160,6 +172,66 @@ public abstract class ActionBar {
public abstract void setCustomView(int resId);
/**
+ * Set the icon to display in the 'home' section of the action bar.
+ * The action bar will use an icon specified by its style or the
+ * activity icon by default.
+ *
+ * Whether the home section shows an icon or logo is controlled
+ * by the display option {@link #DISPLAY_USE_LOGO}.
+ *
+ * @param resId Resource ID of a drawable to show as an icon.
+ *
+ * @see #setDisplayUseLogoEnabled(boolean)
+ * @see #setDisplayShowHomeEnabled(boolean)
+ */
+ public abstract void setIcon(int resId);
+
+ /**
+ * Set the icon to display in the 'home' section of the action bar.
+ * The action bar will use an icon specified by its style or the
+ * activity icon by default.
+ *
+ * Whether the home section shows an icon or logo is controlled
+ * by the display option {@link #DISPLAY_USE_LOGO}.
+ *
+ * @param icon Drawable to show as an icon.
+ *
+ * @see #setDisplayUseLogoEnabled(boolean)
+ * @see #setDisplayShowHomeEnabled(boolean)
+ */
+ public abstract void setIcon(Drawable icon);
+
+ /**
+ * Set the logo to display in the 'home' section of the action bar.
+ * The action bar will use a logo specified by its style or the
+ * activity logo by default.
+ *
+ * Whether the home section shows an icon or logo is controlled
+ * by the display option {@link #DISPLAY_USE_LOGO}.
+ *
+ * @param resId Resource ID of a drawable to show as a logo.
+ *
+ * @see #setDisplayUseLogoEnabled(boolean)
+ * @see #setDisplayShowHomeEnabled(boolean)
+ */
+ public abstract void setLogo(int resId);
+
+ /**
+ * Set the logo to display in the 'home' section of the action bar.
+ * The action bar will use a logo specified by its style or the
+ * activity logo by default.
+ *
+ * Whether the home section shows an icon or logo is controlled
+ * by the display option {@link #DISPLAY_USE_LOGO}.
+ *
+ * @param logo Drawable to show as a logo.
+ *
+ * @see #setDisplayUseLogoEnabled(boolean)
+ * @see #setDisplayShowHomeEnabled(boolean)
+ */
+ public abstract void setLogo(Drawable logo);
+
+ /**
* Set the adapter and navigation callback for list navigation mode.
*
* The supplied adapter will provide views for the expanded list as well as
@@ -333,6 +405,21 @@ public abstract class ActionBar {
public abstract void setDisplayShowCustomEnabled(boolean showCustom);
/**
+ * Set whether the 'home' affordance on the action bar should be disabled.
+ * If set, the 'home' element will not be focusable or clickable, useful if
+ * the user is at the top level of the app's navigation hierarchy.
+ *
+ * <p>To set several display options at once, see the setDisplayOptions methods.
+ *
+ * @param disableHome true to disable the 'home' element.
+ *
+ * @see #setDisplayOptions(int)
+ * @see #setDisplayOptions(int, int)
+ * @see #DISPLAY_DISABLE_HOME
+ */
+ public abstract void setDisplayDisableHomeEnabled(boolean disableHome);
+
+ /**
* Set the ActionBar's background.
*
* @param d Background drawable
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index e3fb358..87369ab 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -51,7 +51,6 @@ import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.text.method.TextKeyListener;
import android.util.AttributeSet;
-import android.util.Config;
import android.util.EventLog;
import android.util.Log;
import android.util.SparseArray;
@@ -2488,6 +2487,7 @@ public class Activity extends ContextThemeWrapper
break;
case Window.FEATURE_ACTION_BAR:
+ initActionBar();
mActionBar.dispatchMenuVisibilityChanged(false);
break;
}
@@ -3633,7 +3633,7 @@ public class Activity extends ContextThemeWrapper
resultCode = mResultCode;
resultData = mResultData;
}
- if (Config.LOGV) Log.v(TAG, "Finishing self: token=" + mToken);
+ if (false) Log.v(TAG, "Finishing self: token=" + mToken);
try {
if (ActivityManagerNative.getDefault()
.finishActivity(mToken, resultCode, resultData)) {
@@ -4569,7 +4569,7 @@ public class Activity extends ContextThemeWrapper
void dispatchActivityResult(String who, int requestCode,
int resultCode, Intent data) {
- if (Config.LOGV) Log.v(
+ if (false) Log.v(
TAG, "Dispatching result: who=" + who + ", reqCode=" + requestCode
+ ", resCode=" + resultCode + ", data=" + data);
mFragments.noteStateNotSaved();
diff --git a/core/java/android/app/ActivityGroup.java b/core/java/android/app/ActivityGroup.java
index f1216f9..5b04253 100644
--- a/core/java/android/app/ActivityGroup.java
+++ b/core/java/android/app/ActivityGroup.java
@@ -110,7 +110,7 @@ public class ActivityGroup extends Activity {
if (who != null) {
Activity act = mLocalActivityManager.getActivity(who);
/*
- if (Config.LOGV) Log.v(
+ if (false) Log.v(
TAG, "Dispatching result: who=" + who + ", reqCode=" + requestCode
+ ", resCode=" + resultCode + ", data=" + data
+ ", rec=" + rec);
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index a70a219..ef2e54a 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -16,6 +16,9 @@
package android.app;
+import com.android.internal.app.IUsageStats;
+import com.android.internal.os.PkgUsageStats;
+
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -26,18 +29,17 @@ import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.os.Debug;
-import android.os.RemoteException;
import android.os.Handler;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.Log;
-import com.android.internal.app.IUsageStats;
-import com.android.internal.os.PkgUsageStats;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -47,8 +49,7 @@ import java.util.Map;
*/
public class ActivityManager {
private static String TAG = "ActivityManager";
- private static boolean DEBUG = false;
- private static boolean localLOGV = DEBUG || android.util.Config.LOGV;
+ private static boolean localLOGV = false;
private final Context mContext;
private final Handler mHandler;
@@ -283,13 +284,6 @@ public class ActivityManager {
public static final int RECENT_IGNORE_UNAVAILABLE = 0x0002;
/**
- * Flag for use with {@link #getRecentTasks}: also return the thumbnail
- * bitmap (if available) for each recent task.
- * @hide
- */
- public static final int TASKS_GET_THUMBNAILS = 0x0001000;
-
- /**
* Return a list of the tasks that the user has recently launched, with
* the most recent being first and older ones after in order.
*
@@ -319,7 +313,7 @@ public class ActivityManager {
/**
* Information you can retrieve about a particular task that is currently
* "running" in the system. Note that a running task does not mean the
- * given task actual has a process it is actively running in; it simply
+ * given task actually has a process it is actively running in; it simply
* means that the user has gone to it and never closed it, but currently
* the system may have killed its process and is only holding on to its
* last state in order to restart it when the user returns.
@@ -474,10 +468,118 @@ public class ActivityManager {
return getRunningTasks(maxNum, 0, null);
}
+ /**
+ * Remove some end of a task's activity stack that is not part of
+ * the main application. The selected activities will be finished, so
+ * they are no longer part of the main task.
+ *
+ * @param taskId The identifier of the task.
+ * @param subTaskIndex The number of the sub-task; this corresponds
+ * to the index of the thumbnail returned by {@link #getTaskThumbnails(int)}.
+ * @return Returns true if the sub-task was found and was removed.
+ *
+ * @hide
+ */
+ public boolean removeSubTask(int taskId, int subTaskIndex)
+ throws SecurityException {
+ try {
+ return ActivityManagerNative.getDefault().removeSubTask(taskId, subTaskIndex);
+ } catch (RemoteException e) {
+ // System dead, we will be dead too soon!
+ return false;
+ }
+ }
+
+ /**
+ * If set, the process of the root activity of the task will be killed
+ * as part of removing the task.
+ * @hide
+ */
+ public static final int REMOVE_TASK_KILL_PROCESS = 0x0001;
+
+ /**
+ * Completely remove the given task.
+ *
+ * @param taskId Identifier of the task to be removed.
+ * @param flags Additional operational flags. May be 0 or
+ * {@link #REMOVE_TASK_KILL_PROCESS}.
+ * @return Returns true if the given task was found and removed.
+ *
+ * @hide
+ */
+ public boolean removeTask(int taskId, int flags)
+ throws SecurityException {
+ try {
+ return ActivityManagerNative.getDefault().removeTask(taskId, flags);
+ } catch (RemoteException e) {
+ // System dead, we will be dead too soon!
+ return false;
+ }
+ }
+
+ /** @hide */
+ public static class TaskThumbnails implements Parcelable {
+ public Bitmap mainThumbnail;
+
+ public int numSubThumbbails;
+
+ /** @hide */
+ public IThumbnailRetriever retriever;
+
+ public TaskThumbnails() {
+ }
+
+ public Bitmap getSubThumbnail(int index) {
+ try {
+ return retriever.getThumbnail(index);
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel dest, int flags) {
+ if (mainThumbnail != null) {
+ dest.writeInt(1);
+ mainThumbnail.writeToParcel(dest, 0);
+ } else {
+ dest.writeInt(0);
+ }
+ dest.writeInt(numSubThumbbails);
+ dest.writeStrongInterface(retriever);
+ }
+
+ public void readFromParcel(Parcel source) {
+ if (source.readInt() != 0) {
+ mainThumbnail = Bitmap.CREATOR.createFromParcel(source);
+ } else {
+ mainThumbnail = null;
+ }
+ numSubThumbbails = source.readInt();
+ retriever = IThumbnailRetriever.Stub.asInterface(source.readStrongBinder());
+ }
+
+ public static final Creator<TaskThumbnails> CREATOR = new Creator<TaskThumbnails>() {
+ public TaskThumbnails createFromParcel(Parcel source) {
+ return new TaskThumbnails(source);
+ }
+ public TaskThumbnails[] newArray(int size) {
+ return new TaskThumbnails[size];
+ }
+ };
+
+ private TaskThumbnails(Parcel source) {
+ readFromParcel(source);
+ }
+ }
+
/** @hide */
- public Bitmap getTaskThumbnail(int id) throws SecurityException {
+ public TaskThumbnails getTaskThumbnails(int id) throws SecurityException {
try {
- return ActivityManagerNative.getDefault().getTaskThumbnail(id);
+ return ActivityManagerNative.getDefault().getTaskThumbnails(id);
} catch (RemoteException e) {
// System dead, we will be dead too soon!
return null;
@@ -693,7 +795,7 @@ public class ActivityManager {
public List<RunningServiceInfo> getRunningServices(int maxNum)
throws SecurityException {
try {
- return (List<RunningServiceInfo>)ActivityManagerNative.getDefault()
+ return ActivityManagerNative.getDefault()
.getServices(maxNum, 0);
} catch (RemoteException e) {
// System dead, we will be dead too soon!
@@ -1348,4 +1450,17 @@ public class ActivityManager {
return new HashMap<String, Integer>();
}
}
+
+ /**
+ * @param userid the user's id. Zero indicates the default user
+ * @hide
+ */
+ public boolean switchUser(int userid) {
+ try {
+ return ActivityManagerNative.getDefault().switchUser(userid);
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
}
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 4beb5cc..2a0d798 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -39,7 +39,6 @@ import android.os.Parcel;
import android.os.ServiceManager;
import android.os.StrictMode;
import android.text.TextUtils;
-import android.util.Config;
import android.util.Log;
import android.util.Singleton;
@@ -442,10 +441,10 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
return true;
}
- case GET_TASK_THUMBNAIL_TRANSACTION: {
+ case GET_TASK_THUMBNAILS_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
int id = data.readInt();
- Bitmap bm = getTaskThumbnail(id);
+ ActivityManager.TaskThumbnails bm = getTaskThumbnails(id);
reply.writeNoException();
if (bm != null) {
reply.writeInt(1);
@@ -1436,6 +1435,53 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
reply.writeNoException();
return true;
}
+
+ case SWITCH_USER_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ int userid = data.readInt();
+ boolean result = switchUser(userid);
+ reply.writeNoException();
+ reply.writeInt(result ? 1 : 0);
+ return true;
+ }
+
+ case REMOVE_SUB_TASK_TRANSACTION:
+ {
+ data.enforceInterface(IActivityManager.descriptor);
+ int taskId = data.readInt();
+ int subTaskIndex = data.readInt();
+ boolean result = removeSubTask(taskId, subTaskIndex);
+ reply.writeNoException();
+ reply.writeInt(result ? 1 : 0);
+ return true;
+ }
+
+ case REMOVE_TASK_TRANSACTION:
+ {
+ data.enforceInterface(IActivityManager.descriptor);
+ int taskId = data.readInt();
+ int fl = data.readInt();
+ boolean result = removeTask(taskId, fl);
+ reply.writeNoException();
+ reply.writeInt(result ? 1 : 0);
+ return true;
+ }
+
+ case REGISTER_PROCESS_OBSERVER_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IProcessObserver observer = IProcessObserver.Stub.asInterface(
+ data.readStrongBinder());
+ registerProcessObserver(observer);
+ return true;
+ }
+
+ case UNREGISTER_PROCESS_OBSERVER_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IProcessObserver observer = IProcessObserver.Stub.asInterface(
+ data.readStrongBinder());
+ unregisterProcessObserver(observer);
+ return true;
+ }
}
@@ -1449,11 +1495,11 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
protected IActivityManager create() {
IBinder b = ServiceManager.getService("activity");
- if (Config.LOGV) {
+ if (false) {
Log.v("ActivityManager", "default service binder = " + b);
}
IActivityManager am = asInterface(b);
- if (Config.LOGV) {
+ if (false) {
Log.v("ActivityManager", "default service = " + am);
}
return am;
@@ -1870,16 +1916,16 @@ class ActivityManagerProxy implements IActivityManager
reply.recycle();
return list;
}
- public Bitmap getTaskThumbnail(int id) throws RemoteException {
+ public ActivityManager.TaskThumbnails getTaskThumbnails(int id) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeInt(id);
- mRemote.transact(GET_TASK_THUMBNAIL_TRANSACTION, data, reply, 0);
+ mRemote.transact(GET_TASK_THUMBNAILS_TRANSACTION, data, reply, 0);
reply.readException();
- Bitmap bm = null;
+ ActivityManager.TaskThumbnails bm = null;
if (reply.readInt() != 0) {
- bm = Bitmap.CREATOR.createFromParcel(reply);
+ bm = ActivityManager.TaskThumbnails.CREATOR.createFromParcel(reply);
}
data.recycle();
reply.recycle();
@@ -3229,5 +3275,68 @@ class ActivityManagerProxy implements IActivityManager
data.recycle();
}
+ public boolean switchUser(int userid) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeInt(userid);
+ mRemote.transact(SWITCH_USER_TRANSACTION, data, reply, 0);
+ reply.readException();
+ boolean result = reply.readInt() != 0;
+ reply.recycle();
+ data.recycle();
+ return result;
+ }
+
+ public boolean removeSubTask(int taskId, int subTaskIndex) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeInt(taskId);
+ data.writeInt(subTaskIndex);
+ mRemote.transact(REMOVE_SUB_TASK_TRANSACTION, data, reply, 0);
+ reply.readException();
+ boolean result = reply.readInt() != 0;
+ reply.recycle();
+ data.recycle();
+ return result;
+ }
+
+ public boolean removeTask(int taskId, int flags) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeInt(taskId);
+ data.writeInt(flags);
+ mRemote.transact(REMOVE_TASK_TRANSACTION, data, reply, 0);
+ reply.readException();
+ boolean result = reply.readInt() != 0;
+ reply.recycle();
+ data.recycle();
+ return result;
+ }
+
+ public void registerProcessObserver(IProcessObserver observer) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(observer != null ? observer.asBinder() : null);
+ mRemote.transact(REGISTER_PROCESS_OBSERVER_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+
+ public void unregisterProcessObserver(IProcessObserver observer) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(observer != null ? observer.asBinder() : null);
+ mRemote.transact(UNREGISTER_PROCESS_OBSERVER_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+
private IBinder mRemote;
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 6c8f85f..85e59b3 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -45,6 +45,7 @@ import android.graphics.Canvas;
import android.net.IConnectivityManager;
import android.net.Proxy;
import android.net.ProxyProperties;
+import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Debug;
import android.os.Handler;
@@ -59,7 +60,6 @@ import android.os.ServiceManager;
import android.os.StrictMode;
import android.os.SystemClock;
import android.util.AndroidRuntimeException;
-import android.util.Config;
import android.util.DisplayMetrics;
import android.util.EventLog;
import android.util.Log;
@@ -70,7 +70,7 @@ import android.view.HardwareRenderer;
import android.view.View;
import android.view.ViewDebug;
import android.view.ViewManager;
-import android.view.ViewRoot;
+import android.view.ViewAncestor;
import android.view.Window;
import android.view.WindowManager;
import android.view.WindowManagerImpl;
@@ -124,12 +124,12 @@ public final class ActivityThread {
public static final String TAG = "ActivityThread";
private static final android.graphics.Bitmap.Config THUMBNAIL_FORMAT = Bitmap.Config.RGB_565;
private static final boolean DEBUG = false;
- static final boolean localLOGV = DEBUG ? Config.LOGD : Config.LOGV;
+ static final boolean localLOGV = false;
static final boolean DEBUG_MESSAGES = false;
/** @hide */
public static final boolean DEBUG_BROADCAST = false;
private static final boolean DEBUG_RESULTS = false;
- private static final boolean DEBUG_BACKUP = false;
+ private static final boolean DEBUG_BACKUP = true;
private static final boolean DEBUG_CONFIGURATION = false;
private static final long MIN_TIME_BETWEEN_GCS = 5*1000;
private static final Pattern PATTERN_SEMICOLON = Pattern.compile(";");
@@ -338,6 +338,7 @@ public final class ActivityThread {
static final class ServiceArgsData {
IBinder token;
+ boolean taskRemoved;
int startId;
int flags;
Intent args;
@@ -401,6 +402,8 @@ public final class ActivityThread {
String pkg;
CompatibilityInfo info;
}
+
+ native private void dumpGraphicsInfo(FileDescriptor fd);
private final class ApplicationThread extends ApplicationThreadNative {
private static final String HEAP_COLUMN = "%17s %8s %8s %8s %8s";
@@ -551,10 +554,11 @@ public final class ActivityThread {
queueOrSendMessage(H.UNBIND_SERVICE, s);
}
- public final void scheduleServiceArgs(IBinder token, int startId,
+ public final void scheduleServiceArgs(IBinder token, boolean taskRemoved, int startId,
int flags ,Intent args) {
ServiceArgsData s = new ServiceArgsData();
s.token = token;
+ s.taskRemoved = taskRemoved;
s.startId = startId;
s.flags = flags;
s.args = args;
@@ -716,9 +720,14 @@ public final class ActivityThread {
Slog.w(TAG, "dumpActivity failed", e);
}
}
-
+
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (args != null && args.length == 1 && args[0].equals("graphics")) {
+ pw.flush();
+ dumpGraphicsInfo(fd);
+ return;
+ }
long nativeMax = Debug.getNativeHeapSize() / 1024;
long nativeAllocated = Debug.getNativeHeapAllocatedSize() / 1024;
long nativeFree = Debug.getNativeHeapFreeSize() / 1024;
@@ -740,7 +749,7 @@ public final class ActivityThread {
long dalvikFree = runtime.freeMemory() / 1024;
long dalvikAllocated = dalvikMax - dalvikFree;
long viewInstanceCount = ViewDebug.getViewInstanceCount();
- long viewRootInstanceCount = ViewDebug.getViewRootInstanceCount();
+ long viewRootInstanceCount = ViewDebug.getViewAncestorInstanceCount();
long appContextInstanceCount = Debug.countInstancesOfClass(ContextImpl.class);
long activityInstanceCount = Debug.countInstancesOfClass(Activity.class);
int globalAssetCount = AssetManager.getGlobalAssetCount();
@@ -855,7 +864,7 @@ public final class ActivityThread {
pw.println(" ");
pw.println(" Objects");
- printRow(pw, TWO_COUNT_COLUMNS, "Views:", viewInstanceCount, "ViewRoots:",
+ printRow(pw, TWO_COUNT_COLUMNS, "Views:", viewInstanceCount, "ViewAncestors:",
viewRootInstanceCount);
printRow(pw, TWO_COUNT_COLUMNS, "AppContexts:", appContextInstanceCount,
@@ -928,7 +937,7 @@ public final class ActivityThread {
public static final int HIDE_WINDOW = 106;
public static final int RESUME_ACTIVITY = 107;
public static final int SEND_RESULT = 108;
- public static final int DESTROY_ACTIVITY = 109;
+ public static final int DESTROY_ACTIVITY = 109;
public static final int BIND_APPLICATION = 110;
public static final int EXIT_APPLICATION = 111;
public static final int NEW_INTENT = 112;
@@ -1146,8 +1155,8 @@ public final class ActivityThread {
if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + msg.what);
}
- void maybeSnapshot() {
- if (mBoundApplication != null) {
+ private void maybeSnapshot() {
+ if (mBoundApplication != null && SamplingProfilerIntegration.isEnabled()) {
// convert the *private* ActivityThread.PackageInfo to *public* known
// android.content.pm.PackageInfo
String packageName = mBoundApplication.info.mPackageName;
@@ -1970,24 +1979,26 @@ public final class ActivityThread {
BackupAgent agent = null;
String classname = data.appInfo.backupAgentName;
- if (classname == null) {
- if (data.backupMode == IApplicationThread.BACKUP_MODE_INCREMENTAL) {
- Slog.e(TAG, "Attempted incremental backup but no defined agent for "
- + packageName);
- return;
+
+ if (data.backupMode == IApplicationThread.BACKUP_MODE_FULL) {
+ classname = "android.app.backup.FullBackupAgent";
+ if ((data.appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+ // system packages can supply their own full-backup agent
+ if (data.appInfo.fullBackupAgentName != null) {
+ classname = data.appInfo.fullBackupAgentName;
+ }
}
- classname = "android.app.FullBackupAgent";
}
+
try {
IBinder binder = null;
try {
+ if (DEBUG_BACKUP) Slog.v(TAG, "Initializing agent class " + classname);
+
java.lang.ClassLoader cl = packageInfo.getClassLoader();
- agent = (BackupAgent) cl.loadClass(data.appInfo.backupAgentName).newInstance();
+ agent = (BackupAgent) cl.loadClass(classname).newInstance();
// set up the agent's context
- if (DEBUG_BACKUP) Slog.v(TAG, "Initializing BackupAgent "
- + data.appInfo.backupAgentName);
-
ContextImpl context = new ContextImpl();
context.init(packageInfo, null, this);
context.setOuterContext(agent);
@@ -2014,7 +2025,7 @@ public final class ActivityThread {
}
} catch (Exception e) {
throw new RuntimeException("Unable to create BackupAgent "
- + data.appInfo.backupAgentName + ": " + e.toString(), e);
+ + classname + ": " + e.toString(), e);
}
}
@@ -2171,7 +2182,13 @@ public final class ActivityThread {
if (data.args != null) {
data.args.setExtrasClassLoader(s.getClassLoader());
}
- int res = s.onStartCommand(data.args, data.flags, data.startId);
+ int res;
+ if (!data.taskRemoved) {
+ res = s.onStartCommand(data.args, data.flags, data.startId);
+ } else {
+ s.onTaskRemoved(data.args);
+ res = Service.START_TASK_REMOVED_COMPLETE;
+ }
QueuedWork.waitToFinish();
@@ -2701,7 +2718,7 @@ public final class ActivityThread {
r.stopped = false;
}
if (r.activity.mDecor != null) {
- if (Config.LOGV) Slog.v(
+ if (false) Slog.v(
TAG, "Handle window " + r + " visibility: " + show);
updateVisibility(r, show);
}
@@ -3442,8 +3459,7 @@ public final class ActivityThread {
}
final void handleLowMemory() {
- ArrayList<ComponentCallbacks> callbacks
- = new ArrayList<ComponentCallbacks>();
+ ArrayList<ComponentCallbacks> callbacks;
synchronized (mPackages) {
callbacks = collectComponentCallbacksLocked(true, null);
@@ -3474,6 +3490,14 @@ public final class ActivityThread {
Process.setArgV0(data.processName);
android.ddm.DdmHandleAppName.setAppName(data.processName);
+ // If the app is Honeycomb MR1 or earlier, switch its AsyncTask
+ // implementation to use the pool executor. Normally, we use the
+ // serialized executor as the default. This has to happen in the
+ // main thread so the main looper is set right.
+ if (data.appInfo.targetSdkVersion <= 12) {
+ AsyncTask.setDefaultExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ }
+
/*
* Before spawning a new process, reset the time zone to be the system time zone.
* This needs to be done because the system time zone could have changed after the
@@ -3922,7 +3946,7 @@ public final class ActivityThread {
info.applicationInfo.sourceDir);
return null;
}
- if (Config.LOGV) Slog.v(
+ if (false) Slog.v(
TAG, "Instantiating local provider " + info.name);
// XXX Need to create the correct context for this provider.
localProvider.attachInfo(c, info);
@@ -3965,7 +3989,7 @@ public final class ActivityThread {
sThreadLocal.set(this);
mSystemThread = system;
if (!system) {
- ViewRoot.addFirstDrawHandler(new Runnable() {
+ ViewAncestor.addFirstDrawHandler(new Runnable() {
public void run() {
ensureJitEnabled();
}
@@ -3995,7 +4019,7 @@ public final class ActivityThread {
}
}
- ViewRoot.addConfigCallback(new ComponentCallbacks() {
+ ViewAncestor.addConfigCallback(new ComponentCallbacks() {
public void onConfigurationChanged(Configuration newConfig) {
synchronized (mPackages) {
// We need to apply this change to the resources
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 5926929..4cff12f 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -33,7 +33,6 @@ import android.content.pm.IPackageMoveObserver;
import android.content.pm.IPackageStatsObserver;
import android.content.pm.InstrumentationInfo;
import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.content.pm.PermissionGroupInfo;
@@ -41,6 +40,7 @@ import android.content.pm.PermissionInfo;
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
+import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.graphics.drawable.Drawable;
@@ -1130,6 +1130,63 @@ final class ApplicationPackageManager extends PackageManager {
return PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
}
+ // Multi-user support
+
+ /**
+ * @hide
+ */
+ @Override
+ public UserInfo createUser(String name, int flags) {
+ try {
+ return mPM.createUser(name, flags);
+ } catch (RemoteException e) {
+ // Should never happen!
+ }
+ return null;
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public List<UserInfo> getUsers() {
+ // TODO:
+ // Dummy code, always returns just the primary user
+ ArrayList<UserInfo> users = new ArrayList<UserInfo>();
+ UserInfo primary = new UserInfo(0, "Root!",
+ UserInfo.FLAG_ADMIN | UserInfo.FLAG_PRIMARY);
+ users.add(primary);
+ return users;
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public boolean removeUser(int id) {
+ try {
+ return mPM.removeUser(id);
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public void updateUserName(int id, String name) {
+ // TODO:
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public void updateUserFlags(int id, int flags) {
+ // TODO:
+ }
+
private final ContextImpl mContext;
private final IPackageManager mPM;
diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java
index 850ad2b..dc0f529 100644
--- a/core/java/android/app/ApplicationThreadNative.java
+++ b/core/java/android/app/ApplicationThreadNative.java
@@ -223,6 +223,7 @@ public abstract class ApplicationThreadNative extends Binder
{
data.enforceInterface(IApplicationThread.descriptor);
IBinder token = data.readStrongBinder();
+ boolean taskRemoved = data.readInt() != 0;
int startId = data.readInt();
int fl = data.readInt();
Intent args;
@@ -231,7 +232,7 @@ public abstract class ApplicationThreadNative extends Binder
} else {
args = null;
}
- scheduleServiceArgs(token, startId, fl, args);
+ scheduleServiceArgs(token, taskRemoved, startId, fl, args);
return true;
}
@@ -710,11 +711,12 @@ class ApplicationThreadProxy implements IApplicationThread {
data.recycle();
}
- public final void scheduleServiceArgs(IBinder token, int startId,
+ public final void scheduleServiceArgs(IBinder token, boolean taskRemoved, int startId,
int flags, Intent args) throws RemoteException {
Parcel data = Parcel.obtain();
data.writeInterfaceToken(IApplicationThread.descriptor);
data.writeStrongBinder(token);
+ data.writeInt(taskRemoved ? 1 : 0);
data.writeInt(startId);
data.writeInt(flags);
if (args != null) {
diff --git a/core/java/android/app/FullBackupAgent.java b/core/java/android/app/FullBackupAgent.java
deleted file mode 100644
index acd20bd..0000000
--- a/core/java/android/app/FullBackupAgent.java
+++ /dev/null
@@ -1,76 +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.
- */
-
-package android.app;
-
-import android.app.backup.BackupAgent;
-import android.app.backup.BackupDataInput;
-import android.app.backup.BackupDataOutput;
-import android.app.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/&lt;package&gt;/... 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<File> dirsToScan = new LinkedList<File>();
- ArrayList<String> allFiles = new ArrayList<String>();
-
- // 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,
- allFiles.toArray(new String[allFiles.size()]));
- 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 24706f9..1f53c0e 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -133,7 +133,7 @@ public interface IActivityManager extends IInterface {
IThumbnailReceiver receiver) throws RemoteException;
public List<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum,
int flags) throws RemoteException;
- public Bitmap getTaskThumbnail(int taskId) throws RemoteException;
+ public ActivityManager.TaskThumbnails getTaskThumbnails(int taskId) throws RemoteException;
public List getServices(int maxNum, int flags) throws RemoteException;
public List<ActivityManager.ProcessErrorStateInfo> getProcessesInErrorState()
throws RemoteException;
@@ -347,6 +347,16 @@ public interface IActivityManager extends IInterface {
public int getPackageScreenCompatMode(String packageName) throws RemoteException;
public void setPackageScreenCompatMode(String packageName, int mode)
throws RemoteException;
+
+ // Multi-user APIs
+ public boolean switchUser(int userid) throws RemoteException;
+
+ public boolean removeSubTask(int taskId, int subTaskIndex) throws RemoteException;
+
+ public boolean removeTask(int taskId, int flags) throws RemoteException;
+
+ public void registerProcessObserver(IProcessObserver observer) throws RemoteException;
+ public void unregisterProcessObserver(IProcessObserver observer) throws RemoteException;
/*
* Private non-Binder interfaces
@@ -521,7 +531,7 @@ public interface IActivityManager extends IInterface {
int FORCE_STOP_PACKAGE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+78;
int KILL_PIDS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+79;
int GET_SERVICES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+80;
- int GET_TASK_THUMBNAIL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+81;
+ int GET_TASK_THUMBNAILS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+81;
int GET_RUNNING_APP_PROCESSES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+82;
int GET_DEVICE_CONFIGURATION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+83;
int PEEK_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+84;
@@ -567,4 +577,9 @@ public interface IActivityManager extends IInterface {
int SET_FRONT_ACTIVITY_SCREEN_COMPAT_MODE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+124;
int GET_PACKAGE_SCREEN_COMPAT_MODE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+125;
int SET_PACKAGE_SCREEN_COMPAT_MODE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+126;
+ int SWITCH_USER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+127;
+ int REMOVE_SUB_TASK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+128;
+ int REMOVE_TASK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+129;
+ int REGISTER_PROCESS_OBSERVER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+130;
+ int UNREGISTER_PROCESS_OBSERVER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+131;
}
diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java
index 93a8ff3..8c31559 100644
--- a/core/java/android/app/IApplicationThread.java
+++ b/core/java/android/app/IApplicationThread.java
@@ -78,8 +78,8 @@ public interface IApplicationThread extends IInterface {
Intent intent, boolean rebind) throws RemoteException;
void scheduleUnbindService(IBinder token,
Intent intent) throws RemoteException;
- void scheduleServiceArgs(IBinder token, int startId, int flags, Intent args)
- throws RemoteException;
+ void scheduleServiceArgs(IBinder token, boolean taskRemoved, int startId,
+ int flags, Intent args) throws RemoteException;
void scheduleStopService(IBinder token) throws RemoteException;
static final int DEBUG_OFF = 0;
static final int DEBUG_ON = 1;
diff --git a/core/java/android/app/IBackupAgent.aidl b/core/java/android/app/IBackupAgent.aidl
index fed2bc5..52fc623 100644
--- a/core/java/android/app/IBackupAgent.aidl
+++ b/core/java/android/app/IBackupAgent.aidl
@@ -51,6 +51,7 @@ oneway interface IBackupAgent {
void doBackup(in ParcelFileDescriptor oldState,
in ParcelFileDescriptor data,
in ParcelFileDescriptor newState,
+ boolean storeApk,
int token, IBackupManager callbackBinder);
/**
diff --git a/core/java/android/app/IProcessObserver.aidl b/core/java/android/app/IProcessObserver.aidl
new file mode 100644
index 0000000..2094294
--- /dev/null
+++ b/core/java/android/app/IProcessObserver.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2011 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 IProcessObserver {
+
+ void onForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities);
+ void onProcessDied(int pid, int uid);
+
+}
diff --git a/core/java/android/app/IThumbnailRetriever.aidl b/core/java/android/app/IThumbnailRetriever.aidl
new file mode 100644
index 0000000..2a6737d
--- /dev/null
+++ b/core/java/android/app/IThumbnailRetriever.aidl
@@ -0,0 +1,24 @@
+/* Copyright 2011, 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.graphics.Bitmap;
+
+/**
+ * System private API for retrieving thumbnails
+ */
+interface IThumbnailRetriever {
+ Bitmap getThumbnail(int index);
+}
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index cd278be..7b02763 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -33,7 +33,6 @@ import android.os.Process;
import android.os.SystemClock;
import android.os.ServiceManager;
import android.util.AndroidRuntimeException;
-import android.util.Config;
import android.util.Log;
import android.view.IWindowManager;
import android.view.KeyCharacterMap;
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 6541c54..4913e78 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -61,8 +61,7 @@ import android.util.Log;
public class NotificationManager
{
private static String TAG = "NotificationManager";
- private static boolean DEBUG = false;
- private static boolean localLOGV = DEBUG || android.util.Config.LOGV;
+ private static boolean localLOGV = false;
private static INotificationManager sService;
diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java
index 05b9781..c179b35 100644
--- a/core/java/android/app/Service.java
+++ b/core/java/android/app/Service.java
@@ -371,6 +371,13 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac
public static final int START_REDELIVER_INTENT = 3;
/**
+ * Special constant for reporting that we are done processing
+ * {@link #onTaskRemoved(Intent)}.
+ * @hide
+ */
+ public static final int START_TASK_REMOVED_COMPLETE = 1000;
+
+ /**
* This flag is set in {@link #onStartCommand} if the Intent is a
* re-delivery of a previously delivered intent, because the service
* had previously returned {@link #START_REDELIVER_INTENT} but had been
@@ -500,6 +507,19 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac
}
/**
+ * This is called if the service is currently running and the user has
+ * removed a task that comes from the service's application. If you have
+ * set {@link android.content.pm.ServiceInfo#FLAG_STOP_WITH_TASK ServiceInfo.FLAG_STOP_WITH_TASK}
+ * then you will not receive this callback; instead, the service will simply
+ * be stopped.
+ *
+ * @param rootIntent The original root Intent that was used to launch
+ * the task that is being removed.
+ */
+ public void onTaskRemoved(Intent rootIntent) {
+ }
+
+ /**
* Stop the service, if it was previously started. This is the same as
* calling {@link android.content.Context#stopService} for this particular service.
*
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 13a8b78..113c610 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -38,7 +38,7 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.DisplayMetrics;
import android.util.Log;
-import android.view.ViewRoot;
+import android.view.ViewAncestor;
import java.io.FileOutputStream;
import java.io.IOException;
@@ -632,7 +632,7 @@ public class WallpaperManager {
public void setWallpaperOffsets(IBinder windowToken, float xOffset, float yOffset) {
try {
//Log.v(TAG, "Sending new wallpaper offsets from app...");
- ViewRoot.getWindowSession(mContext.getMainLooper()).setWallpaperPosition(
+ ViewAncestor.getWindowSession(mContext.getMainLooper()).setWallpaperPosition(
windowToken, xOffset, yOffset, mWallpaperXStep, mWallpaperYStep);
//Log.v(TAG, "...app returning after sending offsets!");
} catch (RemoteException e) {
@@ -670,7 +670,7 @@ public class WallpaperManager {
int x, int y, int z, Bundle extras) {
try {
//Log.v(TAG, "Sending new wallpaper offsets from app...");
- ViewRoot.getWindowSession(mContext.getMainLooper()).sendWallpaperCommand(
+ ViewAncestor.getWindowSession(mContext.getMainLooper()).sendWallpaperCommand(
windowToken, action, x, y, z, extras, false);
//Log.v(TAG, "...app returning after sending offsets!");
} catch (RemoteException e) {
@@ -690,7 +690,7 @@ public class WallpaperManager {
*/
public void clearWallpaperOffsets(IBinder windowToken) {
try {
- ViewRoot.getWindowSession(mContext.getMainLooper()).setWallpaperPosition(
+ ViewAncestor.getWindowSession(mContext.getMainLooper()).setWallpaperPosition(
windowToken, -1, -1, -1, -1);
} catch (RemoteException e) {
// Ignore.
diff --git a/core/java/android/app/admin/DeviceAdminReceiver.java b/core/java/android/app/admin/DeviceAdminReceiver.java
index 29f8caf..473aec6 100644
--- a/core/java/android/app/admin/DeviceAdminReceiver.java
+++ b/core/java/android/app/admin/DeviceAdminReceiver.java
@@ -52,8 +52,7 @@ import android.os.Bundle;
*/
public class DeviceAdminReceiver extends BroadcastReceiver {
private static String TAG = "DevicePolicy";
- private static boolean DEBUG = false;
- private static boolean localLOGV = DEBUG || android.util.Config.LOGV;
+ private static boolean localLOGV = false;
/**
* This is the primary action that a device administrator must implement to be
diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java
index cb4e0e7..dc60e24 100644
--- a/core/java/android/app/backup/BackupAgent.java
+++ b/core/java/android/app/backup/BackupAgent.java
@@ -85,7 +85,7 @@ import java.io.IOException;
*/
public abstract class BackupAgent extends ContextWrapper {
private static final String TAG = "BackupAgent";
- private static final boolean DEBUG = false;
+ private static final boolean DEBUG = true;
public BackupAgent() {
super(null);
@@ -172,11 +172,18 @@ public abstract class BackupAgent extends ContextWrapper {
* @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 the <code>data</code> stream.
+ * When a full-backup dataset is being restored, this will be <code>null</code>.
*/
public abstract void onRestore(BackupDataInput data, int appVersionCode,
ParcelFileDescriptor newState)
throws IOException;
+ /**
+ * Package-private, used only for dispatching an extra step during full backup
+ */
+ void onSaveApk(BackupDataOutput data) {
+ if (DEBUG) Log.v(TAG, "--- base onSaveApk() ---");
+ }
// ----- Core implementation -----
@@ -199,12 +206,18 @@ public abstract class BackupAgent extends ContextWrapper {
public void doBackup(ParcelFileDescriptor oldState,
ParcelFileDescriptor data,
ParcelFileDescriptor newState,
+ boolean storeApk,
int token, IBackupManager callbackBinder) throws RemoteException {
// Ensure that we're running with the app's normal permission level
long ident = Binder.clearCallingIdentity();
if (DEBUG) Log.v(TAG, "doBackup() invoked");
BackupDataOutput output = new BackupDataOutput(data.getFileDescriptor());
+
+ if (storeApk) {
+ onSaveApk(output);
+ }
+
try {
BackupAgent.this.onBackup(oldState, output, newState);
} catch (IOException ex) {
diff --git a/core/java/android/app/backup/FullBackup.java b/core/java/android/app/backup/FullBackup.java
new file mode 100644
index 0000000..9850566
--- /dev/null
+++ b/core/java/android/app/backup/FullBackup.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2011 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.backup;
+
+/**
+ * Global constant definitions et cetera related to the full-backup-to-fd
+ * binary format.
+ *
+ * @hide
+ */
+public class FullBackup {
+ public static String APK_TREE_TOKEN = "a";
+ public static String OBB_TREE_TOKEN = "obb";
+ public static String ROOT_TREE_TOKEN = "r";
+ public static String DATA_TREE_TOKEN = "f";
+ public static String DATABASE_TREE_TOKEN = "db";
+ public static String SHAREDPREFS_TREE_TOKEN = "sp";
+ public static String CACHE_TREE_TOKEN = "c";
+
+ public static String FULL_BACKUP_INTENT_ACTION = "fullback";
+ public static String FULL_RESTORE_INTENT_ACTION = "fullrest";
+ public static String CONF_TOKEN_INTENT_EXTRA = "conftoken";
+
+ static public native int backupToTar(String packageName, String domain,
+ String linkdomain, String rootpath, String path, BackupDataOutput output);
+}
diff --git a/core/java/android/app/backup/FullBackupAgent.java b/core/java/android/app/backup/FullBackupAgent.java
new file mode 100644
index 0000000..f0a1f2a
--- /dev/null
+++ b/core/java/android/app/backup/FullBackupAgent.java
@@ -0,0 +1,183 @@
+/*
+ * 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.backup;
+
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.Environment;
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
+
+import libcore.io.Libcore;
+import libcore.io.ErrnoException;
+import libcore.io.OsConstants;
+import libcore.io.StructStat;
+
+import java.io.File;
+import java.util.HashSet;
+import java.util.LinkedList;
+
+/**
+ * Backs up an application's entire /data/data/&lt;package&gt;/... 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;
+
+ PackageManager mPm;
+
+ private String mMainDir;
+ private String mFilesDir;
+ private String mDatabaseDir;
+ private String mSharedPrefsDir;
+ private String mCacheDir;
+ private String mLibDir;
+
+ @Override
+ public void onCreate() {
+ mPm = getPackageManager();
+ try {
+ ApplicationInfo appInfo = mPm.getApplicationInfo(getPackageName(), 0);
+ mMainDir = new File(appInfo.dataDir).getAbsolutePath();
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(TAG, "Unable to find package " + getPackageName());
+ throw new RuntimeException(e);
+ }
+
+ mFilesDir = getFilesDir().getAbsolutePath();
+ mDatabaseDir = getDatabasePath("foo").getParentFile().getAbsolutePath();
+ mSharedPrefsDir = getSharedPrefsFile("foo").getParentFile().getAbsolutePath();
+ mCacheDir = getCacheDir().getAbsolutePath();
+
+ ApplicationInfo app = getApplicationInfo();
+ mLibDir = (app.nativeLibraryDir != null)
+ ? new File(app.nativeLibraryDir).getAbsolutePath()
+ : null;
+ }
+
+ @Override
+ public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
+ ParcelFileDescriptor newState) {
+ // Filters, the scan queue, and the set of resulting entities
+ HashSet<String> filterSet = new HashSet<String>();
+
+ // Okay, start with the app's root tree, but exclude all of the canonical subdirs
+ if (mLibDir != null) {
+ filterSet.add(mLibDir);
+ }
+ filterSet.add(mCacheDir);
+ filterSet.add(mDatabaseDir);
+ filterSet.add(mSharedPrefsDir);
+ filterSet.add(mFilesDir);
+ processTree(FullBackup.ROOT_TREE_TOKEN, mMainDir, filterSet, data);
+
+ // Now do the same for the files dir, db dir, and shared prefs dir
+ filterSet.add(mMainDir);
+ filterSet.remove(mFilesDir);
+ processTree(FullBackup.DATA_TREE_TOKEN, mFilesDir, filterSet, data);
+
+ filterSet.add(mFilesDir);
+ filterSet.remove(mDatabaseDir);
+ processTree(FullBackup.DATABASE_TREE_TOKEN, mDatabaseDir, filterSet, data);
+
+ filterSet.add(mDatabaseDir);
+ filterSet.remove(mSharedPrefsDir);
+ processTree(FullBackup.SHAREDPREFS_TREE_TOKEN, mSharedPrefsDir, filterSet, data);
+ }
+
+ private void processTree(String domain, String rootPath,
+ HashSet<String> excludes, BackupDataOutput data) {
+ // Scan the dir tree (if it actually exists) and process each entry we find
+ File rootFile = new File(rootPath);
+ if (rootFile.exists()) {
+ LinkedList<File> scanQueue = new LinkedList<File>();
+ scanQueue.add(rootFile);
+
+ while (scanQueue.size() > 0) {
+ File file = scanQueue.remove(0);
+ String filePath = file.getAbsolutePath();
+
+ // prune this subtree?
+ if (excludes.contains(filePath)) {
+ continue;
+ }
+
+ // If it's a directory, enqueue its contents for scanning.
+ try {
+ StructStat stat = Libcore.os.lstat(filePath);
+ if (OsConstants.S_ISLNK(stat.st_mode)) {
+ if (DEBUG) Log.i(TAG, "Symlink (skipping)!: " + file);
+ continue;
+ } else if (OsConstants.S_ISDIR(stat.st_mode)) {
+ File[] contents = file.listFiles();
+ if (contents != null) {
+ for (File entry : contents) {
+ scanQueue.add(0, entry);
+ }
+ }
+ }
+ } catch (ErrnoException e) {
+ if (DEBUG) Log.w(TAG, "Error scanning file " + file + " : " + e);
+ continue;
+ }
+
+ // Finally, back this file up before proceeding
+ FullBackup.backupToTar(getPackageName(), domain, null, rootPath, filePath, data);
+ }
+ }
+ }
+
+ @Override
+ void onSaveApk(BackupDataOutput data) {
+ ApplicationInfo app = getApplicationInfo();
+ if (DEBUG) Log.i(TAG, "APK flags: system=" + ((app.flags & ApplicationInfo.FLAG_SYSTEM) != 0)
+ + " updated=" + ((app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0)
+ + " locked=" + ((app.flags & ApplicationInfo.FLAG_FORWARD_LOCK) != 0) );
+ if (DEBUG) Log.i(TAG, "codepath: " + getPackageCodePath());
+
+ // Forward-locked apps, system-bundled .apks, etc are filtered out before we get here
+ final String pkgName = getPackageName();
+ final String apkDir = new File(getPackageCodePath()).getParent();
+ FullBackup.backupToTar(pkgName, FullBackup.APK_TREE_TOKEN, null,
+ apkDir, getPackageCodePath(), data);
+
+ // Save associated .obb content if it exists and we did save the apk
+ // check for .obb and save those too
+ final File obbDir = Environment.getExternalStorageAppObbDirectory(pkgName);
+ if (obbDir != null) {
+ if (DEBUG) Log.i(TAG, "obb dir: " + obbDir.getAbsolutePath());
+ File[] obbFiles = obbDir.listFiles();
+ if (obbFiles != null) {
+ final String obbDirName = obbDir.getAbsolutePath();
+ for (File obb : obbFiles) {
+ FullBackup.backupToTar(pkgName, FullBackup.OBB_TREE_TOKEN, null,
+ obbDirName, obb.getAbsolutePath(), data);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState) {
+ }
+}
diff --git a/core/java/android/app/backup/IBackupManager.aidl b/core/java/android/app/backup/IBackupManager.aidl
index b315b3a..94e31a8 100644
--- a/core/java/android/app/backup/IBackupManager.aidl
+++ b/core/java/android/app/backup/IBackupManager.aidl
@@ -16,7 +16,9 @@
package android.app.backup;
+import android.app.backup.IFullBackupRestoreObserver;
import android.app.backup.IRestoreSession;
+import android.os.ParcelFileDescriptor;
import android.content.Intent;
/**
@@ -121,6 +123,42 @@ interface IBackupManager {
void backupNow();
/**
+ * Write a full backup of the given package to the supplied file descriptor.
+ * The fd may be a socket or other non-seekable destination. If no package names
+ * are supplied, then every application on the device will be backed up to the output.
+ *
+ * <p>This method is <i>synchronous</i> -- it does not return until the backup has
+ * completed.
+ *
+ * <p>Callers must hold the android.permission.BACKUP permission to use this method.
+ *
+ * @param fd The file descriptor to which a 'tar' file stream is to be written
+ * @param includeApks If <code>true</code>, the resulting tar stream will include the
+ * application .apk files themselves as well as their data.
+ * @param includeShared If <code>true</code>, the resulting tar stream will include
+ * the contents of the device's shared storage (SD card or equivalent).
+ * @param allApps If <code>true</code>, the resulting tar stream will include all
+ * installed applications' data, not just those named in the <code>packageNames</code>
+ * parameter.
+ * @param packageNames The package names of the apps whose data (and optionally .apk files)
+ * are to be backed up. The <code>allApps</code> parameter supersedes this.
+ */
+ void fullBackup(in ParcelFileDescriptor fd, boolean includeApks, boolean includeShared,
+ boolean allApps, in String[] packageNames);
+
+ /**
+ * Confirm that the requested full backup/restore operation can proceed. The system will
+ * not actually perform the operation described to fullBackup() / fullRestore() unless the
+ * UI calls back into the Backup Manager to confirm, passing the correct token. At
+ * the same time, the UI supplies a callback Binder for progress notifications during
+ * the operation.
+ *
+ * <p>Callers must hold the android.permission.BACKUP permission to use this method.
+ */
+ void acknowledgeFullBackupOrRestore(int token, boolean allow,
+ IFullBackupRestoreObserver observer);
+
+ /**
* Identify the currently selected transport. Callers must hold the
* android.permission.BACKUP permission to use this method.
*/
diff --git a/core/java/android/app/backup/IFullBackupRestoreObserver.aidl b/core/java/android/app/backup/IFullBackupRestoreObserver.aidl
new file mode 100644
index 0000000..3e0b73d
--- /dev/null
+++ b/core/java/android/app/backup/IFullBackupRestoreObserver.aidl
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2011 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.backup;
+
+/**
+ * Observer of a full backup or restore process. The observer is told "interesting"
+ * information about an ongoing full backup or restore action.
+ *
+ * {@hide}
+ */
+
+oneway interface IFullBackupRestoreObserver {
+ /**
+ * Notification: a full backup operation has begun.
+ */
+ void onStartBackup();
+
+ /**
+ * Notification: the system has begun backing up the given package.
+ *
+ * @param name The name of the application being saved. This will typically be a
+ * user-meaningful name such as "Browser" rather than a package name such as
+ * "com.android.browser", though this is not guaranteed.
+ */
+ void onBackupPackage(String name);
+
+ /**
+ * Notification: the full backup operation has ended.
+ */
+ void onEndBackup();
+
+ /**
+ * Notification: a restore-from-full-backup operation has begun.
+ */
+ void onStartRestore();
+
+ /**
+ * Notification: the system has begun restore of the given package.
+ *
+ * @param name The name of the application being saved. This will typically be a
+ * user-meaningful name such as "Browser" rather than a package name such as
+ * "com.android.browser", though this is not guaranteed.
+ */
+ void onRestorePackage(String name);
+
+ /**
+ * Notification: the restore-from-full-backup operation has ended.
+ */
+ void onEndRestore();
+
+ /**
+ * The user's window of opportunity for confirming the operation has timed out.
+ */
+ void onTimeout();
+}
diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java
index fa55520..8a9bef0 100644
--- a/core/java/android/bluetooth/BluetoothHeadset.java
+++ b/core/java/android/bluetooth/BluetoothHeadset.java
@@ -603,7 +603,7 @@ public final class BluetoothHeadset implements BluetoothProfile {
*/
public boolean setAudioState(BluetoothDevice device, int state) {
if (DBG) log("setAudioState");
- if (mService != null && isEnabled()) {
+ if (mService != null && !isDisabled()) {
try {
return mService.setAudioState(device, state);
} catch (RemoteException e) {Log.e(TAG, e.toString());}
@@ -622,7 +622,7 @@ public final class BluetoothHeadset implements BluetoothProfile {
*/
public int getAudioState(BluetoothDevice device) {
if (DBG) log("getAudioState");
- if (mService != null && isEnabled()) {
+ if (mService != null && !isDisabled()) {
try {
return mService.getAudioState(device);
} catch (RemoteException e) {Log.e(TAG, e.toString());}
@@ -705,6 +705,11 @@ public final class BluetoothHeadset implements BluetoothProfile {
return false;
}
+ private boolean isDisabled() {
+ if (mAdapter.getState() == BluetoothAdapter.STATE_OFF) return true;
+ return false;
+ }
+
private boolean isValidDevice(BluetoothDevice device) {
if (device == null) return false;
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 2d03e7c..364821e 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -35,7 +35,6 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.text.TextUtils;
-import android.util.Config;
import android.util.EventLog;
import android.util.Log;
@@ -1627,9 +1626,9 @@ public abstract class ContentResolver {
return sContentService;
}
IBinder b = ServiceManager.getService(CONTENT_SERVICE_NAME);
- if (Config.LOGV) Log.v("ContentService", "default service binder = " + b);
+ if (false) Log.v("ContentService", "default service binder = " + b);
sContentService = IContentService.Stub.asInterface(b);
- if (Config.LOGV) Log.v("ContentService", "default service = " + sContentService);
+ if (false) Log.v("ContentService", "default service = " + sContentService);
return sContentService;
}
diff --git a/core/java/android/content/ContentService.java b/core/java/android/content/ContentService.java
index afe8483..a2af558 100644
--- a/core/java/android/content/ContentService.java
+++ b/core/java/android/content/ContentService.java
@@ -25,7 +25,6 @@ import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.util.Config;
import android.util.Log;
import android.Manifest;
@@ -104,7 +103,7 @@ public final class ContentService extends IContentService.Stub {
}
synchronized (mRootNode) {
mRootNode.addObserverLocked(uri, observer, notifyForDescendents, mRootNode);
- if (Config.LOGV) Log.v(TAG, "Registered observer " + observer + " at " + uri +
+ if (false) Log.v(TAG, "Registered observer " + observer + " at " + uri +
" with notifyForDescendents " + notifyForDescendents);
}
}
@@ -115,7 +114,7 @@ public final class ContentService extends IContentService.Stub {
}
synchronized (mRootNode) {
mRootNode.removeObserverLocked(observer);
- if (Config.LOGV) Log.v(TAG, "Unregistered observer " + observer);
+ if (false) Log.v(TAG, "Unregistered observer " + observer);
}
}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 4c7d87f..a660bd7 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -1544,6 +1544,9 @@ public abstract class Context {
*/
public static final String NETWORKMANAGEMENT_SERVICE = "network_management";
+ /** {@hide} */
+ public static final String NETWORK_POLICY_SERVICE = "netpolicy";
+
/**
* Use with {@link #getSystemService} to retrieve a {@link
* android.net.wifi.WifiManager} for handling management of
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 7bdd1b9..3d637e9 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1878,7 +1878,7 @@ public class Intent implements Parcelable, Cloneable {
"android.intent.action.USB_ANLG_HEADSET_PLUG";
/**
- * Broadcast Action: An analog audio speaker/headset plugged in or unplugged.
+ * Broadcast Action: A digital audio speaker/headset plugged in or unplugged.
*
* <p>The intent will have the following extra values:
* <ul>
@@ -1908,6 +1908,21 @@ public class Intent implements Parcelable, Cloneable {
"android.intent.action.HDMI_AUDIO_PLUG";
/**
+ * <p>Broadcast Action: The user has switched on advanced settings in the settings app:</p>
+ * <ul>
+ * <li><em>state</em> - A boolean value indicating whether the settings is on or off.</li>
+ * </ul>
+ *
+ * <p class="note">This is a protected intent that can only be sent
+ * by the system.
+ *
+ * @hide
+ */
+ //@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_ADVANCED_SETTINGS_CHANGED
+ = "android.intent.action.ADVANCED_SETTINGS";
+
+ /**
* Broadcast Action: An outgoing call is about to be placed.
*
* <p>The Intent will have the following extra value:
diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java
index 06c1ecb..2a0ebcf 100644
--- a/core/java/android/content/IntentFilter.java
+++ b/core/java/android/content/IntentFilter.java
@@ -31,7 +31,6 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.os.PatternMatcher;
import android.util.AndroidException;
-import android.util.Config;
import android.util.Log;
import android.util.Printer;
@@ -670,7 +669,7 @@ public class IntentFilter implements Parcelable {
if (host == null) {
return NO_MATCH_DATA;
}
- if (Config.LOGV) Log.v("IntentFilter",
+ if (false) Log.v("IntentFilter",
"Match host " + host + ": " + mHost);
if (mWild) {
if (host.length() < mHost.length()) {
@@ -1095,14 +1094,14 @@ public class IntentFilter implements Parcelable {
public final int match(String action, String type, String scheme,
Uri data, Set<String> categories, String logTag) {
if (action != null && !matchAction(action)) {
- if (Config.LOGV) Log.v(
+ if (false) Log.v(
logTag, "No matching action " + action + " for " + this);
return NO_MATCH_ACTION;
}
int dataMatch = matchData(type, scheme, data);
if (dataMatch < 0) {
- if (Config.LOGV) {
+ if (false) {
if (dataMatch == NO_MATCH_TYPE) {
Log.v(logTag, "No matching type " + type
+ " for " + this);
@@ -1117,7 +1116,7 @@ public class IntentFilter implements Parcelable {
String categoryMismatch = matchCategories(categories);
if (categoryMismatch != null) {
- if (Config.LOGV) {
+ if (false) {
Log.v(logTag, "No matching category " + categoryMismatch + " for " + this);
}
return NO_MATCH_CATEGORY;
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 92b2c3b..4b38d48 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -89,7 +89,16 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
* <p>If android:allowBackup is set to false, this attribute is ignored.
*/
public String backupAgentName;
-
+
+ /**
+ * Class implementing the package's *full* backup functionality. This
+ * is not usable except by system-installed packages. It can be the same
+ * as the backupAgent.
+ *
+ * @hide
+ */
+ public String fullBackupAgentName;
+
/**
* Value for {@link #flags}: if set, this application is installed in the
* device's system image.
@@ -505,6 +514,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
dest.writeInt(installLocation);
dest.writeString(manageSpaceActivityName);
dest.writeString(backupAgentName);
+ dest.writeString(fullBackupAgentName);
dest.writeInt(descriptionRes);
}
@@ -538,6 +548,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
installLocation = source.readInt();
manageSpaceActivityName = source.readString();
backupAgentName = source.readString();
+ fullBackupAgentName = source.readString();
descriptionRes = source.readInt();
}
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 20b1b50..37b6822 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -36,6 +36,7 @@ import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
+import android.content.pm.UserInfo;
import android.net.Uri;
import android.content.IntentSender;
@@ -342,4 +343,7 @@ interface IPackageManager {
boolean setInstallLocation(int loc);
int getInstallLocation();
+
+ UserInfo createUser(in String name, int flags);
+ boolean removeUser(int userId);
}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 80bed0d..ff817c1 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -150,21 +150,21 @@ public abstract class PackageManager {
* {@link PackageInfo#permissions}.
*/
public static final int GET_PERMISSIONS = 0x00001000;
-
+
/**
* Flag parameter to retrieve all applications(even uninstalled ones) with data directories.
- * This state could have resulted if applications have been deleted with flag
+ * This state could have resulted if applications have been deleted with flag
* DONT_DELETE_DATA
* with a possibility of being replaced or reinstalled in future
*/
public static final int GET_UNINSTALLED_PACKAGES = 0x00002000;
-
+
/**
* {@link PackageInfo} flag: return information about
* hardware preferences in
* {@link PackageInfo#configPreferences PackageInfo.configPreferences} and
* requested features in {@link PackageInfo#reqFeatures
- * PackageInfo.reqFeatures}.
+ * PackageInfo.reqFeatures}.
*/
public static final int GET_CONFIGURATIONS = 0x00004000;
@@ -244,7 +244,7 @@ public abstract class PackageManager {
public static final int INSTALL_REPLACE_EXISTING = 0x00000002;
/**
- * Flag parameter for {@link #installPackage} to indicate that you want to
+ * Flag parameter for {@link #installPackage} to indicate that you want to
* allow test packages (those that have set android:testOnly in their
* manifest) to be installed.
* @hide
@@ -555,7 +555,7 @@ public abstract class PackageManager {
* Return code for when package deletion succeeds. This is passed to the
* {@link IPackageDeleteObserver} by {@link #deletePackage()} if the system
* succeeded in deleting the package.
- *
+ *
* @hide
*/
public static final int DELETE_SUCCEEDED = 1;
@@ -564,7 +564,7 @@ public abstract class PackageManager {
* Deletion failed return code: this is passed to the
* {@link IPackageDeleteObserver} by {@link #deletePackage()} if the system
* failed to delete the package for an unspecified reason.
- *
+ *
* @hide
*/
public static final int DELETE_FAILED_INTERNAL_ERROR = -1;
@@ -574,7 +574,7 @@ public abstract class PackageManager {
* {@link IPackageDeleteObserver} by {@link #deletePackage()} if the system
* failed to delete the package because it is the active DevicePolicy
* manager.
- *
+ *
* @hide
*/
public static final int DELETE_FAILED_DEVICE_POLICY_MANAGER = -2;
@@ -583,7 +583,7 @@ public abstract class PackageManager {
* Return code that is passed to the {@link IPackageMoveObserver} by
* {@link #movePackage(android.net.Uri, IPackageMoveObserver)} when the
* package has been successfully moved by the system.
- *
+ *
* @hide
*/
public static final int MOVE_SUCCEEDED = 1;
@@ -641,7 +641,7 @@ public abstract class PackageManager {
* {@link #movePackage(android.net.Uri, IPackageMoveObserver)} if the
* specified package already has an operation pending in the
* {@link PackageHandler} queue.
- *
+ *
* @hide
*/
public static final int MOVE_FAILED_OPERATION_PENDING = -7;
@@ -662,10 +662,15 @@ public abstract class PackageManager {
public static final int MOVE_EXTERNAL_MEDIA = 0x00000002;
/**
- * Feature for {@link #getSystemAvailableFeatures} and
- * {@link #hasSystemFeature}: The device's audio pipeline is low-latency,
- * more suitable for audio applications sensitive to delays or lag in
- * sound input or output.
+ * Range of IDs allocated for a user.
+ * @hide
+ */
+ public static final int PER_USER_RANGE = 100000;
+
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device's
+ * audio pipeline is low-latency, more suitable for audio applications sensitive to delays or
+ * lag in sound input or output.
*/
@SdkConstant(SdkConstantType.FEATURE)
public static final String FEATURE_AUDIO_LOW_LATENCY = "android.hardware.audio.low_latency";
@@ -789,7 +794,7 @@ public abstract class PackageManager {
*/
@SdkConstant(SdkConstantType.FEATURE)
public static final String FEATURE_SENSOR_PROXIMITY = "android.hardware.sensor.proximity";
-
+
/**
* Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device has a telephony radio with data
@@ -797,14 +802,14 @@ public abstract class PackageManager {
*/
@SdkConstant(SdkConstantType.FEATURE)
public static final String FEATURE_TELEPHONY = "android.hardware.telephony";
-
+
/**
* Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device has a CDMA telephony stack.
*/
@SdkConstant(SdkConstantType.FEATURE)
public static final String FEATURE_TELEPHONY_CDMA = "android.hardware.telephony.cdma";
-
+
/**
* Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device has a GSM telephony stack.
@@ -847,8 +852,8 @@ public abstract class PackageManager {
*/
@SdkConstant(SdkConstantType.FEATURE)
public static final String FEATURE_TOUCHSCREEN = "android.hardware.touchscreen";
-
-
+
+
/**
* Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device's touch screen supports
@@ -856,7 +861,7 @@ public abstract class PackageManager {
*/
@SdkConstant(SdkConstantType.FEATURE)
public static final String FEATURE_TOUCHSCREEN_MULTITOUCH = "android.hardware.touchscreen.multitouch";
-
+
/**
* Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device's touch screen is capable of
@@ -932,11 +937,11 @@ public abstract class PackageManager {
* @return Returns a PackageInfo object containing information about the package.
* If flag GET_UNINSTALLED_PACKAGES is set and if the package is not
* found in the list of installed applications, the package information is
- * retrieved from the list of uninstalled applications(which includes
+ * retrieved from the list of uninstalled applications(which includes
* installed applications as well as applications
* with data directory ie applications which had been
* deleted with DONT_DELTE_DATA flag set).
- *
+ *
* @see #GET_ACTIVITIES
* @see #GET_GIDS
* @see #GET_CONFIGURATIONS
@@ -947,7 +952,7 @@ public abstract class PackageManager {
* @see #GET_SERVICES
* @see #GET_SIGNATURES
* @see #GET_UNINSTALLED_PACKAGES
- *
+ *
*/
public abstract PackageInfo getPackageInfo(String packageName, int flags)
throws NameNotFoundException;
@@ -960,7 +965,7 @@ public abstract class PackageManager {
* the canonical name for each package.
*/
public abstract String[] currentToCanonicalPackageNames(String[] names);
-
+
/**
* Map from a packages canonical name to the current name in use on the device.
* @param names Array of new names to be mapped.
@@ -968,7 +973,7 @@ public abstract class PackageManager {
* the current name for each package.
*/
public abstract String[] canonicalToCurrentPackageNames(String[] names);
-
+
/**
* Return a "good" intent to launch a front-door activity in a package,
* for use for example to implement an "open" button when browsing through
@@ -976,12 +981,12 @@ public abstract class PackageManager {
* activity in the category {@link Intent#CATEGORY_INFO}, next for a
* main activity in the category {@link Intent#CATEGORY_LAUNCHER}, or return
* null if neither are found.
- *
+ *
* <p>Throws {@link NameNotFoundException} if a package with the given
* name can not be found on the system.
*
* @param packageName The name of the package to inspect.
- *
+ *
* @return Returns either a fully-qualified Intent that can be used to
* launch the main activity in the package, or null if the package does
* not contain such an activity.
@@ -1077,16 +1082,16 @@ public abstract class PackageManager {
*
* @param packageName The full name (i.e. com.google.apps.contacts) of an
* application.
- * @param flags Additional option flags. Use any combination of
+ * @param flags Additional option flags. Use any combination of
* {@link #GET_META_DATA}, {@link #GET_SHARED_LIBRARY_FILES},
* {@link #GET_UNINSTALLED_PACKAGES} to modify the data returned.
*
- * @return {@link ApplicationInfo} Returns ApplicationInfo object containing
+ * @return {@link ApplicationInfo} Returns ApplicationInfo object containing
* information about the package.
* If flag GET_UNINSTALLED_PACKAGES is set and if the package is not
- * found in the list of installed applications,
- * the application information is retrieved from the
- * list of uninstalled applications(which includes
+ * found in the list of installed applications,
+ * the application information is retrieved from the
+ * list of uninstalled applications(which includes
* installed applications as well as applications
* with data directory ie applications which had been
* deleted with DONT_DELTE_DATA flag set).
@@ -1108,7 +1113,7 @@ public abstract class PackageManager {
* @param component The full component name (i.e.
* com.google.apps.contacts/com.google.apps.contacts.ContactsList) of an Activity
* class.
- * @param flags Additional option flags. Use any combination of
+ * @param flags Additional option flags. Use any combination of
* {@link #GET_META_DATA}, {@link #GET_SHARED_LIBRARY_FILES},
* to modify the data (in ApplicationInfo) returned.
*
@@ -1131,7 +1136,7 @@ public abstract class PackageManager {
* @param component The full component name (i.e.
* com.google.apps.calendar/com.google.apps.calendar.CalendarAlarm) of a Receiver
* class.
- * @param flags Additional option flags. Use any combination of
+ * @param flags Additional option flags. Use any combination of
* {@link #GET_META_DATA}, {@link #GET_SHARED_LIBRARY_FILES},
* to modify the data returned.
*
@@ -1154,12 +1159,12 @@ public abstract class PackageManager {
* @param component The full component name (i.e.
* com.google.apps.media/com.google.apps.media.BackgroundPlayback) of a Service
* class.
- * @param flags Additional option flags. Use any combination of
+ * @param flags Additional option flags. Use any combination of
* {@link #GET_META_DATA}, {@link #GET_SHARED_LIBRARY_FILES},
* to modify the data returned.
*
* @return ServiceInfo containing information about the service.
- *
+ *
* @see #GET_META_DATA
* @see #GET_SHARED_LIBRARY_FILES
*/
@@ -1206,7 +1211,7 @@ public abstract class PackageManager {
*
* @return A List of PackageInfo objects, one for each package that is
* installed on the device. In the unlikely case of there being no
- * installed packages, an empty list is returned.
+ * installed packages, an empty list is returned.
* If flag GET_UNINSTALLED_PACKAGES is set, a list of all
* applications including those deleted with DONT_DELETE_DATA
* (partially installed apps with data directory) will be returned.
@@ -1221,7 +1226,7 @@ public abstract class PackageManager {
* @see #GET_SERVICES
* @see #GET_SIGNATURES
* @see #GET_UNINSTALLED_PACKAGES
- *
+ *
*/
public abstract List<PackageInfo> getInstalledPackages(int flags);
@@ -1283,7 +1288,7 @@ public abstract class PackageManager {
* the device is rebooted before it is written.
*/
public abstract boolean addPermissionAsync(PermissionInfo info);
-
+
/**
* Removes a permission that was previously added with
* {@link #addPermission(PermissionInfo)}. The same ownership rules apply
@@ -1370,7 +1375,7 @@ public abstract class PackageManager {
* user id is not currently assigned.
*/
public abstract String getNameForUid(int uid);
-
+
/**
* Return the user id associated with a shared user name. Multiple
* applications can specify a shared user name in their manifest and thus
@@ -1391,38 +1396,38 @@ public abstract class PackageManager {
* device. If flag GET_UNINSTALLED_PACKAGES has been set, a list of all
* applications including those deleted with DONT_DELETE_DATA(partially
* installed apps with data directory) will be returned.
- *
- * @param flags Additional option flags. Use any combination of
+ *
+ * @param flags Additional option flags. Use any combination of
* {@link #GET_META_DATA}, {@link #GET_SHARED_LIBRARY_FILES},
* {link #GET_UNINSTALLED_PACKAGES} to modify the data returned.
*
* @return A List of ApplicationInfo objects, one for each application that
* is installed on the device. In the unlikely case of there being
- * no installed applications, an empty list is returned.
+ * no installed applications, an empty list is returned.
* If flag GET_UNINSTALLED_PACKAGES is set, a list of all
* applications including those deleted with DONT_DELETE_DATA
* (partially installed apps with data directory) will be returned.
- *
+ *
* @see #GET_META_DATA
* @see #GET_SHARED_LIBRARY_FILES
* @see #GET_UNINSTALLED_PACKAGES
*/
public abstract List<ApplicationInfo> getInstalledApplications(int flags);
-
+
/**
* Get a list of shared libraries that are available on the
* system.
- *
+ *
* @return An array of shared library names that are
* available on the system, or null if none are installed.
- *
+ *
*/
public abstract String[] getSystemSharedLibraryNames();
/**
* Get a list of features that are available on the
* system.
- *
+ *
* @return An array of FeatureInfo classes describing the features
* that are available on the system, or null if there are none(!!).
*/
@@ -1431,7 +1436,7 @@ public abstract class PackageManager {
/**
* Check whether the given feature name is one of the available
* features as returned by {@link #getSystemAvailableFeatures()}.
- *
+ *
* @return Returns true if the devices supports the feature, else
* false.
*/
@@ -1448,7 +1453,7 @@ public abstract class PackageManager {
* that {@link android.content.Context#startActivity(Intent)} and
* {@link android.content.Intent#resolveActivity(PackageManager)
* Intent.resolveActivity(PackageManager)} do.</p>
- *
+ *
* @param intent An intent containing all of the desired specification
* (action, data, type, category, and/or component).
* @param flags Additional option flags. The most important is
@@ -1747,7 +1752,7 @@ public abstract class PackageManager {
*
* @return Returns the image of the logo or null if the activity has no
* logo specified.
- *
+ *
* @throws NameNotFoundException Thrown if the resources for the given
* activity could not be loaded.
*
@@ -1768,7 +1773,7 @@ public abstract class PackageManager {
*
* @return Returns the image of the logo, or null if the activity has no
* logo specified.
- *
+ *
* @throws NameNotFoundException Thrown if the resources for application
* matching the given intent could not be loaded.
*
@@ -1801,7 +1806,7 @@ public abstract class PackageManager {
*
* @return Returns the image of the logo, or null if no application logo
* has been specified.
- *
+ *
* @throws NameNotFoundException Thrown if the resources for the given
* application could not be loaded.
*
@@ -1935,7 +1940,7 @@ public abstract class PackageManager {
* @see #GET_RECEIVERS
* @see #GET_SERVICES
* @see #GET_SIGNATURES
- *
+ *
*/
public PackageInfo getPackageArchiveInfo(String archiveFilePath, int flags) {
PackageParser packageParser = new PackageParser(archiveFilePath);
@@ -1952,7 +1957,7 @@ public abstract class PackageManager {
/**
* @hide
- *
+ *
* Install a package. Since this may take a little while, the result will
* be posted back to the given observer. An installation will fail if the calling context
* lacks the {@link android.Manifest.permission#INSTALL_PACKAGES} permission, if the
@@ -2012,11 +2017,11 @@ public abstract class PackageManager {
/**
* Retrieve the package name of the application that installed a package. This identifies
* which market the package came from.
- *
+ *
* @param packageName The name of the package to query
*/
public abstract String getInstallerPackageName(String packageName);
-
+
/**
* Attempts to clear the user data directory of an application.
* Since this may take a little while, the result will
@@ -2071,7 +2076,7 @@ public abstract class PackageManager {
* of bytes if possible.
* @param observer call back used to notify when
* the operation is completed
- *
+ *
* @hide
*/
public abstract void freeStorageAndNotify(long freeStorageSize, IPackageDataObserver observer);
@@ -2096,7 +2101,7 @@ public abstract class PackageManager {
* @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, IntentSender pi);
@@ -2172,7 +2177,7 @@ public abstract class PackageManager {
* @deprecated This is a protected API that should not have been available
* to third party applications. It is the platform's responsibility for
* assigning preferred activities and this can not be directly modified.
- *
+ *
* Add a new preferred activity mapping to the system. This will be used
* to automatically select the given activity component when
* {@link Context#startActivity(Intent) Context.startActivity()} finds
@@ -2195,7 +2200,7 @@ public abstract class PackageManager {
* @deprecated This is a protected API that should not have been available
* to third party applications. It is the platform's responsibility for
* assigning preferred activities and this can not be directly modified.
- *
+ *
* 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
@@ -2304,7 +2309,7 @@ public abstract class PackageManager {
*/
public abstract void setApplicationEnabledSetting(String packageName,
int newState, int flags);
-
+
/**
* Return the the enabled setting for an application. This returns
* the last value set by
@@ -2345,4 +2350,79 @@ public abstract class PackageManager {
*/
public abstract void movePackage(
String packageName, IPackageMoveObserver observer, int flags);
+
+ /**
+ * Creates a user with the specified name and options.
+ *
+ * @param name the user's name
+ * @param flags flags that identify the type of user and other properties.
+ * @see UserInfo
+ *
+ * @return the UserInfo object for the created user, or null if the user could not be created.
+ * @hide
+ */
+ public abstract UserInfo createUser(String name, int flags);
+
+ /**
+ * @return the list of users that were created
+ * @hide
+ */
+ public abstract List<UserInfo> getUsers();
+
+ /**
+ * @param id the ID of the user, where 0 is the primary user.
+ * @hide
+ */
+ public abstract boolean removeUser(int id);
+
+ /**
+ * Updates the user's name.
+ *
+ * @param id the user's id
+ * @param name the new name for the user
+ * @hide
+ */
+ public abstract void updateUserName(int id, String name);
+
+ /**
+ * Changes the user's properties specified by the flags.
+ *
+ * @param id the user's id
+ * @param flags the new flags for the user
+ * @hide
+ */
+ public abstract void updateUserFlags(int id, int flags);
+
+ /**
+ * Checks to see if the user id is the same for the two uids, i.e., they belong to the same
+ * user.
+ * @hide
+ */
+ public static boolean isSameUser(int uid1, int uid2) {
+ return getUserId(uid1) == getUserId(uid2);
+ }
+
+ /**
+ * Returns the user id for a given uid.
+ * @hide
+ */
+ public static int getUserId(int uid) {
+ return uid / PER_USER_RANGE;
+ }
+
+ /**
+ * Returns the uid that is composed from the userId and the appId.
+ * @hide
+ */
+ public static int getUid(int userId, int appId) {
+ return userId * PER_USER_RANGE + (appId % PER_USER_RANGE);
+ }
+
+ /**
+ * Returns the app id (or base uid) for a given uid, stripping out the user id from it.
+ * @hide
+ */
+ public static int getAppId(int uid) {
+ return uid % PER_USER_RANGE;
+ }
}
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 10799a4..9ff324b 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -24,11 +24,11 @@ import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
+import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.PatternMatcher;
import android.util.AttributeSet;
-import android.util.Config;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.TypedValue;
@@ -384,7 +384,7 @@ public class PackageParser {
return null;
}
- if ((flags&PARSE_CHATTY) != 0 && Config.LOGD) Log.d(
+ if ((flags&PARSE_CHATTY) != 0 && false) Log.d(
TAG, "Scanning package: " + mArchiveSourcePath);
XmlResourceParser parser = null;
@@ -666,7 +666,7 @@ public class PackageParser {
outError[0] = "No start tag found";
return null;
}
- if ((flags&PARSE_CHATTY) != 0 && Config.LOGV) Log.v(
+ if ((flags&PARSE_CHATTY) != 0 && false) Log.v(
TAG, "Root element name: '" + parser.getName() + "'");
if (!parser.getName().equals("manifest")) {
outError[0] = "No <manifest> tag";
@@ -701,7 +701,7 @@ public class PackageParser {
outError[0] = "No start tag found";
return null;
}
- if ((flags&PARSE_CHATTY) != 0 && Config.LOGV) Log.v(
+ if ((flags&PARSE_CHATTY) != 0 && false) Log.v(
TAG, "Root element name: '" + parser.getName() + "'");
if (!parser.getName().equals("manifest")) {
outError[0] = "No <manifest> tag";
@@ -1506,7 +1506,17 @@ public class PackageParser {
}
}
}
-
+
+ name = sa.getNonConfigurationString(
+ com.android.internal.R.styleable.AndroidManifestApplication_fullBackupAgent, 0);
+ if (name != null) {
+ ai.fullBackupAgentName = buildClassName(pkgName, name, outError);
+ if (true) {
+ Log.v(TAG, "android:fullBackupAgent=" + ai.fullBackupAgentName
+ + " from " + pkgName + "+" + name);
+ }
+ }
+
TypedValue v = sa.peekValue(
com.android.internal.R.styleable.AndroidManifestApplication_label);
if (v != null && (ai.labelRes=v.resourceId) == 0) {
@@ -2475,6 +2485,13 @@ public class PackageParser {
s.info.permission = str.length() > 0 ? str.toString().intern() : null;
}
+ s.info.flags = 0;
+ if (sa.getBoolean(
+ com.android.internal.R.styleable.AndroidManifestService_stopWithTask,
+ false)) {
+ s.info.flags |= ServiceInfo.FLAG_STOP_WITH_TASK;
+ }
+
sa.recycle();
if ((owner.applicationInfo.flags&ApplicationInfo.FLAG_CANT_SAVE_STATE) != 0) {
diff --git a/core/java/android/content/pm/ServiceInfo.java b/core/java/android/content/pm/ServiceInfo.java
index 087a4fe..612e345 100644
--- a/core/java/android/content/pm/ServiceInfo.java
+++ b/core/java/android/content/pm/ServiceInfo.java
@@ -33,17 +33,35 @@ public class ServiceInfo extends ComponentInfo
*/
public String permission;
+ /**
+ * Bit in {@link #flags}: If set, the service will automatically be
+ * stopped by the system if the user removes a task that is rooted
+ * in one of the application's activities. Set from the
+ * {@link android.R.attr#stopWithTask} attribute.
+ */
+ public static final int FLAG_STOP_WITH_TASK = 0x0001;
+
+ /**
+ * Options that have been set in the service declaration in the
+ * manifest.
+ * These include:
+ * {@link #FLAG_STOP_WITH_TASK}
+ */
+ public int flags;
+
public ServiceInfo() {
}
public ServiceInfo(ServiceInfo orig) {
super(orig);
permission = orig.permission;
+ flags = orig.flags;
}
public void dump(Printer pw, String prefix) {
super.dumpFront(pw, prefix);
pw.println(prefix + "permission=" + permission);
+ pw.println(prefix + "flags=0x" + Integer.toHexString(flags));
}
public String toString() {
@@ -59,6 +77,7 @@ public class ServiceInfo extends ComponentInfo
public void writeToParcel(Parcel dest, int parcelableFlags) {
super.writeToParcel(dest, parcelableFlags);
dest.writeString(permission);
+ dest.writeInt(flags);
}
public static final Creator<ServiceInfo> CREATOR =
@@ -74,5 +93,6 @@ public class ServiceInfo extends ComponentInfo
private ServiceInfo(Parcel source) {
super(source);
permission = source.readString();
+ flags = source.readInt();
}
}
diff --git a/core/java/android/content/pm/UserInfo.aidl b/core/java/android/content/pm/UserInfo.aidl
new file mode 100644
index 0000000..2e7cb8f
--- /dev/null
+++ b/core/java/android/content/pm/UserInfo.aidl
@@ -0,0 +1,20 @@
+/*
+**
+** Copyright 2011, 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.pm;
+
+parcelable UserInfo;
diff --git a/core/java/android/content/pm/UserInfo.java b/core/java/android/content/pm/UserInfo.java
new file mode 100644
index 0000000..ba5331c
--- /dev/null
+++ b/core/java/android/content/pm/UserInfo.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2011 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.pm;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Per-user information.
+ * @hide
+ */
+public class UserInfo implements Parcelable {
+ /**
+ * Primary user. Only one user can have this flag set. Meaning of this
+ * flag TBD.
+ */
+ public static final int FLAG_PRIMARY = 0x00000001;
+
+ /**
+ * User with administrative privileges. Such a user can create and
+ * delete users.
+ */
+ public static final int FLAG_ADMIN = 0x00000002;
+
+ /**
+ * Indicates a guest user that may be transient.
+ */
+ public static final int FLAG_GUEST = 0x00000004;
+
+ public int id;
+ public String name;
+ public int flags;
+
+ public UserInfo(int id, String name, int flags) {
+ this.id = id;
+ this.name = name;
+ this.flags = flags;
+ }
+
+ public boolean isPrimary() {
+ return (flags & FLAG_PRIMARY) == FLAG_PRIMARY;
+ }
+
+ public boolean isAdmin() {
+ return (flags & FLAG_ADMIN) == FLAG_ADMIN;
+ }
+
+ public boolean isGuest() {
+ return (flags & FLAG_GUEST) == FLAG_GUEST;
+ }
+
+ public UserInfo() {
+ }
+
+ public UserInfo(UserInfo orig) {
+ name = orig.name;
+ id = orig.id;
+ flags = orig.flags;
+ }
+
+ @Override
+ public String toString() {
+ return "UserInfo{" + id + ":" + name + ":" + Integer.toHexString(flags) + "}";
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel dest, int parcelableFlags) {
+ dest.writeInt(id);
+ dest.writeString(name);
+ dest.writeInt(flags);
+ }
+
+ public static final Parcelable.Creator<UserInfo> CREATOR
+ = new Parcelable.Creator<UserInfo>() {
+ public UserInfo createFromParcel(Parcel source) {
+ return new UserInfo(source);
+ }
+ public UserInfo[] newArray(int size) {
+ return new UserInfo[size];
+ }
+ };
+
+ private UserInfo(Parcel source) {
+ id = source.readInt();
+ name = source.readString();
+ flags = source.readInt();
+ }
+}
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index dbb4271..931cb18 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -17,7 +17,6 @@
package android.content.res;
import android.os.ParcelFileDescriptor;
-import android.util.Config;
import android.util.Log;
import android.util.TypedValue;
@@ -58,7 +57,7 @@ public final class AssetManager {
public static final int ACCESS_BUFFER = 3;
private static final String TAG = "AssetManager";
- private static final boolean localLOGV = Config.LOGV || false;
+ private static final boolean localLOGV = false || false;
private static final boolean DEBUG_REFS = false;
diff --git a/core/java/android/content/res/StringBlock.java b/core/java/android/content/res/StringBlock.java
index 23a6f97..63e33ce 100644
--- a/core/java/android/content/res/StringBlock.java
+++ b/core/java/android/content/res/StringBlock.java
@@ -18,7 +18,6 @@ package android.content.res;
import android.text.*;
import android.text.style.*;
-import android.util.Config;
import android.util.Log;
import android.util.SparseArray;
import android.graphics.Paint;
@@ -34,7 +33,7 @@ import com.android.internal.util.XmlUtils;
*/
final class StringBlock {
private static final String TAG = "AssetManager";
- private static final boolean localLOGV = Config.LOGV || false;
+ private static final boolean localLOGV = false || false;
private final int mNative;
private final boolean mUseSparse;
diff --git a/core/java/android/database/AbstractCursor.java b/core/java/android/database/AbstractCursor.java
index 3ffc714..b6487bd 100644
--- a/core/java/android/database/AbstractCursor.java
+++ b/core/java/android/database/AbstractCursor.java
@@ -19,7 +19,6 @@ package android.database;
import android.content.ContentResolver;
import android.net.Uri;
import android.os.Bundle;
-import android.util.Config;
import android.util.Log;
import java.lang.ref.WeakReference;
@@ -285,7 +284,7 @@ public abstract class AbstractCursor implements CrossProcessCursor {
}
}
- if (Config.LOGV) {
+ if (false) {
if (getCount() > 0) {
Log.w("AbstractCursor", "Unknown column " + columnName);
}
diff --git a/core/java/android/database/CursorToBulkCursorAdaptor.java b/core/java/android/database/CursorToBulkCursorAdaptor.java
index 8bc7de2..8fa4d3b 100644
--- a/core/java/android/database/CursorToBulkCursorAdaptor.java
+++ b/core/java/android/database/CursorToBulkCursorAdaptor.java
@@ -19,7 +19,6 @@ package android.database;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
-import android.util.Config;
import android.util.Log;
@@ -77,7 +76,7 @@ public final class CursorToBulkCursorAdaptor extends BulkCursorNative
if (mCursor instanceof AbstractWindowedCursor) {
AbstractWindowedCursor windowedCursor = (AbstractWindowedCursor) cursor;
if (windowedCursor.hasWindow()) {
- if (Log.isLoggable(TAG, Log.VERBOSE) || Config.LOGV) {
+ if (Log.isLoggable(TAG, Log.VERBOSE) || false) {
Log.v(TAG, "Cross process cursor has a local window before setWindow in "
+ providerName, new RuntimeException());
}
diff --git a/core/java/android/database/DatabaseUtils.java b/core/java/android/database/DatabaseUtils.java
index f428aad..8e6f699 100644
--- a/core/java/android/database/DatabaseUtils.java
+++ b/core/java/android/database/DatabaseUtils.java
@@ -33,7 +33,6 @@ import android.database.sqlite.SQLiteStatement;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.text.TextUtils;
-import android.util.Config;
import android.util.Log;
import java.io.FileNotFoundException;
@@ -49,7 +48,7 @@ public class DatabaseUtils {
private static final String TAG = "DatabaseUtils";
private static final boolean DEBUG = false;
- private static final boolean LOCAL_LOGV = DEBUG ? Config.LOGD : Config.LOGV;
+ private static final boolean LOCAL_LOGV = false;
private static final String[] countProjection = new String[]{"count(*)"};
diff --git a/core/java/android/database/sqlite/SQLiteCursor.java b/core/java/android/database/sqlite/SQLiteCursor.java
index 4c2d123..ea9346d 100644
--- a/core/java/android/database/sqlite/SQLiteCursor.java
+++ b/core/java/android/database/sqlite/SQLiteCursor.java
@@ -23,7 +23,6 @@ import android.os.Handler;
import android.os.Message;
import android.os.Process;
import android.os.StrictMode;
-import android.util.Config;
import android.util.Log;
import java.util.HashMap;
@@ -241,7 +240,7 @@ public class SQLiteCursor extends AbstractWindowedCursor {
mColumnNameMap = null;
mQuery = query;
- query.mDatabase.lock();
+ query.mDatabase.lock(query.mSql);
try {
// Setup the list of columns
int columnCount = mQuery.columnCountLocked();
@@ -251,7 +250,7 @@ public class SQLiteCursor extends AbstractWindowedCursor {
for (int i = 0; i < columnCount; i++) {
String columnName = mQuery.columnNameLocked(i);
mColumns[i] = columnName;
- if (Config.LOGV) {
+ if (false) {
Log.v("DatabaseWindow", "mColumns[" + i + "] is "
+ mColumns[i]);
}
@@ -366,13 +365,13 @@ public class SQLiteCursor extends AbstractWindowedCursor {
}
private void deactivateCommon() {
- if (Config.LOGV) Log.v(TAG, "<<< Releasing cursor " + this);
+ if (false) Log.v(TAG, "<<< Releasing cursor " + this);
mCursorState = 0;
if (mWindow != null) {
mWindow.close();
mWindow = null;
}
- if (Config.LOGV) Log.v("DatabaseWindow", "closing window in release()");
+ if (false) Log.v("DatabaseWindow", "closing window in release()");
}
@Override
@@ -398,7 +397,7 @@ public class SQLiteCursor extends AbstractWindowedCursor {
return false;
}
long timeStart = 0;
- if (Config.LOGV) {
+ if (false) {
timeStart = System.currentTimeMillis();
}
@@ -419,7 +418,7 @@ public class SQLiteCursor extends AbstractWindowedCursor {
// since we need to use a different database connection handle,
// re-compile the query
try {
- db.lock();
+ db.lock(mQuery.mSql);
} catch (IllegalStateException e) {
// for backwards compatibility, just return false
Log.w(TAG, "requery() failed " + e.getMessage(), e);
@@ -453,7 +452,7 @@ public class SQLiteCursor extends AbstractWindowedCursor {
}
}
- if (Config.LOGV) {
+ if (false) {
Log.v("DatabaseWindow", "closing window in requery()");
Log.v(TAG, "--- Requery()ed cursor " + this + ": " + mQuery);
}
@@ -465,7 +464,7 @@ public class SQLiteCursor extends AbstractWindowedCursor {
// for backwards compatibility, just return false
Log.w(TAG, "requery() failed " + e.getMessage(), e);
}
- if (Config.LOGV) {
+ if (false) {
long timeEnd = System.currentTimeMillis();
Log.v(TAG, "requery (" + (timeEnd - timeStart) + " ms): " + mDriver.toString());
}
@@ -513,7 +512,7 @@ public class SQLiteCursor extends AbstractWindowedCursor {
close();
SQLiteDebug.notifyActiveCursorFinalized();
} else {
- if (Config.LOGV) {
+ if (false) {
Log.v(TAG, "Finalizing cursor on database = " + mQuery.mDatabase.getPath() +
", table = " + mEditTable + ", query = " + mQuery.mSql);
}
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index 90a5b5d..93a6ad3 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -30,7 +30,6 @@ import android.os.StatFs;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.text.TextUtils;
-import android.util.Config;
import android.util.EventLog;
import android.util.Log;
import android.util.LruCache;
@@ -230,9 +229,23 @@ public class SQLiteDatabase extends SQLiteClosable {
private static int sQueryLogTimeInMillis = 0; // lazily initialized
private static final int QUERY_LOG_SQL_LENGTH = 64;
private static final String COMMIT_SQL = "COMMIT;";
+ private static final String BEGIN_SQL = "BEGIN;";
private final Random mRandom = new Random();
+ /** the last non-commit/rollback sql statement in a transaction */
+ // guarded by 'this'
private String mLastSqlStatement = null;
+ synchronized String getLastSqlStatement() {
+ return mLastSqlStatement;
+ }
+
+ synchronized void setLastSqlStatement(String sql) {
+ mLastSqlStatement = sql;
+ }
+
+ /** guarded by {@link #mLock} */
+ private long mTransStartTime;
+
// String prefix for slow database query EventLog records that show
// lock acquistions of the database.
/* package */ static final String GET_LOCK_LOG_PREFIX = "GETLOCK:";
@@ -386,11 +399,16 @@ public class SQLiteDatabase extends SQLiteClosable {
*
* @see #unlock()
*/
- /* package */ void lock() {
- lock(false);
+ /* package */ void lock(String sql) {
+ lock(sql, false);
+ }
+
+ /* pachage */ void lock() {
+ lock(null, false);
}
+
private static final long LOCK_WAIT_PERIOD = 30L;
- private void lock(boolean forced) {
+ private void lock(String sql, boolean forced) {
// make sure this method is NOT being called from a 'synchronized' method
if (Thread.holdsLock(this)) {
Log.w(TAG, "don't lock() while in a synchronized method");
@@ -398,6 +416,7 @@ public class SQLiteDatabase extends SQLiteClosable {
verifyDbIsOpen();
if (!forced && !mLockingEnabled) return;
boolean done = false;
+ long timeStart = SystemClock.uptimeMillis();
while (!done) {
try {
// wait for 30sec to acquire the lock
@@ -420,6 +439,9 @@ public class SQLiteDatabase extends SQLiteClosable {
mLockAcquiredThreadTime = Debug.threadCpuTimeNanos();
}
}
+ if (sql != null) {
+ logTimeStat(sql, timeStart, GET_LOCK_LOG_PREFIX);
+ }
}
private static class DatabaseReentrantLock extends ReentrantLock {
DatabaseReentrantLock(boolean fair) {
@@ -444,7 +466,11 @@ public class SQLiteDatabase extends SQLiteClosable {
* @see #unlockForced()
*/
private void lockForced() {
- lock(true);
+ lock(null, true);
+ }
+
+ private void lockForced(String sql) {
+ lock(sql, true);
}
/**
@@ -612,7 +638,7 @@ public class SQLiteDatabase extends SQLiteClosable {
private void beginTransaction(SQLiteTransactionListener transactionListener,
boolean exclusive) {
verifyDbIsOpen();
- lockForced();
+ lockForced(BEGIN_SQL);
boolean ok = false;
try {
// If this thread already had the lock then get out
@@ -635,6 +661,7 @@ public class SQLiteDatabase extends SQLiteClosable {
} else {
execSQL("BEGIN IMMEDIATE;");
}
+ mTransStartTime = SystemClock.uptimeMillis();
mTransactionListener = transactionListener;
mTransactionIsSuccessful = true;
mInnerTransactionIsSuccessful = false;
@@ -698,6 +725,8 @@ public class SQLiteDatabase extends SQLiteClosable {
Log.i(TAG, "PRAGMA wal_Checkpoint done");
}
}
+ // log the transaction time to the Eventlog.
+ logTimeStat(getLastSqlStatement(), mTransStartTime, COMMIT_SQL);
} else {
try {
execSQL("ROLLBACK;");
@@ -705,7 +734,7 @@ public class SQLiteDatabase extends SQLiteClosable {
throw savedException;
}
} catch (SQLException e) {
- if (Config.LOGD) {
+ if (false) {
Log.d(TAG, "exception during rollback, maybe the DB previously "
+ "performed an auto-rollback");
}
@@ -714,7 +743,7 @@ public class SQLiteDatabase extends SQLiteClosable {
} finally {
mTransactionListener = null;
unlockForced();
- if (Config.LOGV) {
+ if (false) {
Log.v(TAG, "unlocked " + Thread.currentThread()
+ ", holdCount is " + mLock.getHoldCount());
}
@@ -1527,7 +1556,7 @@ public class SQLiteDatabase extends SQLiteClosable {
BlockGuard.getThreadPolicy().onReadFromDisk();
long timeStart = 0;
- if (Config.LOGV || mSlowQueryThreshold != -1) {
+ if (false || mSlowQueryThreshold != -1) {
timeStart = System.currentTimeMillis();
}
@@ -1540,7 +1569,7 @@ public class SQLiteDatabase extends SQLiteClosable {
cursorFactory != null ? cursorFactory : mFactory,
selectionArgs);
} finally {
- if (Config.LOGV || mSlowQueryThreshold != -1) {
+ if (false || mSlowQueryThreshold != -1) {
// Force query execution
int count = -1;
@@ -1550,7 +1579,7 @@ public class SQLiteDatabase extends SQLiteClosable {
long duration = System.currentTimeMillis() - timeStart;
- if (Config.LOGV || duration >= mSlowQueryThreshold) {
+ if (false || duration >= mSlowQueryThreshold) {
Log.v(SQLiteCursor.TAG,
"query (" + duration + " ms): " + driver.toString() + ", args are "
+ (selectionArgs != null
@@ -1855,24 +1884,7 @@ public class SQLiteDatabase extends SQLiteClosable {
* @throws SQLException if the SQL string is invalid
*/
public void execSQL(String sql) throws SQLException {
- int stmtType = DatabaseUtils.getSqlStatementType(sql);
- if (stmtType == DatabaseUtils.STATEMENT_ATTACH) {
- disableWriteAheadLogging();
- }
- long timeStart = SystemClock.uptimeMillis();
- logTimeStat(mLastSqlStatement, timeStart, GET_LOCK_LOG_PREFIX);
executeSql(sql, null);
-
- if (stmtType == DatabaseUtils.STATEMENT_ATTACH) {
- mHasAttachedDbs = true;
- }
- // Log commit statements along with the most recently executed
- // SQL statement for disambiguation.
- if (stmtType == DatabaseUtils.STATEMENT_COMMIT) {
- logTimeStat(mLastSqlStatement, timeStart, COMMIT_SQL);
- } else {
- logTimeStat(sql, timeStart, null);
- }
}
/**
@@ -1926,19 +1938,19 @@ public class SQLiteDatabase extends SQLiteClosable {
}
private int executeSql(String sql, Object[] bindArgs) throws SQLException {
- long timeStart = SystemClock.uptimeMillis();
- int n;
+ if (DatabaseUtils.getSqlStatementType(sql) == DatabaseUtils.STATEMENT_ATTACH) {
+ disableWriteAheadLogging();
+ mHasAttachedDbs = true;
+ }
SQLiteStatement statement = new SQLiteStatement(this, sql, bindArgs);
try {
- n = statement.executeUpdateDelete();
+ return statement.executeUpdateDelete();
} catch (SQLiteDatabaseCorruptException e) {
onCorruption();
throw e;
} finally {
statement.close();
}
- logTimeStat(sql, timeStart);
- return n;
}
@Override
@@ -2027,12 +2039,7 @@ public class SQLiteDatabase extends SQLiteClosable {
logTimeStat(sql, beginMillis, null);
}
- /* package */ void logTimeStat(String sql, long beginMillis, String prefix) {
- // Keep track of the last statement executed here, as this is
- // the common funnel through which all methods of hitting
- // libsqlite eventually flow.
- mLastSqlStatement = sql;
-
+ private void logTimeStat(String sql, long beginMillis, String prefix) {
// Sample fast queries in proportion to the time taken.
// Quantize the % first, so the logged sampling probability
// exactly equals the actual sampling rate for this query.
@@ -2059,7 +2066,6 @@ public class SQLiteDatabase extends SQLiteClosable {
if (prefix != null) {
sql = prefix + sql;
}
-
if (sql.length() > QUERY_LOG_SQL_LENGTH) sql = sql.substring(0, QUERY_LOG_SQL_LENGTH);
// ActivityThread.currentPackageName() only returns non-null if the
diff --git a/core/java/android/database/sqlite/SQLiteDirectCursorDriver.java b/core/java/android/database/sqlite/SQLiteDirectCursorDriver.java
index de2fca9..a5e762e 100644
--- a/core/java/android/database/sqlite/SQLiteDirectCursorDriver.java
+++ b/core/java/android/database/sqlite/SQLiteDirectCursorDriver.java
@@ -42,7 +42,7 @@ public class SQLiteDirectCursorDriver implements SQLiteCursorDriver {
SQLiteQuery query = null;
try {
- mDatabase.lock();
+ mDatabase.lock(mSql);
mDatabase.closePendingStatements();
query = new SQLiteQuery(mDatabase, mSql, 0, selectionArgs);
diff --git a/core/java/android/database/sqlite/SQLiteProgram.java b/core/java/android/database/sqlite/SQLiteProgram.java
index 88246e8..89552dc 100644
--- a/core/java/android/database/sqlite/SQLiteProgram.java
+++ b/core/java/android/database/sqlite/SQLiteProgram.java
@@ -105,12 +105,9 @@ public abstract class SQLiteProgram extends SQLiteClosable {
case DatabaseUtils.STATEMENT_SELECT:
mStatementType = n | STATEMENT_CACHEABLE | STATEMENT_USE_POOLED_CONN;
break;
- case DatabaseUtils.STATEMENT_ATTACH:
case DatabaseUtils.STATEMENT_BEGIN:
case DatabaseUtils.STATEMENT_COMMIT:
case DatabaseUtils.STATEMENT_ABORT:
- case DatabaseUtils.STATEMENT_DDL:
- case DatabaseUtils.STATEMENT_UNPREPARED:
mStatementType = n | STATEMENT_DONT_PREPARE;
break;
default:
@@ -353,13 +350,10 @@ public abstract class SQLiteProgram extends SQLiteClosable {
/* package */ void compileAndbindAllArgs() {
if ((mStatementType & STATEMENT_DONT_PREPARE) > 0) {
- // no need to prepare this SQL statement
- if (SQLiteDebug.DEBUG_SQL_STATEMENTS) {
- if (mBindArgs != null) {
- throw new IllegalArgumentException("no need to pass bindargs for this sql :" +
- mSql);
- }
+ if (mBindArgs != null) {
+ throw new IllegalArgumentException("Can't pass bindargs for this sql :" + mSql);
}
+ // no need to prepare this SQL statement
return;
}
if (nStatement == 0) {
diff --git a/core/java/android/database/sqlite/SQLiteQuery.java b/core/java/android/database/sqlite/SQLiteQuery.java
index e9e0172..dc882d9 100644
--- a/core/java/android/database/sqlite/SQLiteQuery.java
+++ b/core/java/android/database/sqlite/SQLiteQuery.java
@@ -70,9 +70,8 @@ public class SQLiteQuery extends SQLiteProgram {
*/
/* package */ int fillWindow(CursorWindow window,
int maxRead, int lastPos) {
+ mDatabase.lock(mSql);
long timeStart = SystemClock.uptimeMillis();
- mDatabase.lock();
- mDatabase.logTimeStat(mSql, timeStart, SQLiteDatabase.GET_LOCK_LOG_PREFIX);
try {
acquireReference();
try {
diff --git a/core/java/android/database/sqlite/SQLiteStatement.java b/core/java/android/database/sqlite/SQLiteStatement.java
index c76cc6c..ff973a7 100644
--- a/core/java/android/database/sqlite/SQLiteStatement.java
+++ b/core/java/android/database/sqlite/SQLiteStatement.java
@@ -80,7 +80,8 @@ public class SQLiteStatement extends SQLiteProgram
*/
public int executeUpdateDelete() {
try {
- long timeStart = acquireAndLock(WRITE);
+ saveSqlAsLastSqlStatement();
+ acquireAndLock(WRITE);
int numChanges = 0;
if ((mStatementType & STATEMENT_DONT_PREPARE) > 0) {
// since the statement doesn't have to be prepared,
@@ -90,7 +91,6 @@ public class SQLiteStatement extends SQLiteProgram
} else {
numChanges = native_execute();
}
- mDatabase.logTimeStat(mSql, timeStart);
return numChanges;
} finally {
releaseAndUnlock();
@@ -108,15 +108,22 @@ public class SQLiteStatement extends SQLiteProgram
*/
public long executeInsert() {
try {
- long timeStart = acquireAndLock(WRITE);
- long lastInsertedRowId = native_executeInsert();
- mDatabase.logTimeStat(mSql, timeStart);
- return lastInsertedRowId;
+ saveSqlAsLastSqlStatement();
+ acquireAndLock(WRITE);
+ return native_executeInsert();
} finally {
releaseAndUnlock();
}
}
+ private void saveSqlAsLastSqlStatement() {
+ if (((mStatementType & SQLiteProgram.STATEMENT_TYPE_MASK) ==
+ DatabaseUtils.STATEMENT_UPDATE) ||
+ (mStatementType & SQLiteProgram.STATEMENT_TYPE_MASK) ==
+ DatabaseUtils.STATEMENT_BEGIN) {
+ mDatabase.setLastSqlStatement(mSql);
+ }
+ }
/**
* Execute a statement that returns a 1 by 1 table with a numeric value.
* For example, SELECT COUNT(*) FROM table;
@@ -199,7 +206,7 @@ public class SQLiteStatement extends SQLiteProgram
* <li>if the SQL statement is an update, start transaction if not already in one.
* otherwise, get lock on the database</li>
* <li>acquire reference on this object</li>
- * <li>and then return the current time _before_ the database lock was acquired</li>
+ * <li>and then return the current time _after_ the database lock was acquired</li>
* </ul>
* <p>
* This method removes the duplicate code from the other public
@@ -243,7 +250,7 @@ public class SQLiteStatement extends SQLiteProgram
}
// do I have database lock? if not, grab it.
if (!mDatabase.isDbLockedByCurrentThread()) {
- mDatabase.lock();
+ mDatabase.lock(mSql);
mState = LOCK_ACQUIRED;
}
diff --git a/core/java/android/ddm/DdmHandleAppName.java b/core/java/android/ddm/DdmHandleAppName.java
index 4a57d12..78dd23e 100644
--- a/core/java/android/ddm/DdmHandleAppName.java
+++ b/core/java/android/ddm/DdmHandleAppName.java
@@ -19,7 +19,6 @@ package android.ddm;
import org.apache.harmony.dalvik.ddmc.Chunk;
import org.apache.harmony.dalvik.ddmc.ChunkHandler;
import org.apache.harmony.dalvik.ddmc.DdmServer;
-import android.util.Config;
import android.util.Log;
import java.nio.ByteBuffer;
@@ -88,7 +87,7 @@ public class DdmHandleAppName extends ChunkHandler {
* Send an APNM (APplication NaMe) chunk.
*/
private static void sendAPNM(String appName) {
- if (Config.LOGV)
+ if (false)
Log.v("ddm", "Sending app name");
ByteBuffer out = ByteBuffer.allocate(4 + appName.length()*2);
diff --git a/core/java/android/ddm/DdmHandleExit.java b/core/java/android/ddm/DdmHandleExit.java
index 8a0b9a4..74ae37a 100644
--- a/core/java/android/ddm/DdmHandleExit.java
+++ b/core/java/android/ddm/DdmHandleExit.java
@@ -19,7 +19,6 @@ package android.ddm;
import org.apache.harmony.dalvik.ddmc.Chunk;
import org.apache.harmony.dalvik.ddmc.ChunkHandler;
import org.apache.harmony.dalvik.ddmc.DdmServer;
-import android.util.Config;
import android.util.Log;
import java.nio.ByteBuffer;
@@ -59,7 +58,7 @@ public class DdmHandleExit extends ChunkHandler {
* Handle a chunk of data. We're only registered for "EXIT".
*/
public Chunk handleChunk(Chunk request) {
- if (Config.LOGV)
+ if (false)
Log.v("ddm-exit", "Handling " + name(request.type) + " chunk");
/*
diff --git a/core/java/android/ddm/DdmHandleHeap.java b/core/java/android/ddm/DdmHandleHeap.java
index fa0fbbf..cece556 100644
--- a/core/java/android/ddm/DdmHandleHeap.java
+++ b/core/java/android/ddm/DdmHandleHeap.java
@@ -21,7 +21,6 @@ import org.apache.harmony.dalvik.ddmc.ChunkHandler;
import org.apache.harmony.dalvik.ddmc.DdmServer;
import org.apache.harmony.dalvik.ddmc.DdmVmInternal;
import android.os.Debug;
-import android.util.Config;
import android.util.Log;
import java.io.IOException;
import java.nio.ByteBuffer;
@@ -78,7 +77,7 @@ public class DdmHandleHeap extends ChunkHandler {
* Handle a chunk of data.
*/
public Chunk handleChunk(Chunk request) {
- if (Config.LOGV)
+ if (false)
Log.v("ddm-heap", "Handling " + name(request.type) + " chunk");
int type = request.type;
@@ -113,7 +112,7 @@ public class DdmHandleHeap extends ChunkHandler {
ByteBuffer in = wrapChunk(request);
int when = in.get();
- if (Config.LOGV)
+ if (false)
Log.v("ddm-heap", "Heap segment enable: when=" + when);
boolean ok = DdmVmInternal.heapInfoNotify(when);
@@ -132,7 +131,7 @@ public class DdmHandleHeap extends ChunkHandler {
int when = in.get();
int what = in.get();
- if (Config.LOGV)
+ if (false)
Log.v("ddm-heap", "Heap segment enable: when=" + when
+ ", what=" + what + ", isNative=" + isNative);
@@ -160,7 +159,7 @@ public class DdmHandleHeap extends ChunkHandler {
/* get the filename for the output file */
int len = in.getInt();
String fileName = getString(in, len);
- if (Config.LOGD)
+ if (false)
Log.d("ddm-heap", "Heap dump: file='" + fileName + "'");
try {
@@ -192,7 +191,7 @@ public class DdmHandleHeap extends ChunkHandler {
byte result;
/* get the filename for the output file */
- if (Config.LOGD)
+ if (false)
Log.d("ddm-heap", "Heap dump: [DDMS]");
String failMsg = null;
@@ -218,7 +217,7 @@ public class DdmHandleHeap extends ChunkHandler {
private Chunk handleHPGC(Chunk request) {
//ByteBuffer in = wrapChunk(request);
- if (Config.LOGD)
+ if (false)
Log.d("ddm-heap", "Heap GC request");
System.gc();
@@ -234,7 +233,7 @@ public class DdmHandleHeap extends ChunkHandler {
enable = (in.get() != 0);
- if (Config.LOGD)
+ if (false)
Log.d("ddm-heap", "Recent allocation enable request: " + enable);
DdmVmInternal.enableRecentAllocations(enable);
@@ -259,7 +258,7 @@ public class DdmHandleHeap extends ChunkHandler {
private Chunk handleREAL(Chunk request) {
//ByteBuffer in = wrapChunk(request);
- if (Config.LOGD)
+ if (false)
Log.d("ddm-heap", "Recent allocations request");
/* generate the reply in a ready-to-go format */
diff --git a/core/java/android/ddm/DdmHandleHello.java b/core/java/android/ddm/DdmHandleHello.java
index 714a611..5088d22 100644
--- a/core/java/android/ddm/DdmHandleHello.java
+++ b/core/java/android/ddm/DdmHandleHello.java
@@ -19,7 +19,6 @@ package android.ddm;
import org.apache.harmony.dalvik.ddmc.Chunk;
import org.apache.harmony.dalvik.ddmc.ChunkHandler;
import org.apache.harmony.dalvik.ddmc.DdmServer;
-import android.util.Config;
import android.util.Log;
import android.os.Debug;
@@ -53,7 +52,7 @@ public class DdmHandleHello extends ChunkHandler {
* send messages to the server.
*/
public void connected() {
- if (Config.LOGV)
+ if (false)
Log.v("ddm-hello", "Connected!");
if (false) {
@@ -70,7 +69,7 @@ public class DdmHandleHello extends ChunkHandler {
* periodic transmissions or clean up saved state.
*/
public void disconnected() {
- if (Config.LOGV)
+ if (false)
Log.v("ddm-hello", "Disconnected!");
}
@@ -78,7 +77,7 @@ public class DdmHandleHello extends ChunkHandler {
* Handle a chunk of data.
*/
public Chunk handleChunk(Chunk request) {
- if (Config.LOGV)
+ if (false)
Log.v("ddm-heap", "Handling " + name(request.type) + " chunk");
int type = request.type;
@@ -105,7 +104,7 @@ public class DdmHandleHello extends ChunkHandler {
ByteBuffer in = wrapChunk(request);
int serverProtoVers = in.getInt();
- if (Config.LOGV)
+ if (false)
Log.v("ddm-hello", "Server version is " + serverProtoVers);
/*
@@ -150,7 +149,7 @@ public class DdmHandleHello extends ChunkHandler {
// is actually compiled in
final String[] features = Debug.getVmFeatureList();
- if (Config.LOGV)
+ if (false)
Log.v("ddm-heap", "Got feature list request");
int size = 4 + 4 * features.length;
diff --git a/core/java/android/ddm/DdmHandleProfiling.java b/core/java/android/ddm/DdmHandleProfiling.java
index 63ee445..e0db5e7 100644
--- a/core/java/android/ddm/DdmHandleProfiling.java
+++ b/core/java/android/ddm/DdmHandleProfiling.java
@@ -20,7 +20,6 @@ import org.apache.harmony.dalvik.ddmc.Chunk;
import org.apache.harmony.dalvik.ddmc.ChunkHandler;
import org.apache.harmony.dalvik.ddmc.DdmServer;
import android.os.Debug;
-import android.util.Config;
import android.util.Log;
import java.io.IOException;
import java.nio.ByteBuffer;
@@ -69,7 +68,7 @@ public class DdmHandleProfiling extends ChunkHandler {
* Handle a chunk of data.
*/
public Chunk handleChunk(Chunk request) {
- if (Config.LOGV)
+ if (false)
Log.v("ddm-heap", "Handling " + name(request.type) + " chunk");
int type = request.type;
@@ -99,7 +98,7 @@ public class DdmHandleProfiling extends ChunkHandler {
int flags = in.getInt();
int len = in.getInt();
String fileName = getString(in, len);
- if (Config.LOGV)
+ if (false)
Log.v("ddm-heap", "Method profiling start: filename='" + fileName
+ "', size=" + bufferSize + ", flags=" + flags);
@@ -139,7 +138,7 @@ public class DdmHandleProfiling extends ChunkHandler {
int bufferSize = in.getInt();
int flags = in.getInt();
- if (Config.LOGV) {
+ if (false) {
Log.v("ddm-heap", "Method prof stream start: size=" + bufferSize
+ ", flags=" + flags);
}
@@ -158,7 +157,7 @@ public class DdmHandleProfiling extends ChunkHandler {
private Chunk handleMPSE(Chunk request) {
byte result;
- if (Config.LOGV) {
+ if (false) {
Log.v("ddm-heap", "Method prof stream end");
}
diff --git a/core/java/android/ddm/DdmHandleThread.java b/core/java/android/ddm/DdmHandleThread.java
index c307988..613ab75 100644
--- a/core/java/android/ddm/DdmHandleThread.java
+++ b/core/java/android/ddm/DdmHandleThread.java
@@ -20,7 +20,6 @@ import org.apache.harmony.dalvik.ddmc.Chunk;
import org.apache.harmony.dalvik.ddmc.ChunkHandler;
import org.apache.harmony.dalvik.ddmc.DdmServer;
import org.apache.harmony.dalvik.ddmc.DdmVmInternal;
-import android.util.Config;
import android.util.Log;
import java.nio.ByteBuffer;
@@ -66,7 +65,7 @@ public class DdmHandleThread extends ChunkHandler {
* Handle a chunk of data.
*/
public Chunk handleChunk(Chunk request) {
- if (Config.LOGV)
+ if (false)
Log.v("ddm-thread", "Handling " + name(request.type) + " chunk");
int type = request.type;
diff --git a/core/java/android/ddm/DdmRegister.java b/core/java/android/ddm/DdmRegister.java
index debf189..ecd450d 100644
--- a/core/java/android/ddm/DdmRegister.java
+++ b/core/java/android/ddm/DdmRegister.java
@@ -17,7 +17,6 @@
package android.ddm;
import org.apache.harmony.dalvik.ddmc.DdmServer;
-import android.util.Config;
import android.util.Log;
/**
@@ -44,7 +43,7 @@ public class DdmRegister {
* we finish here.
*/
public static void registerHandlers() {
- if (Config.LOGV)
+ if (false)
Log.v("ddm", "Registering DDM message handlers");
DdmHandleHello.register();
DdmHandleThread.register();
diff --git a/core/java/android/gesture/Gesture.java b/core/java/android/gesture/Gesture.java
index 300cd28..c6a2a87 100755
--- a/core/java/android/gesture/Gesture.java
+++ b/core/java/android/gesture/Gesture.java
@@ -36,7 +36,7 @@ import java.util.concurrent.atomic.AtomicInteger;
/**
* A gesture is a hand-drawn shape on a touch screen. It can have one or multiple strokes.
* Each stroke is a sequence of timed points. A user-defined gesture can be recognized by
- * a GestureLibrary and a built-in alphabet gesture can be recognized by a LetterRecognizer.
+ * a GestureLibrary.
*/
public class Gesture implements Parcelable {
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 97f0e1b..d5c4ace 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -27,6 +27,7 @@ import android.util.Log;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.graphics.ImageFormat;
+import android.graphics.Rect;
import android.graphics.SurfaceTexture;
import android.os.Handler;
import android.os.Looper;
@@ -374,6 +375,12 @@ public class Camera {
* The preview surface texture may not otherwise change while preview is
* running.
*
+ * The timestamps provided by {@link SurfaceTexture#getTimestamp()} for a
+ * SurfaceTexture set as the preview texture have an unspecified zero point,
+ * and cannot be directly compared between different cameras or different
+ * instances of the same camera, or across multiple runs of the same
+ * program.
+ *
* @param surfaceTexture the {@link SurfaceTexture} to which the preview
* images are to be sent or null to remove the current preview surface
* texture
@@ -410,8 +417,9 @@ public class Camera {
/**
* Starts capturing and drawing preview frames to the screen.
- * Preview will not actually start until a surface is supplied with
- * {@link #setPreviewDisplay(SurfaceHolder)}.
+ * Preview will not actually start until a surface is supplied
+ * with {@link #setPreviewDisplay(SurfaceHolder)} or
+ * {@link #setPreviewTexture(SurfaceTexture)}.
*
* <p>If {@link #setPreviewCallback(Camera.PreviewCallback)},
* {@link #setOneShotPreviewCallback(Camera.PreviewCallback)}, or
@@ -1076,6 +1084,51 @@ public class Camera {
};
/**
+ * Area class for focus.
+ *
+ * @see #setFocusAreas(List)
+ * @see #getFocusAreas()
+ */
+ public static class Area {
+ /**
+ * Create an area with specified rectangle and weight.
+ *
+ * @param rect the rectangle of the area
+ * @param weight the weight of the area
+ */
+ public Area(Rect rect, int weight) {
+ this.rect = rect;
+ this.weight = weight;
+ }
+ /**
+ * Compares {@code obj} to this area.
+ *
+ * @param obj the object to compare this area with.
+ * @return {@code true} if the rectangle and weight of {@code obj} is
+ * the same as those of this area. {@code false} otherwise.
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof Area)) {
+ return false;
+ }
+ Area a = (Area) obj;
+ if (rect == null) {
+ if (a.rect != null) return false;
+ } else {
+ if (!rect.equals(a.rect)) return false;
+ }
+ return weight == a.weight;
+ }
+
+ /** rectangle of the area */
+ public Rect rect;
+
+ /** weight of the area */
+ public int weight;
+ };
+
+ /**
* Camera service settings.
*
* <p>To make camera parameters take effect, applications have to call
@@ -1117,6 +1170,8 @@ public class Camera {
private static final String KEY_SCENE_MODE = "scene-mode";
private static final String KEY_FLASH_MODE = "flash-mode";
private static final String KEY_FOCUS_MODE = "focus-mode";
+ private static final String KEY_FOCUS_AREAS = "focus-areas";
+ private static final String KEY_MAX_NUM_FOCUS_AREAS = "max-num-focus-areas";
private static final String KEY_FOCAL_LENGTH = "focal-length";
private static final String KEY_HORIZONTAL_VIEW_ANGLE = "horizontal-view-angle";
private static final String KEY_VERTICAL_VIEW_ANGLE = "vertical-view-angle";
@@ -1124,6 +1179,12 @@ public class Camera {
private static final String KEY_MAX_EXPOSURE_COMPENSATION = "max-exposure-compensation";
private static final String KEY_MIN_EXPOSURE_COMPENSATION = "min-exposure-compensation";
private static final String KEY_EXPOSURE_COMPENSATION_STEP = "exposure-compensation-step";
+ private static final String KEY_AUTO_EXPOSURE_LOCK = "auto-exposure-lock";
+ private static final String KEY_AUTO_EXPOSURE_LOCK_SUPPORTED = "auto-exposure-lock-supported";
+ private static final String KEY_AUTO_WHITEBALANCE_LOCK = "auto-whitebalance-lock";
+ private static final String KEY_AUTO_WHITEBALANCE_LOCK_SUPPORTED = "auto-whitebalance-lock-supported";
+ private static final String KEY_METERING_AREAS = "metering-areas";
+ private static final String KEY_MAX_NUM_METERING_AREAS = "max-num-metering-areas";
private static final String KEY_ZOOM = "zoom";
private static final String KEY_MAX_ZOOM = "max-zoom";
private static final String KEY_ZOOM_RATIOS = "zoom-ratios";
@@ -1138,6 +1199,7 @@ public class Camera {
private static final String SUPPORTED_VALUES_SUFFIX = "-values";
private static final String TRUE = "true";
+ private static final String FALSE = "false";
// Values for white balance settings.
public static final String WHITE_BALANCE_AUTO = "auto";
@@ -1462,6 +1524,31 @@ public class Camera {
mMap.put(key, Integer.toString(value));
}
+ private void set(String key, List<Area> areas) {
+ if (areas == null) {
+ set(key, "(0,0,0,0,0)");
+ } else {
+ StringBuilder buffer = new StringBuilder();
+ for (int i = 0; i < areas.size(); i++) {
+ Area area = areas.get(i);
+ Rect rect = area.rect;
+ buffer.append('(');
+ buffer.append(rect.left);
+ buffer.append(',');
+ buffer.append(rect.top);
+ buffer.append(',');
+ buffer.append(rect.right);
+ buffer.append(',');
+ buffer.append(rect.bottom);
+ buffer.append(',');
+ buffer.append(area.weight);
+ buffer.append(')');
+ if (i != areas.size() - 1) buffer.append(',');
+ }
+ set(key, buffer.toString());
+ }
+ }
+
/**
* Returns the value of a String parameter.
*
@@ -1483,7 +1570,9 @@ public class Camera {
}
/**
- * Sets the dimensions for preview pictures.
+ * Sets the dimensions for preview pictures. If the preview has already
+ * started, applications should stop the preview first before changing
+ * preview size.
*
* The sides of width and height are based on camera orientation. That
* is, the preview size is the size before it is rotated by display
@@ -2381,6 +2470,175 @@ public class Camera {
}
/**
+ * <p>Sets the auto-exposure lock state. Applications should check
+ * {@link #isAutoExposureLockSupported} before using this method.</p>
+ *
+ * <p>If set to true, the camera auto-exposure routine will immediately
+ * pause until the lock is set to false. Exposure compensation settings
+ * changes will still take effect while auto-exposure is locked.</p>
+ *
+ * <p>If auto-exposure is already locked, setting this to true again has
+ * no effect (the driver will not recalculate exposure values).</p>
+ *
+ * <p>Stopping preview with {@link #stopPreview()}, or triggering still
+ * image capture with {@link #takePicture(Camera.ShutterCallback,
+ * Camera.PictureCallback, Camera.PictureCallback)}, will automatically
+ * set the lock to false. However, the lock can be re-enabled before
+ * preview is re-started to keep the same AE parameters.</p>
+ *
+ * <p>Exposure compensation, in conjunction with re-enabling the AE and
+ * AWB locks after each still capture, can be used to capture an
+ * exposure-bracketed burst of images, for example.</p>
+ *
+ * <p>Auto-exposure state, including the lock state, will not be
+ * maintained after camera {@link #release()} is called. Locking
+ * auto-exposure after {@link #open()} but before the first call to
+ * {@link #startPreview()} will not allow the auto-exposure routine to
+ * run at all, and may result in severely over- or under-exposed
+ * images.</p>
+ *
+ * <p>The driver may also independently lock auto-exposure after
+ * auto-focus completes. If this is undesirable, be sure to always set
+ * the auto-exposure lock to false after the
+ * {@link AutoFocusCallback#onAutoFocus(boolean, Camera)} callback is
+ * received. The {@link #getAutoExposureLock()} method can be used after
+ * the callback to determine if the camera has locked auto-exposure
+ * independently.</p>
+ *
+ * @param toggle new state of the auto-exposure lock. True means that
+ * auto-exposure is locked, false means that the auto-exposure
+ * routine is free to run normally.
+ *
+ * @see #getAutoExposureLock()
+ *
+ * @hide
+ */
+ public void setAutoExposureLock(boolean toggle) {
+ set(KEY_AUTO_EXPOSURE_LOCK, toggle ? TRUE : FALSE);
+ }
+
+ /**
+ * Gets the state of the auto-exposure lock. Applications should check
+ * {@link #isAutoExposureLockSupported} before using this method. See
+ * {@link #setAutoExposureLock} for details about the lock.
+ *
+ * @return State of the auto-exposure lock. Returns true if
+ * auto-exposure is currently locked, and false otherwise. The
+ * auto-exposure lock may be independently enabled by the camera
+ * subsystem when auto-focus has completed. This method can be
+ * used after the {@link AutoFocusCallback#onAutoFocus(boolean,
+ * Camera)} callback to determine if the camera has locked AE.
+ *
+ * @see #setAutoExposureLock(boolean)
+ *
+ * @hide
+ */
+ public boolean getAutoExposureLock() {
+ String str = get(KEY_AUTO_EXPOSURE_LOCK);
+ return TRUE.equals(str);
+ }
+
+ /**
+ * Returns true if auto-exposure locking is supported. Applications
+ * should call this before trying to lock auto-exposure. See
+ * {@link #setAutoExposureLock} for details about the lock.
+ *
+ * @return true if auto-exposure lock is supported.
+ * @see #setAutoExposureLock(boolean)
+ *
+ * @hide
+ */
+ public boolean isAutoExposureLockSupported() {
+ String str = get(KEY_AUTO_EXPOSURE_LOCK_SUPPORTED);
+ return TRUE.equals(str);
+ }
+
+ /**
+ * <p>Sets the auto-white balance lock state. Applications should check
+ * {@link #isAutoWhiteBalanceLockSupported} before using this
+ * method.</p>
+ *
+ * <p>If set to true, the camera auto-white balance routine will
+ * immediately pause until the lock is set to false.</p>
+ *
+ * <p>If auto-white balance is already locked, setting this to true
+ * again has no effect (the driver will not recalculate white balance
+ * values).</p>
+ *
+ * <p>Stopping preview with {@link #stopPreview()}, or triggering still
+ * image capture with {@link #takePicture(Camera.ShutterCallback,
+ * Camera.PictureCallback, Camera.PictureCallback)}, will automatically
+ * set the lock to false. However, the lock can be re-enabled before
+ * preview is re-started to keep the same white balance parameters.</p>
+ *
+ * <p>Exposure compensation, in conjunction with re-enabling the AE and
+ * AWB locks after each still capture, can be used to capture an
+ * exposure-bracketed burst of images, for example. Auto-white balance
+ * state, including the lock state, will not be maintained after camera
+ * {@link #release()} is called. Locking auto-white balance after
+ * {@link #open()} but before the first call to {@link #startPreview()}
+ * will not allow the auto-white balance routine to run at all, and may
+ * result in severely incorrect color in captured images.</p>
+ *
+ * <p>The driver may also independently lock auto-white balance after
+ * auto-focus completes. If this is undesirable, be sure to always set
+ * the auto-white balance lock to false after the
+ * {@link AutoFocusCallback#onAutoFocus(boolean, Camera)} callback is
+ * received. The {@link #getAutoWhiteBalanceLock()} method can be used
+ * after the callback to determine if the camera has locked auto-white
+ * balance independently.</p>
+ *
+ * @param toggle new state of the auto-white balance lock. True means
+ * that auto-white balance is locked, false means that the
+ * auto-white balance routine is free to run normally.
+ *
+ * @see #getAutoWhiteBalanceLock()
+ *
+ * @hide
+ */
+ public void setAutoWhiteBalanceLock(boolean toggle) {
+ set(KEY_AUTO_WHITEBALANCE_LOCK, toggle ? TRUE : FALSE);
+ }
+
+ /**
+ * Gets the state of the auto-white balance lock. Applications should
+ * check {@link #isAutoWhiteBalanceLockSupported} before using this
+ * method. See {@link #setAutoWhiteBalanceLock} for details about the
+ * lock.
+ *
+ * @return State of the auto-white balance lock. Returns true if
+ * auto-white balance is currently locked, and false
+ * otherwise. The auto-white balance lock may be independently
+ * enabled by the camera subsystem when auto-focus has
+ * completed. This method can be used after the
+ * {@link AutoFocusCallback#onAutoFocus(boolean, Camera)}
+ * callback to determine if the camera has locked AWB.
+ *
+ * @see #setAutoWhiteBalanceLock(boolean)
+ *
+ * @hide
+ */
+ public boolean getAutoWhiteBalanceLock() {
+ String str = get(KEY_AUTO_WHITEBALANCE_LOCK);
+ return TRUE.equals(str);
+ }
+
+ /**
+ * Returns true if auto-white balance locking is supported. Applications
+ * should call this before trying to lock auto-white balance. See
+ * {@link #setAutoWhiteBalanceLock} for details about the lock.
+ *
+ * @return true if auto-white balance lock is supported.
+ * @see #setAutoWhiteBalanceLock(boolean)
+ *
+ * @hide
+ */
+ public boolean isAutoWhiteBalanceLockSupported() {
+ String str = get(KEY_AUTO_WHITEBALANCE_LOCK_SUPPORTED);
+ return TRUE.equals(str);
+ }
+
+ /**
* Gets current zoom value. This also works when smooth zoom is in
* progress. Applications should check {@link #isZoomSupported} before
* using this method.
@@ -2492,6 +2750,138 @@ public class Camera {
splitFloat(get(KEY_FOCUS_DISTANCES), output);
}
+ /**
+ * Gets the maximum number of focus areas supported. This is the maximum
+ * length of the list in {@link #setFocusAreas(List)} and
+ * {@link #getFocusAreas()}.
+ *
+ * @return the maximum number of focus areas supported by the camera.
+ * @see #getFocusAreas()
+ */
+ public int getMaxNumFocusAreas() {
+ return getInt(KEY_MAX_NUM_FOCUS_AREAS, 0);
+ }
+
+ /**
+ * Gets the current focus areas. Camera driver uses the areas to decide
+ * focus.
+ *
+ * Before using this API or {@link #setFocusAreas(List)}, apps should
+ * call {@link #getMaxNumFocusAreas()} to know the maximum number of
+ * focus areas first. If the value is 0, focus area is not supported.
+ *
+ * Each focus area is a rectangle with specified weight. The direction
+ * is relative to the sensor orientation, that is, what the sensor sees.
+ * The direction is not affected by the rotation or mirroring of
+ * {@link #setDisplayOrientation(int)}. Coordinates of the rectangle
+ * range from -1000 to 1000. (-1000, -1000) is the upper left point.
+ * (1000, 1000) is the lower right point. The length and width of focus
+ * areas cannot be 0 or negative.
+ *
+ * The weight must range from 1 to 1000. The weight should be
+ * interpreted as a per-pixel weight - all pixels in the area have the
+ * specified weight. This means a small area with the same weight as a
+ * larger area will have less influence on the focusing than the larger
+ * area. Focus areas can partially overlap and the driver will add the
+ * weights in the overlap region.
+ *
+ * A special case of null focus area means driver to decide the focus
+ * area. For example, the driver may use more signals to decide focus
+ * areas and change them dynamically. Apps can set all-zero if they want
+ * the driver to decide focus areas.
+ *
+ * Focus areas are relative to the current field of view
+ * ({@link #getZoom()}). No matter what the zoom level is, (-1000,-1000)
+ * represents the top of the currently visible camera frame. The focus
+ * area cannot be set to be outside the current field of view, even
+ * when using zoom.
+ *
+ * Focus area only has effect if the current focus mode is
+ * {@link #FOCUS_MODE_AUTO}, {@link #FOCUS_MODE_MACRO}, or
+ * {@link #FOCUS_MODE_CONTINUOUS_VIDEO}.
+ *
+ * @return a list of current focus areas
+ */
+ public List<Area> getFocusAreas() {
+ return splitArea(get(KEY_FOCUS_AREAS));
+ }
+
+ /**
+ * Sets focus areas. See {@link #getFocusAreas()} for documentation.
+ *
+ * @param focusAreas the focus areas
+ * @see #getFocusAreas()
+ */
+ public void setFocusAreas(List<Area> focusAreas) {
+ set(KEY_FOCUS_AREAS, focusAreas);
+ }
+
+ /**
+ * Gets the maximum number of metering areas supported. This is the
+ * maximum length of the list in {@link #setMeteringAreas(List)} and
+ * {@link #getMeteringAreas()}.
+ *
+ * @return the maximum number of metering areas supported by the camera.
+ * @see #getMeteringAreas()
+ */
+ public int getMaxNumMeteringAreas() {
+ return getInt(KEY_MAX_NUM_METERING_AREAS, 0);
+ }
+
+ /**
+ * Gets the current metering areas. Camera driver uses these areas to
+ * decide exposure.
+ *
+ * Before using this API or {@link #setMeteringAreas(List)}, apps should
+ * call {@link #getMaxNumMeteringAreas()} to know the maximum number of
+ * metering areas first. If the value is 0, metering area is not
+ * supported.
+ *
+ * Each metering area is a rectangle with specified weight. The
+ * direction is relative to the sensor orientation, that is, what the
+ * sensor sees. The direction is not affected by the rotation or
+ * mirroring of {@link #setDisplayOrientation(int)}. Coordinates of the
+ * rectangle range from -1000 to 1000. (-1000, -1000) is the upper left
+ * point. (1000, 1000) is the lower right point. The length and width of
+ * metering areas cannot be 0 or negative.
+ *
+ * The weight must range from 1 to 1000, and represents a weight for
+ * every pixel in the area. This means that a large metering area with
+ * the same weight as a smaller area will have more effect in the
+ * metering result. Metering areas can partially overlap and the driver
+ * will add the weights in the overlap region.
+ *
+ * A special case of null metering area means driver to decide the
+ * metering area. For example, the driver may use more signals to decide
+ * metering areas and change them dynamically. Apps can set all-zero if
+ * they want the driver to decide metering areas.
+ *
+ * Metering areas are relative to the current field of view
+ * ({@link #getZoom()}). No matter what the zoom level is, (-1000,-1000)
+ * represents the top of the currently visible camera frame. The
+ * metering area cannot be set to be outside the current field of view,
+ * even when using zoom.
+ *
+ * No matter what metering areas are, the final exposure are compensated
+ * by {@link #setExposureCompensation(int)}.
+ *
+ * @return a list of current metering areas
+ */
+ public List<Area> getMeteringAreas() {
+ return splitArea(KEY_METERING_AREAS);
+ }
+
+ /**
+ * Sets metering areas. See {@link #getMeteringAreas()} for
+ * documentation.
+ *
+ * @param meteringAreas the metering areas
+ * @see #getMeteringAreas()
+ */
+ public void setMeteringAreas(List<Area> meteringAreas) {
+ set(KEY_METERING_AREAS, meteringAreas);
+ }
+
// Splits a comma delimited string to an ArrayList of String.
// Return null if the passing string is null or the size is 0.
private ArrayList<String> split(String str) {
@@ -2617,5 +3007,41 @@ public class Camera {
if (rangeList.size() == 0) return null;
return rangeList;
}
+
+ // Splits a comma delimited string to an ArrayList of Area objects.
+ // Example string: "(-10,-10,0,0,300),(0,0,10,10,700)". Return null if
+ // the passing string is null or the size is 0 or (0,0,0,0,0).
+ private ArrayList<Area> splitArea(String str) {
+ if (str == null || str.charAt(0) != '('
+ || str.charAt(str.length() - 1) != ')') {
+ Log.e(TAG, "Invalid area string=" + str);
+ return null;
+ }
+
+ ArrayList<Area> result = new ArrayList<Area>();
+ int endIndex, fromIndex = 1;
+ int[] array = new int[5];
+ do {
+ endIndex = str.indexOf("),(", fromIndex);
+ if (endIndex == -1) endIndex = str.length() - 1;
+ splitInt(str.substring(fromIndex, endIndex), array);
+ Rect rect = new Rect(array[0], array[1], array[2], array[3]);
+ result.add(new Area(rect, array[4]));
+ fromIndex = endIndex + 3;
+ } while (endIndex != str.length() - 1);
+
+ if (result.size() == 0) return null;
+
+ if (result.size() == 1) {
+ Area area = (Area) result.get(0);
+ Rect rect = area.rect;
+ if (rect.left == 0 && rect.top == 0 && rect.right == 0
+ && rect.bottom == 0 && area.weight == 0) {
+ return null;
+ }
+ }
+
+ return result;
+ }
};
}
diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java
index 595c7d1..68fc101 100644
--- a/core/java/android/hardware/Sensor.java
+++ b/core/java/android/hardware/Sensor.java
@@ -66,7 +66,14 @@ public class Sensor {
/** A constant describing a pressure sensor type */
public static final int TYPE_PRESSURE = 6;
- /** A constant describing a temperature sensor type */
+ /**
+ * A constant describing a temperature sensor type
+ *
+ * @deprecated use
+ * {@link android.hardware.Sensor#TYPE_AMBIENT_TEMPERATURE
+ * Sensor.TYPE_AMBIENT_TEMPERATURE} instead.
+ */
+ @Deprecated
public static final int TYPE_TEMPERATURE = 7;
/**
@@ -104,6 +111,9 @@ public class Sensor {
*/
public static final int TYPE_RELATIVE_HUMIDITY = 12;
+ /** A constant describing an ambient temperature sensor type */
+ public static final int TYPE_AMBIENT_TEMPERATURE = 13;
+
/**
* A constant describing all sensor types.
*/
diff --git a/core/java/android/hardware/SensorEvent.java b/core/java/android/hardware/SensorEvent.java
index 9b62a68..0411b5c 100644
--- a/core/java/android/hardware/SensorEvent.java
+++ b/core/java/android/hardware/SensorEvent.java
@@ -204,6 +204,12 @@ public class SensorEvent {
* values[0]: Ambient light level in SI lux units
* </ul>
*
+ * <h4>{@link android.hardware.Sensor#TYPE_PRESSURE Sensor.TYPE_PRESSURE}:</h4>
+ * <ul>
+ * <p>
+ * values[0]: Atmospheric pressure in hPa (millibar)
+ * </ul>
+ *
* <h4>{@link android.hardware.Sensor#TYPE_PROXIMITY Sensor.TYPE_PROXIMITY}:
* </h4>
*
@@ -247,6 +253,23 @@ public class SensorEvent {
* <p>Elements of the rotation vector are unitless.
* The x,y, and z axis are defined in the same way as the acceleration
* sensor.</p>
+ * The reference coordinate system is defined as a direct orthonormal basis,
+ * where:
+ * </p>
+ *
+ * <ul>
+ * <li>X is defined as the vector product <b>Y.Z</b> (It is tangential to
+ * the ground at the device's current location and roughly points East).</li>
+ * <li>Y is tangential to the ground at the device's current location and
+ * points towards the magnetic North Pole.</li>
+ * <li>Z points towards the sky and is perpendicular to the ground.</li>
+ * </ul>
+ *
+ * <p>
+ * <center><img src="../../../images/axis_globe.png"
+ * alt="World coordinate-system diagram." border="0" /></center>
+ * </p>
+ *
* <ul>
* <p>
* values[0]: x*sin(&#952/2)
@@ -362,6 +385,14 @@ public class SensorEvent {
* dv = 216.7 *
* (rh / 100.0 * 6.112 * Math.exp(17.62 * t / (243.12 + t)) / (273.15 + t));
* </pre>
+ *
+ * <h4>{@link android.hardware.Sensor#TYPE_AMBIENT_TEMPERATURE Sensor.TYPE_AMBIENT_TEMPERATURE}:
+ * </h4>
+ *
+ * <ul>
+ * <p>
+ * values[0]: ambient (room) temperature in degree Celsius.
+ * </ul>
*
* @see SensorEvent
* @see GeomagneticField
diff --git a/core/java/android/inputmethodservice/KeyboardView.java b/core/java/android/inputmethodservice/KeyboardView.java
index ab5c78a..dfc70ef 100644
--- a/core/java/android/inputmethodservice/KeyboardView.java
+++ b/core/java/android/inputmethodservice/KeyboardView.java
@@ -142,7 +142,8 @@ public class KeyboardView extends View implements View.OnClickListener {
private int mPreviewTextSizeLarge;
private int mPreviewOffset;
private int mPreviewHeight;
- private int[] mOffsetInWindow;
+ // Working variable
+ private final int[] mCoordinates = new int[2];
private PopupWindow mPopupKeyboard;
private View mMiniKeyboardContainer;
@@ -152,7 +153,6 @@ public class KeyboardView extends View implements View.OnClickListener {
private int mMiniKeyboardOffsetX;
private int mMiniKeyboardOffsetY;
private Map<Key,View> mMiniKeyboardCache;
- private int[] mWindowOffset;
private Key[] mKeys;
/** Listener for {@link OnKeyboardActionListener}. */
@@ -905,23 +905,19 @@ public class KeyboardView extends View implements View.OnClickListener {
mPopupPreviewY = - mPreviewText.getMeasuredHeight();
}
mHandler.removeMessages(MSG_REMOVE_PREVIEW);
- if (mOffsetInWindow == null) {
- mOffsetInWindow = new int[2];
- getLocationInWindow(mOffsetInWindow);
- mOffsetInWindow[0] += mMiniKeyboardOffsetX; // Offset may be zero
- mOffsetInWindow[1] += mMiniKeyboardOffsetY; // Offset may be zero
- int[] mWindowLocation = new int[2];
- getLocationOnScreen(mWindowLocation);
- mWindowY = mWindowLocation[1];
- }
+ getLocationInWindow(mCoordinates);
+ mCoordinates[0] += mMiniKeyboardOffsetX; // Offset may be zero
+ mCoordinates[1] += mMiniKeyboardOffsetY; // Offset may be zero
+
// Set the preview background state
mPreviewText.getBackground().setState(
key.popupResId != 0 ? LONG_PRESSABLE_STATE_SET : EMPTY_STATE_SET);
- mPopupPreviewX += mOffsetInWindow[0];
- mPopupPreviewY += mOffsetInWindow[1];
+ mPopupPreviewX += mCoordinates[0];
+ mPopupPreviewY += mCoordinates[1];
// If the popup cannot be shown above the key, put it on the side
- if (mPopupPreviewY + mWindowY < 0) {
+ getLocationOnScreen(mCoordinates);
+ if (mPopupPreviewY + mCoordinates[1] < 0) {
// If the key you're pressing is on the left side of the keyboard, show the popup on
// the right, offset by enough to see at least one key to the left/right.
if (key.x + key.width <= getWidth() / 2) {
@@ -1057,16 +1053,13 @@ public class KeyboardView extends View implements View.OnClickListener {
mMiniKeyboard = (KeyboardView) mMiniKeyboardContainer.findViewById(
com.android.internal.R.id.keyboardView);
}
- if (mWindowOffset == null) {
- mWindowOffset = new int[2];
- getLocationInWindow(mWindowOffset);
- }
+ getLocationInWindow(mCoordinates);
mPopupX = popupKey.x + mPaddingLeft;
mPopupY = popupKey.y + mPaddingTop;
mPopupX = mPopupX + popupKey.width - mMiniKeyboardContainer.getMeasuredWidth();
mPopupY = mPopupY - mMiniKeyboardContainer.getMeasuredHeight();
- final int x = mPopupX + mMiniKeyboardContainer.getPaddingRight() + mWindowOffset[0];
- final int y = mPopupY + mMiniKeyboardContainer.getPaddingBottom() + mWindowOffset[1];
+ final int x = mPopupX + mMiniKeyboardContainer.getPaddingRight() + mCoordinates[0];
+ final int y = mPopupY + mMiniKeyboardContainer.getPaddingBottom() + mCoordinates[1];
mMiniKeyboard.setPopupOffset(x < 0 ? 0 : x, y);
mMiniKeyboard.setShifted(isShifted());
mPopupKeyboard.setContentView(mMiniKeyboardContainer);
diff --git a/core/java/android/inputmethodservice/SoftInputWindow.java b/core/java/android/inputmethodservice/SoftInputWindow.java
index 343242e..7159260 100644
--- a/core/java/android/inputmethodservice/SoftInputWindow.java
+++ b/core/java/android/inputmethodservice/SoftInputWindow.java
@@ -73,8 +73,17 @@ class SoftInputWindow extends Dialog {
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
getWindow().getDecorView().getHitRect(mBounds);
- final MotionEvent event = clipMotionEvent(ev, mBounds);
- return super.dispatchTouchEvent(event);
+
+ if (ev.isWithinBoundsNoHistory(mBounds.left, mBounds.top,
+ mBounds.right - 1, mBounds.bottom - 1)) {
+ return super.dispatchTouchEvent(ev);
+ } else {
+ MotionEvent temp = ev.clampNoHistory(mBounds.left, mBounds.top,
+ mBounds.right - 1, mBounds.bottom - 1);
+ boolean handled = super.dispatchTouchEvent(temp);
+ temp.recycle();
+ return handled;
+ }
}
/**
@@ -163,48 +172,4 @@ class SoftInputWindow extends Dialog {
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
WindowManager.LayoutParams.FLAG_DIM_BEHIND);
}
-
- private static MotionEvent clipMotionEvent(MotionEvent me, Rect bounds) {
- final int pointerCount = me.getPointerCount();
- boolean shouldClip = false;
- for (int pointerIndex = 0; pointerIndex < pointerCount; pointerIndex++) {
- final int x = (int)me.getX(pointerIndex);
- final int y = (int)me.getY(pointerIndex);
- if (!bounds.contains(x, y)) {
- shouldClip = true;
- break;
- }
- }
- if (!shouldClip)
- return me;
-
- if (pointerCount == 1) {
- final int x = (int)me.getX();
- final int y = (int)me.getY();
- me.setLocation(
- Math.max(bounds.left, Math.min(x, bounds.right - 1)),
- Math.max(bounds.top, Math.min(y, bounds.bottom - 1)));
- return me;
- }
-
- final int[] pointerIds = new int[pointerCount];
- final MotionEvent.PointerCoords[] pointerCoords =
- new MotionEvent.PointerCoords[pointerCount];
- for (int pointerIndex = 0; pointerIndex < pointerCount; pointerIndex++) {
- pointerIds[pointerIndex] = me.getPointerId(pointerIndex);
- final MotionEvent.PointerCoords coords = new MotionEvent.PointerCoords();
- me.getPointerCoords(pointerIndex, coords);
- pointerCoords[pointerIndex] = coords;
- final int x = (int)coords.x;
- final int y = (int)coords.y;
- if (!bounds.contains(x, y)) {
- coords.x = Math.max(bounds.left, Math.min(x, bounds.right - 1));
- coords.y = Math.max(bounds.top, Math.min(y, bounds.bottom - 1));
- }
- }
- return MotionEvent.obtain(
- me.getDownTime(), me.getEventTime(), me.getAction(), pointerCount, pointerIds,
- pointerCoords, me.getMetaState(), me.getXPrecision(), me.getYPrecision(),
- me.getDeviceId(), me.getEdgeFlags(), me.getSource(), me.getFlags());
- }
}
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index b541ec3..419288b 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -215,15 +215,20 @@ public class ConnectivityManager
/**
* Bluetooth data connection. This is used for Bluetooth reverse tethering.
- * @hide
*/
public static final int TYPE_BLUETOOTH = 7;
- /** {@hide} */
+ /**
+ * Dummy data connection. This should not be used on shipping devices.
+ */
public static final int TYPE_DUMMY = 8;
- /** {@hide} */
+ /**
+ * Ethernet data connection. This may be via USB dongle or more
+ * traditional means.
+ */
public static final int TYPE_ETHERNET = 9;
+
/**
* Over the air Adminstration.
* {@hide}
diff --git a/core/config/sdk/android/util/ConfigBuildFlags.java b/core/java/android/net/INetworkPolicyManager.aidl
index f903ee4..d9351ee 100644
--- a/core/config/sdk/android/util/ConfigBuildFlags.java
+++ b/core/java/android/net/INetworkPolicyManager.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009 The Android Open Source Project
+ * Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,17 +14,18 @@
* limitations under the License.
*/
-package android.util;
+package android.net;
/**
- * Static flags set by the build process.
+ * Interface that creates and modifies network policy rules.
+ *
+ * {@hide}
*/
-/* package */ final class ConfigBuildFlags {
- /* This field is intentionally declared as non-final. This file
- * is passed to the SDK docs/API tools, and is used to force them
- * to avoid treating DEBUG as a constant value. This is necessary
- * to avoid breaking the API check when switching to and from
- * a debug build.
- */
- /* package */ static boolean DEBUG = false;
+interface INetworkPolicyManager {
+
+ void setUidPolicy(int uid, int policy);
+ int getUidPolicy(int uid);
+
+ // TODO: build API to surface stats details for settings UI
+
}
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
new file mode 100644
index 0000000..1913aa7
--- /dev/null
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2011 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.net;
+
+import android.content.Context;
+import android.os.RemoteException;
+
+/**
+ * Manager for creating and modifying network policy rules.
+ *
+ * {@hide}
+ */
+public class NetworkPolicyManager {
+
+ /** No specific network policy, use system default. */
+ public static final int POLICY_NONE = 0x0;
+ /** Reject network usage when application in background. */
+ public static final int POLICY_REJECT_BACKGROUND = 0x1;
+ /** Reject network usage on paid network connections. */
+ public static final int POLICY_REJECT_PAID = 0x2;
+ /** Application should conserve data. */
+ public static final int POLICY_CONSERVE_DATA = 0x4;
+
+ private INetworkPolicyManager mService;
+
+ public NetworkPolicyManager(INetworkPolicyManager service) {
+ if (service == null) {
+ throw new IllegalArgumentException("missing INetworkPolicyManager");
+ }
+ mService = service;
+ }
+
+ public static NetworkPolicyManager getSystemService(Context context) {
+ return (NetworkPolicyManager) context.getSystemService(Context.NETWORK_POLICY_SERVICE);
+ }
+
+ /**
+ * Set policy flags for specific UID.
+ *
+ * @param policy {@link #POLICY_NONE} or combination of
+ * {@link #POLICY_REJECT_BACKGROUND}, {@link #POLICY_REJECT_PAID},
+ * or {@link #POLICY_CONSERVE_DATA}.
+ */
+ public void setUidPolicy(int uid, int policy) {
+ try {
+ mService.setUidPolicy(uid, policy);
+ } catch (RemoteException e) {
+ }
+ }
+
+ public int getUidPolicy(int uid) {
+ try {
+ return mService.getUidPolicy(uid);
+ } catch (RemoteException e) {
+ return POLICY_NONE;
+ }
+ }
+
+}
diff --git a/core/java/android/net/NetworkStats.aidl b/core/java/android/net/NetworkStats.aidl
new file mode 100644
index 0000000..d06ca65
--- /dev/null
+++ b/core/java/android/net/NetworkStats.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2011, 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.net;
+
+parcelable NetworkStats;
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
new file mode 100644
index 0000000..0f207bc
--- /dev/null
+++ b/core/java/android/net/NetworkStats.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2011 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.net;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.SystemClock;
+
+import java.io.CharArrayWriter;
+import java.io.PrintWriter;
+
+/**
+ * Collection of network statistics. Can contain summary details across all
+ * interfaces, or details with per-UID granularity. Designed to parcel quickly
+ * across process boundaries.
+ *
+ * @hide
+ */
+public class NetworkStats implements Parcelable {
+ /** {@link #iface} value when entry is summarized over all interfaces. */
+ public static final String IFACE_ALL = null;
+ /** {@link #uid} value when entry is summarized over all UIDs. */
+ public static final int UID_ALL = 0;
+
+ // NOTE: data should only be accounted for once in this structure; if data
+ // is broken out, the summarized version should not be included.
+
+ /**
+ * {@link SystemClock#elapsedRealtime()} timestamp when this data was
+ * generated.
+ */
+ public final long elapsedRealtime;
+ public final String[] iface;
+ public final int[] uid;
+ public final long[] rx;
+ public final long[] tx;
+
+ // TODO: add fg/bg stats and tag granularity
+
+ private NetworkStats(long elapsedRealtime, String[] iface, int[] uid, long[] rx, long[] tx) {
+ this.elapsedRealtime = elapsedRealtime;
+ this.iface = iface;
+ this.uid = uid;
+ this.rx = rx;
+ this.tx = tx;
+ }
+
+ public NetworkStats(Parcel parcel) {
+ elapsedRealtime = parcel.readLong();
+ iface = parcel.createStringArray();
+ uid = parcel.createIntArray();
+ rx = parcel.createLongArray();
+ tx = parcel.createLongArray();
+ }
+
+ public static class Builder {
+ private long mElapsedRealtime;
+ private final String[] mIface;
+ private final int[] mUid;
+ private final long[] mRx;
+ private final long[] mTx;
+
+ private int mIndex = 0;
+
+ public Builder(long elapsedRealtime, int size) {
+ mElapsedRealtime = elapsedRealtime;
+ mIface = new String[size];
+ mUid = new int[size];
+ mRx = new long[size];
+ mTx = new long[size];
+ }
+
+ public Builder addEntry(String iface, int uid, long rx, long tx) {
+ mIface[mIndex] = iface;
+ mUid[mIndex] = uid;
+ mRx[mIndex] = rx;
+ mTx[mIndex] = tx;
+ mIndex++;
+ return this;
+ }
+
+ public NetworkStats build() {
+ if (mIndex != mIface.length) {
+ throw new IllegalArgumentException("unexpected number of entries");
+ }
+ return new NetworkStats(mElapsedRealtime, mIface, mUid, mRx, mTx);
+ }
+ }
+
+ public int length() {
+ // length is identical for all fields
+ return iface.length;
+ }
+
+ /**
+ * Find first stats index that matches the requested parameters.
+ */
+ public int findIndex(String iface, int uid) {
+ final int length = length();
+ for (int i = 0; i < length; i++) {
+ if (equal(iface, this.iface[i]) && uid == this.uid[i]) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Subtract the given {@link NetworkStats}, effectively leaving the delta
+ * between two snapshots in time. Assumes that statistics rows collect over
+ * time, and that none of them have disappeared.
+ */
+ public NetworkStats subtract(NetworkStats value) {
+ // result will have our rows, but no meaningful timestamp
+ final int length = length();
+ final NetworkStats.Builder result = new NetworkStats.Builder(-1, length);
+
+ for (int i = 0; i < length; i++) {
+ final String iface = this.iface[i];
+ final int uid = this.uid[i];
+
+ // find remote row that matches, and subtract
+ final int j = value.findIndex(iface, uid);
+ if (j == -1) {
+ // newly appearing row, return entire value
+ result.addEntry(iface, uid, this.rx[i], this.tx[i]);
+ } else {
+ // existing row, subtract remote value
+ final long rx = this.rx[i] - value.rx[j];
+ final long tx = this.tx[i] - value.tx[j];
+ result.addEntry(iface, uid, rx, tx);
+ }
+ }
+
+ return result.build();
+ }
+
+ private static boolean equal(Object a, Object b) {
+ return a == b || (a != null && a.equals(b));
+ }
+
+ public void dump(String prefix, PrintWriter pw) {
+ pw.print(prefix);
+ pw.print("NetworkStats: elapsedRealtime="); pw.println(elapsedRealtime);
+ for (int i = 0; i < iface.length; i++) {
+ pw.print(prefix);
+ pw.print(" iface="); pw.print(iface[i]);
+ pw.print(" uid="); pw.print(uid[i]);
+ pw.print(" rx="); pw.print(rx[i]);
+ pw.print(" tx="); pw.println(tx[i]);
+ }
+ }
+
+ @Override
+ public String toString() {
+ final CharArrayWriter writer = new CharArrayWriter();
+ dump("", new PrintWriter(writer));
+ return writer.toString();
+ }
+
+ /** {@inheritDoc} */
+ public int describeContents() {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeLong(elapsedRealtime);
+ dest.writeStringArray(iface);
+ dest.writeIntArray(uid);
+ dest.writeLongArray(rx);
+ dest.writeLongArray(tx);
+ }
+
+ public static final Creator<NetworkStats> CREATOR = new Creator<NetworkStats>() {
+ public NetworkStats createFromParcel(Parcel in) {
+ return new NetworkStats(in);
+ }
+
+ public NetworkStats[] newArray(int size) {
+ return new NetworkStats[size];
+ }
+ };
+}
diff --git a/core/java/android/net/SSLCertificateSocketFactory.java b/core/java/android/net/SSLCertificateSocketFactory.java
index f8f8a29..3bf64b2 100644
--- a/core/java/android/net/SSLCertificateSocketFactory.java
+++ b/core/java/android/net/SSLCertificateSocketFactory.java
@@ -17,18 +17,12 @@
package android.net;
import android.os.SystemProperties;
-import android.util.Config;
import android.util.Log;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
-import java.security.GeneralSecurityException;
import java.security.KeyManagementException;
-import java.security.KeyStore;
-import java.security.KeyStoreException;
-import java.security.NoSuchAlgorithmException;
-import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import javax.net.SocketFactory;
@@ -40,7 +34,6 @@ import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
-import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
import org.apache.harmony.xnet.provider.jsse.OpenSSLContextImpl;
@@ -128,7 +121,7 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory {
*
* @param handshakeTimeoutMillis to use for SSL connection handshake, or 0
* for none. The socket timeout is reset to 0 after the handshake.
- * @param cache The {@link SSLClientSessionCache} to use, or null for no cache.
+ * @param cache The {@link SSLSessionCache} to use, or null for no cache.
* @return a new SSLSocketFactory with the specified parameters
*/
public static SSLSocketFactory getDefault(int handshakeTimeoutMillis, SSLSessionCache cache) {
@@ -144,7 +137,7 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory {
*
* @param handshakeTimeoutMillis to use for SSL connection handshake, or 0
* for none. The socket timeout is reset to 0 after the handshake.
- * @param cache The {@link SSLClientSessionCache} to use, or null for no cache.
+ * @param cache The {@link SSLSessionCache} to use, or null for no cache.
* @return an insecure SSLSocketFactory with the specified parameters
*/
public static SSLSocketFactory getInsecure(int handshakeTimeoutMillis, SSLSessionCache cache) {
@@ -157,12 +150,11 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory {
*
* @param handshakeTimeoutMillis to use for SSL connection handshake, or 0
* for none. The socket timeout is reset to 0 after the handshake.
- * @param cache The {@link SSLClientSessionCache} to use, or null for no cache.
+ * @param cache The {@link SSLSessionCache} to use, or null for no cache.
* @return a new SocketFactory with the specified parameters
*/
public static org.apache.http.conn.ssl.SSLSocketFactory getHttpSocketFactory(
- int handshakeTimeoutMillis,
- SSLSessionCache cache) {
+ int handshakeTimeoutMillis, SSLSessionCache cache) {
return new org.apache.http.conn.ssl.SSLSocketFactory(
new SSLCertificateSocketFactory(handshakeTimeoutMillis, cache, true));
}
diff --git a/core/java/android/net/SntpClient.java b/core/java/android/net/SntpClient.java
index 3e21e2d..316440f 100644
--- a/core/java/android/net/SntpClient.java
+++ b/core/java/android/net/SntpClient.java
@@ -17,7 +17,6 @@
package android.net;
import android.os.SystemClock;
-import android.util.Config;
import android.util.Log;
import java.io.IOException;
@@ -112,8 +111,8 @@ public class SntpClient
// = (transit + skew - transit + skew)/2
// = (2 * skew)/2 = skew
long clockOffset = ((receiveTime - originateTime) + (transmitTime - responseTime))/2;
- // if (Config.LOGD) Log.d(TAG, "round trip: " + roundTripTime + " ms");
- // if (Config.LOGD) Log.d(TAG, "clock offset: " + clockOffset + " ms");
+ // if (false) Log.d(TAG, "round trip: " + roundTripTime + " ms");
+ // if (false) Log.d(TAG, "clock offset: " + clockOffset + " ms");
// save our results - use the times on this side of the network latency
// (response rather than request time)
@@ -121,7 +120,7 @@ public class SntpClient
mNtpTimeReference = responseTicks;
mRoundTripTime = roundTripTime;
} catch (Exception e) {
- if (Config.LOGD) Log.d(TAG, "request time failed: " + e);
+ if (false) Log.d(TAG, "request time failed: " + e);
return false;
} finally {
if (socket != null) {
diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java
index eca06c5..c0ff734 100644
--- a/core/java/android/net/TrafficStats.java
+++ b/core/java/android/net/TrafficStats.java
@@ -16,11 +16,16 @@
package android.net;
-import android.util.Log;
+import android.content.Context;
+import android.os.IBinder;
+import android.os.INetworkManagementService;
+import android.os.RemoteException;
+import android.os.ServiceManager;
-import java.io.File;
-import java.io.RandomAccessFile;
-import java.io.IOException;
+import dalvik.system.BlockGuard;
+
+import java.net.Socket;
+import java.net.SocketException;
/**
* Class that provides network traffic statistics. These statistics include
@@ -37,6 +42,112 @@ public class TrafficStats {
public final static int UNSUPPORTED = -1;
/**
+ * Snapshot of {@link NetworkStats} when the currently active profiling
+ * session started, or {@code null} if no session active.
+ *
+ * @see #startDataProfiling(Context)
+ * @see #stopDataProfiling(Context)
+ */
+ private static NetworkStats sActiveProfilingStart;
+
+ private static Object sProfilingLock = new Object();
+
+ /**
+ * Set active tag to use when accounting {@link Socket} traffic originating
+ * from the current thread. Only one active tag per thread is supported.
+ * <p>
+ * Changes only take effect during subsequent calls to
+ * {@link #tagSocket(Socket)}.
+ */
+ public static void setThreadStatsTag(String tag) {
+ BlockGuard.setThreadSocketStatsTag(tag);
+ }
+
+ public static void clearThreadStatsTag() {
+ BlockGuard.setThreadSocketStatsTag(null);
+ }
+
+ /**
+ * Set specific UID to use when accounting {@link Socket} traffic
+ * originating from the current thread. Designed for use when performing an
+ * operation on behalf of another application.
+ * <p>
+ * Changes only take effect during subsequent calls to
+ * {@link #tagSocket(Socket)}.
+ * <p>
+ * To take effect, caller must hold
+ * {@link android.Manifest.permission#UPDATE_DEVICE_STATS} permission.
+ *
+ * {@hide}
+ */
+ public static void setThreadStatsUid(int uid) {
+ BlockGuard.setThreadSocketStatsUid(uid);
+ }
+
+ /** {@hide} */
+ public static void clearThreadStatsUid() {
+ BlockGuard.setThreadSocketStatsUid(-1);
+ }
+
+ /**
+ * Tag the given {@link Socket} with any statistics parameters active for
+ * the current thread. Subsequent calls always replace any existing
+ * parameters. When finished, call {@link #untagSocket(Socket)} to remove
+ * statistics parameters.
+ *
+ * @see #setThreadStatsTag(String)
+ * @see #setThreadStatsUid(int)
+ */
+ public static void tagSocket(Socket socket) throws SocketException {
+ BlockGuard.tagSocketFd(socket.getFileDescriptor$());
+ }
+
+ /**
+ * Remove any statistics parameters from the given {@link Socket}.
+ */
+ public static void untagSocket(Socket socket) throws SocketException {
+ BlockGuard.untagSocketFd(socket.getFileDescriptor$());
+ }
+
+ /**
+ * Start profiling data usage for current UID. Only one profiling session
+ * can be active at a time.
+ *
+ * @hide
+ */
+ public static void startDataProfiling(Context context) {
+ synchronized (sProfilingLock) {
+ if (sActiveProfilingStart != null) {
+ throw new IllegalStateException("already profiling data");
+ }
+
+ // take snapshot in time; we calculate delta later
+ sActiveProfilingStart = getNetworkStatsForUid(context);
+ }
+ }
+
+ /**
+ * Stop profiling data usage for current UID.
+ *
+ * @return Detailed {@link NetworkStats} of data that occurred since last
+ * {@link #startDataProfiling(Context)} call.
+ * @hide
+ */
+ public static NetworkStats stopDataProfiling(Context context) {
+ synchronized (sProfilingLock) {
+ if (sActiveProfilingStart == null) {
+ throw new IllegalStateException("not profiling data");
+ }
+
+ // subtract starting values and return delta
+ final NetworkStats profilingStop = getNetworkStatsForUid(context);
+ final NetworkStats profilingDelta = profilingStop.subtract(sActiveProfilingStart);
+ sActiveProfilingStart = null;
+ return profilingDelta;
+ }
+ }
+
+ /**
* Get the total number of packets transmitted through the mobile interface.
*
* @return number of packets. If the statistics are not supported by this device,
@@ -294,4 +405,21 @@ public class TrafficStats {
* {@link #UNSUPPORTED} will be returned.
*/
public static native long getUidUdpRxPackets(int uid);
+
+ /**
+ * Return detailed {@link NetworkStats} for the current UID. Requires no
+ * special permission.
+ */
+ private static NetworkStats getNetworkStatsForUid(Context context) {
+ final IBinder binder = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
+ final INetworkManagementService service = INetworkManagementService.Stub.asInterface(
+ binder);
+
+ final int uid = android.os.Process.myUid();
+ try {
+ return service.getNetworkStatsUidDetail(uid);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
}
diff --git a/core/java/android/net/http/Headers.java b/core/java/android/net/http/Headers.java
index 74c0de8..657e071 100644
--- a/core/java/android/net/http/Headers.java
+++ b/core/java/android/net/http/Headers.java
@@ -16,7 +16,6 @@
package android.net.http;
-import android.util.Config;
import android.util.Log;
import java.util.ArrayList;
@@ -201,7 +200,7 @@ public final class Headers {
try {
contentLength = Long.parseLong(val);
} catch (NumberFormatException e) {
- if (Config.LOGV) {
+ if (false) {
Log.v(LOGTAG, "Headers.headers(): error parsing"
+ " content length: " + buffer.toString());
}
@@ -449,7 +448,7 @@ public final class Headers {
}
int extraLen = mExtraHeaderNames.size();
for (int i = 0; i < extraLen; i++) {
- if (Config.LOGV) {
+ if (false) {
HttpLog.v("Headers.getHeaders() extra: " + i + " " +
mExtraHeaderNames.get(i) + " " + mExtraHeaderValues.get(i));
}
diff --git a/core/java/android/net/http/HttpLog.java b/core/java/android/net/http/HttpLog.java
index 30bf647..0934664 100644
--- a/core/java/android/net/http/HttpLog.java
+++ b/core/java/android/net/http/HttpLog.java
@@ -23,7 +23,6 @@ package android.net.http;
import android.os.SystemClock;
import android.util.Log;
-import android.util.Config;
/**
* {@hide}
@@ -32,7 +31,7 @@ class HttpLog {
private final static String LOGTAG = "http";
private static final boolean DEBUG = false;
- static final boolean LOGV = DEBUG ? Config.LOGD : Config.LOGV;
+ static final boolean LOGV = false;
static void v(String logMe) {
Log.v(LOGTAG, SystemClock.uptimeMillis() + " " + Thread.currentThread().getName() + " " + logMe);
diff --git a/core/java/android/net/http/HttpsConnection.java b/core/java/android/net/http/HttpsConnection.java
index d77e9d9..84765a5 100644
--- a/core/java/android/net/http/HttpsConnection.java
+++ b/core/java/android/net/http/HttpsConnection.java
@@ -289,11 +289,9 @@ public class HttpsConnection extends Connection {
} else {
// if we do not have a proxy, we simply connect to the host
try {
- sslSock = (SSLSocket) getSocketFactory().createSocket();
-
+ sslSock = (SSLSocket) getSocketFactory().createSocket(
+ mHost.getHostName(), mHost.getPort());
sslSock.setSoTimeout(SOCKET_TIMEOUT);
- sslSock.connect(new InetSocketAddress(mHost.getHostName(),
- mHost.getPort()));
} catch(IOException e) {
if (sslSock != null) {
sslSock.close();
diff --git a/core/java/android/os/AsyncTask.java b/core/java/android/os/AsyncTask.java
index 1803604..64bba54 100644
--- a/core/java/android/os/AsyncTask.java
+++ b/core/java/android/os/AsyncTask.java
@@ -153,7 +153,6 @@ public abstract class AsyncTask<Params, Progress, Result> {
private static final int MAXIMUM_POOL_SIZE = 128;
private static final int KEEP_ALIVE = 1;
-
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
private final AtomicInteger mCount = new AtomicInteger(1);
@@ -183,6 +182,7 @@ public abstract class AsyncTask<Params, Progress, Result> {
private static final InternalHandler sHandler = new InternalHandler();
+ private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
private final WorkerRunnable<Params, Result> mWorker;
private final FutureTask<Result> mFuture;
@@ -240,6 +240,11 @@ public abstract class AsyncTask<Params, Progress, Result> {
sHandler.getLooper();
}
+ /** @hide */
+ public static void setDefaultExecutor(Executor exec) {
+ sDefaultExecutor = exec;
+ }
+
/**
* Creates a new asynchronous task. This constructor must be invoked on the UI thread.
*/
@@ -496,7 +501,7 @@ public abstract class AsyncTask<Params, Progress, Result> {
* {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED}.
*/
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
- return executeOnExecutor(THREAD_POOL_EXECUTOR, params);
+ return executeOnExecutor(sDefaultExecutor, params);
}
/**
@@ -559,7 +564,7 @@ public abstract class AsyncTask<Params, Progress, Result> {
* a simple Runnable object.
*/
public static void execute(Runnable runnable) {
- THREAD_POOL_EXECUTOR.execute(runnable);
+ sDefaultExecutor.execute(runnable);
}
/**
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 1d6bc4e..e344197 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -26,6 +26,7 @@ import android.content.pm.ApplicationInfo;
import android.telephony.SignalStrength;
import android.util.Log;
import android.util.Printer;
+import android.util.Slog;
import android.util.SparseArray;
import android.util.TimeUtils;
@@ -408,15 +409,19 @@ public abstract class BatteryStats implements Parcelable {
}
public final static class HistoryItem implements Parcelable {
+ static final String TAG = "HistoryItem";
+ static final boolean DEBUG = false;
+
public HistoryItem next;
public long time;
- public static final byte CMD_UPDATE = 0;
- public static final byte CMD_START = 1;
- public static final byte CMD_OVERFLOW = 2;
+ public static final byte CMD_NULL = 0;
+ public static final byte CMD_UPDATE = 1;
+ public static final byte CMD_START = 2;
+ public static final byte CMD_OVERFLOW = 3;
- public byte cmd;
+ public byte cmd = CMD_NULL;
public byte batteryLevel;
public byte batteryStatus;
@@ -427,33 +432,38 @@ public abstract class BatteryStats implements Parcelable {
public char batteryVoltage;
// Constants from SCREEN_BRIGHTNESS_*
- public static final int STATE_BRIGHTNESS_MASK = 0x000000f;
+ public static final int STATE_BRIGHTNESS_MASK = 0x0000000f;
public static final int STATE_BRIGHTNESS_SHIFT = 0;
// Constants from SIGNAL_STRENGTH_*
- public static final int STATE_SIGNAL_STRENGTH_MASK = 0x00000f0;
+ public static final int STATE_SIGNAL_STRENGTH_MASK = 0x000000f0;
public static final int STATE_SIGNAL_STRENGTH_SHIFT = 4;
// Constants from ServiceState.STATE_*
- public static final int STATE_PHONE_STATE_MASK = 0x0000f00;
+ public static final int STATE_PHONE_STATE_MASK = 0x00000f00;
public static final int STATE_PHONE_STATE_SHIFT = 8;
// Constants from DATA_CONNECTION_*
- public static final int STATE_DATA_CONNECTION_MASK = 0x000f000;
+ public static final int STATE_DATA_CONNECTION_MASK = 0x0000f000;
public static final int STATE_DATA_CONNECTION_SHIFT = 12;
- public static final int STATE_BATTERY_PLUGGED_FLAG = 1<<30;
- public static final int STATE_SCREEN_ON_FLAG = 1<<29;
+ // These states always appear directly in the first int token
+ // of a delta change; they should be ones that change relatively
+ // frequently.
+ public static final int STATE_WAKE_LOCK_FLAG = 1<<30;
+ public static final int STATE_SENSOR_ON_FLAG = 1<<29;
public static final int STATE_GPS_ON_FLAG = 1<<28;
- public static final int STATE_PHONE_IN_CALL_FLAG = 1<<27;
- public static final int STATE_PHONE_SCANNING_FLAG = 1<<26;
- public static final int STATE_WIFI_ON_FLAG = 1<<25;
- public static final int STATE_WIFI_RUNNING_FLAG = 1<<24;
- public static final int STATE_WIFI_FULL_LOCK_FLAG = 1<<23;
- public static final int STATE_WIFI_SCAN_LOCK_FLAG = 1<<22;
- public static final int STATE_WIFI_MULTICAST_ON_FLAG = 1<<21;
- public static final int STATE_BLUETOOTH_ON_FLAG = 1<<20;
- public static final int STATE_AUDIO_ON_FLAG = 1<<19;
- public static final int STATE_VIDEO_ON_FLAG = 1<<18;
- public static final int STATE_WAKE_LOCK_FLAG = 1<<17;
- public static final int STATE_SENSOR_ON_FLAG = 1<<16;
+ public static final int STATE_PHONE_SCANNING_FLAG = 1<<27;
+ public static final int STATE_WIFI_RUNNING_FLAG = 1<<26;
+ public static final int STATE_WIFI_FULL_LOCK_FLAG = 1<<25;
+ public static final int STATE_WIFI_SCAN_LOCK_FLAG = 1<<24;
+ public static final int STATE_WIFI_MULTICAST_ON_FLAG = 1<<23;
+ // These are on the lower bits used for the command; if they change
+ // we need to write another int of data.
+ public static final int STATE_AUDIO_ON_FLAG = 1<<22;
+ public static final int STATE_VIDEO_ON_FLAG = 1<<21;
+ public static final int STATE_SCREEN_ON_FLAG = 1<<20;
+ public static final int STATE_BATTERY_PLUGGED_FLAG = 1<<19;
+ public static final int STATE_PHONE_IN_CALL_FLAG = 1<<18;
+ public static final int STATE_WIFI_ON_FLAG = 1<<17;
+ public static final int STATE_BLUETOOTH_ON_FLAG = 1<<16;
public static final int MOST_INTERESTING_STATES =
STATE_BATTERY_PLUGGED_FLAG | STATE_SCREEN_ON_FLAG
@@ -466,16 +476,7 @@ public abstract class BatteryStats implements Parcelable {
public HistoryItem(long time, Parcel src) {
this.time = time;
- int bat = src.readInt();
- cmd = (byte)(bat&0xff);
- batteryLevel = (byte)((bat>>8)&0xff);
- batteryStatus = (byte)((bat>>16)&0xf);
- batteryHealth = (byte)((bat>>20)&0xf);
- batteryPlugType = (byte)((bat>>24)&0xf);
- bat = src.readInt();
- batteryTemperature = (char)(bat&0xffff);
- batteryVoltage = (char)((bat>>16)&0xffff);
- states = src.readInt();
+ readFromParcel(src);
}
public int describeContents() {
@@ -495,6 +496,174 @@ public abstract class BatteryStats implements Parcelable {
dest.writeInt(bat);
dest.writeInt(states);
}
+
+ private void readFromParcel(Parcel src) {
+ int bat = src.readInt();
+ cmd = (byte)(bat&0xff);
+ batteryLevel = (byte)((bat>>8)&0xff);
+ batteryStatus = (byte)((bat>>16)&0xf);
+ batteryHealth = (byte)((bat>>20)&0xf);
+ batteryPlugType = (byte)((bat>>24)&0xf);
+ bat = src.readInt();
+ batteryTemperature = (char)(bat&0xffff);
+ batteryVoltage = (char)((bat>>16)&0xffff);
+ states = src.readInt();
+ }
+
+ // Part of initial delta int that specifies the time delta.
+ static final int DELTA_TIME_MASK = 0x3ffff;
+ static final int DELTA_TIME_ABS = 0x3fffd; // Following is an entire abs update.
+ static final int DELTA_TIME_INT = 0x3fffe; // The delta is a following int
+ static final int DELTA_TIME_LONG = 0x3ffff; // The delta is a following long
+ // Part of initial delta int holding the command code.
+ static final int DELTA_CMD_MASK = 0x3;
+ static final int DELTA_CMD_SHIFT = 18;
+ // Flag in delta int: a new battery level int follows.
+ static final int DELTA_BATTERY_LEVEL_FLAG = 1<<20;
+ // Flag in delta int: a new full state and battery status int follows.
+ static final int DELTA_STATE_FLAG = 1<<21;
+ static final int DELTA_STATE_MASK = 0xffc00000;
+
+ public void writeDelta(Parcel dest, HistoryItem last) {
+ if (last == null || last.cmd != CMD_UPDATE) {
+ dest.writeInt(DELTA_TIME_ABS);
+ writeToParcel(dest, 0);
+ return;
+ }
+
+ final long deltaTime = time - last.time;
+ final int lastBatteryLevelInt = last.buildBatteryLevelInt();
+ final int lastStateInt = last.buildStateInt();
+
+ int deltaTimeToken;
+ if (deltaTime < 0 || deltaTime > Integer.MAX_VALUE) {
+ deltaTimeToken = DELTA_TIME_LONG;
+ } else if (deltaTime >= DELTA_TIME_ABS) {
+ deltaTimeToken = DELTA_TIME_INT;
+ } else {
+ deltaTimeToken = (int)deltaTime;
+ }
+ int firstToken = deltaTimeToken
+ | (cmd<<DELTA_CMD_SHIFT)
+ | (states&DELTA_STATE_MASK);
+ final int batteryLevelInt = buildBatteryLevelInt();
+ final boolean batteryLevelIntChanged = batteryLevelInt != lastBatteryLevelInt;
+ if (batteryLevelIntChanged) {
+ firstToken |= DELTA_BATTERY_LEVEL_FLAG;
+ }
+ final int stateInt = buildStateInt();
+ final boolean stateIntChanged = stateInt != lastStateInt;
+ if (stateIntChanged) {
+ firstToken |= DELTA_STATE_FLAG;
+ }
+ dest.writeInt(firstToken);
+ if (DEBUG) Slog.i(TAG, "WRITE DELTA: firstToken=0x" + Integer.toHexString(firstToken)
+ + " deltaTime=" + deltaTime);
+
+ if (deltaTimeToken >= DELTA_TIME_INT) {
+ if (deltaTimeToken == DELTA_TIME_INT) {
+ if (DEBUG) Slog.i(TAG, "WRITE DELTA: int deltaTime=" + (int)deltaTime);
+ dest.writeInt((int)deltaTime);
+ } else {
+ if (DEBUG) Slog.i(TAG, "WRITE DELTA: long deltaTime=" + deltaTime);
+ dest.writeLong(deltaTime);
+ }
+ }
+ if (batteryLevelIntChanged) {
+ dest.writeInt(batteryLevelInt);
+ if (DEBUG) Slog.i(TAG, "WRITE DELTA: batteryToken=0x"
+ + Integer.toHexString(batteryLevelInt)
+ + " batteryLevel=" + batteryLevel
+ + " batteryTemp=" + (int)batteryTemperature
+ + " batteryVolt=" + (int)batteryVoltage);
+ }
+ if (stateIntChanged) {
+ dest.writeInt(stateInt);
+ if (DEBUG) Slog.i(TAG, "WRITE DELTA: stateToken=0x"
+ + Integer.toHexString(stateInt)
+ + " batteryStatus=" + batteryStatus
+ + " batteryHealth=" + batteryHealth
+ + " batteryPlugType=" + batteryPlugType
+ + " states=0x" + Integer.toHexString(states));
+ }
+ }
+
+ private int buildBatteryLevelInt() {
+ return ((((int)batteryLevel)<<24)&0xff000000)
+ | ((((int)batteryTemperature)<<14)&0x00ffc000)
+ | (((int)batteryVoltage)&0x00003fff);
+ }
+
+ private int buildStateInt() {
+ return ((((int)batteryStatus)<<28)&0xf0000000)
+ | ((((int)batteryHealth)<<24)&0x0f000000)
+ | ((((int)batteryPlugType)<<22)&0x00c00000)
+ | (states&(~DELTA_STATE_MASK));
+ }
+
+ public void readDelta(Parcel src) {
+ int firstToken = src.readInt();
+ int deltaTimeToken = firstToken&DELTA_TIME_MASK;
+ cmd = (byte)((firstToken>>DELTA_CMD_SHIFT)&DELTA_CMD_MASK);
+ if (DEBUG) Slog.i(TAG, "READ DELTA: firstToken=0x" + Integer.toHexString(firstToken)
+ + " deltaTimeToken=" + deltaTimeToken);
+
+ if (deltaTimeToken < DELTA_TIME_ABS) {
+ time += deltaTimeToken;
+ } else if (deltaTimeToken == DELTA_TIME_ABS) {
+ time = src.readLong();
+ readFromParcel(src);
+ return;
+ } else if (deltaTimeToken == DELTA_TIME_INT) {
+ int delta = src.readInt();
+ time += delta;
+ if (DEBUG) Slog.i(TAG, "READ DELTA: time delta=" + delta + " new time=" + time);
+ } else {
+ long delta = src.readLong();
+ if (DEBUG) Slog.i(TAG, "READ DELTA: time delta=" + delta + " new time=" + time);
+ time += delta;
+ }
+
+ if ((firstToken&DELTA_BATTERY_LEVEL_FLAG) != 0) {
+ int batteryLevelInt = src.readInt();
+ batteryLevel = (byte)((batteryLevelInt>>24)&0xff);
+ batteryTemperature = (char)((batteryLevelInt>>14)&0x3ff);
+ batteryVoltage = (char)(batteryLevelInt&0x3fff);
+ if (DEBUG) Slog.i(TAG, "READ DELTA: batteryToken=0x"
+ + Integer.toHexString(batteryLevelInt)
+ + " batteryLevel=" + batteryLevel
+ + " batteryTemp=" + (int)batteryTemperature
+ + " batteryVolt=" + (int)batteryVoltage);
+ }
+
+ if ((firstToken&DELTA_STATE_FLAG) != 0) {
+ int stateInt = src.readInt();
+ states = (firstToken&DELTA_STATE_MASK) | (stateInt&(~DELTA_STATE_MASK));
+ batteryStatus = (byte)((stateInt>>28)&0xf);
+ batteryHealth = (byte)((stateInt>>24)&0xf);
+ batteryPlugType = (byte)((stateInt>>22)&0x3);
+ if (DEBUG) Slog.i(TAG, "READ DELTA: stateToken=0x"
+ + Integer.toHexString(stateInt)
+ + " batteryStatus=" + batteryStatus
+ + " batteryHealth=" + batteryHealth
+ + " batteryPlugType=" + batteryPlugType
+ + " states=0x" + Integer.toHexString(states));
+ } else {
+ states = (firstToken&DELTA_STATE_MASK) | (states&(~DELTA_STATE_MASK));
+ }
+ }
+
+ public void clear() {
+ time = 0;
+ cmd = CMD_NULL;
+ batteryLevel = 0;
+ batteryStatus = 0;
+ batteryHealth = 0;
+ batteryPlugType = 0;
+ batteryTemperature = 0;
+ batteryVoltage = 0;
+ states = 0;
+ }
public void setTo(HistoryItem o) {
time = o.time;
@@ -556,11 +725,14 @@ public abstract class BatteryStats implements Parcelable {
public abstract boolean getNextHistoryLocked(HistoryItem out);
- /**
- * Return the current history of battery state changes.
- */
- public abstract HistoryItem getHistory();
-
+ public abstract void finishIteratingHistoryLocked();
+
+ public abstract boolean startIteratingOldHistoryLocked();
+
+ public abstract boolean getNextOldHistoryLocked(HistoryItem out);
+
+ public abstract void finishIteratingOldHistoryLocked();
+
/**
* Return the base time offset for the battery history.
*/
@@ -1729,7 +1901,7 @@ public abstract class BatteryStats implements Parcelable {
}
}
- void printBitDescriptions(PrintWriter pw, int oldval, int newval, BitDescription[] descriptions) {
+ static void printBitDescriptions(PrintWriter pw, int oldval, int newval, BitDescription[] descriptions) {
int diff = oldval ^ newval;
if (diff == 0) return;
for (int i=0; i<descriptions.length; i++) {
@@ -1753,6 +1925,125 @@ public abstract class BatteryStats implements Parcelable {
}
}
+ public void prepareForDumpLocked() {
+ }
+
+ public static class HistoryPrinter {
+ int oldState = 0;
+ int oldStatus = -1;
+ int oldHealth = -1;
+ int oldPlug = -1;
+ int oldTemp = -1;
+ int oldVolt = -1;
+
+ public void printNextItem(PrintWriter pw, HistoryItem rec, long now) {
+ pw.print(" ");
+ TimeUtils.formatDuration(rec.time-now, pw, TimeUtils.HUNDRED_DAY_FIELD_LEN);
+ pw.print(" ");
+ if (rec.cmd == HistoryItem.CMD_START) {
+ pw.println(" START");
+ } else if (rec.cmd == HistoryItem.CMD_OVERFLOW) {
+ pw.println(" *OVERFLOW*");
+ } else {
+ if (rec.batteryLevel < 10) pw.print("00");
+ else if (rec.batteryLevel < 100) pw.print("0");
+ pw.print(rec.batteryLevel);
+ pw.print(" ");
+ if (rec.states < 0x10) pw.print("0000000");
+ else if (rec.states < 0x100) pw.print("000000");
+ else if (rec.states < 0x1000) pw.print("00000");
+ else if (rec.states < 0x10000) pw.print("0000");
+ else if (rec.states < 0x100000) pw.print("000");
+ else if (rec.states < 0x1000000) pw.print("00");
+ else if (rec.states < 0x10000000) pw.print("0");
+ pw.print(Integer.toHexString(rec.states));
+ if (oldStatus != rec.batteryStatus) {
+ oldStatus = rec.batteryStatus;
+ pw.print(" status=");
+ switch (oldStatus) {
+ case BatteryManager.BATTERY_STATUS_UNKNOWN:
+ pw.print("unknown");
+ break;
+ case BatteryManager.BATTERY_STATUS_CHARGING:
+ pw.print("charging");
+ break;
+ case BatteryManager.BATTERY_STATUS_DISCHARGING:
+ pw.print("discharging");
+ break;
+ case BatteryManager.BATTERY_STATUS_NOT_CHARGING:
+ pw.print("not-charging");
+ break;
+ case BatteryManager.BATTERY_STATUS_FULL:
+ pw.print("full");
+ break;
+ default:
+ pw.print(oldStatus);
+ break;
+ }
+ }
+ if (oldHealth != rec.batteryHealth) {
+ oldHealth = rec.batteryHealth;
+ pw.print(" health=");
+ switch (oldHealth) {
+ case BatteryManager.BATTERY_HEALTH_UNKNOWN:
+ pw.print("unknown");
+ break;
+ case BatteryManager.BATTERY_HEALTH_GOOD:
+ pw.print("good");
+ break;
+ case BatteryManager.BATTERY_HEALTH_OVERHEAT:
+ pw.print("overheat");
+ break;
+ case BatteryManager.BATTERY_HEALTH_DEAD:
+ pw.print("dead");
+ break;
+ case BatteryManager.BATTERY_HEALTH_OVER_VOLTAGE:
+ pw.print("over-voltage");
+ break;
+ case BatteryManager.BATTERY_HEALTH_UNSPECIFIED_FAILURE:
+ pw.print("failure");
+ break;
+ default:
+ pw.print(oldHealth);
+ break;
+ }
+ }
+ if (oldPlug != rec.batteryPlugType) {
+ oldPlug = rec.batteryPlugType;
+ pw.print(" plug=");
+ switch (oldPlug) {
+ case 0:
+ pw.print("none");
+ break;
+ case BatteryManager.BATTERY_PLUGGED_AC:
+ pw.print("ac");
+ break;
+ case BatteryManager.BATTERY_PLUGGED_USB:
+ pw.print("usb");
+ break;
+ default:
+ pw.print(oldPlug);
+ break;
+ }
+ }
+ if (oldTemp != rec.batteryTemperature) {
+ oldTemp = rec.batteryTemperature;
+ pw.print(" temp=");
+ pw.print(oldTemp);
+ }
+ if (oldVolt != rec.batteryVoltage) {
+ oldVolt = rec.batteryVoltage;
+ pw.print(" volt=");
+ pw.print(oldVolt);
+ }
+ printBitDescriptions(pw, oldState, rec.states,
+ HISTORY_STATE_DESCRIPTIONS);
+ pw.println();
+ }
+ oldState = rec.states;
+ }
+ }
+
/**
* Dumps a human-readable summary of the battery statistics to the given PrintWriter.
*
@@ -1760,122 +2051,28 @@ public abstract class BatteryStats implements Parcelable {
*/
@SuppressWarnings("unused")
public void dumpLocked(PrintWriter pw) {
+ prepareForDumpLocked();
+
+ long now = getHistoryBaseTime() + SystemClock.elapsedRealtime();
+
final HistoryItem rec = new HistoryItem();
if (startIteratingHistoryLocked()) {
pw.println("Battery History:");
- long now = getHistoryBaseTime() + SystemClock.elapsedRealtime();
- int oldState = 0;
- int oldStatus = -1;
- int oldHealth = -1;
- int oldPlug = -1;
- int oldTemp = -1;
- int oldVolt = -1;
+ HistoryPrinter hprinter = new HistoryPrinter();
while (getNextHistoryLocked(rec)) {
- pw.print(" ");
- TimeUtils.formatDuration(rec.time-now, pw, TimeUtils.HUNDRED_DAY_FIELD_LEN);
- pw.print(" ");
- if (rec.cmd == HistoryItem.CMD_START) {
- pw.println(" START");
- } else if (rec.cmd == HistoryItem.CMD_OVERFLOW) {
- pw.println(" *OVERFLOW*");
- } else {
- if (rec.batteryLevel < 10) pw.print("00");
- else if (rec.batteryLevel < 100) pw.print("0");
- pw.print(rec.batteryLevel);
- pw.print(" ");
- if (rec.states < 0x10) pw.print("0000000");
- else if (rec.states < 0x100) pw.print("000000");
- else if (rec.states < 0x1000) pw.print("00000");
- else if (rec.states < 0x10000) pw.print("0000");
- else if (rec.states < 0x100000) pw.print("000");
- else if (rec.states < 0x1000000) pw.print("00");
- else if (rec.states < 0x10000000) pw.print("0");
- pw.print(Integer.toHexString(rec.states));
- if (oldStatus != rec.batteryStatus) {
- oldStatus = rec.batteryStatus;
- pw.print(" status=");
- switch (oldStatus) {
- case BatteryManager.BATTERY_STATUS_UNKNOWN:
- pw.print("unknown");
- break;
- case BatteryManager.BATTERY_STATUS_CHARGING:
- pw.print("charging");
- break;
- case BatteryManager.BATTERY_STATUS_DISCHARGING:
- pw.print("discharging");
- break;
- case BatteryManager.BATTERY_STATUS_NOT_CHARGING:
- pw.print("not-charging");
- break;
- case BatteryManager.BATTERY_STATUS_FULL:
- pw.print("full");
- break;
- default:
- pw.print(oldStatus);
- break;
- }
- }
- if (oldHealth != rec.batteryHealth) {
- oldHealth = rec.batteryHealth;
- pw.print(" health=");
- switch (oldHealth) {
- case BatteryManager.BATTERY_HEALTH_UNKNOWN:
- pw.print("unknown");
- break;
- case BatteryManager.BATTERY_HEALTH_GOOD:
- pw.print("good");
- break;
- case BatteryManager.BATTERY_HEALTH_OVERHEAT:
- pw.print("overheat");
- break;
- case BatteryManager.BATTERY_HEALTH_DEAD:
- pw.print("dead");
- break;
- case BatteryManager.BATTERY_HEALTH_OVER_VOLTAGE:
- pw.print("over-voltage");
- break;
- case BatteryManager.BATTERY_HEALTH_UNSPECIFIED_FAILURE:
- pw.print("failure");
- break;
- default:
- pw.print(oldHealth);
- break;
- }
- }
- if (oldPlug != rec.batteryPlugType) {
- oldPlug = rec.batteryPlugType;
- pw.print(" plug=");
- switch (oldPlug) {
- case 0:
- pw.print("none");
- break;
- case BatteryManager.BATTERY_PLUGGED_AC:
- pw.print("ac");
- break;
- case BatteryManager.BATTERY_PLUGGED_USB:
- pw.print("usb");
- break;
- default:
- pw.print(oldPlug);
- break;
- }
- }
- if (oldTemp != rec.batteryTemperature) {
- oldTemp = rec.batteryTemperature;
- pw.print(" temp=");
- pw.print(oldTemp);
- }
- if (oldVolt != rec.batteryVoltage) {
- oldVolt = rec.batteryVoltage;
- pw.print(" volt=");
- pw.print(oldVolt);
- }
- printBitDescriptions(pw, oldState, rec.states,
- HISTORY_STATE_DESCRIPTIONS);
- pw.println();
- }
- oldState = rec.states;
+ hprinter.printNextItem(pw, rec, now);
+ }
+ finishIteratingHistoryLocked();
+ pw.println("");
+ }
+
+ if (startIteratingOldHistoryLocked()) {
+ pw.println("Old battery History:");
+ HistoryPrinter hprinter = new HistoryPrinter();
+ while (getNextOldHistoryLocked(rec)) {
+ hprinter.printNextItem(pw, rec, now);
}
+ finishIteratingOldHistoryLocked();
pw.println("");
}
@@ -1917,6 +2114,8 @@ public abstract class BatteryStats implements Parcelable {
@SuppressWarnings("unused")
public void dumpCheckinLocked(PrintWriter pw, String[] args, List<ApplicationInfo> apps) {
+ prepareForDumpLocked();
+
boolean isUnpluggedOnly = false;
for (String arg : args) {
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index ae1e1c2..c25ebb7 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -16,7 +16,6 @@
package android.os;
-import android.util.Config;
import android.util.Log;
import java.io.FileDescriptor;
@@ -291,7 +290,7 @@ public class Binder implements IBinder {
*/
public final boolean transact(int code, Parcel data, Parcel reply,
int flags) throws RemoteException {
- if (Config.LOGV) Log.v("Binder", "Transact: " + code + " to " + this);
+ if (false) Log.v("Binder", "Transact: " + code + " to " + this);
if (data != null) {
data.setDataPosition(0);
}
@@ -413,7 +412,7 @@ final class BinderProxy implements IBinder {
private native final void destroy();
private static final void sendDeathNotice(DeathRecipient recipient) {
- if (Config.LOGV) Log.v("JavaBinder", "sendDeathNotice to " + recipient);
+ if (false) Log.v("JavaBinder", "sendDeathNotice to " + recipient);
try {
recipient.binderDied();
}
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 8735019..9accf99 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -235,6 +235,11 @@ public class Build {
* Current development version.
*/
public static final int HONEYCOMB_MR2 = CUR_DEVELOPMENT;
+
+ /**
+ * Current version under development.
+ */
+ public static final int ICE_CREAM_SANDWICH = CUR_DEVELOPMENT;
}
/** The type of build, like "user" or "eng". */
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index 87aeccb..ba69246 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -18,7 +18,6 @@ package android.os;
import com.android.internal.util.TypedProperties;
-import android.util.Config;
import android.util.Log;
import java.io.FileDescriptor;
@@ -1031,7 +1030,7 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo
* Load the debug properties from the standard files into debugProperties.
*/
static {
- if (Config.DEBUG) {
+ if (false) {
final String TAG = "DebugProperties";
final String[] files = { "/system/debug.prop", "/debug.prop", "/data/debug.prop" };
final TypedProperties tp = new TypedProperties();
@@ -1157,10 +1156,10 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo
/**
* Reflectively sets static fields of a class based on internal debugging
- * properties. This method is a no-op if android.util.Config.DEBUG is
+ * properties. This method is a no-op if false is
* false.
* <p>
- * <strong>NOTE TO APPLICATION DEVELOPERS</strong>: Config.DEBUG will
+ * <strong>NOTE TO APPLICATION DEVELOPERS</strong>: false will
* always be false in release builds. This API is typically only useful
* for platform developers.
* </p>
@@ -1211,7 +1210,7 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo
* the internal debugging property value.
*/
public static void setFieldsOn(Class<?> cl, boolean partial) {
- if (Config.DEBUG) {
+ if (false) {
if (debugProperties != null) {
/* Only look for fields declared directly by the class,
* so we don't mysteriously change static fields in superclasses.
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 1f3f6d9..6b58877 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -16,13 +16,13 @@
package android.os;
-import java.io.File;
-
import android.content.res.Resources;
import android.os.storage.IMountService;
import android.os.storage.StorageVolume;
import android.util.Log;
+import java.io.File;
+
/**
* Provides access to environment variables.
*/
@@ -357,54 +357,54 @@ public class Environment {
}
/**
- * getExternalStorageState() returns MEDIA_REMOVED if the media is not present.
+ * {@link #getExternalStorageState()} returns MEDIA_REMOVED if the media is not present.
*/
public static final String MEDIA_REMOVED = "removed";
/**
- * getExternalStorageState() returns MEDIA_UNMOUNTED if the media is present
+ * {@link #getExternalStorageState()} returns MEDIA_UNMOUNTED if the media is present
* but not mounted.
*/
public static final String MEDIA_UNMOUNTED = "unmounted";
/**
- * getExternalStorageState() returns MEDIA_CHECKING if the media is present
+ * {@link #getExternalStorageState()} returns MEDIA_CHECKING if the media is present
* and being disk-checked
*/
public static final String MEDIA_CHECKING = "checking";
/**
- * getExternalStorageState() returns MEDIA_NOFS if the media is present
+ * {@link #getExternalStorageState()} returns MEDIA_NOFS if the media is present
* but is blank or is using an unsupported filesystem
*/
public static final String MEDIA_NOFS = "nofs";
/**
- * getExternalStorageState() returns MEDIA_MOUNTED if the media is present
+ * {@link #getExternalStorageState()} returns MEDIA_MOUNTED if the media is present
* and mounted at its mount point with read/write access.
*/
public static final String MEDIA_MOUNTED = "mounted";
/**
- * getExternalStorageState() returns MEDIA_MOUNTED_READ_ONLY if the media is present
+ * {@link #getExternalStorageState()} returns MEDIA_MOUNTED_READ_ONLY if the media is present
* and mounted at its mount point with read only access.
*/
public static final String MEDIA_MOUNTED_READ_ONLY = "mounted_ro";
/**
- * getExternalStorageState() returns MEDIA_SHARED if the media is present
+ * {@link #getExternalStorageState()} returns MEDIA_SHARED if the media is present
* not mounted, and shared via USB mass storage.
*/
public static final String MEDIA_SHARED = "shared";
/**
- * getExternalStorageState() returns MEDIA_BAD_REMOVAL if the media was
+ * {@link #getExternalStorageState()} returns MEDIA_BAD_REMOVAL if the media was
* removed before it was unmounted.
*/
public static final String MEDIA_BAD_REMOVAL = "bad_removal";
/**
- * getExternalStorageState() returns MEDIA_UNMOUNTABLE if the media is present
+ * {@link #getExternalStorageState()} returns MEDIA_UNMOUNTABLE if the media is present
* but cannot be mounted. Typically this happens if the file system on the
* media is corrupted.
*/
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index 5a245f8..f17a6f2 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -19,6 +19,7 @@ package android.os;
import android.net.InterfaceConfiguration;
import android.net.INetworkManagementEventObserver;
+import android.net.NetworkStats;
import android.net.RouteInfo;
import android.net.wifi.WifiConfiguration;
@@ -82,7 +83,6 @@ interface INetworkManagementService
** TETHERING RELATED
**/
-
/**
* Returns true if IP forwarding is enabled
*/
@@ -198,17 +198,29 @@ interface INetworkManagementService
void setAccessPoint(in WifiConfiguration wifiConfig, String wlanIface, String softapIface);
/**
- * Read number of bytes sent over an interface
+ ** DATA USAGE RELATED
+ **/
+
+ /**
+ * Return global network statistics summarized at an interface level,
+ * without any UID-level granularity.
+ */
+ NetworkStats getNetworkStatsSummary();
+
+ /**
+ * Return detailed network statistics with UID-level granularity,
+ * including interface and tag details.
*/
- long getInterfaceTxCounter(String iface);
+ NetworkStats getNetworkStatsDetail();
/**
- * Read number of bytes received over an interface
+ * Return detailed network statistics for the requested UID,
+ * including interface and tag details.
*/
- long getInterfaceRxCounter(String iface);
+ NetworkStats getNetworkStatsUidDetail(int uid);
/**
- * Configures bandwidth throttling on an interface
+ * Configures bandwidth throttling on an interface.
*/
void setInterfaceThrottle(String iface, int rxKbps, int txKbps);
diff --git a/core/java/android/os/Looper.java b/core/java/android/os/Looper.java
index ccf642c..3edd692 100644
--- a/core/java/android/os/Looper.java
+++ b/core/java/android/os/Looper.java
@@ -16,7 +16,6 @@
package android.os;
-import android.util.Config;
import android.util.Log;
import android.util.Printer;
import android.util.PrefixPrinter;
diff --git a/core/java/android/os/MemoryFile.java b/core/java/android/os/MemoryFile.java
index f82702a..e8148f7 100644
--- a/core/java/android/os/MemoryFile.java
+++ b/core/java/android/os/MemoryFile.java
@@ -28,7 +28,7 @@ import java.io.OutputStream;
* MemoryFile is a wrapper for the Linux ashmem driver.
* MemoryFiles are backed by shared memory, which can be optionally
* set to be purgeable.
- * Purgeable files may have their contents reclaimed by the kernel
+ * Purgeable files may have their contents reclaimed by the kernel
* in low memory conditions (only if allowPurging is set to true).
* After a file is purged, attempts to read or write the file will
* cause an IOException to be thrown.
@@ -126,7 +126,7 @@ public class MemoryFile
close();
}
}
-
+
/**
* Returns the length of the memory file.
*
@@ -190,7 +190,7 @@ public class MemoryFile
* @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)
+ 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.");
@@ -330,6 +330,7 @@ public class MemoryFile
@Override
public void write(byte buffer[], int offset, int count) throws IOException {
writeBytes(buffer, offset, mOffset, count);
+ mOffset += count;
}
@Override
diff --git a/core/java/android/os/MessageQueue.java b/core/java/android/os/MessageQueue.java
index bb07825..a658fc4 100644
--- a/core/java/android/os/MessageQueue.java
+++ b/core/java/android/os/MessageQueue.java
@@ -17,7 +17,6 @@
package android.os;
import android.util.AndroidRuntimeException;
-import android.util.Config;
import android.util.Log;
import java.util.ArrayList;
@@ -128,7 +127,7 @@ public class MessageQueue {
mBlocked = false;
mMessages = msg.next;
msg.next = null;
- if (Config.LOGV) Log.v("MessageQueue", "Returning message: " + msg);
+ if (false) Log.v("MessageQueue", "Returning message: " + msg);
msg.markInUse();
return msg;
} else {
diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java
index 0f1354b..3ea3f56 100644
--- a/core/java/android/os/ParcelFileDescriptor.java
+++ b/core/java/android/os/ParcelFileDescriptor.java
@@ -21,6 +21,7 @@ import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.net.DatagramSocket;
import java.net.Socket;
/**
@@ -35,64 +36,64 @@ public class ParcelFileDescriptor implements Parcelable {
//consider ParcelFileDescriptor A(fileDescriptor fd), ParcelFileDescriptor B(A)
//in this particular case fd.close might be invoked twice.
private final ParcelFileDescriptor mParcelDescriptor;
-
+
/**
* For use with {@link #open}: if {@link #MODE_CREATE} has been supplied
* and this file doesn't already exist, then create the file with
* permissions such that any application can read it.
*/
public static final int MODE_WORLD_READABLE = 0x00000001;
-
+
/**
* For use with {@link #open}: if {@link #MODE_CREATE} has been supplied
* and this file doesn't already exist, then create the file with
* permissions such that any application can write it.
*/
public static final int MODE_WORLD_WRITEABLE = 0x00000002;
-
+
/**
* For use with {@link #open}: open the file with read-only access.
*/
public static final int MODE_READ_ONLY = 0x10000000;
-
+
/**
* For use with {@link #open}: open the file with write-only access.
*/
public static final int MODE_WRITE_ONLY = 0x20000000;
-
+
/**
* For use with {@link #open}: open the file with read and write access.
*/
public static final int MODE_READ_WRITE = 0x30000000;
-
+
/**
* For use with {@link #open}: create the file if it doesn't already exist.
*/
public static final int MODE_CREATE = 0x08000000;
-
+
/**
* For use with {@link #open}: erase contents of file when opening.
*/
public static final int MODE_TRUNCATE = 0x04000000;
-
+
/**
* For use with {@link #open}: append to end of file while writing.
*/
public static final int MODE_APPEND = 0x02000000;
-
+
/**
* Create a new ParcelFileDescriptor accessing a given file.
- *
+ *
* @param file The file to be opened.
* @param mode The desired access mode, must be one of
* {@link #MODE_READ_ONLY}, {@link #MODE_WRITE_ONLY}, or
* {@link #MODE_READ_WRITE}; may also be any combination of
* {@link #MODE_CREATE}, {@link #MODE_TRUNCATE},
* {@link #MODE_WORLD_READABLE}, and {@link #MODE_WORLD_WRITEABLE}.
- *
+ *
* @return Returns a new ParcelFileDescriptor pointing to the given
* file.
- *
+ *
* @throws FileNotFoundException Throws FileNotFoundException if the given
* file does not exist or can not be opened with the requested mode.
*/
@@ -106,12 +107,12 @@ public class ParcelFileDescriptor implements Parcelable {
security.checkWrite(path);
}
}
-
+
if ((mode&MODE_READ_WRITE) == 0) {
throw new IllegalArgumentException(
"Must specify MODE_READ_ONLY, MODE_WRITE_ONLY, or MODE_READ_WRITE");
}
-
+
FileDescriptor fd = Parcel.openFileDescriptor(path, mode);
return fd != null ? new ParcelFileDescriptor(fd) : null;
}
@@ -176,12 +177,23 @@ public class ParcelFileDescriptor implements Parcelable {
* specified Socket.
*/
public static ParcelFileDescriptor fromSocket(Socket socket) {
- FileDescriptor fd = getFileDescriptorFromSocket(socket);
+ FileDescriptor fd = socket.getFileDescriptor$();
return fd != null ? new ParcelFileDescriptor(fd) : null;
}
- // Extracts the file descriptor from the specified socket and returns it untouched
- private static native FileDescriptor getFileDescriptorFromSocket(Socket socket);
+ /**
+ * Create a new ParcelFileDescriptor from the specified DatagramSocket.
+ *
+ * @param datagramSocket The DatagramSocket whose FileDescriptor is used
+ * to create a new ParcelFileDescriptor.
+ *
+ * @return A new ParcelFileDescriptor with the FileDescriptor of the
+ * specified DatagramSocket.
+ */
+ public static ParcelFileDescriptor fromDatagramSocket(DatagramSocket datagramSocket) {
+ FileDescriptor fd = datagramSocket.getFileDescriptor$();
+ return fd != null ? new ParcelFileDescriptor(fd) : null;
+ }
/**
* Create two ParcelFileDescriptors structured as a data pipe. The first
@@ -223,26 +235,26 @@ public class ParcelFileDescriptor implements Parcelable {
/**
* Retrieve the actual FileDescriptor associated with this object.
- *
+ *
* @return Returns the FileDescriptor associated with this object.
*/
public FileDescriptor getFileDescriptor() {
return mFileDescriptor;
}
-
+
/**
* Return the total size of the file representing this fd, as determined
* by stat(). Returns -1 if the fd is not a file.
*/
public native long getStatSize();
-
+
/**
* This is needed for implementing AssetFileDescriptor.AutoCloseOutputStream,
* and I really don't think we want it to be public.
* @hide
*/
public native long seekTo(long pos);
-
+
/**
* Return the native fd int for this ParcelFileDescriptor. The
* ParcelFileDescriptor still owns the fd, and it still must be closed
@@ -254,9 +266,9 @@ public class ParcelFileDescriptor implements Parcelable {
}
return getFdNative();
}
-
+
private native int getFdNative();
-
+
/**
* Return the native fd int for this ParcelFileDescriptor and detach it
* from the object here. You are now responsible for closing the fd in
@@ -276,11 +288,11 @@ public class ParcelFileDescriptor implements Parcelable {
Parcel.clearFileDescriptor(mFileDescriptor);
return fd;
}
-
+
/**
* Close the ParcelFileDescriptor. This implementation closes the underlying
* OS resources allocated to represent this stream.
- *
+ *
* @throws IOException
* If an error occurs attempting to close this ParcelFileDescriptor.
*/
@@ -297,7 +309,7 @@ public class ParcelFileDescriptor implements Parcelable {
Parcel.closeFileDescriptor(mFileDescriptor);
}
}
-
+
/**
* An InputStream you can create on a ParcelFileDescriptor, which will
* take care of calling {@link ParcelFileDescriptor#close
@@ -305,7 +317,7 @@ public class ParcelFileDescriptor implements Parcelable {
*/
public static class AutoCloseInputStream extends FileInputStream {
private final ParcelFileDescriptor mFd;
-
+
public AutoCloseInputStream(ParcelFileDescriptor fd) {
super(fd.getFileDescriptor());
mFd = fd;
@@ -320,7 +332,7 @@ public class ParcelFileDescriptor implements Parcelable {
}
}
}
-
+
/**
* An OutputStream you can create on a ParcelFileDescriptor, which will
* take care of calling {@link ParcelFileDescriptor#close
@@ -328,7 +340,7 @@ public class ParcelFileDescriptor implements Parcelable {
*/
public static class AutoCloseOutputStream extends FileOutputStream {
private final ParcelFileDescriptor mFd;
-
+
public AutoCloseOutputStream(ParcelFileDescriptor fd) {
super(fd.getFileDescriptor());
mFd = fd;
@@ -343,12 +355,12 @@ public class ParcelFileDescriptor implements Parcelable {
}
}
}
-
+
@Override
public String toString() {
return "{ParcelFileDescriptor: " + mFileDescriptor + "}";
}
-
+
@Override
protected void finalize() throws Throwable {
try {
@@ -359,13 +371,13 @@ public class ParcelFileDescriptor implements Parcelable {
super.finalize();
}
}
-
+
public ParcelFileDescriptor(ParcelFileDescriptor descriptor) {
super();
mParcelDescriptor = descriptor;
mFileDescriptor = mParcelDescriptor.mFileDescriptor;
}
-
+
/*package */ParcelFileDescriptor(FileDescriptor descriptor) {
super();
if (descriptor == null) {
@@ -374,7 +386,7 @@ public class ParcelFileDescriptor implements Parcelable {
mFileDescriptor = descriptor;
mParcelDescriptor = null;
}
-
+
/* Parcelable interface */
public int describeContents() {
return Parcelable.CONTENTS_FILE_DESCRIPTOR;
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 78275a4..a17983a 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -97,7 +97,8 @@ import android.util.Log;
* </tbody>
* </table>
*
- *
+ * Any application using a WakeLock must request the {@code android.permission.WAKE_LOCK}
+ * permission in an {@code &lt;uses-permission&gt;} element of the application's manifest.
*/
public class PowerManager
{
@@ -199,8 +200,11 @@ public class PowerManager
/**
* Class lets you say that you need to have the device on.
- *
- * <p>Call release when you are done and don't need the lock anymore.
+ * <p>
+ * Call release when you are done and don't need the lock anymore.
+ * <p>
+ * Any application using a WakeLock must request the {@code android.permission.WAKE_LOCK}
+ * permission in an {@code &lt;uses-permission&gt;} element of the application's manifest.
*/
public class WakeLock
{
@@ -257,16 +261,10 @@ public class PowerManager
public void acquire()
{
synchronized (mToken) {
- if (!mRefCounted || mCount++ == 0) {
- try {
- mService.acquireWakeLock(mFlags, mToken, mTag, mWorkSource);
- } catch (RemoteException e) {
- }
- mHeld = true;
- }
+ acquireLocked();
}
}
-
+
/**
* Makes sure the device is on at the level you asked when you created
* the wake lock. The lock will be released after the given timeout.
@@ -274,10 +272,22 @@ public class PowerManager
* @param timeout Release the lock after the give timeout in milliseconds.
*/
public void acquire(long timeout) {
- acquire();
- mHandler.postDelayed(mReleaser, timeout);
+ synchronized (mToken) {
+ acquireLocked();
+ mHandler.postDelayed(mReleaser, timeout);
+ }
}
+ private void acquireLocked() {
+ if (!mRefCounted || mCount++ == 0) {
+ mHandler.removeCallbacks(mReleaser);
+ try {
+ mService.acquireWakeLock(mFlags, mToken, mTag, mWorkSource);
+ } catch (RemoteException e) {
+ }
+ mHeld = true;
+ }
+ }
/**
* Release your claim to the CPU or screen being on.
@@ -286,8 +296,7 @@ public class PowerManager
* It may turn off shortly after you release it, or it may not if there
* are other wake locks held.
*/
- public void release()
- {
+ public void release() {
release(0);
}
@@ -302,9 +311,9 @@ public class PowerManager
*
* {@hide}
*/
- public void release(int flags)
- {
+ public void release(int flags) {
synchronized (mToken) {
+ mHandler.removeCallbacks(mReleaser);
if (!mRefCounted || --mCount == 0) {
try {
mService.releaseWakeLock(mToken, flags);
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 2bfada0..f85df6c 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -92,6 +92,12 @@ public class Process {
public static final int SDCARD_RW_GID = 1015;
/**
+ * Defines the UID for the KeyChain service.
+ * @hide
+ */
+ public static final int KEYCHAIN_UID = 1020;
+
+ /**
* Defines the UID/GID for the NFC service process.
* @hide
*/
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index c1dd911..ae605fb 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -70,7 +70,7 @@ public class RecoverySystem {
private static File RECOVERY_DIR = new File("/cache/recovery");
private static File COMMAND_FILE = new File(RECOVERY_DIR, "command");
private static File LOG_FILE = new File(RECOVERY_DIR, "log");
- private static String LAST_LOG_FILENAME = "last_log";
+ private static String LAST_PREFIX = "last_";
// Length limits for reading files.
private static int LOG_FILE_MAX_LENGTH = 64 * 1024;
@@ -415,10 +415,11 @@ public class RecoverySystem {
Log.e(TAG, "Error reading recovery log", e);
}
- // Delete everything in RECOVERY_DIR except LAST_LOG_FILENAME
+ // Delete everything in RECOVERY_DIR except those beginning
+ // with LAST_PREFIX
String[] names = RECOVERY_DIR.list();
for (int i = 0; names != null && i < names.length; i++) {
- if (names[i].equals(LAST_LOG_FILENAME)) continue;
+ if (names[i].startsWith(LAST_PREFIX)) continue;
File f = new File(RECOVERY_DIR, names[i]);
if (!f.delete()) {
Log.e(TAG, "Can't delete: " + f);
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index 1375a29..01c640a 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -1481,6 +1481,13 @@ public final class StrictMode {
onVmPolicyViolation(message, originStack);
}
+ /**
+ * @hide
+ */
+ public static void onWebViewMethodCalledOnWrongThread(Throwable originStack) {
+ onVmPolicyViolation(null, originStack);
+ }
+
// Map from VM violation fingerprint to uptime millis.
private static final HashMap<Integer, Long> sLastVmViolationTime = new HashMap<Integer, Long>();
diff --git a/core/java/android/pim/ICalendar.java b/core/java/android/pim/ICalendar.java
index 9c4eaf4..58c5c63 100644
--- a/core/java/android/pim/ICalendar.java
+++ b/core/java/android/pim/ICalendar.java
@@ -17,7 +17,6 @@
package android.pim;
import android.util.Log;
-import android.util.Config;
import java.util.LinkedHashMap;
import java.util.LinkedList;
@@ -447,7 +446,7 @@ public class ICalendar {
component = current;
}
} catch (FormatException fe) {
- if (Config.LOGV) {
+ if (false) {
Log.v(TAG, "Cannot parse " + line, fe);
}
// for now, we ignore the parse error. Google Calendar seems
diff --git a/core/java/android/pim/RecurrenceSet.java b/core/java/android/pim/RecurrenceSet.java
index 282417d..fdd0783 100644
--- a/core/java/android/pim/RecurrenceSet.java
+++ b/core/java/android/pim/RecurrenceSet.java
@@ -21,7 +21,6 @@ import android.database.Cursor;
import android.provider.Calendar;
import android.text.TextUtils;
import android.text.format.Time;
-import android.util.Config;
import android.util.Log;
import java.util.List;
@@ -197,7 +196,7 @@ public class RecurrenceSet {
(TextUtils.isEmpty(duration))||
((TextUtils.isEmpty(rrule))&&
(TextUtils.isEmpty(rdate)))) {
- if (Config.LOGD) {
+ if (false) {
Log.d(TAG, "Recurrence missing DTSTART, DTEND/DURATION, "
+ "or RRULE/RDATE: "
+ component.toString());
@@ -211,7 +210,7 @@ public class RecurrenceSet {
long millis = start.toMillis(false /* use isDst */);
values.put(Calendar.Events.DTSTART, millis);
if (millis == -1) {
- if (Config.LOGD) {
+ if (false) {
Log.d(TAG, "DTSTART is out of range: " + component.toString());
}
return false;
diff --git a/core/java/android/preference/MultiSelectListPreference.java b/core/java/android/preference/MultiSelectListPreference.java
index 42d555c..2e8d551 100644
--- a/core/java/android/preference/MultiSelectListPreference.java
+++ b/core/java/android/preference/MultiSelectListPreference.java
@@ -169,9 +169,9 @@ public class MultiSelectListPreference extends DialogPreference {
new DialogInterface.OnMultiChoiceClickListener() {
public void onClick(DialogInterface dialog, int which, boolean isChecked) {
if (isChecked) {
- mPreferenceChanged |= mNewValues.add(mEntries[which].toString());
+ mPreferenceChanged |= mNewValues.add(mEntryValues[which].toString());
} else {
- mPreferenceChanged |= mNewValues.remove(mEntries[which].toString());
+ mPreferenceChanged |= mNewValues.remove(mEntryValues[which].toString());
}
}
});
@@ -180,7 +180,7 @@ public class MultiSelectListPreference extends DialogPreference {
}
private boolean[] getSelectedItems() {
- final CharSequence[] entries = mEntries;
+ final CharSequence[] entries = mEntryValues;
final int entryCount = entries.length;
final Set<String> values = mValues;
boolean[] result = new boolean[entryCount];
diff --git a/core/java/android/preference/Preference.java b/core/java/android/preference/Preference.java
index 7d37e5b..5e1be21 100644
--- a/core/java/android/preference/Preference.java
+++ b/core/java/android/preference/Preference.java
@@ -89,6 +89,7 @@ public class Preference implements Comparable<Preference>, OnDependencyChangeLis
private int mOrder = DEFAULT_ORDER;
private CharSequence mTitle;
+ private int mTitleRes;
private CharSequence mSummary;
/**
* mIconResId is overridden by mIcon, if mIcon is specified.
@@ -214,6 +215,7 @@ public class Preference implements Comparable<Preference>, OnDependencyChangeLis
break;
case com.android.internal.R.styleable.Preference_title:
+ mTitleRes = a.getResourceId(attr, 0);
mTitle = a.getString(attr);
break;
@@ -582,6 +584,7 @@ public class Preference implements Comparable<Preference>, OnDependencyChangeLis
*/
public void setTitle(CharSequence title) {
if (title == null && mTitle != null || title != null && !title.equals(mTitle)) {
+ mTitleRes = 0;
mTitle = title;
notifyChanged();
}
@@ -595,9 +598,21 @@ public class Preference implements Comparable<Preference>, OnDependencyChangeLis
*/
public void setTitle(int titleResId) {
setTitle(mContext.getString(titleResId));
+ mTitleRes = titleResId;
}
/**
+ * Returns the title resource ID of this Preference. If the title did
+ * not come from a resource, 0 is returned.
+ *
+ * @return The title resource.
+ * @see #setTitle(int)
+ */
+ public int getTitleRes() {
+ return mTitleRes;
+ }
+
+ /**
* Returns the title of this Preference.
*
* @return The title.
diff --git a/core/java/android/preference/PreferenceActivity.java b/core/java/android/preference/PreferenceActivity.java
index ad0bc84..15d5898 100644
--- a/core/java/android/preference/PreferenceActivity.java
+++ b/core/java/android/preference/PreferenceActivity.java
@@ -132,13 +132,28 @@ public abstract class PreferenceActivity extends ListActivity implements
/**
* When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT},
- * this extra can also be specify to supply a Bundle of arguments to pass
+ * this extra can also be specified to supply a Bundle of arguments to pass
* to that fragment when it is instantiated during the initial creation
* of PreferenceActivity.
*/
public static final String EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":android:show_fragment_args";
/**
+ * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT},
+ * this extra can also be specify to supply the title to be shown for
+ * that fragment.
+ */
+ public static final String EXTRA_SHOW_FRAGMENT_TITLE = ":android:show_fragment_title";
+
+ /**
+ * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT},
+ * this extra can also be specify to supply the short title to be shown for
+ * that fragment.
+ */
+ public static final String EXTRA_SHOW_FRAGMENT_SHORT_TITLE
+ = ":android:show_fragment_short_title";
+
+ /**
* When starting this activity, the invoking Intent can contain this extra
* boolean that the header list should not be displayed. This is most often
* used in conjunction with {@link #EXTRA_SHOW_FRAGMENT} to launch
@@ -488,7 +503,12 @@ public abstract class PreferenceActivity extends ListActivity implements
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setContentView(com.android.internal.R.layout.preference_list_content);
+ if (getResources().getConfiguration().isLayoutSizeAtLeast(
+ Configuration.SCREENLAYOUT_SIZE_LARGE)) {
+ setContentView(com.android.internal.R.layout.preference_list_content_large);
+ } else {
+ setContentView(com.android.internal.R.layout.preference_list_content);
+ }
mListFooter = (FrameLayout)findViewById(com.android.internal.R.id.list_footer);
mPrefsContainer = (ViewGroup) findViewById(com.android.internal.R.id.prefs_frame);
@@ -496,6 +516,8 @@ public abstract class PreferenceActivity extends ListActivity implements
mSinglePane = hidingHeaders || !onIsMultiPane();
String initialFragment = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT);
Bundle initialArguments = getIntent().getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
+ int initialTitle = getIntent().getIntExtra(EXTRA_SHOW_FRAGMENT_TITLE, 0);
+ int initialShortTitle = getIntent().getIntExtra(EXTRA_SHOW_FRAGMENT_SHORT_TITLE, 0);
if (savedInstanceState != null) {
// We are restarting from a previous saved state; used that to
@@ -516,6 +538,12 @@ public abstract class PreferenceActivity extends ListActivity implements
// new fragment mode, but don't need to compute and show
// the headers.
switchToHeader(initialFragment, initialArguments);
+ if (initialTitle != 0) {
+ CharSequence initialTitleStr = getText(initialTitle);
+ CharSequence initialShortTitleStr = initialShortTitle != 0
+ ? getText(initialShortTitle) : null;
+ showBreadCrumbs(initialTitleStr, initialShortTitleStr);
+ }
} else {
// We need to try to build the headers.
@@ -557,7 +585,12 @@ public abstract class PreferenceActivity extends ListActivity implements
} else {
// If there are no headers, we are in the old "just show a screen
// of preferences" mode.
- setContentView(com.android.internal.R.layout.preference_list_content_single);
+ if (getResources().getConfiguration().isLayoutSizeAtLeast(
+ Configuration.SCREENLAYOUT_SIZE_LARGE)) {
+ setContentView(com.android.internal.R.layout.preference_list_content_single_large);
+ } else {
+ setContentView(com.android.internal.R.layout.preference_list_content_single);
+ }
mListFooter = (FrameLayout) findViewById(com.android.internal.R.id.list_footer);
mPrefsContainer = (ViewGroup) findViewById(com.android.internal.R.id.prefs);
mPreferenceManager = new PreferenceManager(this, FIRST_REQUEST_CODE);
@@ -942,7 +975,8 @@ public abstract class PreferenceActivity extends ListActivity implements
/**
* Called when the user selects an item in the header list. The default
- * implementation will call either {@link #startWithFragment(String, Bundle, Fragment, int)}
+ * implementation will call either
+ * {@link #startWithFragment(String, Bundle, Fragment, int, int, int)}
* or {@link #switchToHeader(Header)} as appropriate.
*
* @param header The header that was selected.
@@ -951,7 +985,14 @@ public abstract class PreferenceActivity extends ListActivity implements
public void onHeaderClick(Header header, int position) {
if (header.fragment != null) {
if (mSinglePane) {
- startWithFragment(header.fragment, header.fragmentArguments, null, 0);
+ int titleRes = header.breadCrumbTitleRes;
+ int shortTitleRes = header.breadCrumbShortTitleRes;
+ if (titleRes == 0) {
+ titleRes = header.titleRes;
+ shortTitleRes = 0;
+ }
+ startWithFragment(header.fragment, header.fragmentArguments, null, 0,
+ titleRes, shortTitleRes);
} else {
switchToHeader(header);
}
@@ -961,6 +1002,41 @@ public abstract class PreferenceActivity extends ListActivity implements
}
/**
+ * Called by {@link #startWithFragment(String, Bundle, Fragment, int, int, int)} when
+ * in single-pane mode, to build an Intent to launch a new activity showing
+ * the selected fragment. The default implementation constructs an Intent
+ * that re-launches the current activity with the appropriate arguments to
+ * display the fragment.
+ *
+ * @param fragmentName The name of the fragment to display.
+ * @param args Optional arguments to supply to the fragment.
+ * @param titleRes Optional resource ID of title to show for this item.
+ * @param titleRes Optional resource ID of short title to show for this item.
+ * @return Returns an Intent that can be launched to display the given
+ * fragment.
+ */
+ public Intent onBuildStartFragmentIntent(String fragmentName, Bundle args,
+ int titleRes, int shortTitleRes) {
+ Intent intent = new Intent(Intent.ACTION_MAIN);
+ intent.setClass(this, getClass());
+ intent.putExtra(EXTRA_SHOW_FRAGMENT, fragmentName);
+ intent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, args);
+ intent.putExtra(EXTRA_SHOW_FRAGMENT_TITLE, titleRes);
+ intent.putExtra(EXTRA_SHOW_FRAGMENT_SHORT_TITLE, shortTitleRes);
+ intent.putExtra(EXTRA_NO_HEADERS, true);
+ return intent;
+ }
+
+ /**
+ * Like {@link #startWithFragment(String, Bundle, Fragment, int, int, int)}
+ * but uses a 0 titleRes.
+ */
+ public void startWithFragment(String fragmentName, Bundle args,
+ Fragment resultTo, int resultRequestCode) {
+ startWithFragment(fragmentName, args, resultTo, resultRequestCode, 0, 0);
+ }
+
+ /**
* Start a new instance of this activity, showing only the given
* preference fragment. When launched in this mode, the header list
* will be hidden and the given preference fragment will be instantiated
@@ -968,14 +1044,18 @@ public abstract class PreferenceActivity extends ListActivity implements
*
* @param fragmentName The name of the fragment to display.
* @param args Optional arguments to supply to the fragment.
+ * @param resultTo Option fragment that should receive the result of
+ * the activity launch.
+ * @param resultRequestCode If resultTo is non-null, this is the request
+ * code in which to report the result.
+ * @param titleRes Resource ID of string to display for the title of
+ * this set of preferences.
+ * @param titleRes Resource ID of string to display for the short title of
+ * this set of preferences.
*/
public void startWithFragment(String fragmentName, Bundle args,
- Fragment resultTo, int resultRequestCode) {
- Intent intent = new Intent(Intent.ACTION_MAIN);
- intent.setClass(this, getClass());
- intent.putExtra(EXTRA_SHOW_FRAGMENT, fragmentName);
- intent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, args);
- intent.putExtra(EXTRA_NO_HEADERS, true);
+ Fragment resultTo, int resultRequestCode, int titleRes, int shortTitleRes) {
+ Intent intent = onBuildStartFragmentIntent(fragmentName, args, titleRes, shortTitleRes);
if (resultTo == null) {
startActivity(intent);
} else {
@@ -992,16 +1072,16 @@ public abstract class PreferenceActivity extends ListActivity implements
if (mFragmentBreadCrumbs == null) {
View crumbs = findViewById(android.R.id.title);
// For screens with a different kind of title, don't create breadcrumbs.
- if (!(crumbs instanceof FragmentBreadCrumbs)) return;
- mFragmentBreadCrumbs = (FragmentBreadCrumbs) findViewById(android.R.id.title);
+ try {
+ mFragmentBreadCrumbs = (FragmentBreadCrumbs)crumbs;
+ } catch (ClassCastException e) {
+ return;
+ }
if (mFragmentBreadCrumbs == null) {
- mFragmentBreadCrumbs = new FragmentBreadCrumbs(this);
- ActionBar actionBar = getActionBar();
- if (actionBar != null) {
- actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM,
- ActionBar.DISPLAY_SHOW_TITLE | ActionBar.DISPLAY_SHOW_CUSTOM);
- actionBar.setCustomView(mFragmentBreadCrumbs);
+ if (title != null) {
+ setTitle(title);
}
+ return;
}
mFragmentBreadCrumbs.setMaxVisible(2);
mFragmentBreadCrumbs.setActivity(this);
@@ -1169,7 +1249,7 @@ public abstract class PreferenceActivity extends ListActivity implements
public void startPreferencePanel(String fragmentClass, Bundle args, int titleRes,
CharSequence titleText, Fragment resultTo, int resultRequestCode) {
if (mSinglePane) {
- startWithFragment(fragmentClass, args, resultTo, resultRequestCode);
+ startWithFragment(fragmentClass, args, resultTo, resultRequestCode, titleRes, 0);
} else {
Fragment f = Fragment.instantiate(this, fragmentClass, args);
if (resultTo != null) {
@@ -1215,7 +1295,8 @@ public abstract class PreferenceActivity extends ListActivity implements
@Override
public boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref) {
- startPreferencePanel(pref.getFragment(), pref.getExtras(), 0, pref.getTitle(), null, 0);
+ startPreferencePanel(pref.getFragment(), pref.getExtras(), pref.getTitleRes(),
+ pref.getTitle(), null, 0);
return true;
}
diff --git a/core/java/android/preference/PreferenceFragment.java b/core/java/android/preference/PreferenceFragment.java
index 4e22ba0..7511e14 100644
--- a/core/java/android/preference/PreferenceFragment.java
+++ b/core/java/android/preference/PreferenceFragment.java
@@ -20,6 +20,7 @@ import android.app.Activity;
import android.app.Fragment;
import android.content.Intent;
import android.content.SharedPreferences;
+import android.content.res.Configuration;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
@@ -151,8 +152,14 @@ public abstract class PreferenceFragment extends Fragment implements
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
- return inflater.inflate(com.android.internal.R.layout.preference_list_fragment,
- container, false);
+ if (getResources().getConfiguration().isLayoutSizeAtLeast(
+ Configuration.SCREENLAYOUT_SIZE_LARGE)) {
+ return inflater.inflate(com.android.internal.R.layout.preference_list_fragment_large,
+ container, false);
+ } else {
+ return inflater.inflate(com.android.internal.R.layout.preference_list_fragment,
+ container, false);
+ }
}
@Override
diff --git a/core/java/android/provider/BrowserContract.java b/core/java/android/provider/BrowserContract.java
index 03bc41a..d678205 100644
--- a/core/java/android/provider/BrowserContract.java
+++ b/core/java/android/provider/BrowserContract.java
@@ -332,6 +332,13 @@ public class BrowserContract {
* <P>Type: TEXT</P>
*/
public static final String ACCOUNT_TYPE = "account_type";
+
+ /**
+ * The ID of the account's root folder. This will be the ID of the folder
+ * returned when querying {@link Bookmarks#CONTENT_URI_DEFAULT_FOLDER}.
+ * <P>Type: INTEGER</P>
+ */
+ public static final String ROOT_ID = "root_id";
}
/**
diff --git a/core/java/android/provider/Calendar.java b/core/java/android/provider/Calendar.java
index de71763..4141879 100644
--- a/core/java/android/provider/Calendar.java
+++ b/core/java/android/provider/Calendar.java
@@ -98,6 +98,8 @@ public final class Calendar {
public static final String SYNC4 = "sync4";
/** Generic column for use by sync adapters. */
public static final String SYNC5 = "sync5";
+ /** Generic column for use by sync adapters. */
+ public static final String SYNC6 = "sync6";
}
/**
@@ -108,13 +110,13 @@ public final class Calendar {
* The account that was used to sync the entry to the device.
* <P>Type: TEXT</P>
*/
- public static final String _SYNC_ACCOUNT = "_sync_account";
+ public static final String ACCOUNT_NAME = "account_name";
/**
* The type of the account that was used to sync the entry to the device.
* <P>Type: TEXT</P>
*/
- public static final String _SYNC_ACCOUNT_TYPE = "_sync_account_type";
+ public static final String ACCOUNT_TYPE = "account_type";
/**
* The unique ID for a row assigned by the sync source. NULL if the row has never been synced.
@@ -135,15 +137,6 @@ public final class Calendar {
public static final String _SYNC_VERSION = "_sync_version";
/**
- * For use by sync adapter at its discretion; not modified by CalendarProvider
- * Note that this column was formerly named _SYNC_LOCAL_ID. We are using it to avoid a
- * schema change.
- * TODO Replace this with something more general in the future.
- * <P>Type: INTEGER (long)</P>
- */
- public static final String _SYNC_DATA = "_sync_local_id";
-
- /**
* Used only in persistent providers, and only during merging.
* <P>Type: INTEGER (long)</P>
*/
@@ -153,30 +146,11 @@ public final class Calendar {
* Used to indicate that local, unsynced, changes are present.
* <P>Type: INTEGER (long)</P>
*/
- public static final String _SYNC_DIRTY = "_sync_dirty";
+ public static final String DIRTY = "dirty";
}
/**
- * Columns from the Account information used by Calendars and Events tables.
- */
- public interface AccountColumns {
- /**
- * The name of the account instance to which this row belongs, which when paired with
- * {@link #ACCOUNT_TYPE} identifies a specific account.
- * <P>Type: TEXT</P>
- */
- public static final String ACCOUNT_NAME = "account_name";
-
- /**
- * The type of account to which this row belongs, which when paired with
- * {@link #ACCOUNT_NAME} identifies a specific account.
- * <P>Type: TEXT</P>
- */
- public static final String ACCOUNT_TYPE = "account_type";
- }
-
- /**
* Columns from the Calendars table that other tables join into themselves.
*/
public interface CalendarsColumns {
@@ -184,7 +158,7 @@ public final class Calendar {
* The color of the calendar
* <P>Type: INTEGER (color value)</P>
*/
- public static final String COLOR = "color";
+ public static final String CALENDAR_COLOR = "calendar_color";
/**
* The level of access that the user has for the calendar
@@ -212,13 +186,13 @@ public final class Calendar {
* Is the calendar selected to be displayed?
* <P>Type: INTEGER (boolean)</P>
*/
- public static final String SELECTED = "selected";
+ public static final String VISIBLE = "visible";
/**
* The timezone the calendar's events occurs in
* <P>Type: TEXT</P>
*/
- public static final String TIMEZONE = "timezone";
+ public static final String CALENDAR_TIMEZONE = "calendar_timezone";
/**
* If this calendar is in the list of calendars that are selected for
@@ -282,35 +256,39 @@ public final class Calendar {
ContentValues cv = new ContentValues();
cv.put(_ID, calendarId);
- DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, _SYNC_ACCOUNT);
- DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, _SYNC_ACCOUNT_TYPE);
+ DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, ACCOUNT_NAME);
+ DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, ACCOUNT_TYPE);
DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, _SYNC_ID);
DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, _SYNC_VERSION);
DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, _SYNC_TIME);
- DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, _SYNC_DATA);
- DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, _SYNC_DIRTY);
- DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, _SYNC_MARK);
+ DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, DIRTY);
DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, Calendars.SYNC1);
DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, Calendars.SYNC2);
DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, Calendars.SYNC3);
DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, Calendars.SYNC4);
DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, Calendars.SYNC5);
+ DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, Calendars.SYNC6);
DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, Calendars.NAME);
DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv,
Calendars.DISPLAY_NAME);
- DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, Calendars.COLOR);
+ DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, Calendars.CALENDAR_COLOR);
DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, ACCESS_LEVEL);
- DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, SELECTED);
+ DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, VISIBLE);
DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, SYNC_EVENTS);
- DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, Calendars.LOCATION);
- DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, TIMEZONE);
+ DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv,
+ Calendars.CALENDAR_LOCATION);
+ DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, CALENDAR_TIMEZONE);
DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv,
Calendars.OWNER_ACCOUNT);
DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv,
- Calendars.ORGANIZER_CAN_RESPOND);
+ Calendars.CAN_ORGANIZER_RESPOND);
+ DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv,
+ Calendars.CAN_MODIFY_TIME_ZONE);
+ DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv,
+ Calendars.MAX_REMINDERS);
DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, DELETED);
@@ -329,11 +307,12 @@ public final class Calendar {
/**
* Contains a list of available calendars.
*/
- public static class Calendars implements BaseColumns, SyncColumns, AccountColumns,
+ public static class Calendars implements BaseColumns, SyncColumns,
CalendarsColumns
{
- private static final String WHERE_DELETE_FOR_ACCOUNT = Calendars._SYNC_ACCOUNT + "=?"
- + " AND " + Calendars._SYNC_ACCOUNT_TYPE + "=?";
+ private static final String WHERE_DELETE_FOR_ACCOUNT = Calendars.ACCOUNT_NAME + "=?"
+ + " AND "
+ + Calendars.ACCOUNT_TYPE + "=?";
public static final Cursor query(ContentResolver cr, String[] projection,
String where, String orderBy)
@@ -418,7 +397,7 @@ public final class Calendar {
* The location the of the events in the calendar
* <P>Type: TEXT</P>
*/
- public static final String LOCATION = "location";
+ public static final String CALENDAR_LOCATION = "calendar_location";
/**
* The owner account for this calendar, based on the calendar feed.
@@ -432,7 +411,56 @@ public final class Calendar {
* organizer should not be shown by the UI. Defaults to 1
* <P>Type: INTEGER (boolean)</P>
*/
- public static final String ORGANIZER_CAN_RESPOND = "organizerCanRespond";
+ public static final String CAN_ORGANIZER_RESPOND = "canOrganizerRespond";
+
+ /**
+ * Can the organizer modify the time zone of the event?
+ * <P>Type: INTEGER (boolean)</P>
+ */
+ public static final String CAN_MODIFY_TIME_ZONE = "canModifyTimeZone";
+
+ /**
+ * The maximum number of reminders allowed for an event.
+ * <P>Type: INTEGER</P>
+ */
+ public static final String MAX_REMINDERS = "maxReminders";
+
+ /**
+ * The maximum number of reminders allowed for an event.
+ * <P>
+ * Type: INTEGER
+ * </P>
+ */
+ public static final String ALLOWED_REMINDERS = "allowedReminders";
+
+ /**
+ * These fields are only writable by a sync adapter. To modify them the
+ * caller must include CALLER_IS_SYNCADAPTER, _SYNC_ACCOUNT, and
+ * _SYNC_ACCOUNT_TYPE in the query parameters.
+ */
+ public static final String[] SYNC_WRITABLE_COLUMNS = new String[] {
+ ACCOUNT_NAME,
+ ACCOUNT_TYPE,
+ _SYNC_ID,
+ _SYNC_TIME,
+ _SYNC_VERSION,
+ DIRTY,
+ OWNER_ACCOUNT,
+ MAX_REMINDERS,
+ CAN_MODIFY_TIME_ZONE,
+ CAN_ORGANIZER_RESPOND,
+ CALENDAR_LOCATION,
+ CALENDAR_TIMEZONE,
+ ACCESS_LEVEL,
+ DELETED,
+ SYNC1,
+ SYNC2,
+ SYNC3,
+ SYNC4,
+ SYNC5,
+ SYNC6,
+ SYNC_STATE,
+ };
}
/**
@@ -505,6 +533,15 @@ public final class Calendar {
*/
public interface EventsColumns {
/**
+ * For use by sync adapter at its discretion; not modified by CalendarProvider
+ * Note that this column was formerly named _SYNC_LOCAL_ID. We are using it to avoid a
+ * schema change.
+ * TODO Replace this with something more general in the future.
+ * <P>Type: INTEGER (long)</P>
+ */
+ public static final String _SYNC_DATA = "_sync_local_id";
+
+ /**
* The calendar the event belongs to
* <P>Type: INTEGER (foreign key to the Calendars table)</P>
*/
@@ -558,7 +595,7 @@ public final class Calendar {
* This column is available for use by sync adapters
* <P>Type: TEXT</P>
*/
- public static final String SYNC_ADAPTER_DATA = "syncAdapterData";
+ public static final String SYNC_DATA1 = "sync_data1";
/**
* The comments feed uri.
@@ -606,7 +643,7 @@ public final class Calendar {
* The timezone for the event, allDay events will have a local tz instead of UTC
* <P>Type: TEXT
*/
- public static final String EVENT_TIMEZONE2 = "eventTimezone2";
+ public static final String EVENT_END_TIMEZONE = "eventEndTimezone";
/**
* Whether the event lasts all day or not
@@ -615,25 +652,49 @@ public final class Calendar {
public static final String ALL_DAY = "allDay";
/**
- * Visibility for the event.
+ * Defines how the event shows up for others when the calendar is
+ * shared.
* <P>Type: INTEGER</P>
*/
- public static final String VISIBILITY = "visibility";
+ public static final String ACCESS_LEVEL = "accessLevel";
- public static final int VISIBILITY_DEFAULT = 0;
- public static final int VISIBILITY_CONFIDENTIAL = 1;
- public static final int VISIBILITY_PRIVATE = 2;
- public static final int VISIBILITY_PUBLIC = 3;
+ /**
+ * Default access is controlled by the server and will be treated as
+ * public on the device.
+ */
+ public static final int ACCESS_DEFAULT = 0;
+ /**
+ * Confidential is not used by the app.
+ */
+ public static final int ACCESS_CONFIDENTIAL = 1;
+ /**
+ * Private assumes the event appears as a free/busy slot with no
+ * details.
+ */
+ public static final int ACCESS_PRIVATE = 2;
+ /**
+ * Public assumes the contents are visible to anyone with access to the
+ * calendar.
+ */
+ public static final int ACCESS_PUBLIC = 3;
/**
- * Transparency for the event -- does the event consume time on the calendar?
+ * If this event counts as busy time or is still free time that can be
+ * scheduled over.
* <P>Type: INTEGER</P>
*/
- public static final String TRANSPARENCY = "transparency";
-
- public static final int TRANSPARENCY_OPAQUE = 0;
+ public static final String AVAILABILITY = "availability";
- public static final int TRANSPARENCY_TRANSPARENT = 1;
+ /**
+ * Indicates that this event takes up time and will conflict with other
+ * events.
+ */
+ public static final int AVAILABILITY_BUSY = 0;
+ /**
+ * Indicates that this event is free time and will not conflict with
+ * other events.
+ */
+ public static final int AVAILABILITY_FREE = 1;
/**
* Whether the event has an alarm or not
@@ -677,7 +738,7 @@ public final class Calendar {
* an exception.
* <P>Type: TEXT</P>
*/
- public static final String ORIGINAL_EVENT = "originalEvent";
+ public static final String ORIGINAL_SYNC_ID = "original_sync_id";
/**
* The original instance time of the recurring event for which this
@@ -755,10 +816,10 @@ public final class Calendar {
}
/**
- * Contains one entry per calendar event. Recurring events show up as a single entry.
+ * Contains one entry per calendar event. Recurring events show up as a
+ * single entry.
*/
- public static final class EventsEntity implements BaseColumns, SyncColumns, AccountColumns,
- EventsColumns {
+ public static final class EventsEntity implements BaseColumns, SyncColumns, EventsColumns {
/**
* The content:// style URL for this table
*/
@@ -839,8 +900,8 @@ public final class Calendar {
DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, DURATION);
DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, EVENT_TIMEZONE);
DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, ALL_DAY);
- DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, VISIBILITY);
- DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, TRANSPARENCY);
+ DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, ACCESS_LEVEL);
+ DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, AVAILABILITY);
DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, HAS_ALARM);
DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv,
HAS_EXTENDED_PROPERTIES);
@@ -848,7 +909,7 @@ public final class Calendar {
DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, RDATE);
DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, EXRULE);
DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, EXDATE);
- DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, ORIGINAL_EVENT);
+ DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, ORIGINAL_SYNC_ID);
DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv,
ORIGINAL_INSTANCE_TIME);
DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, ORIGINAL_ALL_DAY);
@@ -861,12 +922,12 @@ public final class Calendar {
DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, ORGANIZER);
DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, _SYNC_ID);
DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, _SYNC_DATA);
- DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, _SYNC_DIRTY);
+ DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, DIRTY);
DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, _SYNC_VERSION);
DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, EventsColumns.DELETED);
DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, Calendars.SYNC1);
DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv,
- Events.SYNC_ADAPTER_DATA);
+ Events.SYNC_DATA1);
Entity entity = new Entity(cv);
Cursor subCursor;
@@ -957,11 +1018,10 @@ public final class Calendar {
/**
* Contains one entry per calendar event. Recurring events show up as a single entry.
*/
- public static final class Events implements BaseColumns, SyncColumns, AccountColumns,
- EventsColumns {
+ public static final class Events implements BaseColumns, SyncColumns, EventsColumns {
private static final String[] FETCH_ENTRY_COLUMNS =
- new String[] { Events._SYNC_ACCOUNT, Events._SYNC_ID };
+ new String[] { Events.ACCOUNT_NAME, Events._SYNC_ID };
private static final String[] ATTENDEES_COLUMNS =
new String[] { AttendeesColumns.ATTENDEE_NAME,
@@ -1003,6 +1063,29 @@ public final class Calendar {
* The default sort order for this table
*/
public static final String DEFAULT_SORT_ORDER = "";
+
+ /**
+ * These are columns that should only ever be updated by the provider,
+ * either because they are views mapped to another table or because they
+ * are used for provider only functionality.
+ */
+ public static String[] PROVIDER_WRITABLE_COLUMNS = new String[] {
+ ACCOUNT_NAME,
+ ACCOUNT_TYPE
+ };
+
+ /**
+ * These fields are only writable by a sync adapter. To modify them the
+ * caller must include CALLER_IS_SYNCADAPTER, _SYNC_ACCOUNT, and
+ * _SYNC_ACCOUNT_TYPE in the query parameters.
+ */
+ public static final String[] SYNC_WRITABLE_COLUMNS = new String[] {
+ _SYNC_ID,
+ _SYNC_TIME,
+ _SYNC_VERSION,
+ DIRTY,
+ SYNC_DATA1
+ };
}
/**
@@ -1011,7 +1094,7 @@ public final class Calendar {
*/
public static final class Instances implements BaseColumns, EventsColumns, CalendarsColumns {
- private static final String WHERE_CALENDARS_SELECTED = Calendars.SELECTED + "=1";
+ private static final String WHERE_CALENDARS_SELECTED = Calendars.VISIBLE + "=1";
public static final Cursor query(ContentResolver cr, String[] projection,
long begin, long end) {
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 4f88612..22b4c76 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -144,6 +144,27 @@ public final class ContactsContract {
public static final String LIMIT_PARAM_KEY = "limit";
/**
+ * A query parameter specifing a primary account. This parameter should be used with
+ * {@link #PRIMARY_ACCOUNT_TYPE}. The contacts provider handling a query may rely on
+ * this information to optimize its query results.
+ *
+ * For example, in an email composition screen, its implementation can specify an account when
+ * obtaining possible recipients, letting the provider know which account is selected during
+ * the composition. The provider may use the "primary account" information to optimize
+ * the search result.
+ * @hide
+ */
+ public static final String PRIMARY_ACCOUNT_NAME = "name_for_primary_account";
+
+ /**
+ * A query parameter specifing a primary account. This parameter should be used with
+ * {@link #PRIMARY_ACCOUNT_NAME}. See the doc in {@link #PRIMARY_ACCOUNT_NAME}.
+ * @hide
+ */
+ public static final String PRIMARY_ACCOUNT_TYPE = "type_for_primary_account";
+
+
+ /**
* @hide
*/
public static final class Preferences {
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index c9b2f97..3bc1348 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -166,7 +166,6 @@ public final class MediaStore {
* If the EXTRA_OUTPUT is present, then the full-sized image will be written to the Uri
* value of EXTRA_OUTPUT.
* @see #EXTRA_OUTPUT
- * @see #EXTRA_VIDEO_QUALITY
*/
public final static String ACTION_IMAGE_CAPTURE = "android.media.action.IMAGE_CAPTURE";
@@ -181,6 +180,9 @@ public final class MediaStore {
* written to the standard location for videos, and the Uri of that location will be
* returned in the data field of the Uri.
* @see #EXTRA_OUTPUT
+ * @see #EXTRA_VIDEO_QUALITY
+ * @see #EXTRA_SIZE_LIMIT
+ * @see #EXTRA_DURATION_LIMIT
*/
public final static String ACTION_VIDEO_CAPTURE = "android.media.action.VIDEO_CAPTURE";
@@ -1094,12 +1096,6 @@ public final class MediaStore {
public static final String ALBUM_KEY = "album_key";
/**
- * A URI to the album art, if any
- * <P>Type: TEXT</P>
- */
- public static final String ALBUM_ART = "album_art";
-
- /**
* The track number of this song on the album, if any.
* This number encodes both the track number and the
* disc number. For multi-disc sets, this number will
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index b09b44e..570b801 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -41,7 +41,6 @@ import android.os.RemoteException;
import android.os.SystemProperties;
import android.text.TextUtils;
import android.util.AndroidException;
-import android.util.Config;
import android.util.Log;
import java.net.URISyntaxException;
@@ -569,7 +568,7 @@ public final class Settings {
public static final String AUTHORITY = "settings";
private static final String TAG = "Settings";
- private static final boolean LOCAL_LOGV = Config.LOGV || false;
+ private static final boolean LOCAL_LOGV = false || false;
public static class SettingNotFoundException extends AndroidException {
public SettingNotFoundException(String msg) {
@@ -1103,6 +1102,18 @@ public final class Settings {
public static final int END_BUTTON_BEHAVIOR_DEFAULT = END_BUTTON_BEHAVIOR_SLEEP;
/**
+ * Is advanced settings mode turned on. 0 == no, 1 == yes
+ * @hide
+ */
+ public static final String ADVANCED_SETTINGS = "advanced_settings";
+
+ /**
+ * ADVANCED_SETTINGS default value.
+ * @hide
+ */
+ public static final int ADVANCED_SETTINGS_DEFAULT = 0;
+
+ /**
* Whether Airplane Mode is on.
*/
public static final String AIRPLANE_MODE_ON = "airplane_mode_on";
@@ -1494,6 +1505,13 @@ public final class Settings {
public static final Uri DEFAULT_ALARM_ALERT_URI = getUriFor(ALARM_ALERT);
/**
+ * Persistent store for the system default media button event receiver.
+ *
+ * @hide
+ */
+ public static final String MEDIA_BUTTON_RECEIVER = "media_button_receiver";
+
+ /**
* Setting to enable Auto Replace (AutoText) in text editors. 1 = On, 0 = Off
*/
public static final String TEXT_AUTO_REPLACE = "auto_replace";
@@ -3303,12 +3321,20 @@ public final class Settings {
public static final String WIFI_IDLE_MS = "wifi_idle_ms";
/**
- * The interval in milliseconds to issue scans when the driver is
- * started. This is necessary to allow wifi to connect to an
- * access point when the driver is suspended.
+ * The interval in milliseconds to issue wake up scans when wifi needs
+ * to connect. This is necessary to connect to an access point when
+ * device is on the move and the screen is off.
+ * @hide
+ */
+ public static final String WIFI_FRAMEWORK_SCAN_INTERVAL_MS =
+ "wifi_framework_scan_interval_ms";
+
+ /**
+ * The interval in milliseconds to scan as used by the wifi supplicant
* @hide
*/
- public static final String WIFI_SCAN_INTERVAL_MS = "wifi_scan_interval_ms";
+ public static final String WIFI_SUPPLICANT_SCAN_INTERVAL_MS =
+ "wifi_supplicant_scan_interval_ms";
/**
* The interval in milliseconds at which to check packet counts on the
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index d2d2557..91a72a5 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -28,7 +28,6 @@ import android.net.Uri;
import android.os.Environment;
import android.telephony.SmsMessage;
import android.text.TextUtils;
-import android.util.Config;
import android.util.Log;
import android.util.Patterns;
@@ -46,7 +45,7 @@ import java.util.regex.Pattern;
public final class Telephony {
private static final String TAG = "Telephony";
private static final boolean DEBUG = true;
- private static final boolean LOCAL_LOGV = DEBUG ? Config.LOGD : Config.LOGV;
+ private static final boolean LOCAL_LOGV = false;
// Constructor
public Telephony() {
@@ -90,12 +89,18 @@ public final class Telephony {
public static final String PERSON_ID = "person";
/**
- * The date the message was sent
+ * The date the message was received
* <P>Type: INTEGER (long)</P>
*/
public static final String DATE = "date";
/**
+ * The date the message was sent
+ * <P>Type: INTEGER (long)</P>
+ */
+ public static final String DATE_SENT = "date_sent";
+
+ /**
* Has the message been read
* <P>Type: INTEGER (boolean)</P>
*/
@@ -650,12 +655,18 @@ public final class Telephony {
public static final int MESSAGE_BOX_OUTBOX = 4;
/**
- * The date the message was sent.
+ * The date the message was received.
* <P>Type: INTEGER (long)</P>
*/
public static final String DATE = "date";
/**
+ * The date the message was sent.
+ * <P>Type: INTEGER (long)</P>
+ */
+ public static final String DATE_SENT = "date_sent";
+
+ /**
* The box which the message belong to, for example, MESSAGE_BOX_INBOX.
* <P>Type: INTEGER</P>
*/
diff --git a/core/java/android/server/BluetoothA2dpService.java b/core/java/android/server/BluetoothA2dpService.java
index 132c346..ca2212c 100644
--- a/core/java/android/server/BluetoothA2dpService.java
+++ b/core/java/android/server/BluetoothA2dpService.java
@@ -83,19 +83,6 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub {
onBluetoothDisable();
break;
}
- } else if (action.equals(BluetoothDevice.ACTION_BOND_STATE_CHANGED)) {
- int bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE,
- BluetoothDevice.ERROR);
- switch(bondState) {
- case BluetoothDevice.BOND_BONDED:
- if (getPriority(device) == BluetoothA2dp.PRIORITY_UNDEFINED) {
- setPriority(device, BluetoothA2dp.PRIORITY_ON);
- }
- break;
- case BluetoothDevice.BOND_NONE:
- setPriority(device, BluetoothA2dp.PRIORITY_UNDEFINED);
- break;
- }
} else if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)) {
synchronized (this) {
if (mAudioDevices.containsKey(device)) {
@@ -158,7 +145,6 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub {
mAdapter = BluetoothAdapter.getDefaultAdapter();
mIntentFilter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
- mIntentFilter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
mIntentFilter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
mIntentFilter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
mIntentFilter.addAction(AudioManager.VOLUME_CHANGED_ACTION);
diff --git a/core/java/android/server/BluetoothAdapterProperties.java b/core/java/android/server/BluetoothAdapterProperties.java
index ae8104b..9723f60 100644
--- a/core/java/android/server/BluetoothAdapterProperties.java
+++ b/core/java/android/server/BluetoothAdapterProperties.java
@@ -76,14 +76,13 @@ class BluetoothAdapterProperties {
for (int i = 0; i < properties.length; i++) {
String name = properties[i];
String newValue = null;
- int len;
if (name == null) {
Log.e(TAG, "Error:Adapter Property at index " + i + " is null");
continue;
}
if (name.equals("Devices") || name.equals("UUIDs")) {
StringBuilder str = new StringBuilder();
- len = Integer.valueOf(properties[++i]);
+ int len = Integer.valueOf(properties[++i]);
for (int j = 0; j < len; j++) {
str.append(properties[++i]);
str.append(",");
diff --git a/core/java/android/server/BluetoothBondState.java b/core/java/android/server/BluetoothBondState.java
index 2304a70..a36cd24 100644
--- a/core/java/android/server/BluetoothBondState.java
+++ b/core/java/android/server/BluetoothBondState.java
@@ -18,6 +18,9 @@ package android.server;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothA2dp;
+import android.bluetooth.BluetoothHeadset;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
@@ -68,6 +71,8 @@ class BluetoothBondState {
private final Context mContext;
private final BluetoothService mService;
private final BluetoothInputProfileHandler mBluetoothInputProfileHandler;
+ private BluetoothA2dp mA2dpProxy;
+ private BluetoothHeadset mHeadsetProxy;
BluetoothBondState(Context context, BluetoothService service) {
mContext = context;
@@ -126,14 +131,15 @@ class BluetoothBondState {
if (state == BluetoothDevice.BOND_BONDED) {
mService.addProfileState(address);
+ } else if (state == BluetoothDevice.BOND_BONDING) {
+ if (mA2dpProxy == null || mHeadsetProxy == null) {
+ getProfileProxy();
+ }
} else if (state == BluetoothDevice.BOND_NONE) {
mService.removeProfileState(address);
}
- // HID is handled by BluetoothService, other profiles
- // will be handled by their respective services.
- mBluetoothInputProfileHandler.setInitialInputDevicePriority(
- mService.getRemoteDevice(address), state);
+ setProfilePriorities(address, state);
if (DBG) {
Log.d(TAG, address + " bond state " + oldState + " -> " + state
@@ -261,6 +267,52 @@ class BluetoothBondState {
mPinAttempt.put(address, new Integer(newAttempt));
}
+ private void getProfileProxy() {
+ BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+
+ if (mA2dpProxy == null) {
+ bluetoothAdapter.getProfileProxy(mContext, mProfileServiceListener,
+ BluetoothProfile.A2DP);
+ }
+
+ if (mHeadsetProxy == null) {
+ bluetoothAdapter.getProfileProxy(mContext, mProfileServiceListener,
+ BluetoothProfile.HEADSET);
+ }
+ }
+
+ private void closeProfileProxy() {
+ BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+
+ if (mA2dpProxy != null) {
+ bluetoothAdapter.closeProfileProxy(BluetoothProfile.A2DP, mA2dpProxy);
+ }
+
+ if (mHeadsetProxy != null) {
+ bluetoothAdapter.closeProfileProxy(BluetoothProfile.HEADSET, mHeadsetProxy);
+ }
+ }
+
+ private BluetoothProfile.ServiceListener mProfileServiceListener =
+ new BluetoothProfile.ServiceListener() {
+
+ public void onServiceConnected(int profile, BluetoothProfile proxy) {
+ if (profile == BluetoothProfile.A2DP) {
+ mA2dpProxy = (BluetoothA2dp) proxy;
+ } else if (profile == BluetoothProfile.HEADSET) {
+ mHeadsetProxy = (BluetoothHeadset) proxy;
+ }
+ }
+
+ public void onServiceDisconnected(int profile) {
+ if (profile == BluetoothProfile.A2DP) {
+ mA2dpProxy = null;
+ } else if (profile == BluetoothProfile.HEADSET) {
+ mHeadsetProxy = null;
+ }
+ }
+ };
+
private void copyAutoPairingData() {
FileInputStream in = null;
FileOutputStream out = null;
@@ -365,4 +417,30 @@ class BluetoothBondState {
}
}
}
+
+ // Set service priority of Hid, A2DP and Headset profiles depending on
+ // the bond state change
+ private void setProfilePriorities(String address, int state) {
+ BluetoothDevice remoteDevice = mService.getRemoteDevice(address);
+ // HID is handled by BluetoothService
+ mBluetoothInputProfileHandler.setInitialInputDevicePriority(remoteDevice, state);
+
+ // Set service priority of A2DP and Headset
+ // We used to do the priority change in the 2 services after the broadcast
+ // intent reach them. But that left a small time gap that could reject
+ // incoming connection due to undefined priorities.
+ if (state == BluetoothDevice.BOND_BONDED) {
+ if (mA2dpProxy.getPriority(remoteDevice) == BluetoothProfile.PRIORITY_UNDEFINED) {
+ mA2dpProxy.setPriority(remoteDevice, BluetoothProfile.PRIORITY_ON);
+ }
+
+ if (mHeadsetProxy.getPriority(remoteDevice) == BluetoothProfile.PRIORITY_UNDEFINED) {
+ mHeadsetProxy.setPriority(remoteDevice, BluetoothProfile.PRIORITY_ON);
+ }
+ } else if (state == BluetoothDevice.BOND_NONE) {
+ mA2dpProxy.setPriority(remoteDevice, BluetoothProfile.PRIORITY_UNDEFINED);
+ mHeadsetProxy.setPriority(remoteDevice, BluetoothProfile.PRIORITY_UNDEFINED);
+ }
+ }
+
}
diff --git a/core/java/android/server/BluetoothInputProfileHandler.java b/core/java/android/server/BluetoothInputProfileHandler.java
index e6513fd..247e297 100644
--- a/core/java/android/server/BluetoothInputProfileHandler.java
+++ b/core/java/android/server/BluetoothInputProfileHandler.java
@@ -60,7 +60,7 @@ final class BluetoothInputProfileHandler {
return sInstance;
}
- synchronized boolean connectInputDevice(BluetoothDevice device,
+ boolean connectInputDevice(BluetoothDevice device,
BluetoothDeviceProfileState state) {
String objectPath = mBluetoothService.getObjectPathFromAddress(device.getAddress());
if (objectPath == null ||
@@ -78,7 +78,7 @@ final class BluetoothInputProfileHandler {
return false;
}
- synchronized boolean connectInputDeviceInternal(BluetoothDevice device) {
+ boolean connectInputDeviceInternal(BluetoothDevice device) {
String objectPath = mBluetoothService.getObjectPathFromAddress(device.getAddress());
handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_CONNECTING);
if (!mBluetoothService.connectInputDeviceNative(objectPath)) {
@@ -88,7 +88,7 @@ final class BluetoothInputProfileHandler {
return true;
}
- synchronized boolean disconnectInputDevice(BluetoothDevice device,
+ boolean disconnectInputDevice(BluetoothDevice device,
BluetoothDeviceProfileState state) {
String objectPath = mBluetoothService.getObjectPathFromAddress(device.getAddress());
if (objectPath == null ||
@@ -105,7 +105,7 @@ final class BluetoothInputProfileHandler {
return false;
}
- synchronized boolean disconnectInputDeviceInternal(BluetoothDevice device) {
+ boolean disconnectInputDeviceInternal(BluetoothDevice device) {
String objectPath = mBluetoothService.getObjectPathFromAddress(device.getAddress());
handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_DISCONNECTING);
if (!mBluetoothService.disconnectInputDeviceNative(objectPath)) {
@@ -115,31 +115,31 @@ final class BluetoothInputProfileHandler {
return true;
}
- synchronized int getInputDeviceConnectionState(BluetoothDevice device) {
+ int getInputDeviceConnectionState(BluetoothDevice device) {
if (mInputDevices.get(device) == null) {
return BluetoothInputDevice.STATE_DISCONNECTED;
}
return mInputDevices.get(device);
}
- synchronized List<BluetoothDevice> getConnectedInputDevices() {
+ List<BluetoothDevice> getConnectedInputDevices() {
List<BluetoothDevice> devices = lookupInputDevicesMatchingStates(
new int[] {BluetoothInputDevice.STATE_CONNECTED});
return devices;
}
- synchronized List<BluetoothDevice> getInputDevicesMatchingConnectionStates(int[] states) {
+ List<BluetoothDevice> getInputDevicesMatchingConnectionStates(int[] states) {
List<BluetoothDevice> devices = lookupInputDevicesMatchingStates(states);
return devices;
}
- synchronized int getInputDevicePriority(BluetoothDevice device) {
+ int getInputDevicePriority(BluetoothDevice device) {
return Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.getBluetoothInputDevicePriorityKey(device.getAddress()),
BluetoothInputDevice.PRIORITY_UNDEFINED);
}
- synchronized boolean setInputDevicePriority(BluetoothDevice device, int priority) {
+ boolean setInputDevicePriority(BluetoothDevice device, int priority) {
if (!BluetoothAdapter.checkBluetoothAddress(device.getAddress())) {
return false;
}
@@ -148,7 +148,7 @@ final class BluetoothInputProfileHandler {
priority);
}
- synchronized List<BluetoothDevice> lookupInputDevicesMatchingStates(int[] states) {
+ List<BluetoothDevice> lookupInputDevicesMatchingStates(int[] states) {
List<BluetoothDevice> inputDevices = new ArrayList<BluetoothDevice>();
for (BluetoothDevice device: mInputDevices.keySet()) {
@@ -163,7 +163,7 @@ final class BluetoothInputProfileHandler {
return inputDevices;
}
- private synchronized void handleInputDeviceStateChange(BluetoothDevice device, int state) {
+ private void handleInputDeviceStateChange(BluetoothDevice device, int state) {
int prevState;
if (mInputDevices.get(device) == null) {
prevState = BluetoothInputDevice.STATE_DISCONNECTED;
@@ -194,7 +194,7 @@ final class BluetoothInputProfileHandler {
mBluetoothService.sendConnectionStateChange(device, state, prevState);
}
- synchronized void handleInputDevicePropertyChange(String address, boolean connected) {
+ void handleInputDevicePropertyChange(String address, boolean connected) {
int state = connected ? BluetoothInputDevice.STATE_CONNECTED :
BluetoothInputDevice.STATE_DISCONNECTED;
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
@@ -202,7 +202,7 @@ final class BluetoothInputProfileHandler {
handleInputDeviceStateChange(device, state);
}
- synchronized void setInitialInputDevicePriority(BluetoothDevice device, int state) {
+ void setInitialInputDevicePriority(BluetoothDevice device, int state) {
switch (state) {
case BluetoothDevice.BOND_BONDED:
if (getInputDevicePriority(device) == BluetoothInputDevice.PRIORITY_UNDEFINED) {
diff --git a/core/java/android/server/BluetoothPanProfileHandler.java b/core/java/android/server/BluetoothPanProfileHandler.java
index 8925856..0d63e19 100644
--- a/core/java/android/server/BluetoothPanProfileHandler.java
+++ b/core/java/android/server/BluetoothPanProfileHandler.java
@@ -76,17 +76,17 @@ final class BluetoothPanProfileHandler {
}
}
- static synchronized BluetoothPanProfileHandler getInstance(Context context,
+ static BluetoothPanProfileHandler getInstance(Context context,
BluetoothService service) {
if (sInstance == null) sInstance = new BluetoothPanProfileHandler(context, service);
return sInstance;
}
- synchronized boolean isTetheringOn() {
+ boolean isTetheringOn() {
return mTetheringOn;
}
- synchronized boolean allowIncomingTethering() {
+ boolean allowIncomingTethering() {
if (isTetheringOn() && getConnectedPanDevices().size() < mMaxPanDevices)
return true;
return false;
@@ -94,7 +94,7 @@ final class BluetoothPanProfileHandler {
private BroadcastReceiver mTetheringReceiver = null;
- synchronized void setBluetoothTethering(boolean value) {
+ void setBluetoothTethering(boolean value) {
if (!value) {
disconnectPanServerDevices();
}
@@ -104,7 +104,7 @@ final class BluetoothPanProfileHandler {
filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
mTetheringReceiver = new BroadcastReceiver() {
@Override
- public synchronized void onReceive(Context context, Intent intent) {
+ public void onReceive(Context context, Intent intent) {
if (intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_OFF)
== BluetoothAdapter.STATE_ON) {
mTetheringOn = true;
@@ -118,7 +118,7 @@ final class BluetoothPanProfileHandler {
}
}
- synchronized int getPanDeviceConnectionState(BluetoothDevice device) {
+ int getPanDeviceConnectionState(BluetoothDevice device) {
BluetoothPanDevice panDevice = mPanDevices.get(device);
if (panDevice == null) {
return BluetoothPan.STATE_DISCONNECTED;
@@ -126,7 +126,7 @@ final class BluetoothPanProfileHandler {
return panDevice.mState;
}
- synchronized boolean connectPanDevice(BluetoothDevice device) {
+ boolean connectPanDevice(BluetoothDevice device) {
String objectPath = mBluetoothService.getObjectPathFromAddress(device.getAddress());
if (DBG) Log.d(TAG, "connect PAN(" + objectPath + ")");
if (getPanDeviceConnectionState(device) != BluetoothPan.STATE_DISCONNECTED) {
@@ -158,7 +158,7 @@ final class BluetoothPanProfileHandler {
}
}
- private synchronized boolean disconnectPanServerDevices() {
+ private boolean disconnectPanServerDevices() {
debugLog("disconnect all PAN devices");
for (BluetoothDevice device: mPanDevices.keySet()) {
@@ -187,7 +187,7 @@ final class BluetoothPanProfileHandler {
return true;
}
- synchronized List<BluetoothDevice> getConnectedPanDevices() {
+ List<BluetoothDevice> getConnectedPanDevices() {
List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>();
for (BluetoothDevice device: mPanDevices.keySet()) {
@@ -198,7 +198,7 @@ final class BluetoothPanProfileHandler {
return devices;
}
- synchronized List<BluetoothDevice> getPanDevicesMatchingConnectionStates(int[] states) {
+ List<BluetoothDevice> getPanDevicesMatchingConnectionStates(int[] states) {
List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>();
for (BluetoothDevice device: mPanDevices.keySet()) {
@@ -213,7 +213,7 @@ final class BluetoothPanProfileHandler {
return devices;
}
- synchronized boolean disconnectPanDevice(BluetoothDevice device) {
+ boolean disconnectPanDevice(BluetoothDevice device) {
String objectPath = mBluetoothService.getObjectPathFromAddress(device.getAddress());
debugLog("disconnect PAN(" + objectPath + ")");
@@ -249,7 +249,7 @@ final class BluetoothPanProfileHandler {
return true;
}
- synchronized void handlePanDeviceStateChange(BluetoothDevice device,
+ void handlePanDeviceStateChange(BluetoothDevice device,
String iface, int state, int role) {
int prevState;
String ifaceAddr = null;
@@ -304,7 +304,7 @@ final class BluetoothPanProfileHandler {
mBluetoothService.sendConnectionStateChange(device, state, prevState);
}
- synchronized void handlePanDeviceStateChange(BluetoothDevice device,
+ void handlePanDeviceStateChange(BluetoothDevice device,
int state, int role) {
handlePanDeviceStateChange(device, null, state, role);
}
@@ -343,7 +343,7 @@ final class BluetoothPanProfileHandler {
}
// configured when we start tethering
- private synchronized String enableTethering(String iface) {
+ private String enableTethering(String iface) {
debugLog("updateTetherState:" + iface);
IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java
index b3cbf50..60bee9a 100755
--- a/core/java/android/server/BluetoothService.java
+++ b/core/java/android/server/BluetoothService.java
@@ -1928,120 +1928,163 @@ public class BluetoothService extends IBluetooth.Stub {
}
/**** Handlers for PAN Profile ****/
+ // TODO: This needs to be converted to a state machine.
- public synchronized boolean isTetheringOn() {
+ public boolean isTetheringOn() {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
- return mBluetoothPanProfileHandler.isTetheringOn();
+ synchronized (mBluetoothPanProfileHandler) {
+ return mBluetoothPanProfileHandler.isTetheringOn();
+ }
}
- /*package*/ synchronized boolean allowIncomingTethering() {
- return mBluetoothPanProfileHandler.allowIncomingTethering();
+ /*package*/boolean allowIncomingTethering() {
+ synchronized (mBluetoothPanProfileHandler) {
+ return mBluetoothPanProfileHandler.allowIncomingTethering();
+ }
}
- public synchronized void setBluetoothTethering(boolean value) {
+ public void setBluetoothTethering(boolean value) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
- mBluetoothPanProfileHandler.setBluetoothTethering(value);
+ synchronized (mBluetoothPanProfileHandler) {
+ mBluetoothPanProfileHandler.setBluetoothTethering(value);
+ }
}
- public synchronized int getPanDeviceConnectionState(BluetoothDevice device) {
+ public int getPanDeviceConnectionState(BluetoothDevice device) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
- return mBluetoothPanProfileHandler.getPanDeviceConnectionState(device);
+ synchronized (mBluetoothPanProfileHandler) {
+ return mBluetoothPanProfileHandler.getPanDeviceConnectionState(device);
+ }
}
- public synchronized boolean connectPanDevice(BluetoothDevice device) {
+ public boolean connectPanDevice(BluetoothDevice device) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
"Need BLUETOOTH_ADMIN permission");
- return mBluetoothPanProfileHandler.connectPanDevice(device);
+ synchronized (mBluetoothPanProfileHandler) {
+ return mBluetoothPanProfileHandler.connectPanDevice(device);
+ }
}
- public synchronized List<BluetoothDevice> getConnectedPanDevices() {
+ public List<BluetoothDevice> getConnectedPanDevices() {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
- return mBluetoothPanProfileHandler.getConnectedPanDevices();
+ synchronized (mBluetoothPanProfileHandler) {
+ return mBluetoothPanProfileHandler.getConnectedPanDevices();
+ }
}
- public synchronized List<BluetoothDevice> getPanDevicesMatchingConnectionStates(
+ public List<BluetoothDevice> getPanDevicesMatchingConnectionStates(
int[] states) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
- return mBluetoothPanProfileHandler.getPanDevicesMatchingConnectionStates(states);
+ synchronized (mBluetoothPanProfileHandler) {
+ return mBluetoothPanProfileHandler.getPanDevicesMatchingConnectionStates(states);
+ }
}
- public synchronized boolean disconnectPanDevice(BluetoothDevice device) {
+ public boolean disconnectPanDevice(BluetoothDevice device) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
"Need BLUETOOTH_ADMIN permission");
- return mBluetoothPanProfileHandler.disconnectPanDevice(device);
+ synchronized (mBluetoothPanProfileHandler) {
+ return mBluetoothPanProfileHandler.disconnectPanDevice(device);
+ }
}
- /*package*/ synchronized void handlePanDeviceStateChange(BluetoothDevice device,
+ /*package*/void handlePanDeviceStateChange(BluetoothDevice device,
String iface,
int state,
int role) {
- mBluetoothPanProfileHandler.handlePanDeviceStateChange(device, iface, state, role);
+ synchronized (mBluetoothPanProfileHandler) {
+ mBluetoothPanProfileHandler.handlePanDeviceStateChange(device, iface, state, role);
+ }
}
- /*package*/ synchronized void handlePanDeviceStateChange(BluetoothDevice device,
+ /*package*/void handlePanDeviceStateChange(BluetoothDevice device,
int state, int role) {
- mBluetoothPanProfileHandler.handlePanDeviceStateChange(device, null, state, role);
+ synchronized (mBluetoothPanProfileHandler) {
+ mBluetoothPanProfileHandler.handlePanDeviceStateChange(device, null, state, role);
+ }
}
/**** Handlers for Input Device Profile ****/
+ // This needs to be converted to state machine
- public synchronized boolean connectInputDevice(BluetoothDevice device) {
+ public boolean connectInputDevice(BluetoothDevice device) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
"Need BLUETOOTH_ADMIN permission");
BluetoothDeviceProfileState state = mDeviceProfileState.get(device.getAddress());
- return mBluetoothInputProfileHandler.connectInputDevice(device, state);
+ synchronized (mBluetoothInputProfileHandler) {
+ return mBluetoothInputProfileHandler.connectInputDevice(device, state);
+ }
}
- public synchronized boolean connectInputDeviceInternal(BluetoothDevice device) {
- return mBluetoothInputProfileHandler.connectInputDeviceInternal(device);
+ public boolean connectInputDeviceInternal(BluetoothDevice device) {
+ synchronized (mBluetoothInputProfileHandler) {
+ return mBluetoothInputProfileHandler.connectInputDeviceInternal(device);
+ }
}
- public synchronized boolean disconnectInputDevice(BluetoothDevice device) {
+ public boolean disconnectInputDevice(BluetoothDevice device) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
"Need BLUETOOTH_ADMIN permission");
BluetoothDeviceProfileState state = mDeviceProfileState.get(device.getAddress());
- return mBluetoothInputProfileHandler.disconnectInputDevice(device, state);
+ synchronized (mBluetoothInputProfileHandler) {
+ return mBluetoothInputProfileHandler.disconnectInputDevice(device, state);
+ }
}
- public synchronized boolean disconnectInputDeviceInternal(BluetoothDevice device) {
- return mBluetoothInputProfileHandler.disconnectInputDeviceInternal(device);
+ public boolean disconnectInputDeviceInternal(BluetoothDevice device) {
+ synchronized (mBluetoothInputProfileHandler) {
+ return mBluetoothInputProfileHandler.disconnectInputDeviceInternal(device);
+ }
}
- public synchronized int getInputDeviceConnectionState(BluetoothDevice device) {
+ public int getInputDeviceConnectionState(BluetoothDevice device) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
- return mBluetoothInputProfileHandler.getInputDeviceConnectionState(device);
-
+ synchronized (mBluetoothInputProfileHandler) {
+ return mBluetoothInputProfileHandler.getInputDeviceConnectionState(device);
+ }
}
- public synchronized List<BluetoothDevice> getConnectedInputDevices() {
+ public List<BluetoothDevice> getConnectedInputDevices() {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
- return mBluetoothInputProfileHandler.getConnectedInputDevices();
+ synchronized (mBluetoothInputProfileHandler) {
+ return mBluetoothInputProfileHandler.getConnectedInputDevices();
+ }
}
- public synchronized List<BluetoothDevice> getInputDevicesMatchingConnectionStates(
+ public List<BluetoothDevice> getInputDevicesMatchingConnectionStates(
int[] states) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
- return mBluetoothInputProfileHandler.getInputDevicesMatchingConnectionStates(states);
+ synchronized (mBluetoothInputProfileHandler) {
+ return mBluetoothInputProfileHandler.getInputDevicesMatchingConnectionStates(states);
+ }
}
- public synchronized int getInputDevicePriority(BluetoothDevice device) {
+ public int getInputDevicePriority(BluetoothDevice device) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
- return mBluetoothInputProfileHandler.getInputDevicePriority(device);
+ synchronized (mBluetoothInputProfileHandler) {
+ return mBluetoothInputProfileHandler.getInputDevicePriority(device);
+ }
}
- public synchronized boolean setInputDevicePriority(BluetoothDevice device, int priority) {
+ public boolean setInputDevicePriority(BluetoothDevice device, int priority) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
"Need BLUETOOTH_ADMIN permission");
- return mBluetoothInputProfileHandler.setInputDevicePriority(device, priority);
+ synchronized (mBluetoothInputProfileHandler) {
+ return mBluetoothInputProfileHandler.setInputDevicePriority(device, priority);
+ }
}
- /*package*/synchronized List<BluetoothDevice> lookupInputDevicesMatchingStates(int[] states) {
- return mBluetoothInputProfileHandler.lookupInputDevicesMatchingStates(states);
+ /*package*/List<BluetoothDevice> lookupInputDevicesMatchingStates(int[] states) {
+ synchronized (mBluetoothInputProfileHandler) {
+ return mBluetoothInputProfileHandler.lookupInputDevicesMatchingStates(states);
+ }
}
- /*package*/ synchronized void handleInputDevicePropertyChange(String address, boolean connected) {
- mBluetoothInputProfileHandler.handleInputDevicePropertyChange(address, connected);
+ /*package*/void handleInputDevicePropertyChange(String address, boolean connected) {
+ synchronized (mBluetoothInputProfileHandler) {
+ mBluetoothInputProfileHandler.handleInputDevicePropertyChange(address, connected);
+ }
}
public boolean connectHeadset(String address) {
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 20661d7..eae7574 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -51,7 +51,7 @@ import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.View;
import android.view.ViewGroup;
-import android.view.ViewRoot;
+import android.view.ViewAncestor;
import android.view.WindowManager;
import android.view.WindowManagerImpl;
import android.view.WindowManagerPolicy;
@@ -650,7 +650,7 @@ public abstract class WallpaperService extends Service {
mWindowToken = wrapper.mWindowToken;
mSurfaceHolder.setSizeFromLayout();
mInitializing = true;
- mSession = ViewRoot.getWindowSession(getMainLooper());
+ mSession = ViewAncestor.getWindowSession(getMainLooper());
mWindow.setSession(mSession);
diff --git a/core/java/android/speech/RecognitionListener.java b/core/java/android/speech/RecognitionListener.java
index 5eb71d7..bdb3ba9 100644
--- a/core/java/android/speech/RecognitionListener.java
+++ b/core/java/android/speech/RecognitionListener.java
@@ -70,7 +70,8 @@ public interface RecognitionListener {
*
* @param results the recognition results. To retrieve the results in {@code
* ArrayList&lt;String&gt;} format use {@link Bundle#getStringArrayList(String)} with
- * {@link SpeechRecognizer#RESULTS_RECOGNITION} as a parameter
+ * {@link SpeechRecognizer#RESULTS_RECOGNITION} as a parameter. A float array of
+ * confidence values might also be given in {@link SpeechRecognizer#CONFIDENCE_SCORES}.
*/
void onResults(Bundle results);
diff --git a/core/java/android/speech/RecognizerIntent.java b/core/java/android/speech/RecognizerIntent.java
index 02c324c..fd709f2 100644
--- a/core/java/android/speech/RecognizerIntent.java
+++ b/core/java/android/speech/RecognizerIntent.java
@@ -46,7 +46,7 @@ public class RecognizerIntent {
}
/**
- * Starts an activity that will prompt the user for speech and sends it through a
+ * Starts an activity that will prompt the user for speech and send it through a
* speech recognizer. The results will be returned via activity results (in
* {@link Activity#onActivityResult}, if you start the intent using
* {@link Activity#startActivityForResult(Intent, int)}), or forwarded via a PendingIntent
@@ -81,8 +81,8 @@ public class RecognizerIntent {
public static final String ACTION_RECOGNIZE_SPEECH = "android.speech.action.RECOGNIZE_SPEECH";
/**
- * Starts an activity that will prompt the user for speech, sends it through a
- * speech recognizer, and invokes and either displays a web search result or triggers
+ * Starts an activity that will prompt the user for speech, send it through a
+ * speech recognizer, and either display a web search result or trigger
* another type of action based on the user's speech.
*
* <p>If you want to avoid triggering any type of action besides web search, you can use
@@ -100,11 +100,13 @@ public class RecognizerIntent {
* <li>{@link #EXTRA_MAX_RESULTS}
* <li>{@link #EXTRA_PARTIAL_RESULTS}
* <li>{@link #EXTRA_WEB_SEARCH_ONLY}
+ * <li>{@link #EXTRA_ORIGIN}
* </ul>
*
* <p> Result extras (returned in the result, not to be specified in the request):
* <ul>
* <li>{@link #EXTRA_RESULTS}
+ * <li>{@link #EXTRA_CONFIDENCE_SCORES} (optional)
* </ul>
*
* <p>NOTE: There may not be any applications installed to handle this action, so you should
@@ -181,6 +183,13 @@ public class RecognizerIntent {
* {@link java.util.Locale#getDefault()}.
*/
public static final String EXTRA_LANGUAGE = "android.speech.extra.LANGUAGE";
+
+ /**
+ * Optional value which can be used to indicate the referer url of a page in which
+ * speech was requested. For example, a web browser may choose to provide this for
+ * uses of speech on a given page.
+ */
+ public static final String EXTRA_ORIGIN = "android.speech.extra.ORIGIN";
/**
* Optional limit on the maximum number of results to return. If omitted the recognizer
@@ -232,13 +241,31 @@ public class RecognizerIntent {
/**
* An ArrayList&lt;String&gt; of the recognition results when performing
- * {@link #ACTION_RECOGNIZE_SPEECH}. Returned in the results; not to be specified in the
- * recognition request. Only present when {@link Activity#RESULT_OK} is returned in
- * an activity result. In a PendingIntent, the lack of this extra indicates failure.
+ * {@link #ACTION_RECOGNIZE_SPEECH}. Generally this list should be ordered in
+ * descending order of speech recognizer confidence. (See {@link #EXTRA_CONFIDENCE_SCORES}).
+ * Returned in the results; not to be specified in the recognition request. Only present
+ * when {@link Activity#RESULT_OK} is returned in an activity result. In a PendingIntent,
+ * the lack of this extra indicates failure.
*/
public static final String EXTRA_RESULTS = "android.speech.extra.RESULTS";
/**
+ * A float array of confidence scores of the recognition results when performing
+ * {@link #ACTION_RECOGNIZE_SPEECH}. The array should be the same size as the ArrayList
+ * returned in {@link #EXTRA_RESULTS}, and should contain values ranging from 0.0 to 1.0,
+ * or -1 to represent an unavailable confidence score.
+ * <p>
+ * Confidence values close to 1.0 indicate high confidence (the speech recognizer is
+ * confident that the recognition result is correct), while values close to 0.0 indicate
+ * low confidence.
+ * <p>
+ * Returned in the results; not to be specified in the recognition request. This extra is
+ * optional and might not be provided. Only present when {@link Activity#RESULT_OK} is
+ * returned in an activity result.
+ */
+ public static final String EXTRA_CONFIDENCE_SCORES = "android.speech.extra.CONFIDENCE_SCORES";
+
+ /**
* Returns the broadcast intent to fire with
* {@link Context#sendOrderedBroadcast(Intent, String, BroadcastReceiver, android.os.Handler, int, String, Bundle)}
* to receive details from the package that implements voice search.
diff --git a/core/java/android/speech/SpeechRecognizer.java b/core/java/android/speech/SpeechRecognizer.java
index cd73ba8..8fee41d 100644
--- a/core/java/android/speech/SpeechRecognizer.java
+++ b/core/java/android/speech/SpeechRecognizer.java
@@ -50,12 +50,26 @@ public class SpeechRecognizer {
private static final String TAG = "SpeechRecognizer";
/**
- * Used to retrieve an {@code ArrayList<String>} from the {@link Bundle} passed to the
+ * Key used to retrieve an {@code ArrayList<String>} from the {@link Bundle} passed to the
* {@link RecognitionListener#onResults(Bundle)} and
* {@link RecognitionListener#onPartialResults(Bundle)} methods. These strings are the possible
* recognition results, where the first element is the most likely candidate.
*/
public static final String RESULTS_RECOGNITION = "results_recognition";
+
+ /**
+ * Key used to retrieve a float array from the {@link Bundle} passed to the
+ * {@link RecognitionListener#onResults(Bundle)} and
+ * {@link RecognitionListener#onPartialResults(Bundle)} methods. The array should be
+ * the same size as the ArrayList provided in {@link #RESULTS_RECOGNITION}, and should contain
+ * values ranging from 0.0 to 1.0, or -1 to represent an unavailable confidence score.
+ * <p>
+ * Confidence values close to 1.0 indicate high confidence (the speech recognizer is confident
+ * that the recognition result is correct), while values close to 0.0 indicate low confidence.
+ * <p>
+ * This value is optional and might not be provided.
+ */
+ public static final String CONFIDENCE_SCORES = "confidence_scores";
/** Network operation timed out. */
public static final int ERROR_NETWORK_TIMEOUT = 1;
diff --git a/core/java/android/speech/srec/Recognizer.java b/core/java/android/speech/srec/Recognizer.java
index a03a36a..8a2bc7d 100644
--- a/core/java/android/speech/srec/Recognizer.java
+++ b/core/java/android/speech/srec/Recognizer.java
@@ -22,7 +22,6 @@
package android.speech.srec;
-import android.util.Config;
import android.util.Log;
import java.io.File;
diff --git a/core/java/android/speech/tts/BlockingMediaPlayer.java b/core/java/android/speech/tts/BlockingMediaPlayer.java
new file mode 100644
index 0000000..3cf60dd
--- /dev/null
+++ b/core/java/android/speech/tts/BlockingMediaPlayer.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2011 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.content.Context;
+import android.media.MediaPlayer;
+import android.net.Uri;
+import android.os.ConditionVariable;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.util.Log;
+
+/**
+ * A media player that allows blocking to wait for it to finish.
+ */
+class BlockingMediaPlayer {
+
+ private static final String TAG = "BlockMediaPlayer";
+
+ private static final String MEDIA_PLAYER_THREAD_NAME = "TTS-MediaPlayer";
+
+ private final Context mContext;
+ private final Uri mUri;
+ private final int mStreamType;
+ private final ConditionVariable mDone;
+ // Only accessed on the Handler thread
+ private MediaPlayer mPlayer;
+ private volatile boolean mFinished;
+
+ /**
+ * Creates a new blocking media player.
+ * Creating a blocking media player is a cheap operation.
+ *
+ * @param context
+ * @param uri
+ * @param streamType
+ */
+ public BlockingMediaPlayer(Context context, Uri uri, int streamType) {
+ mContext = context;
+ mUri = uri;
+ mStreamType = streamType;
+ mDone = new ConditionVariable();
+
+ }
+
+ /**
+ * Starts playback and waits for it to finish.
+ * Can be called from any thread.
+ *
+ * @return {@code true} if the playback finished normally, {@code false} if the playback
+ * failed or {@link #stop} was called before the playback finished.
+ */
+ public boolean startAndWait() {
+ HandlerThread thread = new HandlerThread(MEDIA_PLAYER_THREAD_NAME);
+ thread.start();
+ Handler handler = new Handler(thread.getLooper());
+ mFinished = false;
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ startPlaying();
+ }
+ });
+ mDone.block();
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ finish();
+ // No new messages should get posted to the handler thread after this
+ Looper.myLooper().quit();
+ }
+ });
+ return mFinished;
+ }
+
+ /**
+ * Stops playback. Can be called multiple times.
+ * Can be called from any thread.
+ */
+ public void stop() {
+ mDone.open();
+ }
+
+ /**
+ * Starts playback.
+ * Called on the handler thread.
+ */
+ private void startPlaying() {
+ mPlayer = MediaPlayer.create(mContext, mUri);
+ if (mPlayer == null) {
+ Log.w(TAG, "Failed to play " + mUri);
+ mDone.open();
+ return;
+ }
+ try {
+ mPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
+ @Override
+ public boolean onError(MediaPlayer mp, int what, int extra) {
+ Log.w(TAG, "Audio playback error: " + what + ", " + extra);
+ mDone.open();
+ return true;
+ }
+ });
+ mPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
+ @Override
+ public void onCompletion(MediaPlayer mp) {
+ mFinished = true;
+ mDone.open();
+ }
+ });
+ mPlayer.setAudioStreamType(mStreamType);
+ mPlayer.start();
+ } catch (IllegalArgumentException ex) {
+ Log.w(TAG, "MediaPlayer failed", ex);
+ mDone.open();
+ }
+ }
+
+ /**
+ * Stops playback and release the media player.
+ * Called on the handler thread.
+ */
+ private void finish() {
+ try {
+ mPlayer.stop();
+ } catch (IllegalStateException ex) {
+ // Do nothing, the player is already stopped
+ }
+ mPlayer.release();
+ }
+
+} \ No newline at end of file
diff --git a/core/java/android/speech/tts/FileSynthesisRequest.java b/core/java/android/speech/tts/FileSynthesisRequest.java
new file mode 100644
index 0000000..62be2bf
--- /dev/null
+++ b/core/java/android/speech/tts/FileSynthesisRequest.java
@@ -0,0 +1,252 @@
+/*
+ * Copyright (C) 2011 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.media.AudioFormat;
+import android.os.Bundle;
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * Speech synthesis request that writes the audio to a WAV file.
+ */
+class FileSynthesisRequest extends SynthesisRequest {
+
+ private static final String TAG = "FileSynthesisRequest";
+ private static final boolean DBG = false;
+
+ private static final int MAX_AUDIO_BUFFER_SIZE = 8192;
+
+ private static final int WAV_HEADER_LENGTH = 44;
+ private static final short WAV_FORMAT_PCM = 0x0001;
+
+ private final Object mStateLock = new Object();
+ private final File mFileName;
+ private int mSampleRateInHz;
+ private int mAudioFormat;
+ private int mChannelCount;
+ private RandomAccessFile mFile;
+ private boolean mStopped = false;
+ private boolean mDone = false;
+
+ FileSynthesisRequest(String text, Bundle params, File fileName) {
+ super(text, params);
+ mFileName = fileName;
+ }
+
+ @Override
+ void stop() {
+ synchronized (mStateLock) {
+ mStopped = true;
+ cleanUp();
+ }
+ }
+
+ /**
+ * Must be called while holding the monitor on {@link #mStateLock}.
+ */
+ private void cleanUp() {
+ closeFile();
+ if (mFile != null) {
+ mFileName.delete();
+ }
+ }
+
+ /**
+ * Must be called while holding the monitor on {@link #mStateLock}.
+ */
+ private void closeFile() {
+ try {
+ if (mFile != null) {
+ mFile.close();
+ mFile = null;
+ }
+ } catch (IOException ex) {
+ Log.e(TAG, "Failed to close " + mFileName + ": " + ex);
+ }
+ }
+
+ @Override
+ public int getMaxBufferSize() {
+ return MAX_AUDIO_BUFFER_SIZE;
+ }
+
+ @Override
+ boolean isDone() {
+ return mDone;
+ }
+
+ @Override
+ public int start(int sampleRateInHz, int audioFormat, int channelCount) {
+ if (DBG) {
+ Log.d(TAG, "FileSynthesisRequest.start(" + sampleRateInHz + "," + audioFormat
+ + "," + channelCount + ")");
+ }
+ synchronized (mStateLock) {
+ if (mStopped) {
+ if (DBG) Log.d(TAG, "Request has been aborted.");
+ return TextToSpeech.ERROR;
+ }
+ if (mFile != null) {
+ cleanUp();
+ throw new IllegalArgumentException("FileSynthesisRequest.start() called twice");
+ }
+ mSampleRateInHz = sampleRateInHz;
+ mAudioFormat = audioFormat;
+ mChannelCount = channelCount;
+ try {
+ mFile = new RandomAccessFile(mFileName, "rw");
+ // Reserve space for WAV header
+ mFile.write(new byte[WAV_HEADER_LENGTH]);
+ return TextToSpeech.SUCCESS;
+ } catch (IOException ex) {
+ Log.e(TAG, "Failed to open " + mFileName + ": " + ex);
+ cleanUp();
+ return TextToSpeech.ERROR;
+ }
+ }
+ }
+
+ @Override
+ public int audioAvailable(byte[] buffer, int offset, int length) {
+ if (DBG) {
+ Log.d(TAG, "FileSynthesisRequest.audioAvailable(" + buffer + "," + offset
+ + "," + length + ")");
+ }
+ synchronized (mStateLock) {
+ if (mStopped) {
+ if (DBG) Log.d(TAG, "Request has been aborted.");
+ return TextToSpeech.ERROR;
+ }
+ if (mFile == null) {
+ Log.e(TAG, "File not open");
+ return TextToSpeech.ERROR;
+ }
+ try {
+ mFile.write(buffer, offset, length);
+ return TextToSpeech.SUCCESS;
+ } catch (IOException ex) {
+ Log.e(TAG, "Failed to write to " + mFileName + ": " + ex);
+ cleanUp();
+ return TextToSpeech.ERROR;
+ }
+ }
+ }
+
+ @Override
+ public int done() {
+ if (DBG) Log.d(TAG, "FileSynthesisRequest.done()");
+ synchronized (mStateLock) {
+ if (mStopped) {
+ if (DBG) Log.d(TAG, "Request has been aborted.");
+ return TextToSpeech.ERROR;
+ }
+ if (mFile == null) {
+ Log.e(TAG, "File not open");
+ return TextToSpeech.ERROR;
+ }
+ try {
+ // Write WAV header at start of file
+ mFile.seek(0);
+ int dataLength = (int) (mFile.length() - WAV_HEADER_LENGTH);
+ mFile.write(
+ makeWavHeader(mSampleRateInHz, mAudioFormat, mChannelCount, dataLength));
+ closeFile();
+ mDone = true;
+ return TextToSpeech.SUCCESS;
+ } catch (IOException ex) {
+ Log.e(TAG, "Failed to write to " + mFileName + ": " + ex);
+ cleanUp();
+ return TextToSpeech.ERROR;
+ }
+ }
+ }
+
+ @Override
+ public void error() {
+ if (DBG) Log.d(TAG, "FileSynthesisRequest.error()");
+ synchronized (mStateLock) {
+ cleanUp();
+ }
+ }
+
+ @Override
+ public int completeAudioAvailable(int sampleRateInHz, int audioFormat, int channelCount,
+ byte[] buffer, int offset, int length) {
+ synchronized (mStateLock) {
+ if (mStopped) {
+ if (DBG) Log.d(TAG, "Request has been aborted.");
+ return TextToSpeech.ERROR;
+ }
+ }
+ FileOutputStream out = null;
+ try {
+ out = new FileOutputStream(mFileName);
+ out.write(makeWavHeader(sampleRateInHz, audioFormat, channelCount, length));
+ out.write(buffer, offset, length);
+ mDone = true;
+ return TextToSpeech.SUCCESS;
+ } catch (IOException ex) {
+ Log.e(TAG, "Failed to write to " + mFileName + ": " + ex);
+ mFileName.delete();
+ return TextToSpeech.ERROR;
+ } finally {
+ try {
+ if (out != null) {
+ out.close();
+ }
+ } catch (IOException ex) {
+ Log.e(TAG, "Failed to close " + mFileName + ": " + ex);
+ }
+ }
+ }
+
+ private byte[] makeWavHeader(int sampleRateInHz, int audioFormat, int channelCount,
+ int dataLength) {
+ // TODO: is AudioFormat.ENCODING_DEFAULT always the same as ENCODING_PCM_16BIT?
+ int sampleSizeInBytes = (audioFormat == AudioFormat.ENCODING_PCM_8BIT ? 1 : 2);
+ int byteRate = sampleRateInHz * sampleSizeInBytes * channelCount;
+ short blockAlign = (short) (sampleSizeInBytes * channelCount);
+ short bitsPerSample = (short) (sampleSizeInBytes * 8);
+
+ byte[] headerBuf = new byte[WAV_HEADER_LENGTH];
+ ByteBuffer header = ByteBuffer.wrap(headerBuf);
+ header.order(ByteOrder.LITTLE_ENDIAN);
+
+ header.put(new byte[]{ 'R', 'I', 'F', 'F' });
+ header.putInt(dataLength + WAV_HEADER_LENGTH - 8); // RIFF chunk size
+ header.put(new byte[]{ 'W', 'A', 'V', 'E' });
+ header.put(new byte[]{ 'f', 'm', 't', ' ' });
+ header.putInt(16); // size of fmt chunk
+ header.putShort(WAV_FORMAT_PCM);
+ header.putShort((short) channelCount);
+ header.putInt(sampleRateInHz);
+ header.putInt(byteRate);
+ header.putShort(blockAlign);
+ header.putShort(bitsPerSample);
+ header.put(new byte[]{ 'd', 'a', 't', 'a' });
+ header.putInt(dataLength);
+
+ return headerBuf;
+ }
+
+}
diff --git a/core/java/android/speech/tts/ITtsCallback.aidl b/core/java/android/speech/tts/ITextToSpeechCallback.aidl
index c9898eb..40902ae 100755
--- a/core/java/android/speech/tts/ITtsCallback.aidl
+++ b/core/java/android/speech/tts/ITextToSpeechCallback.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009 The Android Open Source Project
+ * Copyright (C) 2011 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.
@@ -13,15 +13,13 @@
* 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.
+ * Interface for callbacks from TextToSpeechService
*
* {@hide}
*/
-oneway interface ITtsCallback {
+oneway interface ITextToSpeechCallback {
void utteranceCompleted(String utteranceId);
}
diff --git a/core/java/android/speech/tts/ITextToSpeechService.aidl b/core/java/android/speech/tts/ITextToSpeechService.aidl
new file mode 100644
index 0000000..ff3fa11
--- /dev/null
+++ b/core/java/android/speech/tts/ITextToSpeechService.aidl
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2011 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.net.Uri;
+import android.os.Bundle;
+import android.speech.tts.ITextToSpeechCallback;
+
+/**
+ * Interface for TextToSpeech to talk to TextToSpeechService.
+ *
+ * {@hide}
+ */
+interface ITextToSpeechService {
+
+ /**
+ * Tells the engine to synthesize some speech and play it back.
+ *
+ * @param callingApp The package name of the calling app. Used to connect requests
+ * callbacks and to clear requests when the calling app is stopping.
+ * @param text The text to synthesize.
+ * @param queueMode Determines what to do to requests already in the queue.
+ * @param param Request parameters.
+ */
+ int speak(in String callingApp, in String text, in int queueMode, in Bundle params);
+
+ /**
+ * Tells the engine to synthesize some speech and write it to a file.
+ *
+ * @param callingApp The package name of the calling app. Used to connect requests
+ * callbacks and to clear requests when the calling app is stopping.
+ * @param text The text to synthesize.
+ * @param filename The file to write the synthesized audio to.
+ * @param param Request parameters.
+ */
+ int synthesizeToFile(in String callingApp, in String text,
+ in String filename, in Bundle params);
+
+ /**
+ * Plays an existing audio resource.
+ *
+ * @param callingApp The package name of the calling app. Used to connect requests
+ * callbacks and to clear requests when the calling app is stopping.
+ * @param audioUri URI for the audio resource (a file or android.resource URI)
+ * @param queueMode Determines what to do to requests already in the queue.
+ * @param param Request parameters.
+ */
+ int playAudio(in String callingApp, in Uri audioUri, in int queueMode, in Bundle params);
+
+ /**
+ * Plays silence.
+ *
+ * @param callingApp The package name of the calling app. Used to connect requests
+ * callbacks and to clear requests when the calling app is stopping.
+ * @param duration Number of milliseconds of silence to play.
+ * @param queueMode Determines what to do to requests already in the queue.
+ * @param param Request parameters.
+ */
+ int playSilence(in String callingApp, in long duration, in int queueMode, in Bundle params);
+
+ /**
+ * Checks whether the service is currently playing some audio.
+ */
+ boolean isSpeaking();
+
+ /**
+ * Interrupts the current utterance (if from the given app) and removes any utterances
+ * in the queue that are from the given app.
+ *
+ * @param callingApp Package name of the app whose utterances
+ * should be interrupted and cleared.
+ */
+ int stop(in String callingApp);
+
+ /**
+ * Returns the language, country and variant currently being used by the TTS engine.
+ *
+ * Can be called from multiple threads.
+ *
+ * @return A 3-element array, containing language (ISO 3-letter code),
+ * country (ISO 3-letter code) and variant used by the engine.
+ * The country and variant may be {@code ""}. If country is empty, then variant must
+ * be empty too.
+ */
+ String[] getLanguage();
+
+ /**
+ * Checks whether the engine supports a given language.
+ *
+ * @param lang ISO-3 language code.
+ * @param country ISO-3 country code. May be empty or null.
+ * @param variant Language variant. May be empty or null.
+ * @return Code indicating the support status for the locale.
+ * One of {@link TextToSpeech#LANG_AVAILABLE},
+ * {@link TextToSpeech#LANG_COUNTRY_AVAILABLE},
+ * {@link TextToSpeech#LANG_COUNTRY_VAR_AVAILABLE},
+ * {@link TextToSpeech#LANG_MISSING_DATA}
+ * {@link TextToSpeech#LANG_NOT_SUPPORTED}.
+ */
+ int isLanguageAvailable(in String lang, in String country, in String variant);
+
+ /**
+ * Notifies the engine that it should load a speech synthesis language.
+ *
+ * @param lang ISO-3 language code.
+ * @param country ISO-3 country code. May be empty or null.
+ * @param variant Language variant. May be empty or null.
+ * @return Code indicating the support status for the locale.
+ * One of {@link TextToSpeech#LANG_AVAILABLE},
+ * {@link TextToSpeech#LANG_COUNTRY_AVAILABLE},
+ * {@link TextToSpeech#LANG_COUNTRY_VAR_AVAILABLE},
+ * {@link TextToSpeech#LANG_MISSING_DATA}
+ * {@link TextToSpeech#LANG_NOT_SUPPORTED}.
+ */
+ int loadLanguage(in String lang, in String country, in String variant);
+
+ /**
+ * Sets the callback that will be notified when playback of utterance from the
+ * given app are completed.
+ *
+ * @param callingApp Package name for the app whose utterance the callback will handle.
+ * @param cb The callback.
+ */
+ void setCallback(in String callingApp, ITextToSpeechCallback cb);
+
+}
diff --git a/core/java/android/speech/tts/ITts.aidl b/core/java/android/speech/tts/ITts.aidl
deleted file mode 100755
index c1051c4..0000000
--- a/core/java/android/speech/tts/ITts.aidl
+++ /dev/null
@@ -1,69 +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.
- */
-
-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 String callingApp, in int speechRate);
-
- int setPitch(in String callingApp, in int pitch);
-
- int speak(in String callingApp, in String text, in int queueMode, in String[] params);
-
- boolean isSpeaking();
-
- int stop(in String callingApp);
-
- void addSpeech(in String callingApp, in String text, in String packageName, in int resId);
-
- void addSpeechFile(in String callingApp, in String text, in String filename);
-
- String[] getLanguage();
-
- int isLanguageAvailable(in String language, in String country, in String variant, in String[] params);
-
- int setLanguage(in String callingApp, in String language, in String country, in String variant);
-
- boolean synthesizeToFile(in String callingApp, in String text, in String[] params, in String outputDirectory);
-
- int playEarcon(in String callingApp, in String earcon, in int queueMode, in String[] params);
-
- void addEarcon(in String callingApp, in String earcon, in String packageName, in int resId);
-
- void addEarconFile(in String callingApp, in String earcon, in String filename);
-
- int registerCallback(in String callingApp, ITtsCallback cb);
-
- int unregisterCallback(in String callingApp, ITtsCallback cb);
-
- int playSilence(in String callingApp, in long duration, in int queueMode, in String[] params);
-
- int setEngineByPackageName(in String enginePackageName);
-
- String getDefaultEngine();
-
- boolean areDefaultsEnforced();
-}
diff --git a/core/java/android/speech/tts/PlaybackSynthesisRequest.java b/core/java/android/speech/tts/PlaybackSynthesisRequest.java
new file mode 100644
index 0000000..6f4c15b
--- /dev/null
+++ b/core/java/android/speech/tts/PlaybackSynthesisRequest.java
@@ -0,0 +1,324 @@
+/*
+ * Copyright (C) 2011 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.media.AudioFormat;
+import android.media.AudioTrack;
+import android.os.Bundle;
+import android.util.Log;
+
+/**
+ * Speech synthesis request that plays the audio as it is received.
+ */
+class PlaybackSynthesisRequest extends SynthesisRequest {
+
+ private static final String TAG = "PlaybackSynthesisRequest";
+ private static final boolean DBG = false;
+
+ private static final int MIN_AUDIO_BUFFER_SIZE = 8192;
+
+ /**
+ * Audio stream type. Must be one of the STREAM_ contants defined in
+ * {@link android.media.AudioManager}.
+ */
+ private final int mStreamType;
+
+ /**
+ * Volume, in the range [0.0f, 1.0f]. The default value is
+ * {@link TextToSpeech.Engine#DEFAULT_VOLUME} (1.0f).
+ */
+ private final float mVolume;
+
+ /**
+ * Left/right position of the audio, in the range [-1.0f, 1.0f].
+ * The default value is {@link TextToSpeech.Engine#DEFAULT_PAN} (0.0f).
+ */
+ private final float mPan;
+
+ private final Object mStateLock = new Object();
+ private AudioTrack mAudioTrack = null;
+ private boolean mStopped = false;
+ private boolean mDone = false;
+
+ PlaybackSynthesisRequest(String text, Bundle params,
+ int streamType, float volume, float pan) {
+ super(text, params);
+ mStreamType = streamType;
+ mVolume = volume;
+ mPan = pan;
+ }
+
+ @Override
+ void stop() {
+ if (DBG) Log.d(TAG, "stop()");
+ synchronized (mStateLock) {
+ mStopped = true;
+ cleanUp();
+ }
+ }
+
+ private void cleanUp() {
+ if (DBG) Log.d(TAG, "cleanUp()");
+ if (mAudioTrack != null) {
+ mAudioTrack.flush();
+ mAudioTrack.stop();
+ mAudioTrack.release();
+ mAudioTrack = null;
+ }
+ }
+
+ @Override
+ public int getMaxBufferSize() {
+ // The AudioTrack buffer will be at least MIN_AUDIO_BUFFER_SIZE, so that should always be
+ // a safe buffer size to pass in.
+ return MIN_AUDIO_BUFFER_SIZE;
+ }
+
+ @Override
+ boolean isDone() {
+ return mDone;
+ }
+
+ // TODO: add a thread that writes to the AudioTrack?
+ @Override
+ public int start(int sampleRateInHz, int audioFormat, int channelCount) {
+ if (DBG) {
+ Log.d(TAG, "start(" + sampleRateInHz + "," + audioFormat
+ + "," + channelCount + ")");
+ }
+
+ synchronized (mStateLock) {
+ if (mStopped) {
+ if (DBG) Log.d(TAG, "Request has been aborted.");
+ return TextToSpeech.ERROR;
+ }
+ if (mAudioTrack != null) {
+ Log.e(TAG, "start() called twice");
+ cleanUp();
+ return TextToSpeech.ERROR;
+ }
+
+ mAudioTrack = createStreamingAudioTrack(sampleRateInHz, audioFormat, channelCount);
+ if (mAudioTrack == null) {
+ return TextToSpeech.ERROR;
+ }
+ }
+
+ return TextToSpeech.SUCCESS;
+ }
+
+ private void setupVolume(AudioTrack audioTrack, float volume, float pan) {
+ float vol = clip(volume, 0.0f, 1.0f);
+ float panning = clip(pan, -1.0f, 1.0f);
+ float volLeft = vol;
+ float volRight = vol;
+ if (panning > 0.0f) {
+ volLeft *= (1.0f - panning);
+ } else if (panning < 0.0f) {
+ volRight *= (1.0f + panning);
+ }
+ if (DBG) Log.d(TAG, "volLeft=" + volLeft + ",volRight=" + volRight);
+ if (audioTrack.setStereoVolume(volLeft, volRight) != AudioTrack.SUCCESS) {
+ Log.e(TAG, "Failed to set volume");
+ }
+ }
+
+ private float clip(float value, float min, float max) {
+ return value > max ? max : (value < min ? min : value);
+ }
+
+ @Override
+ public int audioAvailable(byte[] buffer, int offset, int length) {
+ if (DBG) {
+ Log.d(TAG, "audioAvailable(byte[" + buffer.length + "],"
+ + offset + "," + length + ")");
+ }
+ if (length > getMaxBufferSize()) {
+ throw new IllegalArgumentException("buffer is too large (" + length + " bytes)");
+ }
+ synchronized (mStateLock) {
+ if (mStopped) {
+ if (DBG) Log.d(TAG, "Request has been aborted.");
+ return TextToSpeech.ERROR;
+ }
+ if (mAudioTrack == null) {
+ Log.e(TAG, "audioAvailable(): Not started");
+ return TextToSpeech.ERROR;
+ }
+ int playState = mAudioTrack.getPlayState();
+ if (playState == AudioTrack.PLAYSTATE_STOPPED) {
+ if (DBG) Log.d(TAG, "AudioTrack stopped, restarting");
+ mAudioTrack.play();
+ }
+ // TODO: loop until all data is written?
+ if (DBG) Log.d(TAG, "AudioTrack.write()");
+ int count = mAudioTrack.write(buffer, offset, length);
+ if (DBG) Log.d(TAG, "AudioTrack.write() returned " + count);
+ if (count < 0) {
+ Log.e(TAG, "Writing to AudioTrack failed: " + count);
+ cleanUp();
+ return TextToSpeech.ERROR;
+ } else {
+ return TextToSpeech.SUCCESS;
+ }
+ }
+ }
+
+ @Override
+ public int done() {
+ if (DBG) Log.d(TAG, "done()");
+ synchronized (mStateLock) {
+ if (mStopped) {
+ if (DBG) Log.d(TAG, "Request has been aborted.");
+ return TextToSpeech.ERROR;
+ }
+ if (mAudioTrack == null) {
+ Log.e(TAG, "done(): Not started");
+ return TextToSpeech.ERROR;
+ }
+ mDone = true;
+ cleanUp();
+ }
+ return TextToSpeech.SUCCESS;
+ }
+
+ @Override
+ public void error() {
+ if (DBG) Log.d(TAG, "error()");
+ synchronized (mStateLock) {
+ cleanUp();
+ }
+ }
+
+ @Override
+ public int completeAudioAvailable(int sampleRateInHz, int audioFormat, int channelCount,
+ byte[] buffer, int offset, int length) {
+ if (DBG) {
+ Log.d(TAG, "completeAudioAvailable(" + sampleRateInHz + "," + audioFormat
+ + "," + channelCount + "byte[" + buffer.length + "],"
+ + offset + "," + length + ")");
+ }
+
+ synchronized (mStateLock) {
+ if (mStopped) {
+ if (DBG) Log.d(TAG, "Request has been aborted.");
+ return TextToSpeech.ERROR;
+ }
+ if (mAudioTrack != null) {
+ Log.e(TAG, "start() called before completeAudioAvailable()");
+ cleanUp();
+ return TextToSpeech.ERROR;
+ }
+
+ int channelConfig = getChannelConfig(channelCount);
+ if (channelConfig < 0) {
+ Log.e(TAG, "Unsupported number of channels :" + channelCount);
+ cleanUp();
+ return TextToSpeech.ERROR;
+ }
+ int bytesPerFrame = getBytesPerFrame(audioFormat);
+ if (bytesPerFrame < 0) {
+ Log.e(TAG, "Unsupported audio format :" + audioFormat);
+ cleanUp();
+ return TextToSpeech.ERROR;
+ }
+
+ mAudioTrack = new AudioTrack(mStreamType, sampleRateInHz, channelConfig,
+ audioFormat, buffer.length, AudioTrack.MODE_STATIC);
+ if (mAudioTrack == null) {
+ return TextToSpeech.ERROR;
+ }
+
+ try {
+ mAudioTrack.write(buffer, offset, length);
+ setupVolume(mAudioTrack, mVolume, mPan);
+ mAudioTrack.play();
+ blockUntilDone(mAudioTrack, bytesPerFrame, length);
+ mDone = true;
+ if (DBG) Log.d(TAG, "Wrote data to audio track succesfully : " + length);
+ } catch (IllegalStateException ex) {
+ Log.e(TAG, "Playback error", ex);
+ return TextToSpeech.ERROR;
+ } finally {
+ cleanUp();
+ }
+ }
+
+ return TextToSpeech.SUCCESS;
+ }
+
+ private void blockUntilDone(AudioTrack audioTrack, int bytesPerFrame, int length) {
+ int lengthInFrames = length / bytesPerFrame;
+ int currentPosition = 0;
+ while ((currentPosition = audioTrack.getPlaybackHeadPosition()) < lengthInFrames) {
+ long estimatedTimeMs = ((lengthInFrames - currentPosition) * 1000) /
+ audioTrack.getSampleRate();
+ if (DBG) Log.d(TAG, "About to sleep for : " + estimatedTimeMs + " ms," +
+ " Playback position : " + currentPosition);
+ try {
+ Thread.sleep(estimatedTimeMs);
+ } catch (InterruptedException ie) {
+ break;
+ }
+ }
+ }
+
+ private int getBytesPerFrame(int audioFormat) {
+ if (audioFormat == AudioFormat.ENCODING_PCM_8BIT) {
+ return 1;
+ } else if (audioFormat == AudioFormat.ENCODING_PCM_16BIT) {
+ return 2;
+ }
+
+ return -1;
+ }
+
+ private int getChannelConfig(int channelCount) {
+ if (channelCount == 1) {
+ return AudioFormat.CHANNEL_OUT_MONO;
+ } else if (channelCount == 2){
+ return AudioFormat.CHANNEL_OUT_STEREO;
+ }
+
+ return -1;
+ }
+
+ private AudioTrack createStreamingAudioTrack(int sampleRateInHz, int audioFormat,
+ int channelCount) {
+ int channelConfig = getChannelConfig(channelCount);
+
+ if (channelConfig < 0) {
+ Log.e(TAG, "Unsupported number of channels : " + channelCount);
+ return null;
+ }
+
+ int minBufferSizeInBytes
+ = AudioTrack.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat);
+ int bufferSizeInBytes = Math.max(MIN_AUDIO_BUFFER_SIZE, minBufferSizeInBytes);
+ AudioTrack audioTrack = new AudioTrack(mStreamType, sampleRateInHz, channelConfig,
+ audioFormat, bufferSizeInBytes, AudioTrack.MODE_STREAM);
+ if (audioTrack == null) {
+ return null;
+ }
+
+ if (audioTrack.getState() != AudioTrack.STATE_INITIALIZED) {
+ audioTrack.release();
+ return null;
+ }
+ setupVolume(audioTrack, mVolume, mPan);
+ return audioTrack;
+ }
+}
diff --git a/core/java/android/speech/tts/SynthesisRequest.java b/core/java/android/speech/tts/SynthesisRequest.java
new file mode 100644
index 0000000..57ae10d
--- /dev/null
+++ b/core/java/android/speech/tts/SynthesisRequest.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2011 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.os.Bundle;
+
+/**
+ * A request for speech synthesis given to a TTS engine for processing.
+ *
+ * The engine can provide streaming audio by calling
+ * {@link #start}, then {@link #audioAvailable} until all audio has been provided, then finally
+ * {@link #done}.
+ *
+ * Alternatively, the engine can provide all the audio at once, by using
+ * {@link #completeAudioAvailable}.
+ */
+public abstract class SynthesisRequest {
+
+ private final String mText;
+ private final Bundle mParams;
+ private String mLanguage;
+ private String mCountry;
+ private String mVariant;
+ private int mSpeechRate;
+ private int mPitch;
+
+ public SynthesisRequest(String text, Bundle params) {
+ mText = text;
+ mParams = new Bundle(params);
+ }
+
+ /**
+ * Sets the locale for the request.
+ */
+ void setLanguage(String language, String country, String variant) {
+ mLanguage = language;
+ mCountry = country;
+ mVariant = variant;
+ }
+
+ /**
+ * Sets the speech rate.
+ */
+ void setSpeechRate(int speechRate) {
+ mSpeechRate = speechRate;
+ }
+
+ /**
+ * Sets the pitch.
+ */
+ void setPitch(int pitch) {
+ mPitch = pitch;
+ }
+
+ /**
+ * Gets the text which should be synthesized.
+ */
+ public String getText() {
+ return mText;
+ }
+
+ /**
+ * Gets the ISO 3-letter language code for the language to use.
+ */
+ public String getLanguage() {
+ return mLanguage;
+ }
+
+ /**
+ * Gets the ISO 3-letter country code for the language to use.
+ */
+ public String getCountry() {
+ return mCountry;
+ }
+
+ /**
+ * Gets the language variant to use.
+ */
+ public String getVariant() {
+ return mVariant;
+ }
+
+ /**
+ * Gets the speech rate to use. The normal rate is 100.
+ */
+ public int getSpeechRate() {
+ return mSpeechRate;
+ }
+
+ /**
+ * Gets the pitch to use. The normal pitch is 100.
+ */
+ public int getPitch() {
+ return mPitch;
+ }
+
+ /**
+ * Gets the additional params, if any.
+ */
+ public Bundle getParams() {
+ return mParams;
+ }
+
+ /**
+ * Gets the maximum number of bytes that the TTS engine can pass in a single call of
+ * {@link #audioAvailable}. This does not apply to {@link #completeAudioAvailable}.
+ */
+ public abstract int getMaxBufferSize();
+
+ /**
+ * Checks whether the synthesis request completed successfully.
+ */
+ abstract boolean isDone();
+
+ /**
+ * Aborts the speech request.
+ *
+ * Can be called from multiple threads.
+ */
+ abstract void stop();
+
+ /**
+ * The service should call this when it starts to synthesize audio for this
+ * request.
+ *
+ * This method should only be called on the synthesis thread,
+ * while in {@link TextToSpeechService#onSynthesizeText}.
+ *
+ * @param sampleRateInHz Sample rate in HZ of the generated audio.
+ * @param audioFormat Audio format of the generated audio. Must be one of
+ * the ENCODING_ constants defined in {@link android.media.AudioFormat}.
+ * @param channelCount The number of channels. Must be {@code 1} or {@code 2}.
+ * @return {@link TextToSpeech#SUCCESS} or {@link TextToSpeech#ERROR}.
+ */
+ public abstract int start(int sampleRateInHz, int audioFormat, int channelCount);
+
+ /**
+ * The service should call this method when synthesized audio is ready for consumption.
+ *
+ * This method should only be called on the synthesis thread,
+ * while in {@link TextToSpeechService#onSynthesizeText}.
+ *
+ * @param buffer The generated audio data. This method will not hold on to {@code buffer},
+ * so the caller is free to modify it after this method returns.
+ * @param offset The offset into {@code buffer} where the audio data starts.
+ * @param length The number of bytes of audio data in {@code buffer}. This must be
+ * less than or equal to the return value of {@link #getMaxBufferSize}.
+ * @return {@link TextToSpeech#SUCCESS} or {@link TextToSpeech#ERROR}.
+ */
+ public abstract int audioAvailable(byte[] buffer, int offset, int length);
+
+ /**
+ * The service should call this method when all the synthesized audio for a request has
+ * been passed to {@link #audioAvailable}.
+ *
+ * This method should only be called on the synthesis thread,
+ * while in {@link TextToSpeechService#onSynthesizeText}.
+ *
+ * @return {@link TextToSpeech#SUCCESS} or {@link TextToSpeech#ERROR}.
+ */
+ public abstract int done();
+
+ /**
+ * The service should call this method if the speech synthesis fails.
+ *
+ * This method should only be called on the synthesis thread,
+ * while in {@link TextToSpeechService#onSynthesizeText}.
+ */
+ public abstract void error();
+
+ /**
+ * The service can call this method instead of using {@link #start}, {@link #audioAvailable}
+ * and {@link #done} if all the audio data is available in a single buffer.
+ *
+ * @param sampleRateInHz Sample rate in HZ of the generated audio.
+ * @param audioFormat Audio format of the generated audio. Must be one of
+ * the ENCODING_ constants defined in {@link android.media.AudioFormat}.
+ * @param channelCount The number of channels. Must be {@code 1} or {@code 2}.
+ * @param buffer The generated audio data. This method will not hold on to {@code buffer},
+ * so the caller is free to modify it after this method returns.
+ * @param offset The offset into {@code buffer} where the audio data starts.
+ * @param length The number of bytes of audio data in {@code buffer}.
+ * @return {@link TextToSpeech#SUCCESS} or {@link TextToSpeech#ERROR}.
+ */
+ public abstract int completeAudioAvailable(int sampleRateInHz, int audioFormat,
+ int channelCount, byte[] buffer, int offset, int length);
+} \ No newline at end of file
diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java
index 186af70..4b19469 100755
--- a/core/java/android/speech/tts/TextToSpeech.java
+++ b/core/java/android/speech/tts/TextToSpeech.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009 Google Inc.
+ * 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
@@ -15,22 +15,31 @@
*/
package android.speech.tts;
-import android.speech.tts.ITts;
-import android.speech.tts.ITtsCallback;
-
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.content.ComponentName;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
import android.media.AudioManager;
+import android.net.Uri;
+import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
+import android.provider.Settings;
+import android.text.TextUtils;
import android.util.Log;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
+import java.util.List;
import java.util.Locale;
+import java.util.Map;
/**
*
@@ -44,14 +53,16 @@ import java.util.Locale;
*/
public class TextToSpeech {
+ private static final String TAG = "TextToSpeech";
+
/**
* Denotes a successful operation.
*/
- public static final int SUCCESS = 0;
+ public static final int SUCCESS = 0;
/**
* Denotes a generic operation failure.
*/
- public static final int ERROR = -1;
+ public static final int ERROR = -1;
/**
* Queue mode where all entries in the playback queue (media to be played
@@ -63,22 +74,19 @@ public class TextToSpeech {
*/
public static final int QUEUE_ADD = 1;
-
/**
* Denotes the language is available exactly as specified by the locale.
*/
public static final int LANG_COUNTRY_VAR_AVAILABLE = 2;
-
/**
- * Denotes the language is available for the language and country specified
+ * Denotes the language is available for the language and country specified
* by the locale, but not the variant.
*/
public static final int LANG_COUNTRY_AVAILABLE = 1;
-
/**
- * Denotes the language is available for the language by the locale,
+ * Denotes the language is available for the language by the locale,
* but not the country and variant.
*/
public static final int LANG_AVAILABLE = 0;
@@ -93,7 +101,6 @@ public class TextToSpeech {
*/
public static final int LANG_NOT_SUPPORTED = -2;
-
/**
* Broadcast Action: The TextToSpeech synthesizer has completed processing
* of all the text in the speech queue.
@@ -102,7 +109,6 @@ public class TextToSpeech {
public static final String ACTION_TTS_QUEUE_PROCESSING_COMPLETED =
"android.speech.tts.TTS_QUEUE_PROCESSING_COMPLETED";
-
/**
* Interface definition of a callback to be invoked indicating the completion of the
* TextToSpeech engine initialization.
@@ -110,103 +116,115 @@ public class TextToSpeech {
public interface OnInitListener {
/**
* Called to signal the completion of the TextToSpeech engine initialization.
+ *
* @param status {@link TextToSpeech#SUCCESS} or {@link TextToSpeech#ERROR}.
*/
public void onInit(int status);
}
/**
- * Interface definition of a callback to be invoked indicating the TextToSpeech engine has
- * completed synthesizing an utterance with an utterance ID set.
- *
+ * Listener that will be called when the TTS service has
+ * completed synthesizing an utterance. This is only called if the utterance
+ * has an utterance ID (see {@link TextToSpeech.Engine#KEY_PARAM_UTTERANCE_ID}).
*/
public interface OnUtteranceCompletedListener {
/**
- * Called to signal the completion of the synthesis of the utterance that was identified
- * with the string parameter. This identifier is the one originally passed in the
- * parameter hashmap of the synthesis request in
- * {@link TextToSpeech#speak(String, int, HashMap)} or
- * {@link TextToSpeech#synthesizeToFile(String, HashMap, String)} with the
- * {@link TextToSpeech.Engine#KEY_PARAM_UTTERANCE_ID} key.
+ * Called when an utterance has been synthesized.
+ *
* @param utteranceId the identifier of the utterance.
*/
public void onUtteranceCompleted(String utteranceId);
}
-
/**
- * Internal constants for the TextToSpeech functionality
- *
+ * Constants and parameter names for controlling text-to-speech.
*/
public class Engine {
- // default values for a TTS engine when settings are not found in the provider
+
/**
- * {@hide}
+ * Default speech rate.
+ * @hide
*/
- public static final int DEFAULT_RATE = 100; // 1x
+ public static final int DEFAULT_RATE = 100;
+
/**
- * {@hide}
+ * Default pitch.
+ * @hide
*/
- public static final int DEFAULT_PITCH = 100;// 1x
+ public static final int DEFAULT_PITCH = 100;
+
/**
- * {@hide}
+ * Default volume.
+ * @hide
*/
public static final float DEFAULT_VOLUME = 1.0f;
+
/**
- * {@hide}
- */
- protected static final String DEFAULT_VOLUME_STRING = "1.0";
- /**
- * {@hide}
+ * Default pan (centered).
+ * @hide
*/
public static final float DEFAULT_PAN = 0.0f;
- /**
- * {@hide}
- */
- protected static final String DEFAULT_PAN_STRING = "0.0";
/**
- * {@hide}
+ * Default value for {@link Settings.Secure#TTS_USE_DEFAULTS}.
+ * @hide
*/
public static final int USE_DEFAULTS = 0; // false
+
/**
- * {@hide}
+ * Package name of the default TTS engine.
+ *
+ * TODO: This should come from a system property
+ *
+ * @hide
*/
- public static final String DEFAULT_SYNTH = "com.svox.pico";
+ public static final String DEFAULT_ENGINE = "com.svox.pico";
- // default values for rendering
/**
* Default audio stream used when playing synthesized speech.
*/
public static final int DEFAULT_STREAM = AudioManager.STREAM_MUSIC;
- // return codes for a TTS engine's check data activity
/**
* Indicates success when checking the installation status of the resources used by the
* TextToSpeech engine with the {@link #ACTION_CHECK_TTS_DATA} intent.
*/
public static final int CHECK_VOICE_DATA_PASS = 1;
+
/**
* Indicates failure when checking the installation status of the resources used by the
* TextToSpeech engine with the {@link #ACTION_CHECK_TTS_DATA} intent.
*/
public static final int CHECK_VOICE_DATA_FAIL = 0;
+
/**
* Indicates erroneous data when checking the installation status of the resources used by
* the TextToSpeech engine with the {@link #ACTION_CHECK_TTS_DATA} intent.
*/
public static final int CHECK_VOICE_DATA_BAD_DATA = -1;
+
/**
* Indicates missing resources when checking the installation status of the resources used
* by the TextToSpeech engine with the {@link #ACTION_CHECK_TTS_DATA} intent.
*/
public static final int CHECK_VOICE_DATA_MISSING_DATA = -2;
+
/**
* Indicates missing storage volume when checking the installation status of the resources
* used by the TextToSpeech engine with the {@link #ACTION_CHECK_TTS_DATA} intent.
*/
public static final int CHECK_VOICE_DATA_MISSING_VOLUME = -3;
+ /**
+ * Intent for starting a TTS service. Services that handle this intent must
+ * extend {@link TextToSpeechService}. Normal applications should not use this intent
+ * directly, instead they should talk to the TTS service using the the methods in this
+ * class.
+ */
+ @SdkConstant(SdkConstantType.SERVICE_ACTION)
+ public static final String INTENT_ACTION_TTS_SERVICE =
+ "android.intent.action.TTS_SERVICE";
+
// intents to ask engine to install data or check its data
/**
* Activity Action: Triggers the platform TextToSpeech engine to
@@ -229,6 +247,7 @@ public class TextToSpeech {
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_TTS_DATA_INSTALLED =
"android.speech.tts.engine.TTS_DATA_INSTALLED";
+
/**
* Activity Action: Starts the activity from the platform TextToSpeech
* engine to verify the proper installation and availability of the
@@ -256,23 +275,36 @@ public class TextToSpeech {
public static final String ACTION_CHECK_TTS_DATA =
"android.speech.tts.engine.CHECK_TTS_DATA";
+ /**
+ * Activity intent for getting some sample text to use for demonstrating TTS.
+ *
+ * @hide This intent was used by engines written against the old API.
+ * Not sure if it should be exposed.
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_GET_SAMPLE_TEXT =
+ "android.speech.tts.engine.GET_SAMPLE_TEXT";
+
// extras for a TTS engine's check data activity
/**
* Extra information received with the {@link #ACTION_CHECK_TTS_DATA} intent where
* the TextToSpeech engine specifies the path to its resources.
*/
public static final String EXTRA_VOICE_DATA_ROOT_DIRECTORY = "dataRoot";
+
/**
* Extra information received with the {@link #ACTION_CHECK_TTS_DATA} intent where
* the TextToSpeech engine specifies the file names of its resources under the
* resource path.
*/
public static final String EXTRA_VOICE_DATA_FILES = "dataFiles";
+
/**
* Extra information received with the {@link #ACTION_CHECK_TTS_DATA} intent where
* the TextToSpeech engine specifies the locale associated with each resource file.
*/
public static final String EXTRA_VOICE_DATA_FILES_INFO = "dataFilesInfo";
+
/**
* Extra information received with the {@link #ACTION_CHECK_TTS_DATA} intent where
* the TextToSpeech engine returns an ArrayList<String> of all the available voices.
@@ -280,6 +312,7 @@ public class TextToSpeech {
* optional (ie, "eng" or "eng-USA" or "eng-USA-FEMALE").
*/
public static final String EXTRA_AVAILABLE_VOICES = "availableVoices";
+
/**
* Extra information received with the {@link #ACTION_CHECK_TTS_DATA} intent where
* the TextToSpeech engine returns an ArrayList<String> of all the unavailable voices.
@@ -287,6 +320,7 @@ public class TextToSpeech {
* optional (ie, "eng" or "eng-USA" or "eng-USA-FEMALE").
*/
public static final String EXTRA_UNAVAILABLE_VOICES = "unavailableVoices";
+
/**
* Extra information sent with the {@link #ACTION_CHECK_TTS_DATA} intent where the
* caller indicates to the TextToSpeech engine which specific sets of voice data to
@@ -309,137 +343,105 @@ public class TextToSpeech {
// keys for the parameters passed with speak commands. Hidden keys are used internally
// to maintain engine state for each TextToSpeech instance.
/**
- * {@hide}
+ * @hide
*/
public static final String KEY_PARAM_RATE = "rate";
+
/**
- * {@hide}
+ * @hide
*/
public static final String KEY_PARAM_LANGUAGE = "language";
+
/**
- * {@hide}
+ * @hide
*/
public static final String KEY_PARAM_COUNTRY = "country";
+
/**
- * {@hide}
+ * @hide
*/
public static final String KEY_PARAM_VARIANT = "variant";
+
/**
- * {@hide}
+ * @hide
*/
public static final String KEY_PARAM_ENGINE = "engine";
+
/**
- * {@hide}
+ * @hide
*/
public static final String KEY_PARAM_PITCH = "pitch";
+
/**
* Parameter key to specify the audio stream type to be used when speaking text
- * or playing back a file.
+ * or playing back a file. The value should be one of the STREAM_ constants
+ * defined in {@link AudioManager}.
+ *
* @see TextToSpeech#speak(String, int, HashMap)
* @see TextToSpeech#playEarcon(String, int, HashMap)
*/
public static final String KEY_PARAM_STREAM = "streamType";
+
/**
* Parameter key to identify an utterance in the
* {@link TextToSpeech.OnUtteranceCompletedListener} after text has been
* spoken, a file has been played back or a silence duration has elapsed.
+ *
* @see TextToSpeech#speak(String, int, HashMap)
* @see TextToSpeech#playEarcon(String, int, HashMap)
* @see TextToSpeech#synthesizeToFile(String, HashMap, String)
*/
public static final String KEY_PARAM_UTTERANCE_ID = "utteranceId";
+
/**
* Parameter key to specify the speech volume relative to the current stream type
* volume used when speaking text. Volume is specified as a float ranging from 0 to 1
* where 0 is silence, and 1 is the maximum volume (the default behavior).
+ *
* @see TextToSpeech#speak(String, int, HashMap)
* @see TextToSpeech#playEarcon(String, int, HashMap)
*/
public static final String KEY_PARAM_VOLUME = "volume";
+
/**
* Parameter key to specify how the speech is panned from left to right when speaking text.
* Pan is specified as a float ranging from -1 to +1 where -1 maps to a hard-left pan,
* 0 to center (the default behavior), and +1 to hard-right.
+ *
* @see TextToSpeech#speak(String, int, HashMap)
* @see TextToSpeech#playEarcon(String, int, HashMap)
*/
public static final String KEY_PARAM_PAN = "pan";
- // key positions in the array of cached parameters
- /**
- * {@hide}
- */
- protected static final int PARAM_POSITION_RATE = 0;
- /**
- * {@hide}
- */
- protected static final int PARAM_POSITION_LANGUAGE = 2;
- /**
- * {@hide}
- */
- protected static final int PARAM_POSITION_COUNTRY = 4;
- /**
- * {@hide}
- */
- protected static final int PARAM_POSITION_VARIANT = 6;
- /**
- * {@hide}
- */
- protected static final int PARAM_POSITION_STREAM = 8;
- /**
- * {@hide}
- */
- protected static final int PARAM_POSITION_UTTERANCE_ID = 10;
-
- /**
- * {@hide}
- */
- protected static final int PARAM_POSITION_ENGINE = 12;
-
- /**
- * {@hide}
- */
- protected static final int PARAM_POSITION_PITCH = 14;
-
- /**
- * {@hide}
- */
- protected static final int PARAM_POSITION_VOLUME = 16;
-
- /**
- * {@hide}
- */
- protected static final int PARAM_POSITION_PAN = 18;
-
-
- /**
- * {@hide}
- * Total number of cached speech parameters.
- * This number should be equal to (max param position/2) + 1.
- */
- protected static final int NB_CACHED_PARAMS = 10;
}
- /**
- * Connection needed for the TTS.
- */
- private ServiceConnection mServiceConnection;
-
- private ITts mITts = null;
- private ITtsCallback mITtscallback = null;
- private Context mContext = null;
- private String mPackageName = "";
- private OnInitListener mInitListener = null;
- private boolean mStarted = false;
+ private final Context mContext;
+ private Connection mServiceConnection;
+ private OnInitListener mInitListener;
private final Object mStartLock = new Object();
+
+ private String mRequestedEngine;
+ private final Map<String, Uri> mEarcons;
+ private final Map<String, Uri> mUtterances;
+ private final Bundle mParams = new Bundle();
+ private String mCurrentEngine = null;
+
/**
- * Used to store the cached parameters sent along with each synthesis request to the
- * TTS service.
+ * The constructor for the TextToSpeech class, using the default TTS engine.
+ * This will also initialize the associated TextToSpeech engine if it isn't already running.
+ *
+ * @param context
+ * The context this instance is running in.
+ * @param listener
+ * The {@link TextToSpeech.OnInitListener} that will be called when the
+ * TextToSpeech engine has initialized.
*/
- private String[] mCachedParams;
+ public TextToSpeech(Context context, OnInitListener listener) {
+ this(context, listener, null);
+ }
/**
- * The constructor for the TextToSpeech class.
+ * The constructor for the TextToSpeech class, using the given TTS engine.
* This will also initialize the associated TextToSpeech engine if it isn't already running.
*
* @param context
@@ -447,86 +449,95 @@ public class TextToSpeech {
* @param listener
* The {@link TextToSpeech.OnInitListener} that will be called when the
* TextToSpeech engine has initialized.
+ * @param engine Package name of the TTS engine to use.
*/
- public TextToSpeech(Context context, OnInitListener listener) {
+ public TextToSpeech(Context context, OnInitListener listener, String engine) {
mContext = context;
- mPackageName = mContext.getPackageName();
mInitListener = listener;
+ mRequestedEngine = engine;
- mCachedParams = new String[2*Engine.NB_CACHED_PARAMS]; // store key and value
- mCachedParams[Engine.PARAM_POSITION_RATE] = Engine.KEY_PARAM_RATE;
- mCachedParams[Engine.PARAM_POSITION_LANGUAGE] = Engine.KEY_PARAM_LANGUAGE;
- mCachedParams[Engine.PARAM_POSITION_COUNTRY] = Engine.KEY_PARAM_COUNTRY;
- mCachedParams[Engine.PARAM_POSITION_VARIANT] = Engine.KEY_PARAM_VARIANT;
- mCachedParams[Engine.PARAM_POSITION_STREAM] = Engine.KEY_PARAM_STREAM;
- mCachedParams[Engine.PARAM_POSITION_UTTERANCE_ID] = Engine.KEY_PARAM_UTTERANCE_ID;
- mCachedParams[Engine.PARAM_POSITION_ENGINE] = Engine.KEY_PARAM_ENGINE;
- mCachedParams[Engine.PARAM_POSITION_PITCH] = Engine.KEY_PARAM_PITCH;
- mCachedParams[Engine.PARAM_POSITION_VOLUME] = Engine.KEY_PARAM_VOLUME;
- mCachedParams[Engine.PARAM_POSITION_PAN] = Engine.KEY_PARAM_PAN;
-
- // Leave all defaults that are shown in Settings uninitialized/at the default
- // so that the values set in Settings will take effect if the application does
- // not try to change these settings itself.
- mCachedParams[Engine.PARAM_POSITION_RATE + 1] = "";
- mCachedParams[Engine.PARAM_POSITION_LANGUAGE + 1] = "";
- mCachedParams[Engine.PARAM_POSITION_COUNTRY + 1] = "";
- mCachedParams[Engine.PARAM_POSITION_VARIANT + 1] = "";
- mCachedParams[Engine.PARAM_POSITION_STREAM + 1] =
- String.valueOf(Engine.DEFAULT_STREAM);
- mCachedParams[Engine.PARAM_POSITION_UTTERANCE_ID + 1] = "";
- mCachedParams[Engine.PARAM_POSITION_ENGINE + 1] = "";
- mCachedParams[Engine.PARAM_POSITION_PITCH + 1] = "100";
- mCachedParams[Engine.PARAM_POSITION_VOLUME + 1] = Engine.DEFAULT_VOLUME_STRING;
- mCachedParams[Engine.PARAM_POSITION_PAN + 1] = Engine.DEFAULT_PAN_STRING;
+ mEarcons = new HashMap<String, Uri>();
+ mUtterances = new HashMap<String, Uri>();
initTts();
}
+ private String getPackageName() {
+ return mContext.getPackageName();
+ }
- 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;
- // Cache the default engine and current language
- setEngineByPackageName(getDefaultEngine());
- setLanguage(getLanguage());
- if (mInitListener != null) {
- // TODO manage failures and missing resources
- mInitListener.onInit(SUCCESS);
- }
- }
+ private <R> R runActionNoReconnect(Action<R> action, R errorResult, String method) {
+ return runAction(action, errorResult, method, false);
+ }
+
+ private <R> R runAction(Action<R> action, R errorResult, String method) {
+ return runAction(action, errorResult, method, true);
+ }
+
+ private <R> R runAction(Action<R> action, R errorResult, String method, boolean reconnect) {
+ synchronized (mStartLock) {
+ if (mServiceConnection == null) {
+ Log.w(TAG, method + " failed: not bound to TTS engine");
+ return errorResult;
}
+ return mServiceConnection.runAction(action, errorResult, method, reconnect);
+ }
+ }
- public void onServiceDisconnected(ComponentName name) {
- synchronized(mStartLock) {
- mITts = null;
- mInitListener = null;
- mStarted = false;
- }
+ private int initTts() {
+ String defaultEngine = getDefaultEngine();
+ String engine = defaultEngine;
+ if (!areDefaultsEnforced() && !TextUtils.isEmpty(mRequestedEngine)
+ && isEngineEnabled(engine)) {
+ engine = mRequestedEngine;
+ }
+
+ // Try requested engine
+ if (connectToEngine(engine)) {
+ return SUCCESS;
+ }
+
+ // Fall back to user's default engine if different from the already tested one
+ if (!engine.equals(defaultEngine)) {
+ if (connectToEngine(defaultEngine)) {
+ return SUCCESS;
}
- };
+ }
- Intent intent = new Intent("android.intent.action.START_TTS_SERVICE");
- intent.addCategory("android.intent.category.TTS");
- boolean bound = mContext.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
- if (!bound) {
- Log.e("TextToSpeech.java", "initTts() failed to bind to service");
- if (mInitListener != null) {
- mInitListener.onInit(ERROR);
+ // Fall back to the hardcoded default if different from the two above
+ if (!defaultEngine.equals(Engine.DEFAULT_ENGINE)
+ && !engine.equals(Engine.DEFAULT_ENGINE)) {
+ if (connectToEngine(Engine.DEFAULT_ENGINE)) {
+ return SUCCESS;
}
+ }
+
+ return ERROR;
+ }
+
+ private boolean connectToEngine(String engine) {
+ Connection connection = new Connection();
+ Intent intent = new Intent(Engine.INTENT_ACTION_TTS_SERVICE);
+ intent.setPackage(engine);
+ boolean bound = mContext.bindService(intent, connection, Context.BIND_AUTO_CREATE);
+ if (!bound) {
+ Log.e(TAG, "Failed to bind to " + engine);
+ dispatchOnInit(ERROR);
+ return false;
} else {
- // initialization listener will be called inside ServiceConnection
- Log.i("TextToSpeech.java", "initTts() successfully bound to service");
+ mCurrentEngine = engine;
+ return true;
}
- // TODO handle plugin failures
}
+ private void dispatchOnInit(int result) {
+ synchronized (mStartLock) {
+ if (mInitListener != null) {
+ mInitListener.onInit(result);
+ mInitListener = null;
+ }
+ }
+ }
/**
* Releases the resources used by the TextToSpeech engine.
@@ -534,15 +545,17 @@ public class TextToSpeech {
* so the TextToSpeech engine can be cleanly stopped.
*/
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.
- }
+ runActionNoReconnect(new Action<Void>() {
+ @Override
+ public Void run(ITextToSpeechService service) throws RemoteException {
+ service.setCallback(getPackageName(), null);
+ service.stop(getPackageName());
+ mServiceConnection.disconnect();
+ return null;
+ }
+ }, null, "shutdown");
}
-
/**
* Adds a mapping between a string of text and a sound resource in a
* package. After a call to this method, subsequent calls to
@@ -571,37 +584,12 @@ public class TextToSpeech {
* @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
*/
public int addSpeech(String text, String packagename, int resourceId) {
- synchronized(mStartLock) {
- if (!mStarted) {
- return ERROR;
- }
- try {
- mITts.addSpeech(mPackageName, text, packagename, resourceId);
- return SUCCESS;
- } catch (RemoteException e) {
- // TTS died; restart it.
- Log.e("TextToSpeech.java - addSpeech", "RemoteException");
- e.printStackTrace();
- mStarted = false;
- initTts();
- } catch (NullPointerException e) {
- // TTS died; restart it.
- Log.e("TextToSpeech.java - addSpeech", "NullPointerException");
- e.printStackTrace();
- mStarted = false;
- initTts();
- } catch (IllegalStateException e) {
- // TTS died; restart it.
- Log.e("TextToSpeech.java - addSpeech", "IllegalStateException");
- e.printStackTrace();
- mStarted = false;
- initTts();
- }
- return ERROR;
+ synchronized (mStartLock) {
+ mUtterances.put(text, makeResourceUri(packagename, resourceId));
+ return SUCCESS;
}
}
-
/**
* Adds a mapping between a string of text and a sound file. Using this, it
* is possible to add custom pronounciations for a string of text.
@@ -619,32 +607,8 @@ public class TextToSpeech {
*/
public int addSpeech(String text, String filename) {
synchronized (mStartLock) {
- if (!mStarted) {
- return ERROR;
- }
- try {
- mITts.addSpeechFile(mPackageName, text, filename);
- return SUCCESS;
- } catch (RemoteException e) {
- // TTS died; restart it.
- Log.e("TextToSpeech.java - addSpeech", "RemoteException");
- e.printStackTrace();
- mStarted = false;
- initTts();
- } catch (NullPointerException e) {
- // TTS died; restart it.
- Log.e("TextToSpeech.java - addSpeech", "NullPointerException");
- e.printStackTrace();
- mStarted = false;
- initTts();
- } catch (IllegalStateException e) {
- // TTS died; restart it.
- Log.e("TextToSpeech.java - addSpeech", "IllegalStateException");
- e.printStackTrace();
- mStarted = false;
- initTts();
- }
- return ERROR;
+ mUtterances.put(text, Uri.parse(filename));
+ return SUCCESS;
}
}
@@ -676,36 +640,11 @@ public class TextToSpeech {
*/
public int addEarcon(String earcon, String packagename, int resourceId) {
synchronized(mStartLock) {
- if (!mStarted) {
- return ERROR;
- }
- try {
- mITts.addEarcon(mPackageName, earcon, packagename, resourceId);
- return SUCCESS;
- } catch (RemoteException e) {
- // TTS died; restart it.
- Log.e("TextToSpeech.java - addEarcon", "RemoteException");
- e.printStackTrace();
- mStarted = false;
- initTts();
- } catch (NullPointerException e) {
- // TTS died; restart it.
- Log.e("TextToSpeech.java - addEarcon", "NullPointerException");
- e.printStackTrace();
- mStarted = false;
- initTts();
- } catch (IllegalStateException e) {
- // TTS died; restart it.
- Log.e("TextToSpeech.java - addEarcon", "IllegalStateException");
- e.printStackTrace();
- mStarted = false;
- initTts();
- }
- return ERROR;
+ mEarcons.put(earcon, makeResourceUri(packagename, resourceId));
+ return SUCCESS;
}
}
-
/**
* Adds a mapping between a string of text and a sound file.
* Use this to add custom earcons.
@@ -722,403 +661,211 @@ public class TextToSpeech {
* @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
*/
public int addEarcon(String earcon, String filename) {
- synchronized (mStartLock) {
- if (!mStarted) {
- return ERROR;
- }
- try {
- mITts.addEarconFile(mPackageName, earcon, filename);
- return SUCCESS;
- } catch (RemoteException e) {
- // TTS died; restart it.
- Log.e("TextToSpeech.java - addEarcon", "RemoteException");
- e.printStackTrace();
- mStarted = false;
- initTts();
- } catch (NullPointerException e) {
- // TTS died; restart it.
- Log.e("TextToSpeech.java - addEarcon", "NullPointerException");
- e.printStackTrace();
- mStarted = false;
- initTts();
- } catch (IllegalStateException e) {
- // TTS died; restart it.
- Log.e("TextToSpeech.java - addEarcon", "IllegalStateException");
- e.printStackTrace();
- mStarted = false;
- initTts();
- }
- return ERROR;
+ synchronized(mStartLock) {
+ mEarcons.put(earcon, Uri.parse(filename));
+ return SUCCESS;
}
}
+ private Uri makeResourceUri(String packageName, int resourceId) {
+ return new Uri.Builder()
+ .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
+ .encodedAuthority(packageName)
+ .appendEncodedPath(String.valueOf(resourceId))
+ .build();
+ }
/**
* Speaks the string using the specified queuing strategy and speech
* parameters.
*
- * @param text
- * The string of text to be spoken.
- * @param queueMode
- * The queuing strategy to use.
- * {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}.
- * @param params
- * The list of parameters to be used. Can be null if no parameters are given.
- * They are specified using a (key, value) pair, where the key can be
- * {@link Engine#KEY_PARAM_STREAM} or
- * {@link Engine#KEY_PARAM_UTTERANCE_ID}.
+ * @param text The string of text to be spoken.
+ * @param queueMode The queuing strategy to use, {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}.
+ * @param params Parameters for the request. Can be null.
+ * Supported parameter names:
+ * {@link Engine#KEY_PARAM_STREAM},
+ * {@link Engine#KEY_PARAM_UTTERANCE_ID},
+ * {@link Engine#KEY_PARAM_VOLUME},
+ * {@link Engine#KEY_PARAM_PAN}.
+ * Engine specific parameters may be passed in but the parameter keys
+ * must be prefixed by the name of the engine they are intended for. For example
+ * the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the
+ * engine named "com.svox.pico" if it is being used.
*
- * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
+ * @return {@link #ERROR} or {@link #SUCCESS}.
*/
- public int speak(String text, int queueMode, HashMap<String,String> params)
- {
- synchronized (mStartLock) {
- int result = ERROR;
- Log.i("TextToSpeech.java - speak", "speak text of length " + text.length());
- if (!mStarted) {
- Log.e("TextToSpeech.java - speak", "service isn't started");
- return result;
- }
- try {
- if ((params != null) && (!params.isEmpty())) {
- setCachedParam(params, Engine.KEY_PARAM_STREAM, Engine.PARAM_POSITION_STREAM);
- setCachedParam(params, Engine.KEY_PARAM_UTTERANCE_ID,
- Engine.PARAM_POSITION_UTTERANCE_ID);
- setCachedParam(params, Engine.KEY_PARAM_ENGINE, Engine.PARAM_POSITION_ENGINE);
- setCachedParam(params, Engine.KEY_PARAM_VOLUME, Engine.PARAM_POSITION_VOLUME);
- setCachedParam(params, Engine.KEY_PARAM_PAN, Engine.PARAM_POSITION_PAN);
+ public int speak(final String text, final int queueMode, final HashMap<String, String> params) {
+ return runAction(new Action<Integer>() {
+ @Override
+ public Integer run(ITextToSpeechService service) throws RemoteException {
+ Uri utteranceUri = mUtterances.get(text);
+ if (utteranceUri != null) {
+ return service.playAudio(getPackageName(), utteranceUri, queueMode,
+ getParams(params));
+ } else {
+ return service.speak(getPackageName(), text, queueMode, getParams(params));
}
- result = mITts.speak(mPackageName, text, queueMode, mCachedParams);
- } catch (RemoteException e) {
- // TTS died; restart it.
- Log.e("TextToSpeech.java - speak", "RemoteException");
- e.printStackTrace();
- mStarted = false;
- initTts();
- } catch (NullPointerException e) {
- // TTS died; restart it.
- Log.e("TextToSpeech.java - speak", "NullPointerException");
- e.printStackTrace();
- mStarted = false;
- initTts();
- } catch (IllegalStateException e) {
- // TTS died; restart it.
- Log.e("TextToSpeech.java - speak", "IllegalStateException");
- e.printStackTrace();
- mStarted = false;
- initTts();
- } finally {
- resetCachedParams();
- return result;
}
- }
+ }, ERROR, "speak");
}
-
/**
* Plays the earcon using the specified queueing mode and parameters.
+ * The earcon must already have been added with {@link #addEarcon(String, String)} or
+ * {@link #addEarcon(String, String, int)}.
*
- * @param earcon
- * The earcon that should be played
- * @param queueMode
- * {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}.
- * @param params
- * The list of parameters to be used. Can be null if no parameters are given.
- * They are specified using a (key, value) pair, where the key can be
- * {@link Engine#KEY_PARAM_STREAM} or
+ * @param earcon The earcon that should be played
+ * @param queueMode {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}.
+ * @param params Parameters for the request. Can be null.
+ * Supported parameter names:
+ * {@link Engine#KEY_PARAM_STREAM},
* {@link Engine#KEY_PARAM_UTTERANCE_ID}.
+ * Engine specific parameters may be passed in but the parameter keys
+ * must be prefixed by the name of the engine they are intended for. For example
+ * the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the
+ * engine named "com.svox.pico" if it is being used.
*
- * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
+ * @return {@link #ERROR} or {@link #SUCCESS}.
*/
- public int playEarcon(String earcon, int queueMode,
- HashMap<String,String> params) {
- synchronized (mStartLock) {
- int result = ERROR;
- if (!mStarted) {
- return result;
- }
- try {
- if ((params != null) && (!params.isEmpty())) {
- String extra = params.get(Engine.KEY_PARAM_STREAM);
- if (extra != null) {
- mCachedParams[Engine.PARAM_POSITION_STREAM + 1] = extra;
- }
- setCachedParam(params, Engine.KEY_PARAM_STREAM, Engine.PARAM_POSITION_STREAM);
- setCachedParam(params, Engine.KEY_PARAM_UTTERANCE_ID,
- Engine.PARAM_POSITION_UTTERANCE_ID);
+ public int playEarcon(final String earcon, final int queueMode,
+ final HashMap<String, String> params) {
+ return runAction(new Action<Integer>() {
+ @Override
+ public Integer run(ITextToSpeechService service) throws RemoteException {
+ Uri earconUri = mEarcons.get(earcon);
+ if (earconUri == null) {
+ return ERROR;
}
- result = mITts.playEarcon(mPackageName, earcon, queueMode, null);
- } catch (RemoteException e) {
- // TTS died; restart it.
- Log.e("TextToSpeech.java - playEarcon", "RemoteException");
- e.printStackTrace();
- mStarted = false;
- initTts();
- } catch (NullPointerException e) {
- // TTS died; restart it.
- Log.e("TextToSpeech.java - playEarcon", "NullPointerException");
- e.printStackTrace();
- mStarted = false;
- initTts();
- } catch (IllegalStateException e) {
- // TTS died; restart it.
- Log.e("TextToSpeech.java - playEarcon", "IllegalStateException");
- e.printStackTrace();
- mStarted = false;
- initTts();
- } finally {
- resetCachedParams();
- return result;
+ return service.playAudio(getPackageName(), earconUri, queueMode,
+ getParams(params));
}
- }
+ }, ERROR, "playEarcon");
}
/**
* 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
- * {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}.
- * @param params
- * The list of parameters to be used. Can be null if no parameters are given.
- * They are specified using a (key, value) pair, where the key can be
+ * @param durationInMs The duration of the silence.
+ * @param queueMode {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}.
+ * @param params Parameters for the request. Can be null.
+ * Supported parameter names:
* {@link Engine#KEY_PARAM_UTTERANCE_ID}.
+ * Engine specific parameters may be passed in but the parameter keys
+ * must be prefixed by the name of the engine they are intended for. For example
+ * the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the
+ * engine named "com.svox.pico" if it is being used.
*
- * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
+ * @return {@link #ERROR} or {@link #SUCCESS}.
*/
- public int playSilence(long durationInMs, int queueMode, HashMap<String,String> params) {
- synchronized (mStartLock) {
- int result = ERROR;
- if (!mStarted) {
- return result;
- }
- try {
- if ((params != null) && (!params.isEmpty())) {
- setCachedParam(params, Engine.KEY_PARAM_UTTERANCE_ID,
- Engine.PARAM_POSITION_UTTERANCE_ID);
- }
- result = mITts.playSilence(mPackageName, durationInMs, queueMode, mCachedParams);
- } catch (RemoteException e) {
- // TTS died; restart it.
- Log.e("TextToSpeech.java - playSilence", "RemoteException");
- e.printStackTrace();
- mStarted = false;
- initTts();
- } catch (NullPointerException e) {
- // TTS died; restart it.
- Log.e("TextToSpeech.java - playSilence", "NullPointerException");
- e.printStackTrace();
- mStarted = false;
- initTts();
- } catch (IllegalStateException e) {
- // TTS died; restart it.
- Log.e("TextToSpeech.java - playSilence", "IllegalStateException");
- e.printStackTrace();
- mStarted = false;
- initTts();
- } finally {
- resetCachedParams();
- return result;
+ public int playSilence(final long durationInMs, final int queueMode,
+ final HashMap<String, String> params) {
+ return runAction(new Action<Integer>() {
+ @Override
+ public Integer run(ITextToSpeechService service) throws RemoteException {
+ return service.playSilence(getPackageName(), durationInMs, queueMode,
+ getParams(params));
}
- }
+ }, ERROR, "playSilence");
}
-
/**
- * Returns whether or not the TextToSpeech engine is busy speaking.
+ * Checks whether the TTS engine is busy speaking.
*
- * @return Whether or not the TextToSpeech engine is busy speaking.
+ * @return {@code true} if the TTS engine is speaking.
*/
public boolean isSpeaking() {
- synchronized (mStartLock) {
- if (!mStarted) {
- return false;
+ return runAction(new Action<Boolean>() {
+ @Override
+ public Boolean run(ITextToSpeechService service) throws RemoteException {
+ return service.isSpeaking();
}
- try {
- return mITts.isSpeaking();
- } catch (RemoteException e) {
- // TTS died; restart it.
- Log.e("TextToSpeech.java - isSpeaking", "RemoteException");
- e.printStackTrace();
- mStarted = false;
- initTts();
- } catch (NullPointerException e) {
- // TTS died; restart it.
- Log.e("TextToSpeech.java - isSpeaking", "NullPointerException");
- e.printStackTrace();
- mStarted = false;
- initTts();
- } catch (IllegalStateException e) {
- // TTS died; restart it.
- Log.e("TextToSpeech.java - isSpeaking", "IllegalStateException");
- e.printStackTrace();
- mStarted = false;
- initTts();
- }
- return false;
- }
+ }, false, "isSpeaking");
}
-
/**
* Interrupts the current utterance (whether played or rendered to file) and discards other
* utterances in the queue.
*
- * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
+ * @return {@link #ERROR} or {@link #SUCCESS}.
*/
public int stop() {
- synchronized (mStartLock) {
- int result = ERROR;
- if (!mStarted) {
- return result;
- }
- try {
- result = mITts.stop(mPackageName);
- } catch (RemoteException e) {
- // TTS died; restart it.
- Log.e("TextToSpeech.java - stop", "RemoteException");
- e.printStackTrace();
- mStarted = false;
- initTts();
- } catch (NullPointerException e) {
- // TTS died; restart it.
- Log.e("TextToSpeech.java - stop", "NullPointerException");
- e.printStackTrace();
- mStarted = false;
- initTts();
- } catch (IllegalStateException e) {
- // TTS died; restart it.
- Log.e("TextToSpeech.java - stop", "IllegalStateException");
- e.printStackTrace();
- mStarted = false;
- initTts();
- } finally {
- return result;
+ return runAction(new Action<Integer>() {
+ @Override
+ public Integer run(ITextToSpeechService service) throws RemoteException {
+ return service.stop(getPackageName());
}
- }
+ }, ERROR, "stop");
}
-
/**
- * Sets the speech rate for the TextToSpeech engine.
+ * Sets the speech rate.
*
* This has no effect on any pre-recorded speech.
*
- * @param speechRate
- * The speech rate for the TextToSpeech 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).
+ * @param speechRate Speech rate. {@code 1.0} is the normal speech rate,
+ * lower values slow down the speech ({@code 0.5} is half the normal speech rate),
+ * greater values accelerate it ({@code 2.0} is twice the normal speech rate).
*
- * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
+ * @return {@link #ERROR} or {@link #SUCCESS}.
*/
public int setSpeechRate(float speechRate) {
- synchronized (mStartLock) {
- int result = ERROR;
- if (!mStarted) {
- return result;
- }
- try {
- if (speechRate > 0) {
- int rate = (int)(speechRate*100);
- mCachedParams[Engine.PARAM_POSITION_RATE + 1] = String.valueOf(rate);
- // the rate is not set here, instead it is cached so it will be associated
- // with all upcoming utterances.
- if (speechRate > 0.0f) {
- result = SUCCESS;
- } else {
- result = ERROR;
- }
+ if (speechRate > 0.0f) {
+ int intRate = (int)(speechRate * 100);
+ if (intRate > 0) {
+ synchronized (mStartLock) {
+ mParams.putInt(Engine.KEY_PARAM_RATE, intRate);
}
- } catch (NullPointerException e) {
- // TTS died; restart it.
- Log.e("TextToSpeech.java - setSpeechRate", "NullPointerException");
- e.printStackTrace();
- mStarted = false;
- initTts();
- } catch (IllegalStateException e) {
- // TTS died; restart it.
- Log.e("TextToSpeech.java - setSpeechRate", "IllegalStateException");
- e.printStackTrace();
- mStarted = false;
- initTts();
- } finally {
- return result;
+ return SUCCESS;
}
}
+ return ERROR;
}
-
/**
* Sets the speech pitch for the TextToSpeech engine.
*
* This has no effect on any pre-recorded speech.
*
- * @param pitch
- * The pitch for the TextToSpeech engine. 1 is the normal pitch,
+ * @param pitch Speech pitch. {@code 1.0} is the normal pitch,
* lower values lower the tone of the synthesized voice,
* greater values increase it.
*
- * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
+ * @return {@link #ERROR} or {@link #SUCCESS}.
*/
public int setPitch(float pitch) {
- synchronized (mStartLock) {
- int result = ERROR;
- if (!mStarted) {
- return result;
- }
- try {
- // the pitch is not set here, instead it is cached so it will be associated
- // with all upcoming utterances.
- if (pitch > 0) {
- int p = (int)(pitch*100);
- mCachedParams[Engine.PARAM_POSITION_PITCH + 1] = String.valueOf(p);
- result = SUCCESS;
+ if (pitch > 0.0f) {
+ int intPitch = (int)(pitch * 100);
+ if (intPitch > 0) {
+ synchronized (mStartLock) {
+ mParams.putInt(Engine.KEY_PARAM_PITCH, intPitch);
}
- } catch (NullPointerException e) {
- // TTS died; restart it.
- Log.e("TextToSpeech.java - setPitch", "NullPointerException");
- e.printStackTrace();
- mStarted = false;
- initTts();
- } catch (IllegalStateException e) {
- // TTS died; restart it.
- Log.e("TextToSpeech.java - setPitch", "IllegalStateException");
- e.printStackTrace();
- mStarted = false;
- initTts();
- } finally {
- return result;
+ return SUCCESS;
}
}
+ return ERROR;
}
-
/**
- * Sets the language for the TextToSpeech engine.
- * The TextToSpeech engine will try to use the closest match to the specified
+ * Sets the text-to-speech language.
+ * The TTS engine will try to use the closest match to the specified
* language as represented by the Locale, but there is no guarantee that the exact same Locale
* will be used. Use {@link #isLanguageAvailable(Locale)} to check the level of support
* before choosing the language to use for the next utterances.
*
- * @param loc
- * The locale describing the language to be used.
+ * @param loc The locale describing the language to be used.
*
- * @return code indicating the support status for the locale. See {@link #LANG_AVAILABLE},
+ * @return Code indicating the support status for the locale. See {@link #LANG_AVAILABLE},
* {@link #LANG_COUNTRY_AVAILABLE}, {@link #LANG_COUNTRY_VAR_AVAILABLE},
* {@link #LANG_MISSING_DATA} and {@link #LANG_NOT_SUPPORTED}.
*/
- public int setLanguage(Locale loc) {
- synchronized (mStartLock) {
- int result = LANG_NOT_SUPPORTED;
- if (!mStarted) {
- return result;
- }
- if (loc == null) {
- return result;
- }
- try {
+ public int setLanguage(final Locale loc) {
+ return runAction(new Action<Integer>() {
+ @Override
+ public Integer run(ITextToSpeechService service) throws RemoteException {
+ if (loc == null) {
+ return LANG_NOT_SUPPORTED;
+ }
String language = loc.getISO3Language();
String country = loc.getISO3Country();
String variant = loc.getVariant();
@@ -1126,386 +873,331 @@ public class TextToSpeech {
// the available parts.
// Note that the language is not actually set here, instead it is cached so it
// will be associated with all upcoming utterances.
- result = mITts.isLanguageAvailable(language, country, variant, mCachedParams);
+ int result = service.loadLanguage(language, country, variant);
if (result >= LANG_AVAILABLE){
- mCachedParams[Engine.PARAM_POSITION_LANGUAGE + 1] = language;
- if (result >= LANG_COUNTRY_AVAILABLE){
- mCachedParams[Engine.PARAM_POSITION_COUNTRY + 1] = country;
- } else {
- mCachedParams[Engine.PARAM_POSITION_COUNTRY + 1] = "";
- }
- if (result >= LANG_COUNTRY_VAR_AVAILABLE){
- mCachedParams[Engine.PARAM_POSITION_VARIANT + 1] = variant;
- } else {
- mCachedParams[Engine.PARAM_POSITION_VARIANT + 1] = "";
+ if (result < LANG_COUNTRY_VAR_AVAILABLE) {
+ variant = "";
+ if (result < LANG_COUNTRY_AVAILABLE) {
+ country = "";
+ }
}
+ mParams.putString(Engine.KEY_PARAM_LANGUAGE, language);
+ mParams.putString(Engine.KEY_PARAM_COUNTRY, country);
+ mParams.putString(Engine.KEY_PARAM_VARIANT, variant);
}
- } catch (RemoteException e) {
- // TTS died; restart it.
- Log.e("TextToSpeech.java - setLanguage", "RemoteException");
- e.printStackTrace();
- mStarted = false;
- initTts();
- } catch (NullPointerException e) {
- // TTS died; restart it.
- Log.e("TextToSpeech.java - setLanguage", "NullPointerException");
- e.printStackTrace();
- mStarted = false;
- initTts();
- } catch (IllegalStateException e) {
- // TTS died; restart it.
- Log.e("TextToSpeech.java - setLanguage", "IllegalStateException");
- e.printStackTrace();
- mStarted = false;
- initTts();
- } finally {
return result;
}
- }
+ }, LANG_NOT_SUPPORTED, "setLanguage");
}
-
/**
* Returns a Locale instance describing the language currently being used by the TextToSpeech
* engine.
+ *
* @return language, country (if any) and variant (if any) used by the engine stored in a Locale
- * instance, or null is the TextToSpeech engine has failed.
+ * instance, or {@code null} on error.
*/
public Locale getLanguage() {
- synchronized (mStartLock) {
- if (!mStarted) {
- return null;
- }
- try {
- // Only do a call to the native synth if there is nothing in the cached params
- if (mCachedParams[Engine.PARAM_POSITION_LANGUAGE + 1].length() < 1){
- String[] locStrings = mITts.getLanguage();
- if ((locStrings != null) && (locStrings.length == 3)) {
- return new Locale(locStrings[0], locStrings[1], locStrings[2]);
- } else {
- return null;
- }
- } else {
- return new Locale(mCachedParams[Engine.PARAM_POSITION_LANGUAGE + 1],
- mCachedParams[Engine.PARAM_POSITION_COUNTRY + 1],
- mCachedParams[Engine.PARAM_POSITION_VARIANT + 1]);
+ return runAction(new Action<Locale>() {
+ @Override
+ public Locale run(ITextToSpeechService service) throws RemoteException {
+ String[] locStrings = service.getLanguage();
+ if (locStrings != null && locStrings.length == 3) {
+ return new Locale(locStrings[0], locStrings[1], locStrings[2]);
}
- } catch (RemoteException e) {
- // TTS died; restart it.
- Log.e("TextToSpeech.java - getLanguage", "RemoteException");
- e.printStackTrace();
- mStarted = false;
- initTts();
- } catch (NullPointerException e) {
- // TTS died; restart it.
- Log.e("TextToSpeech.java - getLanguage", "NullPointerException");
- e.printStackTrace();
- mStarted = false;
- initTts();
- } catch (IllegalStateException e) {
- // TTS died; restart it.
- Log.e("TextToSpeech.java - getLanguage", "IllegalStateException");
- e.printStackTrace();
- mStarted = false;
- initTts();
+ return null;
}
- return null;
- }
+ }, null, "getLanguage");
}
/**
* Checks if the specified language as represented by the Locale is available and supported.
*
- * @param loc
- * The Locale describing the language to be used.
+ * @param loc The Locale describing the language to be used.
*
- * @return code indicating the support status for the locale. See {@link #LANG_AVAILABLE},
+ * @return Code indicating the support status for the locale. See {@link #LANG_AVAILABLE},
* {@link #LANG_COUNTRY_AVAILABLE}, {@link #LANG_COUNTRY_VAR_AVAILABLE},
* {@link #LANG_MISSING_DATA} and {@link #LANG_NOT_SUPPORTED}.
*/
- public int isLanguageAvailable(Locale loc) {
- synchronized (mStartLock) {
- int result = LANG_NOT_SUPPORTED;
- if (!mStarted) {
- return result;
+ public int isLanguageAvailable(final Locale loc) {
+ return runAction(new Action<Integer>() {
+ @Override
+ public Integer run(ITextToSpeechService service) throws RemoteException {
+ return service.isLanguageAvailable(loc.getISO3Language(),
+ loc.getISO3Country(), loc.getVariant());
}
- try {
- result = mITts.isLanguageAvailable(loc.getISO3Language(),
- loc.getISO3Country(), loc.getVariant(), mCachedParams);
- } catch (RemoteException e) {
- // TTS died; restart it.
- Log.e("TextToSpeech.java - isLanguageAvailable", "RemoteException");
- e.printStackTrace();
- mStarted = false;
- initTts();
- } catch (NullPointerException e) {
- // TTS died; restart it.
- Log.e("TextToSpeech.java - isLanguageAvailable", "NullPointerException");
- e.printStackTrace();
- mStarted = false;
- initTts();
- } catch (IllegalStateException e) {
- // TTS died; restart it.
- Log.e("TextToSpeech.java - isLanguageAvailable", "IllegalStateException");
- e.printStackTrace();
- mStarted = false;
- initTts();
- } finally {
- return result;
- }
- }
+ }, LANG_NOT_SUPPORTED, "isLanguageAvailable");
}
-
/**
* Synthesizes the given text to a file using the specified parameters.
*
- * @param text
- * The String of text that should be synthesized
- * @param params
- * The list of parameters to be used. Can be null if no parameters are given.
- * They are specified using a (key, value) pair, where the key can be
+ * @param text Thetext that should be synthesized
+ * @param params Parameters for the request. Can be null.
+ * Supported parameter names:
* {@link Engine#KEY_PARAM_UTTERANCE_ID}.
- * @param filename
- * The string that gives the full output filename; it should be
+ * Engine specific parameters may be passed in but the parameter keys
+ * must be prefixed by the name of the engine they are intended for. For example
+ * the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the
+ * engine named "com.svox.pico" if it is being used.
+ * @param filename Absolute file filename to write the generated audio data to.It should be
* something like "/sdcard/myappsounds/mysound.wav".
*
- * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
+ * @return {@link #ERROR} or {@link #SUCCESS}.
*/
- public int synthesizeToFile(String text, HashMap<String,String> params,
- String filename) {
- Log.i("TextToSpeech.java", "synthesizeToFile()");
- synchronized (mStartLock) {
- int result = ERROR;
- Log.i("TextToSpeech.java - synthesizeToFile", "synthesizeToFile text of length "
- + text.length());
- if (!mStarted) {
- Log.e("TextToSpeech.java - synthesizeToFile", "service isn't started");
- return result;
+ public int synthesizeToFile(final String text, final HashMap<String, String> params,
+ final String filename) {
+ return runAction(new Action<Integer>() {
+ @Override
+ public Integer run(ITextToSpeechService service) throws RemoteException {
+ return service.synthesizeToFile(getPackageName(), text, filename,
+ getParams(params));
}
- try {
- if ((params != null) && (!params.isEmpty())) {
- // no need to read the stream type here
- setCachedParam(params, Engine.KEY_PARAM_UTTERANCE_ID,
- Engine.PARAM_POSITION_UTTERANCE_ID);
- setCachedParam(params, Engine.KEY_PARAM_ENGINE, Engine.PARAM_POSITION_ENGINE);
+ }, ERROR, "synthesizeToFile");
+ }
+
+ private Bundle getParams(HashMap<String, String> params) {
+ if (params != null && !params.isEmpty()) {
+ Bundle bundle = new Bundle(mParams);
+ copyIntParam(bundle, params, Engine.KEY_PARAM_STREAM);
+ copyStringParam(bundle, params, Engine.KEY_PARAM_UTTERANCE_ID);
+ copyFloatParam(bundle, params, Engine.KEY_PARAM_VOLUME);
+ copyFloatParam(bundle, params, Engine.KEY_PARAM_PAN);
+
+ // Copy over all parameters that start with the name of the
+ // engine that we are currently connected to. The engine is
+ // free to interpret them as it chooses.
+ if (!TextUtils.isEmpty(mCurrentEngine)) {
+ for (Map.Entry<String, String> entry : params.entrySet()) {
+ final String key = entry.getKey();
+ if (key != null && key.startsWith(mCurrentEngine)) {
+ bundle.putString(key, entry.getValue());
+ }
}
- result = mITts.synthesizeToFile(mPackageName, text, mCachedParams, filename) ?
- SUCCESS : ERROR;
- } catch (RemoteException e) {
- // TTS died; restart it.
- Log.e("TextToSpeech.java - synthesizeToFile", "RemoteException");
- e.printStackTrace();
- mStarted = false;
- initTts();
- } catch (NullPointerException e) {
- // TTS died; restart it.
- Log.e("TextToSpeech.java - synthesizeToFile", "NullPointerException");
- e.printStackTrace();
- mStarted = false;
- initTts();
- } catch (IllegalStateException e) {
- // TTS died; restart it.
- Log.e("TextToSpeech.java - synthesizeToFile", "IllegalStateException");
- e.printStackTrace();
- mStarted = false;
- initTts();
- } finally {
- resetCachedParams();
- return result;
}
+
+ return bundle;
+ } else {
+ return mParams;
}
}
+ private void copyStringParam(Bundle bundle, HashMap<String, String> params, String key) {
+ String value = params.get(key);
+ if (value != null) {
+ bundle.putString(key, value);
+ }
+ }
- /**
- * Convenience method to reset the cached parameters to the current default values
- * if they are not persistent between calls to the service.
- */
- private void resetCachedParams() {
- mCachedParams[Engine.PARAM_POSITION_STREAM + 1] =
- String.valueOf(Engine.DEFAULT_STREAM);
- mCachedParams[Engine.PARAM_POSITION_UTTERANCE_ID+ 1] = "";
- mCachedParams[Engine.PARAM_POSITION_VOLUME + 1] = Engine.DEFAULT_VOLUME_STRING;
- mCachedParams[Engine.PARAM_POSITION_PAN + 1] = Engine.DEFAULT_PAN_STRING;
+ private void copyIntParam(Bundle bundle, HashMap<String, String> params, String key) {
+ String valueString = params.get(key);
+ if (!TextUtils.isEmpty(valueString)) {
+ try {
+ int value = Integer.parseInt(valueString);
+ bundle.putInt(key, value);
+ } catch (NumberFormatException ex) {
+ // don't set the value in the bundle
+ }
+ }
}
- /**
- * Convenience method to save a parameter in the cached parameter array, at the given index,
- * for a property saved in the given hashmap.
- */
- private void setCachedParam(HashMap<String,String> params, String key, int keyIndex) {
- String extra = params.get(key);
- if (extra != null) {
- mCachedParams[keyIndex+1] = extra;
+ private void copyFloatParam(Bundle bundle, HashMap<String, String> params, String key) {
+ String valueString = params.get(key);
+ if (!TextUtils.isEmpty(valueString)) {
+ try {
+ float value = Float.parseFloat(valueString);
+ bundle.putFloat(key, value);
+ } catch (NumberFormatException ex) {
+ // don't set the value in the bundle
+ }
}
}
/**
- * Sets the OnUtteranceCompletedListener that will fire when an utterance completes.
+ * Sets the listener that will be notified when synthesis of an utterance completes.
*
- * @param listener
- * The OnUtteranceCompletedListener
+ * @param listener The listener to use.
*
- * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
+ * @return {@link #ERROR} or {@link #SUCCESS}.
*/
- public int setOnUtteranceCompletedListener(
- final OnUtteranceCompletedListener listener) {
- synchronized (mStartLock) {
- int result = ERROR;
- if (!mStarted) {
- return result;
- }
- mITtscallback = new ITtsCallback.Stub() {
- public void utteranceCompleted(String utteranceId) throws RemoteException {
- if (listener != null) {
- listener.onUtteranceCompleted(utteranceId);
+ public int setOnUtteranceCompletedListener(final OnUtteranceCompletedListener listener) {
+ return runAction(new Action<Integer>() {
+ @Override
+ public Integer run(ITextToSpeechService service) throws RemoteException {
+ ITextToSpeechCallback.Stub callback = new ITextToSpeechCallback.Stub() {
+ public void utteranceCompleted(String utteranceId) {
+ if (listener != null) {
+ listener.onUtteranceCompleted(utteranceId);
+ }
}
- }
- };
- try {
- result = mITts.registerCallback(mPackageName, mITtscallback);
- } catch (RemoteException e) {
- // TTS died; restart it.
- Log.e("TextToSpeech.java - registerCallback", "RemoteException");
- e.printStackTrace();
- mStarted = false;
- initTts();
- } catch (NullPointerException e) {
- // TTS died; restart it.
- Log.e("TextToSpeech.java - registerCallback", "NullPointerException");
- e.printStackTrace();
- mStarted = false;
- initTts();
- } catch (IllegalStateException e) {
- // TTS died; restart it.
- Log.e("TextToSpeech.java - registerCallback", "IllegalStateException");
- e.printStackTrace();
- mStarted = false;
- initTts();
- } finally {
- return result;
+ };
+ service.setCallback(getPackageName(), callback);
+ return SUCCESS;
}
- }
+ }, ERROR, "setOnUtteranceCompletedListener");
}
/**
- * Sets the speech synthesis engine to be used by its packagename.
+ * Sets the TTS engine to use.
*
- * @param enginePackageName
- * The packagename for the synthesis engine (ie, "com.svox.pico")
+ * @param enginePackageName The package name for the synthesis engine (e.g. "com.svox.pico")
*
- * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
+ * @return {@link #ERROR} or {@link #SUCCESS}.
*/
+ // TODO: add @Deprecated{This method does not tell the caller when the new engine
+ // has been initialized. You should create a new TextToSpeech object with the new
+ // engine instead.}
public int setEngineByPackageName(String enginePackageName) {
- synchronized (mStartLock) {
- int result = TextToSpeech.ERROR;
- if (!mStarted) {
- return result;
- }
- try {
- result = mITts.setEngineByPackageName(enginePackageName);
- if (result == TextToSpeech.SUCCESS){
- mCachedParams[Engine.PARAM_POSITION_ENGINE + 1] = enginePackageName;
- }
- } catch (RemoteException e) {
- // TTS died; restart it.
- Log.e("TextToSpeech.java - setEngineByPackageName", "RemoteException");
- e.printStackTrace();
- mStarted = false;
- initTts();
- } catch (NullPointerException e) {
- // TTS died; restart it.
- Log.e("TextToSpeech.java - setEngineByPackageName", "NullPointerException");
- e.printStackTrace();
- mStarted = false;
- initTts();
- } catch (IllegalStateException e) {
- // TTS died; restart it.
- Log.e("TextToSpeech.java - setEngineByPackageName", "IllegalStateException");
- e.printStackTrace();
- mStarted = false;
- initTts();
- } finally {
- return result;
+ mRequestedEngine = enginePackageName;
+ return initTts();
+ }
+
+ /**
+ * Gets the package name of the default speech synthesis engine.
+ *
+ * @return Package name of the TTS engine that the user has chosen as their default.
+ */
+ public String getDefaultEngine() {
+ String engine = Settings.Secure.getString(mContext.getContentResolver(),
+ Settings.Secure.TTS_DEFAULT_SYNTH);
+ return engine != null ? engine : Engine.DEFAULT_ENGINE;
+ }
+
+ /**
+ * Checks whether the user's settings should override settings requested by the calling
+ * application.
+ */
+ public boolean areDefaultsEnforced() {
+ return Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.TTS_USE_DEFAULTS, Engine.USE_DEFAULTS) == 1;
+ }
+
+ private boolean isEngineEnabled(String engine) {
+ if (Engine.DEFAULT_ENGINE.equals(engine)) {
+ return true;
+ }
+ for (String enabled : getEnabledEngines()) {
+ if (engine.equals(enabled)) {
+ return true;
}
}
+ return false;
}
+ private String[] getEnabledEngines() {
+ String str = Settings.Secure.getString(mContext.getContentResolver(),
+ Settings.Secure.TTS_ENABLED_PLUGINS);
+ if (TextUtils.isEmpty(str)) {
+ return new String[0];
+ }
+ return str.split(" ");
+ }
/**
- * Gets the packagename of the default speech synthesis engine.
+ * Gets a list of all installed TTS engines.
*
- * @return Packagename of the TTS engine that the user has chosen as their default.
+ * @return A list of engine info objects. The list can be empty, but will never by {@code null}.
*/
- public String getDefaultEngine() {
- synchronized (mStartLock) {
- String engineName = "";
- if (!mStarted) {
- return engineName;
+ public List<EngineInfo> getEngines() {
+ PackageManager pm = mContext.getPackageManager();
+ Intent intent = new Intent(Engine.INTENT_ACTION_TTS_SERVICE);
+ List<ResolveInfo> resolveInfos =
+ pm.queryIntentServices(intent, PackageManager.MATCH_DEFAULT_ONLY);
+ if (resolveInfos == null) return Collections.emptyList();
+ List<EngineInfo> engines = new ArrayList<EngineInfo>(resolveInfos.size());
+ for (ResolveInfo resolveInfo : resolveInfos) {
+ ServiceInfo service = resolveInfo.serviceInfo;
+ if (service != null) {
+ EngineInfo engine = new EngineInfo();
+ // Using just the package name isn't great, since it disallows having
+ // multiple engines in the same package, but that's what the existing API does.
+ engine.name = service.packageName;
+ CharSequence label = service.loadLabel(pm);
+ engine.label = TextUtils.isEmpty(label) ? engine.name : label.toString();
+ engine.icon = service.getIconResource();
+ engines.add(engine);
+ }
+ }
+ return engines;
+ }
+
+ private class Connection implements ServiceConnection {
+ private ITextToSpeechService mService;
+
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ Log.i(TAG, "Connected to " + name);
+ synchronized(mStartLock) {
+ if (mServiceConnection != null) {
+ // Disconnect any previous service connection
+ mServiceConnection.disconnect();
+ }
+ mServiceConnection = this;
+ mService = ITextToSpeechService.Stub.asInterface(service);
+ dispatchOnInit(SUCCESS);
}
+ }
+
+ public void onServiceDisconnected(ComponentName name) {
+ synchronized(mStartLock) {
+ mService = null;
+ // If this is the active connection, clear it
+ if (mServiceConnection == this) {
+ mServiceConnection = null;
+ }
+ }
+ }
+
+ public void disconnect() {
+ mContext.unbindService(this);
+ }
+
+ public <R> R runAction(Action<R> action, R errorResult, String method, boolean reconnect) {
try {
- engineName = mITts.getDefaultEngine();
- } catch (RemoteException e) {
- // TTS died; restart it.
- Log.e("TextToSpeech.java - setEngineByPackageName", "RemoteException");
- e.printStackTrace();
- mStarted = false;
- initTts();
- } catch (NullPointerException e) {
- // TTS died; restart it.
- Log.e("TextToSpeech.java - setEngineByPackageName", "NullPointerException");
- e.printStackTrace();
- mStarted = false;
- initTts();
- } catch (IllegalStateException e) {
- // TTS died; restart it.
- Log.e("TextToSpeech.java - setEngineByPackageName", "IllegalStateException");
- e.printStackTrace();
- mStarted = false;
- initTts();
- } finally {
- return engineName;
+ synchronized (mStartLock) {
+ if (mService == null) {
+ Log.w(TAG, method + " failed: not connected to TTS engine");
+ return errorResult;
+ }
+ return action.run(mService);
+ }
+ } catch (RemoteException ex) {
+ Log.e(TAG, method + " failed", ex);
+ if (reconnect) {
+ disconnect();
+ initTts();
+ }
+ return errorResult;
}
}
}
+ private interface Action<R> {
+ R run(ITextToSpeechService service) throws RemoteException;
+ }
/**
- * Returns whether or not the user is forcing their defaults to override the
- * Text-To-Speech settings set by applications.
+ * Information about an installed text-to-speech engine.
*
- * @return Whether or not defaults are enforced.
+ * @see TextToSpeech#getEngines
*/
- public boolean areDefaultsEnforced() {
- synchronized (mStartLock) {
- boolean defaultsEnforced = false;
- if (!mStarted) {
- return defaultsEnforced;
- }
- try {
- defaultsEnforced = mITts.areDefaultsEnforced();
- } catch (RemoteException e) {
- // TTS died; restart it.
- Log.e("TextToSpeech.java - areDefaultsEnforced", "RemoteException");
- e.printStackTrace();
- mStarted = false;
- initTts();
- } catch (NullPointerException e) {
- // TTS died; restart it.
- Log.e("TextToSpeech.java - areDefaultsEnforced", "NullPointerException");
- e.printStackTrace();
- mStarted = false;
- initTts();
- } catch (IllegalStateException e) {
- // TTS died; restart it.
- Log.e("TextToSpeech.java - areDefaultsEnforced", "IllegalStateException");
- e.printStackTrace();
- mStarted = false;
- initTts();
- } finally {
- return defaultsEnforced;
- }
+ public static class EngineInfo {
+ /**
+ * Engine package name..
+ */
+ public String name;
+ /**
+ * Localized label for the engine.
+ */
+ public String label;
+ /**
+ * Icon for the engine.
+ */
+ public int icon;
+
+ @Override
+ public String toString() {
+ return "EngineInfo{name=" + name + "}";
}
+
}
}
diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java
new file mode 100644
index 0000000..f32474f
--- /dev/null
+++ b/core/java/android/speech/tts/TextToSpeechService.java
@@ -0,0 +1,713 @@
+/*
+ * Copyright (C) 2011 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.app.Service;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.ConditionVariable;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.MessageQueue;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
+import android.provider.Settings;
+import android.speech.tts.TextToSpeech.Engine;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Locale;
+
+
+/**
+ * Abstract base class for TTS engine implementations.
+ */
+public abstract class TextToSpeechService extends Service {
+
+ private static final boolean DBG = false;
+ private static final String TAG = "TextToSpeechService";
+
+ private static final int MAX_SPEECH_ITEM_CHAR_LENGTH = 4000;
+ private static final String SYNTH_THREAD_NAME = "SynthThread";
+
+ private SynthHandler mSynthHandler;
+
+ private CallbackMap mCallbacks;
+
+ @Override
+ public void onCreate() {
+ if (DBG) Log.d(TAG, "onCreate()");
+ super.onCreate();
+
+ SynthThread synthThread = new SynthThread();
+ synthThread.start();
+ mSynthHandler = new SynthHandler(synthThread.getLooper());
+
+ mCallbacks = new CallbackMap();
+
+ // Load default language
+ onLoadLanguage(getDefaultLanguage(), getDefaultCountry(), getDefaultVariant());
+ }
+
+ @Override
+ public void onDestroy() {
+ if (DBG) Log.d(TAG, "onDestroy()");
+
+ // Tell the synthesizer to stop
+ mSynthHandler.quit();
+
+ // Unregister all callbacks.
+ mCallbacks.kill();
+
+ super.onDestroy();
+ }
+
+ /**
+ * Checks whether the engine supports a given language.
+ *
+ * Can be called on multiple threads.
+ *
+ * @param lang ISO-3 language code.
+ * @param country ISO-3 country code. May be empty or null.
+ * @param variant Language variant. May be empty or null.
+ * @return Code indicating the support status for the locale.
+ * One of {@link TextToSpeech#LANG_AVAILABLE},
+ * {@link TextToSpeech#LANG_COUNTRY_AVAILABLE},
+ * {@link TextToSpeech#LANG_COUNTRY_VAR_AVAILABLE},
+ * {@link TextToSpeech#LANG_MISSING_DATA}
+ * {@link TextToSpeech#LANG_NOT_SUPPORTED}.
+ */
+ protected abstract int onIsLanguageAvailable(String lang, String country, String variant);
+
+ /**
+ * Returns the language, country and variant currently being used by the TTS engine.
+ *
+ * Can be called on multiple threads.
+ *
+ * @return A 3-element array, containing language (ISO 3-letter code),
+ * country (ISO 3-letter code) and variant used by the engine.
+ * The country and variant may be {@code ""}. If country is empty, then variant must
+ * be empty too.
+ * @see Locale#getISO3Language()
+ * @see Locale#getISO3Country()
+ * @see Locale#getVariant()
+ */
+ protected abstract String[] onGetLanguage();
+
+ /**
+ * Notifies the engine that it should load a speech synthesis language. There is no guarantee
+ * that this method is always called before the language is used for synthesis. It is merely
+ * a hint to the engine that it will probably get some synthesis requests for this language
+ * at some point in the future.
+ *
+ * Can be called on multiple threads.
+ *
+ * @param lang ISO-3 language code.
+ * @param country ISO-3 country code. May be empty or null.
+ * @param variant Language variant. May be empty or null.
+ * @return Code indicating the support status for the locale.
+ * One of {@link TextToSpeech#LANG_AVAILABLE},
+ * {@link TextToSpeech#LANG_COUNTRY_AVAILABLE},
+ * {@link TextToSpeech#LANG_COUNTRY_VAR_AVAILABLE},
+ * {@link TextToSpeech#LANG_MISSING_DATA}
+ * {@link TextToSpeech#LANG_NOT_SUPPORTED}.
+ */
+ protected abstract int onLoadLanguage(String lang, String country, String variant);
+
+ /**
+ * Notifies the service that it should stop any in-progress speech synthesis.
+ * This method can be called even if no speech synthesis is currently in progress.
+ *
+ * Can be called on multiple threads, but not on the synthesis thread.
+ */
+ protected abstract void onStop();
+
+ /**
+ * Tells the service to synthesize speech from the given text. This method should
+ * block until the synthesis is finished.
+ *
+ * Called on the synthesis thread.
+ *
+ * @param request The synthesis request. The method should use the methods in the request
+ * object to communicate the results of the synthesis.
+ */
+ protected abstract void onSynthesizeText(SynthesisRequest request);
+
+ private boolean areDefaultsEnforced() {
+ return getSecureSettingInt(Settings.Secure.TTS_USE_DEFAULTS,
+ TextToSpeech.Engine.USE_DEFAULTS) == 1;
+ }
+
+ private int getDefaultSpeechRate() {
+ return getSecureSettingInt(Settings.Secure.TTS_DEFAULT_RATE, Engine.DEFAULT_RATE);
+ }
+
+ private String getDefaultLanguage() {
+ return getSecureSettingString(Settings.Secure.TTS_DEFAULT_LANG,
+ Locale.getDefault().getISO3Language());
+ }
+
+ private String getDefaultCountry() {
+ return getSecureSettingString(Settings.Secure.TTS_DEFAULT_COUNTRY,
+ Locale.getDefault().getISO3Country());
+ }
+
+ private String getDefaultVariant() {
+ return getSecureSettingString(Settings.Secure.TTS_DEFAULT_VARIANT,
+ Locale.getDefault().getVariant());
+ }
+
+ private int getSecureSettingInt(String name, int defaultValue) {
+ return Settings.Secure.getInt(getContentResolver(), name, defaultValue);
+ }
+
+ private String getSecureSettingString(String name, String defaultValue) {
+ String value = Settings.Secure.getString(getContentResolver(), name);
+ return value != null ? value : defaultValue;
+ }
+
+ /**
+ * Synthesizer thread. This thread is used to run {@link SynthHandler}.
+ */
+ private class SynthThread extends HandlerThread implements MessageQueue.IdleHandler {
+
+ private boolean mFirstIdle = true;
+
+ public SynthThread() {
+ super(SYNTH_THREAD_NAME, android.os.Process.THREAD_PRIORITY_AUDIO);
+ }
+
+ @Override
+ protected void onLooperPrepared() {
+ getLooper().getQueue().addIdleHandler(this);
+ }
+
+ @Override
+ public boolean queueIdle() {
+ if (mFirstIdle) {
+ mFirstIdle = false;
+ } else {
+ broadcastTtsQueueProcessingCompleted();
+ }
+ return true;
+ }
+
+ private void broadcastTtsQueueProcessingCompleted() {
+ Intent i = new Intent(TextToSpeech.ACTION_TTS_QUEUE_PROCESSING_COMPLETED);
+ if (DBG) Log.d(TAG, "Broadcasting: " + i);
+ sendBroadcast(i);
+ }
+ }
+
+ private class SynthHandler extends Handler {
+
+ private SpeechItem mCurrentSpeechItem = null;
+
+ public SynthHandler(Looper looper) {
+ super(looper);
+ }
+
+ private void dispatchUtteranceCompleted(SpeechItem item) {
+ String utteranceId = item.getUtteranceId();
+ if (!TextUtils.isEmpty(utteranceId)) {
+ mCallbacks.dispatchUtteranceCompleted(item.getCallingApp(), utteranceId);
+ }
+ }
+
+ private synchronized SpeechItem getCurrentSpeechItem() {
+ return mCurrentSpeechItem;
+ }
+
+ private synchronized SpeechItem setCurrentSpeechItem(SpeechItem speechItem) {
+ SpeechItem old = mCurrentSpeechItem;
+ mCurrentSpeechItem = speechItem;
+ return old;
+ }
+
+ public boolean isSpeaking() {
+ return getCurrentSpeechItem() != null;
+ }
+
+ public void quit() {
+ // Don't process any more speech items
+ getLooper().quit();
+ // Stop the current speech item
+ SpeechItem current = setCurrentSpeechItem(null);
+ if (current != null) {
+ current.stop();
+ }
+ }
+
+ /**
+ * Adds a speech item to the queue.
+ *
+ * Called on a service binder thread.
+ */
+ public int enqueueSpeechItem(int queueMode, final SpeechItem speechItem) {
+ if (!speechItem.isValid()) {
+ return TextToSpeech.ERROR;
+ }
+ // TODO: The old code also supported the undocumented queueMode == 2,
+ // which clears out all pending items from the calling app, as well as all
+ // non-file items from other apps.
+ if (queueMode == TextToSpeech.QUEUE_FLUSH) {
+ stop(speechItem.getCallingApp());
+ }
+ Runnable runnable = new Runnable() {
+ @Override
+ public void run() {
+ setCurrentSpeechItem(speechItem);
+ if (speechItem.play() == TextToSpeech.SUCCESS) {
+ dispatchUtteranceCompleted(speechItem);
+ }
+ setCurrentSpeechItem(null);
+ }
+ };
+ Message msg = Message.obtain(this, runnable);
+ // The obj is used to remove all callbacks from the given app in stop(String).
+ msg.obj = speechItem.getCallingApp();
+ if (sendMessage(msg)) {
+ return TextToSpeech.SUCCESS;
+ } else {
+ Log.w(TAG, "SynthThread has quit");
+ return TextToSpeech.ERROR;
+ }
+ }
+
+ /**
+ * Stops all speech output and removes any utterances still in the queue for
+ * the calling app.
+ *
+ * Called on a service binder thread.
+ */
+ public int stop(String callingApp) {
+ if (TextUtils.isEmpty(callingApp)) {
+ return TextToSpeech.ERROR;
+ }
+ removeCallbacksAndMessages(callingApp);
+ SpeechItem current = setCurrentSpeechItem(null);
+ if (current != null && TextUtils.equals(callingApp, current.getCallingApp())) {
+ current.stop();
+ }
+ return TextToSpeech.SUCCESS;
+ }
+ }
+
+ /**
+ * An item in the synth thread queue.
+ */
+ private static abstract class SpeechItem {
+ private final String mCallingApp;
+ protected final Bundle mParams;
+ private boolean mStarted = false;
+ private boolean mStopped = false;
+
+ public SpeechItem(String callingApp, Bundle params) {
+ mCallingApp = callingApp;
+ mParams = params;
+ }
+
+ public String getCallingApp() {
+ return mCallingApp;
+ }
+
+ /**
+ * Checker whether the item is valid. If this method returns false, the item should not
+ * be played.
+ */
+ public abstract boolean isValid();
+
+ /**
+ * Plays the speech item. Blocks until playback is finished.
+ * Must not be called more than once.
+ *
+ * Only called on the synthesis thread.
+ *
+ * @return {@link TextToSpeech#SUCCESS} or {@link TextToSpeech#ERROR}.
+ */
+ public int play() {
+ synchronized (this) {
+ if (mStarted) {
+ throw new IllegalStateException("play() called twice");
+ }
+ mStarted = true;
+ }
+ return playImpl();
+ }
+
+ /**
+ * Stops the speech item.
+ * Must not be called more than once.
+ *
+ * Can be called on multiple threads, but not on the synthesis thread.
+ */
+ public void stop() {
+ synchronized (this) {
+ if (mStopped) {
+ throw new IllegalStateException("stop() called twice");
+ }
+ mStopped = true;
+ }
+ stopImpl();
+ }
+
+ protected abstract int playImpl();
+
+ protected abstract void stopImpl();
+
+ public int getStreamType() {
+ return getIntParam(Engine.KEY_PARAM_STREAM, Engine.DEFAULT_STREAM);
+ }
+
+ public float getVolume() {
+ return getFloatParam(Engine.KEY_PARAM_VOLUME, Engine.DEFAULT_VOLUME);
+ }
+
+ public float getPan() {
+ return getFloatParam(Engine.KEY_PARAM_PAN, Engine.DEFAULT_PAN);
+ }
+
+ public String getUtteranceId() {
+ return getStringParam(Engine.KEY_PARAM_UTTERANCE_ID, null);
+ }
+
+ protected String getStringParam(String key, String defaultValue) {
+ return mParams == null ? defaultValue : mParams.getString(key, defaultValue);
+ }
+
+ protected int getIntParam(String key, int defaultValue) {
+ return mParams == null ? defaultValue : mParams.getInt(key, defaultValue);
+ }
+
+ protected float getFloatParam(String key, float defaultValue) {
+ return mParams == null ? defaultValue : mParams.getFloat(key, defaultValue);
+ }
+ }
+
+ private class SynthesisSpeechItem extends SpeechItem {
+ private final String mText;
+ private SynthesisRequest mSynthesisRequest;
+
+ public SynthesisSpeechItem(String callingApp, Bundle params, String text) {
+ super(callingApp, params);
+ mText = text;
+ }
+
+ public String getText() {
+ return mText;
+ }
+
+ @Override
+ public boolean isValid() {
+ if (TextUtils.isEmpty(mText)) {
+ Log.w(TAG, "Got empty text");
+ return false;
+ }
+ if (mText.length() >= MAX_SPEECH_ITEM_CHAR_LENGTH){
+ Log.w(TAG, "Text too long: " + mText.length() + " chars");
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ protected int playImpl() {
+ SynthesisRequest synthesisRequest;
+ synchronized (this) {
+ mSynthesisRequest = createSynthesisRequest();
+ synthesisRequest = mSynthesisRequest;
+ }
+ setRequestParams(synthesisRequest);
+ TextToSpeechService.this.onSynthesizeText(synthesisRequest);
+ return synthesisRequest.isDone() ? TextToSpeech.SUCCESS : TextToSpeech.ERROR;
+ }
+
+ protected SynthesisRequest createSynthesisRequest() {
+ return new PlaybackSynthesisRequest(mText, mParams,
+ getStreamType(), getVolume(), getPan());
+ }
+
+ private void setRequestParams(SynthesisRequest request) {
+ if (areDefaultsEnforced()) {
+ request.setLanguage(getDefaultLanguage(), getDefaultCountry(), getDefaultVariant());
+ request.setSpeechRate(getDefaultSpeechRate());
+ } else {
+ request.setLanguage(getLanguage(), getCountry(), getVariant());
+ request.setSpeechRate(getSpeechRate());
+ }
+ request.setPitch(getPitch());
+ }
+
+ @Override
+ protected void stopImpl() {
+ SynthesisRequest synthesisRequest;
+ synchronized (this) {
+ synthesisRequest = mSynthesisRequest;
+ }
+ synthesisRequest.stop();
+ TextToSpeechService.this.onStop();
+ }
+
+ public String getLanguage() {
+ return getStringParam(Engine.KEY_PARAM_LANGUAGE, getDefaultLanguage());
+ }
+
+ private boolean hasLanguage() {
+ return !TextUtils.isEmpty(getStringParam(Engine.KEY_PARAM_LANGUAGE, null));
+ }
+
+ private String getCountry() {
+ if (!hasLanguage()) return getDefaultCountry();
+ return getStringParam(Engine.KEY_PARAM_COUNTRY, "");
+ }
+
+ private String getVariant() {
+ if (!hasLanguage()) return getDefaultVariant();
+ return getStringParam(Engine.KEY_PARAM_VARIANT, "");
+ }
+
+ private int getSpeechRate() {
+ return getIntParam(Engine.KEY_PARAM_RATE, getDefaultSpeechRate());
+ }
+
+ private int getPitch() {
+ return getIntParam(Engine.KEY_PARAM_PITCH, Engine.DEFAULT_PITCH);
+ }
+ }
+
+ private class SynthesisToFileSpeechItem extends SynthesisSpeechItem {
+ private final File mFile;
+
+ public SynthesisToFileSpeechItem(String callingApp, Bundle params, String text,
+ File file) {
+ super(callingApp, params, text);
+ mFile = file;
+ }
+
+ @Override
+ public boolean isValid() {
+ if (!super.isValid()) {
+ return false;
+ }
+ return checkFile(mFile);
+ }
+
+ @Override
+ protected SynthesisRequest createSynthesisRequest() {
+ return new FileSynthesisRequest(getText(), mParams, mFile);
+ }
+
+ /**
+ * Checks that the given file can be used for synthesis output.
+ */
+ private boolean checkFile(File file) {
+ try {
+ if (file.exists()) {
+ Log.v(TAG, "File " + file + " exists, deleting.");
+ if (!file.delete()) {
+ Log.e(TAG, "Failed to delete " + file);
+ return false;
+ }
+ }
+ if (!file.createNewFile()) {
+ Log.e(TAG, "Can't create file " + file);
+ return false;
+ }
+ if (!file.delete()) {
+ Log.e(TAG, "Failed to delete " + file);
+ return false;
+ }
+ return true;
+ } catch (IOException e) {
+ Log.e(TAG, "Can't use " + file + " due to exception " + e);
+ return false;
+ }
+ }
+ }
+
+ private class AudioSpeechItem extends SpeechItem {
+
+ private final BlockingMediaPlayer mPlayer;
+
+ public AudioSpeechItem(String callingApp, Bundle params, Uri uri) {
+ super(callingApp, params);
+ mPlayer = new BlockingMediaPlayer(TextToSpeechService.this, uri, getStreamType());
+ }
+
+ @Override
+ public boolean isValid() {
+ return true;
+ }
+
+ @Override
+ protected int playImpl() {
+ return mPlayer.startAndWait() ? TextToSpeech.SUCCESS : TextToSpeech.ERROR;
+ }
+
+ @Override
+ protected void stopImpl() {
+ mPlayer.stop();
+ }
+ }
+
+ private class SilenceSpeechItem extends SpeechItem {
+ private final long mDuration;
+ private final ConditionVariable mDone;
+
+ public SilenceSpeechItem(String callingApp, Bundle params, long duration) {
+ super(callingApp, params);
+ mDuration = duration;
+ mDone = new ConditionVariable();
+ }
+
+ @Override
+ public boolean isValid() {
+ return true;
+ }
+
+ @Override
+ protected int playImpl() {
+ boolean aborted = mDone.block(mDuration);
+ return aborted ? TextToSpeech.ERROR : TextToSpeech.SUCCESS;
+ }
+
+ @Override
+ protected void stopImpl() {
+ mDone.open();
+ }
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ if (TextToSpeech.Engine.INTENT_ACTION_TTS_SERVICE.equals(intent.getAction())) {
+ return mBinder;
+ }
+ return null;
+ }
+
+ /**
+ * Binder returned from {@code #onBind(Intent)}. The methods in this class can be
+ * called called from several different threads.
+ */
+ private final ITextToSpeechService.Stub mBinder = new ITextToSpeechService.Stub() {
+
+ public int speak(String callingApp, String text, int queueMode, Bundle params) {
+ SpeechItem item = new SynthesisSpeechItem(callingApp, params, text);
+ return mSynthHandler.enqueueSpeechItem(queueMode, item);
+ }
+
+ public int synthesizeToFile(String callingApp, String text, String filename,
+ Bundle params) {
+ File file = new File(filename);
+ SpeechItem item = new SynthesisToFileSpeechItem(callingApp, params, text, file);
+ return mSynthHandler.enqueueSpeechItem(TextToSpeech.QUEUE_ADD, item);
+ }
+
+ public int playAudio(String callingApp, Uri audioUri, int queueMode, Bundle params) {
+ SpeechItem item = new AudioSpeechItem(callingApp, params, audioUri);
+ return mSynthHandler.enqueueSpeechItem(queueMode, item);
+ }
+
+ public int playSilence(String callingApp, long duration, int queueMode, Bundle params) {
+ SpeechItem item = new SilenceSpeechItem(callingApp, params, duration);
+ return mSynthHandler.enqueueSpeechItem(queueMode, item);
+ }
+
+ public boolean isSpeaking() {
+ return mSynthHandler.isSpeaking();
+ }
+
+ public int stop(String callingApp) {
+ return mSynthHandler.stop(callingApp);
+ }
+
+ public String[] getLanguage() {
+ return onGetLanguage();
+ }
+
+ public int isLanguageAvailable(String lang, String country, String variant) {
+ return onIsLanguageAvailable(lang, country, variant);
+ }
+
+ public int loadLanguage(String lang, String country, String variant) {
+ return onLoadLanguage(lang, country, variant);
+ }
+
+ public void setCallback(String packageName, ITextToSpeechCallback cb) {
+ mCallbacks.setCallback(packageName, cb);
+ }
+ };
+
+ private class CallbackMap extends RemoteCallbackList<ITextToSpeechCallback> {
+
+ private final HashMap<String, ITextToSpeechCallback> mAppToCallback
+ = new HashMap<String, ITextToSpeechCallback>();
+
+ public void setCallback(String packageName, ITextToSpeechCallback cb) {
+ synchronized (mAppToCallback) {
+ ITextToSpeechCallback old;
+ if (cb != null) {
+ register(cb, packageName);
+ old = mAppToCallback.put(packageName, cb);
+ } else {
+ old = mAppToCallback.remove(packageName);
+ }
+ if (old != null && old != cb) {
+ unregister(old);
+ }
+ }
+ }
+
+ public void dispatchUtteranceCompleted(String packageName, String utteranceId) {
+ ITextToSpeechCallback cb;
+ synchronized (mAppToCallback) {
+ cb = mAppToCallback.get(packageName);
+ }
+ if (cb == null) return;
+ try {
+ cb.utteranceCompleted(utteranceId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Callback failed: " + e);
+ }
+ }
+
+ @Override
+ public void onCallbackDied(ITextToSpeechCallback callback, Object cookie) {
+ String packageName = (String) cookie;
+ synchronized (mAppToCallback) {
+ mAppToCallback.remove(packageName);
+ }
+ mSynthHandler.stop(packageName);
+ }
+
+ @Override
+ public void kill() {
+ synchronized (mAppToCallback) {
+ mAppToCallback.clear();
+ super.kill();
+ }
+ }
+
+ }
+
+}
diff --git a/core/java/android/text/BoringLayout.java b/core/java/android/text/BoringLayout.java
index 9309b05..757a8c3 100644
--- a/core/java/android/text/BoringLayout.java
+++ b/core/java/android/text/BoringLayout.java
@@ -234,18 +234,17 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback
* provided Metrics object (or a new one if the provided one was null)
* if boring.
*/
- public static Metrics isBoring(CharSequence text, TextPaint paint,
- Metrics metrics) {
+ public static Metrics isBoring(CharSequence text, TextPaint paint, Metrics metrics) {
char[] temp = TextUtils.obtain(500);
- int len = text.length();
+ int length = text.length();
boolean boring = true;
outer:
- for (int i = 0; i < len; i += 500) {
+ for (int i = 0; i < length; i += 500) {
int j = i + 500;
- if (j > len)
- j = len;
+ if (j > length)
+ j = length;
TextUtils.getChars(text, i, j, temp, 0);
@@ -265,7 +264,7 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback
if (boring && text instanceof Spanned) {
Spanned sp = (Spanned) text;
- Object[] styles = sp.getSpans(0, text.length(), ParagraphStyle.class);
+ Object[] styles = sp.getSpans(0, length, ParagraphStyle.class);
if (styles.length > 0) {
boring = false;
}
@@ -278,7 +277,7 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback
}
TextLine line = TextLine.obtain();
- line.set(paint, text, 0, text.length(), Layout.DIR_LEFT_TO_RIGHT,
+ line.set(paint, text, 0, length, Layout.DIR_LEFT_TO_RIGHT,
Layout.DIRS_ALL_LEFT_TO_RIGHT, false, null);
fm.width = (int) FloatMath.ceil(line.metrics(fm));
TextLine.recycle(line);
@@ -289,52 +288,63 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback
}
}
- @Override public int getHeight() {
+ @Override
+ public int getHeight() {
return mBottom;
}
- @Override public int getLineCount() {
+ @Override
+ public int getLineCount() {
return 1;
}
- @Override public int getLineTop(int line) {
+ @Override
+ public int getLineTop(int line) {
if (line == 0)
return 0;
else
return mBottom;
}
- @Override public int getLineDescent(int line) {
+ @Override
+ public int getLineDescent(int line) {
return mDesc;
}
- @Override public int getLineStart(int line) {
+ @Override
+ public int getLineStart(int line) {
if (line == 0)
return 0;
else
return getText().length();
}
- @Override public int getParagraphDirection(int line) {
+ @Override
+ public int getParagraphDirection(int line) {
return DIR_LEFT_TO_RIGHT;
}
- @Override public boolean getLineContainsTab(int line) {
+ @Override
+ public boolean getLineContainsTab(int line) {
return false;
}
- @Override public float getLineMax(int line) {
+ @Override
+ public float getLineMax(int line) {
return mMax;
}
- @Override public final Directions getLineDirections(int line) {
+ @Override
+ public final Directions getLineDirections(int line) {
return Layout.DIRS_ALL_LEFT_TO_RIGHT;
}
+ @Override
public int getTopPadding() {
return mTopPadding;
}
+ @Override
public int getBottomPadding() {
return mBottomPadding;
}
diff --git a/core/java/android/text/CharSequenceIterator.java b/core/java/android/text/CharSequenceIterator.java
new file mode 100644
index 0000000..4b8ac10
--- /dev/null
+++ b/core/java/android/text/CharSequenceIterator.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.text;
+
+import java.text.CharacterIterator;
+
+/** {@hide} */
+public class CharSequenceIterator implements CharacterIterator {
+ private final CharSequence mValue;
+
+ private final int mLength;
+ private int mIndex;
+
+ public CharSequenceIterator(CharSequence value) {
+ mValue = value;
+ mLength = value.length();
+ mIndex = 0;
+ }
+
+ @Override
+ public Object clone() {
+ try {
+ return super.clone();
+ } catch (CloneNotSupportedException e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public char current() {
+ if (mIndex == mLength) {
+ return DONE;
+ }
+ return mValue.charAt(mIndex);
+ }
+
+ /** {@inheritDoc} */
+ public int getBeginIndex() {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ public int getEndIndex() {
+ return mLength;
+ }
+
+ /** {@inheritDoc} */
+ public int getIndex() {
+ return mIndex;
+ }
+
+ /** {@inheritDoc} */
+ public char first() {
+ return setIndex(0);
+ }
+
+ /** {@inheritDoc} */
+ public char last() {
+ return setIndex(mLength - 1);
+ }
+
+ /** {@inheritDoc} */
+ public char next() {
+ if (mIndex == mLength) {
+ return DONE;
+ }
+ return setIndex(mIndex + 1);
+ }
+
+ /** {@inheritDoc} */
+ public char previous() {
+ if (mIndex == 0) {
+ return DONE;
+ }
+ return setIndex(mIndex - 1);
+ }
+
+ /** {@inheritDoc} */
+ public char setIndex(int index) {
+ if ((index < 0) || (index > mLength)) {
+ throw new IllegalArgumentException("Valid range is [" + 0 + "..." + mLength + "]");
+ }
+ mIndex = index;
+ return current();
+ }
+}
diff --git a/core/java/android/text/GraphicsOperations.java b/core/java/android/text/GraphicsOperations.java
index d426d12..831ccc5 100644
--- a/core/java/android/text/GraphicsOperations.java
+++ b/core/java/android/text/GraphicsOperations.java
@@ -58,6 +58,13 @@ extends CharSequence
int flags, float[] advances, int advancesIndex, Paint paint);
/**
+ * Just like {@link Paint#getTextRunAdvances}.
+ * @hide
+ */
+ float getTextRunAdvances(int start, int end, int contextStart, int contextEnd,
+ int flags, float[] advances, int advancesIndex, Paint paint, int reserved);
+
+ /**
* Just like {@link Paint#getTextRunCursor}.
* @hide
*/
diff --git a/core/java/android/text/Selection.java b/core/java/android/text/Selection.java
index 13cb5e6..679e2cc 100644
--- a/core/java/android/text/Selection.java
+++ b/core/java/android/text/Selection.java
@@ -16,6 +16,8 @@
package android.text;
+import java.text.BreakIterator;
+
/**
* Utility class for manipulating cursors and selections in CharSequences.
@@ -38,7 +40,7 @@ public class Selection {
else
return -1;
}
-
+
/**
* Return the offset of the selection edge or cursor, or -1 if
* there is no selection or cursor.
@@ -57,7 +59,7 @@ public class Selection {
// private static int pin(int value, int min, int max) {
// return value < min ? 0 : (value > max ? max : value);
// }
-
+
/**
* Set the selection anchor to <code>start</code> and the selection edge
* to <code>stop</code>.
@@ -69,7 +71,7 @@ public class Selection {
int ostart = getSelectionStart(text);
int oend = getSelectionEnd(text);
-
+
if (ostart != start || oend != stop) {
text.setSpan(SELECTION_START, start, start,
Spanned.SPAN_POINT_POINT|Spanned.SPAN_INTERMEDIATE);
@@ -357,6 +359,42 @@ public class Selection {
return true;
}
+ /** {@hide} */
+ public static interface PositionIterator {
+ public static final int DONE = BreakIterator.DONE;
+
+ public int preceding(int position);
+ public int following(int position);
+ }
+
+ /** {@hide} */
+ public static boolean moveToPreceding(
+ Spannable text, PositionIterator iter, boolean extendSelection) {
+ final int offset = iter.preceding(getSelectionEnd(text));
+ if (offset != PositionIterator.DONE) {
+ if (extendSelection) {
+ extendSelection(text, offset);
+ } else {
+ setSelection(text, offset);
+ }
+ }
+ return true;
+ }
+
+ /** {@hide} */
+ public static boolean moveToFollowing(
+ Spannable text, PositionIterator iter, boolean extendSelection) {
+ final int offset = iter.following(getSelectionEnd(text));
+ if (offset != PositionIterator.DONE) {
+ if (extendSelection) {
+ extendSelection(text, offset);
+ } else {
+ setSelection(text, offset);
+ }
+ }
+ return true;
+ }
+
private static int findEdge(Spannable text, Layout layout, int dir) {
int pt = getSelectionEnd(text);
int line = layout.getLineForOffset(pt);
@@ -419,7 +457,7 @@ public class Selection {
private static final class START implements NoCopySpan { }
private static final class END implements NoCopySpan { }
-
+
/*
* Public constants
*/
diff --git a/core/java/android/text/SpannableStringBuilder.java b/core/java/android/text/SpannableStringBuilder.java
index ea5cdfe..6bde802 100644
--- a/core/java/android/text/SpannableStringBuilder.java
+++ b/core/java/android/text/SpannableStringBuilder.java
@@ -20,6 +20,7 @@ import com.android.internal.util.ArrayUtils;
import android.graphics.Canvas;
import android.graphics.Paint;
+import android.text.style.SuggestionSpan;
import java.lang.reflect.Array;
@@ -277,8 +278,7 @@ implements CharSequence, GetChars, Spannable, Editable, Appendable,
TextWatcher[] recipients = null;
if (notify)
- recipients = sendTextWillChange(start, end - start,
- tbend - tbstart);
+ recipients = sendTextWillChange(start, end - start, tbend - tbstart);
for (int i = mSpanCount - 1; i >= 0; i--) {
if ((mSpanFlags[i] & SPAN_PARAGRAPH) == SPAN_PARAGRAPH) {
@@ -353,6 +353,7 @@ implements CharSequence, GetChars, Spannable, Editable, Appendable,
// no need for span fixup on pure insertion
if (tbend > tbstart && end - start == 0) {
if (notify) {
+ removeSuggestionSpans(start, end);
sendTextChange(recipients, start, end - start, tbend - tbstart);
sendTextHasChanged(recipients);
}
@@ -384,20 +385,10 @@ implements CharSequence, GetChars, Spannable, Editable, Appendable,
}
// remove 0-length SPAN_EXCLUSIVE_EXCLUSIVE
- // XXX send notification on removal
-
if (mSpanEnds[i] < mSpanStarts[i]) {
- System.arraycopy(mSpans, i + 1,
- mSpans, i, mSpanCount - (i + 1));
- System.arraycopy(mSpanStarts, i + 1,
- mSpanStarts, i, mSpanCount - (i + 1));
- System.arraycopy(mSpanEnds, i + 1,
- mSpanEnds, i, mSpanCount - (i + 1));
- System.arraycopy(mSpanFlags, i + 1,
- mSpanFlags, i, mSpanCount - (i + 1));
-
- mSpanCount--;
+ removeSpan(i);
}
+ removeSuggestionSpans(start, end);
}
if (notify) {
@@ -408,6 +399,32 @@ implements CharSequence, GetChars, Spannable, Editable, Appendable,
return ret;
}
+ /**
+ * Removes the SuggestionSpan that overlap the [start, end] range, and that would
+ * not make sense anymore after the change.
+ */
+ private void removeSuggestionSpans(int start, int end) {
+ for (int i = mSpanCount - 1; i >= 0; i--) {
+ final int spanEnd = mSpanEnds[i];
+ final int spanSpart = mSpanStarts[i];
+ if ((mSpans[i] instanceof SuggestionSpan) && (
+ (spanSpart < start && spanEnd > start) ||
+ (spanSpart < end && spanEnd > end))) {
+ removeSpan(i);
+ }
+ }
+ }
+
+ private void removeSpan(int i) {
+ // XXX send notification on removal
+ System.arraycopy(mSpans, i + 1, mSpans, i, mSpanCount - (i + 1));
+ System.arraycopy(mSpanStarts, i + 1, mSpanStarts, i, mSpanCount - (i + 1));
+ System.arraycopy(mSpanEnds, i + 1, mSpanEnds, i, mSpanCount - (i + 1));
+ System.arraycopy(mSpanFlags, i + 1, mSpanFlags, i, mSpanCount - (i + 1));
+
+ mSpanCount--;
+ }
+
// Documentation from interface
public SpannableStringBuilder replace(int start, int end, CharSequence tb) {
return replace(start, end, tb, 0, tb.length());
@@ -465,16 +482,15 @@ implements CharSequence, GetChars, Spannable, Editable, Appendable,
mGapStart++;
mGapLength--;
- if (mGapLength < 1)
+ if (mGapLength < 1) {
new Exception("mGapLength < 1").printStackTrace();
+ }
int oldlen = (end + 1) - start;
- int inserted = change(false, start + 1, start + 1,
- tb, tbstart, tbend);
+ int inserted = change(false, start + 1, start + 1, tb, tbstart, tbend);
change(false, start, start + 1, "", 0, 0);
- change(false, start + inserted, start + inserted + oldlen - 1,
- "", 0, 0);
+ change(false, start + inserted, start + inserted + oldlen - 1, "", 0, 0);
/*
* Special case to keep the cursor in the same position
@@ -1170,6 +1186,35 @@ implements CharSequence, GetChars, Spannable, Editable, Appendable,
}
/**
+ * Don't call this yourself -- exists for Paint to use internally.
+ * {@hide}
+ */
+ public float getTextRunAdvances(int start, int end, int contextStart, int contextEnd, int flags,
+ float[] advances, int advancesPos, Paint p, int reserved) {
+
+ float ret;
+
+ int contextLen = contextEnd - contextStart;
+ int len = end - start;
+
+ if (end <= mGapStart) {
+ ret = p.getTextRunAdvances(mText, start, len, contextStart, contextLen,
+ flags, advances, advancesPos, reserved);
+ } else if (start >= mGapStart) {
+ ret = p.getTextRunAdvances(mText, start + mGapLength, len,
+ contextStart + mGapLength, contextLen, flags, advances, advancesPos, reserved);
+ } else {
+ char[] buf = TextUtils.obtain(contextLen);
+ getChars(contextStart, contextEnd, buf, 0);
+ ret = p.getTextRunAdvances(buf, start - contextStart, len,
+ 0, contextLen, flags, advances, advancesPos, reserved);
+ TextUtils.recycle(buf);
+ }
+
+ return ret;
+ }
+
+ /**
* Returns the next cursor position in the run. This avoids placing the cursor between
* surrogates, between characters that form conjuncts, between base characters and combining
* marks, or within a reordering cluster.
@@ -1245,7 +1290,6 @@ implements CharSequence, GetChars, Spannable, Editable, Appendable,
private int[] mSpanFlags;
private int mSpanCount;
- private static final int MARK = 1;
private static final int POINT = 2;
private static final int PARAGRAPH = 3;
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index a826a97..9e48eff 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -235,6 +235,8 @@ public class StaticLayout extends Layout {
} else {
MetricAffectingSpan[] spans =
spanned.getSpans(spanStart, spanEnd, MetricAffectingSpan.class);
+ spans = TextUtils.removeEmptySpans(spans, spanned,
+ MetricAffectingSpan.class);
measured.addStyleRun(paint, spans, spanLen, fm);
}
}
diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java
index 90279d1..0f8097a 100644
--- a/core/java/android/text/TextLine.java
+++ b/core/java/android/text/TextLine.java
@@ -127,12 +127,12 @@ class TextLine {
boolean hasReplacement = false;
if (text instanceof Spanned) {
mSpanned = (Spanned) text;
- hasReplacement = mSpanned.getSpans(start, limit,
- ReplacementSpan.class).length > 0;
+ ReplacementSpan[] spans = mSpanned.getSpans(start, limit, ReplacementSpan.class);
+ spans = TextUtils.removeEmptySpans(spans, mSpanned, ReplacementSpan.class);
+ hasReplacement = spans.length > 0;
}
- mCharsValid = hasReplacement || hasTabs ||
- directions != Layout.DIRS_ALL_LEFT_TO_RIGHT;
+ mCharsValid = hasReplacement || hasTabs || directions != Layout.DIRS_ALL_LEFT_TO_RIGHT;
if (mCharsValid) {
if (mChars == null || mChars.length < mLen) {
@@ -147,10 +147,11 @@ class TextLine {
// zero-width characters.
char[] chars = mChars;
for (int i = start, inext; i < limit; i = inext) {
- inext = mSpanned.nextSpanTransition(i, limit,
- ReplacementSpan.class);
- if (mSpanned.getSpans(i, inext, ReplacementSpan.class)
- .length > 0) { // transition into a span
+ inext = mSpanned.nextSpanTransition(i, limit, ReplacementSpan.class);
+ ReplacementSpan[] spans = mSpanned.getSpans(i, inext, ReplacementSpan.class);
+ spans = TextUtils.removeEmptySpans(spans, mSpanned, ReplacementSpan.class);
+ if (spans.length > 0) {
+ // transition into a span
chars[i - start] = '\ufffc';
for (int j = i - start + 1, e = inext - start; j < e; ++j) {
chars[j] = '\ufeff'; // used as ZWNBS, marks positions to skip
@@ -197,7 +198,6 @@ class TextLine {
boolean runIsRtl = (runs[i+1] & Layout.RUN_RTL_FLAG) != 0;
int segstart = runStart;
- char[] chars = mChars;
for (int j = mHasTabs ? runStart : runLimit; j <= runLimit; j++) {
int codept = 0;
Bitmap bm = null;
@@ -629,6 +629,7 @@ class TextLine {
MetricAffectingSpan[] spans = mSpanned.getSpans(mStart + spanStart,
mStart + spanLimit, MetricAffectingSpan.class);
+ spans = TextUtils.removeEmptySpans(spans, mSpanned, MetricAffectingSpan.class);
if (spans.length > 0) {
ReplacementSpan replacement = null;
@@ -814,6 +815,13 @@ class TextLine {
int limit, boolean runIsRtl, Canvas c, float x, int top, int y,
int bottom, FontMetricsInt fmi, boolean needWidth) {
+ // Case of an empty line, make sure we update fmi according to mPaint
+ if (start == measureLimit) {
+ TextPaint wp = mWorkPaint;
+ wp.set(mPaint);
+ return handleText(wp, 0, 0, 0, 0, runIsRtl, c, x, top, y, bottom, fmi, needWidth);
+ }
+
// Shaping needs to take into account context up to metric boundaries,
// but rendering needs to take into account character style boundaries.
// So we iterate through metric runs to get metric bounds,
@@ -835,6 +843,7 @@ class TextLine {
mlimit = inext < measureLimit ? inext : measureLimit;
MetricAffectingSpan[] spans = mSpanned.getSpans(mStart + i,
mStart + mlimit, MetricAffectingSpan.class);
+ spans = TextUtils.removeEmptySpans(spans, mSpanned, MetricAffectingSpan.class);
if (spans.length > 0) {
ReplacementSpan replacement = null;
@@ -868,6 +877,7 @@ class TextLine {
CharacterStyle[] spans = mSpanned.getSpans(mStart + j,
mStart + jnext, CharacterStyle.class);
+ spans = TextUtils.removeEmptySpans(spans, mSpanned, CharacterStyle.class);
wp.set(mPaint);
for (int k = 0; k < spans.length; k++) {
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index cdb7228..6741059 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -37,6 +37,7 @@ import android.text.style.ScaleXSpan;
import android.text.style.StrikethroughSpan;
import android.text.style.StyleSpan;
import android.text.style.SubscriptSpan;
+import android.text.style.SuggestionSpan;
import android.text.style.SuperscriptSpan;
import android.text.style.TextAppearanceSpan;
import android.text.style.TypefaceSpan;
@@ -44,6 +45,7 @@ import android.text.style.URLSpan;
import android.text.style.UnderlineSpan;
import android.util.Printer;
+import java.lang.reflect.Array;
import java.util.Iterator;
import java.util.regex.Pattern;
@@ -54,7 +56,7 @@ public class TextUtils {
public static void getChars(CharSequence s, int start, int end,
char[] dest, int destoff) {
- Class c = s.getClass();
+ Class<? extends CharSequence> c = s.getClass();
if (c == String.class)
((String) s).getChars(start, end, dest, destoff);
@@ -75,7 +77,7 @@ public class TextUtils {
}
public static int indexOf(CharSequence s, char ch, int start) {
- Class c = s.getClass();
+ Class<? extends CharSequence> c = s.getClass();
if (c == String.class)
return ((String) s).indexOf(ch, start);
@@ -84,7 +86,7 @@ public class TextUtils {
}
public static int indexOf(CharSequence s, char ch, int start, int end) {
- Class c = s.getClass();
+ Class<? extends CharSequence> c = s.getClass();
if (s instanceof GetChars || c == StringBuffer.class ||
c == StringBuilder.class || c == String.class) {
@@ -125,7 +127,7 @@ public class TextUtils {
}
public static int lastIndexOf(CharSequence s, char ch, int last) {
- Class c = s.getClass();
+ Class<? extends CharSequence> c = s.getClass();
if (c == String.class)
return ((String) s).lastIndexOf(ch, last);
@@ -142,7 +144,7 @@ public class TextUtils {
int end = last + 1;
- Class c = s.getClass();
+ Class<? extends CharSequence> c = s.getClass();
if (s instanceof GetChars || c == StringBuffer.class ||
c == StringBuilder.class || c == String.class) {
@@ -499,6 +501,7 @@ public class TextUtils {
return new String(buf);
}
+ @Override
public String toString() {
return subSequence(0, length()).toString();
}
@@ -563,6 +566,8 @@ public class TextUtils {
public static final int TEXT_APPEARANCE_SPAN = 17;
/** @hide */
public static final int ANNOTATION = 18;
+ /** @hide */
+ public static final int SUGGESTION_SPAN = 19;
/**
* Flatten a CharSequence and whatever styles can be copied across processes
@@ -621,7 +626,7 @@ public class TextUtils {
* Read and return a new CharSequence, possibly with styles,
* from the parcel.
*/
- public CharSequence createFromParcel(Parcel p) {
+ public CharSequence createFromParcel(Parcel p) {
int kind = p.readInt();
String string = p.readString();
@@ -714,6 +719,10 @@ public class TextUtils {
readSpan(p, sp, new Annotation(p));
break;
+ case SUGGESTION_SPAN:
+ readSpan(p, sp, new SuggestionSpan(p));
+ break;
+
default:
throw new RuntimeException("bogus span encoding " + kind);
}
@@ -766,7 +775,7 @@ public class TextUtils {
if (where >= 0)
tb.setSpan(sources[i], where, where + sources[i].length(),
- Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
for (int i = 0; i < sources.length; i++) {
@@ -1120,7 +1129,6 @@ public class TextUtils {
int remaining = commaCount + 1;
int ok = 0;
- int okRemaining = remaining;
String okFormat = "";
int w = 0;
@@ -1152,7 +1160,6 @@ public class TextUtils {
if (w + moreWid <= avail) {
ok = i + 1;
- okRemaining = remaining;
okFormat = format;
}
}
@@ -1185,6 +1192,7 @@ public class TextUtils {
MetricAffectingSpan.class);
MetricAffectingSpan[] spans = sp.getSpans(
spanStart, spanEnd, MetricAffectingSpan.class);
+ spans = TextUtils.removeEmptySpans(spans, sp, MetricAffectingSpan.class);
width += mt.addStyleRun(paint, spans, spanEnd - spanStart, null);
}
}
@@ -1543,6 +1551,56 @@ public class TextUtils {
return false;
}
+ /**
+ * Removes empty spans from the <code>spans</code> array.
+ *
+ * When parsing a Spanned using {@link Spanned#nextSpanTransition(int, int, Class)}, empty spans
+ * will (correctly) create span transitions, and calling getSpans on a slice of text bounded by
+ * one of these transitions will (correctly) include the empty overlapping span.
+ *
+ * However, these empty spans should not be taken into account when layouting or rendering the
+ * string and this method provides a way to filter getSpans' results accordingly.
+ *
+ * @param spans A list of spans retrieved using {@link Spanned#getSpans(int, int, Class)} from
+ * the <code>spanned</code>
+ * @param spanned The Spanned from which spans were extracted
+ * @return A subset of spans where empty spans ({@link Spanned#getSpanStart(Object)} ==
+ * {@link Spanned#getSpanEnd(Object)} have been removed. The initial order is preserved
+ * @hide
+ */
+ @SuppressWarnings("unchecked")
+ public static <T> T[] removeEmptySpans(T[] spans, Spanned spanned, Class<T> klass) {
+ T[] copy = null;
+ int count = 0;
+
+ for (int i = 0; i < spans.length; i++) {
+ final T span = spans[i];
+ final int start = spanned.getSpanStart(span);
+ final int end = spanned.getSpanEnd(span);
+
+ if (start == end) {
+ if (copy == null) {
+ copy = (T[]) Array.newInstance(klass, spans.length - 1);
+ System.arraycopy(spans, 0, copy, 0, i);
+ count = i;
+ }
+ } else {
+ if (copy != null) {
+ copy[count] = span;
+ count++;
+ }
+ }
+ }
+
+ if (copy != null) {
+ T[] result = (T[]) Array.newInstance(klass, count);
+ System.arraycopy(copy, 0, result, 0, count);
+ return result;
+ } else {
+ return spans;
+ }
+ }
+
private static Object sLock = new Object();
private static char[] sTemp = null;
}
diff --git a/core/java/android/text/method/ArrowKeyMovementMethod.java b/core/java/android/text/method/ArrowKeyMovementMethod.java
index a61ff13..d432dee 100644
--- a/core/java/android/text/method/ArrowKeyMovementMethod.java
+++ b/core/java/android/text/method/ArrowKeyMovementMethod.java
@@ -193,6 +193,20 @@ public class ArrowKeyMovementMethod extends BaseMovementMethod implements Moveme
}
}
+ /** {@hide} */
+ @Override
+ protected boolean leftWord(TextView widget, Spannable buffer) {
+ mWordIterator.setCharSequence(buffer);
+ return Selection.moveToPreceding(buffer, mWordIterator, isSelecting(buffer));
+ }
+
+ /** {@hide} */
+ @Override
+ protected boolean rightWord(TextView widget, Spannable buffer) {
+ mWordIterator.setCharSequence(buffer);
+ return Selection.moveToFollowing(buffer, mWordIterator, isSelecting(buffer));
+ }
+
@Override
protected boolean home(TextView widget, Spannable buffer) {
return lineStart(widget, buffer);
@@ -205,7 +219,8 @@ public class ArrowKeyMovementMethod extends BaseMovementMethod implements Moveme
@Override
public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) {
- int initialScrollX = -1, initialScrollY = -1;
+ int initialScrollX = -1;
+ int initialScrollY = -1;
final int action = event.getAction();
if (action == MotionEvent.ACTION_UP) {
@@ -220,7 +235,7 @@ public class ArrowKeyMovementMethod extends BaseMovementMethod implements Moveme
boolean cap = isSelecting(buffer);
if (cap) {
int offset = widget.getOffset((int) event.getX(), (int) event.getY());
-
+
buffer.setSpan(LAST_TAP_DOWN, offset, offset, Spannable.SPAN_POINT_POINT);
// Disallow intercepting of the touch events, so that
@@ -308,6 +323,7 @@ public class ArrowKeyMovementMethod extends BaseMovementMethod implements Moveme
return sInstance;
}
+ private WordIterator mWordIterator = new WordIterator();
private static final Object LAST_TAP_DOWN = new Object();
private static ArrowKeyMovementMethod sInstance;
diff --git a/core/java/android/text/method/BaseMovementMethod.java b/core/java/android/text/method/BaseMovementMethod.java
index 94c6ed0..f554b90 100644
--- a/core/java/android/text/method/BaseMovementMethod.java
+++ b/core/java/android/text/method/BaseMovementMethod.java
@@ -164,6 +164,9 @@ public class BaseMovementMethod implements MovementMethod {
if (KeyEvent.metaStateHasNoModifiers(movementMetaState)) {
return left(widget, buffer);
} else if (KeyEvent.metaStateHasModifiers(movementMetaState,
+ KeyEvent.META_CTRL_ON)) {
+ return leftWord(widget, buffer);
+ } else if (KeyEvent.metaStateHasModifiers(movementMetaState,
KeyEvent.META_ALT_ON)) {
return lineStart(widget, buffer);
}
@@ -173,6 +176,9 @@ public class BaseMovementMethod implements MovementMethod {
if (KeyEvent.metaStateHasNoModifiers(movementMetaState)) {
return right(widget, buffer);
} else if (KeyEvent.metaStateHasModifiers(movementMetaState,
+ KeyEvent.META_CTRL_ON)) {
+ return rightWord(widget, buffer);
+ } else if (KeyEvent.metaStateHasModifiers(movementMetaState,
KeyEvent.META_ALT_ON)) {
return lineEnd(widget, buffer);
}
@@ -217,12 +223,18 @@ public class BaseMovementMethod implements MovementMethod {
case KeyEvent.KEYCODE_MOVE_HOME:
if (KeyEvent.metaStateHasNoModifiers(movementMetaState)) {
return home(widget, buffer);
+ } else if (KeyEvent.metaStateHasModifiers(movementMetaState,
+ KeyEvent.META_CTRL_ON)) {
+ return top(widget, buffer);
}
break;
case KeyEvent.KEYCODE_MOVE_END:
if (KeyEvent.metaStateHasNoModifiers(movementMetaState)) {
return end(widget, buffer);
+ } else if (KeyEvent.metaStateHasModifiers(movementMetaState,
+ KeyEvent.META_CTRL_ON)) {
+ return bottom(widget, buffer);
}
break;
}
@@ -349,6 +361,16 @@ public class BaseMovementMethod implements MovementMethod {
return false;
}
+ /** {@hide} */
+ protected boolean leftWord(TextView widget, Spannable buffer) {
+ return false;
+ }
+
+ /** {@hide} */
+ protected boolean rightWord(TextView widget, Spannable buffer) {
+ return false;
+ }
+
/**
* Performs a home movement action.
* Moves the cursor or scrolls to the start of the line or to the top of the
diff --git a/core/java/android/text/method/WordIterator.java b/core/java/android/text/method/WordIterator.java
new file mode 100644
index 0000000..b250414
--- /dev/null
+++ b/core/java/android/text/method/WordIterator.java
@@ -0,0 +1,220 @@
+
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.text.method;
+
+import android.text.CharSequenceIterator;
+import android.text.Editable;
+import android.text.Selection;
+import android.text.Spanned;
+import android.text.TextWatcher;
+
+import java.text.BreakIterator;
+import java.text.CharacterIterator;
+import java.util.Locale;
+
+/**
+ * Walks through cursor positions at word boundaries. Internally uses
+ * {@link BreakIterator#getWordInstance()}, and caches {@link CharSequence}
+ * for performance reasons.
+ *
+ * Also provides methods to determine word boundaries.
+ * {@hide}
+ */
+public class WordIterator implements Selection.PositionIterator {
+ private CharSequence mCurrent;
+ private boolean mCurrentDirty = false;
+
+ private BreakIterator mIterator;
+
+ /**
+ * Constructs a WordIterator using the default locale.
+ */
+ public WordIterator() {
+ this(Locale.getDefault());
+ }
+
+ /**
+ * Constructs a new WordIterator for the specified locale.
+ * @param locale The locale to be used when analysing the text.
+ */
+ public WordIterator(Locale locale) {
+ mIterator = BreakIterator.getWordInstance(locale);
+ }
+
+ private final TextWatcher mWatcher = new TextWatcher() {
+ /** {@inheritDoc} */
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ // ignored
+ }
+
+ /** {@inheritDoc} */
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ mCurrentDirty = true;
+ }
+
+ /** {@inheritDoc} */
+ public void afterTextChanged(Editable s) {
+ // ignored
+ }
+ };
+
+ public void setCharSequence(CharSequence incoming) {
+ // When incoming is different object, move listeners to new sequence
+ // and mark as dirty so we reload contents.
+ if (mCurrent != incoming) {
+ if (mCurrent instanceof Editable) {
+ ((Editable) mCurrent).removeSpan(mWatcher);
+ }
+
+ if (incoming instanceof Editable) {
+ ((Editable) incoming).setSpan(
+ mWatcher, 0, incoming.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+ }
+
+ mCurrent = incoming;
+ mCurrentDirty = true;
+ }
+
+ if (mCurrentDirty) {
+ final CharacterIterator charIterator = new CharSequenceIterator(mCurrent);
+ mIterator.setText(charIterator);
+
+ mCurrentDirty = false;
+ }
+ }
+
+ /** {@inheritDoc} */
+ public int preceding(int offset) {
+ do {
+ offset = mIterator.preceding(offset);
+ if (offset == BreakIterator.DONE || isOnLetterOrDigit(offset)) {
+ break;
+ }
+ } while (true);
+
+ return offset;
+ }
+
+ /** {@inheritDoc} */
+ public int following(int offset) {
+ do {
+ offset = mIterator.following(offset);
+ if (offset == BreakIterator.DONE || isAfterLetterOrDigit(offset)) {
+ break;
+ }
+ } while (true);
+
+ return offset;
+ }
+
+ /** If <code>offset</code> is within a word, returns the index of the first character of that
+ * word, otherwise returns BreakIterator.DONE.
+ *
+ * The offsets that are considered to be part of a word are the indexes of its characters,
+ * <i>as well as</i> the index of its last character plus one.
+ * If offset is the index of a low surrogate character, BreakIterator.DONE will be returned.
+ *
+ * Valid range for offset is [0..textLength] (note the inclusive upper bound).
+ * The returned value is within [0..offset] or BreakIterator.DONE.
+ *
+ * @throws IllegalArgumentException is offset is not valid.
+ */
+ public int getBeginning(int offset) {
+ checkOffsetIsValid(offset);
+
+ if (isOnLetterOrDigit(offset)) {
+ if (mIterator.isBoundary(offset)) {
+ return offset;
+ } else {
+ return mIterator.preceding(offset);
+ }
+ } else {
+ if (isAfterLetterOrDigit(offset)) {
+ return mIterator.preceding(offset);
+ }
+ }
+ return BreakIterator.DONE;
+ }
+
+ /** If <code>offset</code> is within a word, returns the index of the last character of that
+ * word plus one, otherwise returns BreakIterator.DONE.
+ *
+ * The offsets that are considered to be part of a word are the indexes of its characters,
+ * <i>as well as</i> the index of its last character plus one.
+ * If offset is the index of a low surrogate character, BreakIterator.DONE will be returned.
+ *
+ * Valid range for offset is [0..textLength] (note the inclusive upper bound).
+ * The returned value is within [offset..textLength] or BreakIterator.DONE.
+ *
+ * @throws IllegalArgumentException is offset is not valid.
+ */
+ public int getEnd(int offset) {
+ checkOffsetIsValid(offset);
+
+ if (isAfterLetterOrDigit(offset)) {
+ if (mIterator.isBoundary(offset)) {
+ return offset;
+ } else {
+ return mIterator.following(offset);
+ }
+ } else {
+ if (isOnLetterOrDigit(offset)) {
+ return mIterator.following(offset);
+ }
+ }
+ return BreakIterator.DONE;
+ }
+
+ private boolean isAfterLetterOrDigit(int offset) {
+ if (offset - 1 >= 0) {
+ final char previousChar = mCurrent.charAt(offset - 1);
+ if (Character.isLetterOrDigit(previousChar)) return true;
+ if (offset - 2 >= 0) {
+ final char previousPreviousChar = mCurrent.charAt(offset - 2);
+ if (Character.isSurrogatePair(previousPreviousChar, previousChar)) {
+ final int codePoint = Character.toCodePoint(previousPreviousChar, previousChar);
+ return Character.isLetterOrDigit(codePoint);
+ }
+ }
+ }
+ return false;
+ }
+
+ private boolean isOnLetterOrDigit(int offset) {
+ final int length = mCurrent.length();
+ if (offset < length) {
+ final char currentChar = mCurrent.charAt(offset);
+ if (Character.isLetterOrDigit(currentChar)) return true;
+ if (offset + 1 < length) {
+ final char nextChar = mCurrent.charAt(offset + 1);
+ if (Character.isSurrogatePair(currentChar, nextChar)) {
+ final int codePoint = Character.toCodePoint(currentChar, nextChar);
+ return Character.isLetterOrDigit(codePoint);
+ }
+ }
+ }
+ return false;
+ }
+
+ private void checkOffsetIsValid(int offset) {
+ if (offset < 0 || offset > mCurrent.length()) {
+ final String message = "Valid range is [0, " + mCurrent.length() + "]";
+ throw new IllegalArgumentException(message);
+ }
+ }
+}
diff --git a/core/config/debug/android/util/ConfigBuildFlags.java b/core/java/android/text/style/SuggestionSpan.aidl
index eb00163..3c56cfe 100644
--- a/core/config/debug/android/util/ConfigBuildFlags.java
+++ b/core/java/android/text/style/SuggestionSpan.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009 The Android Open Source Project
+ * Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,11 +14,6 @@
* limitations under the License.
*/
-package android.util;
+package android.text.style;
-/**
- * Static flags set by the build process.
- */
-/* package */ final class ConfigBuildFlags {
- /* package */ static final boolean DEBUG = true;
-}
+parcelable SuggestionSpan;
diff --git a/core/java/android/text/style/SuggestionSpan.java b/core/java/android/text/style/SuggestionSpan.java
new file mode 100644
index 0000000..dcb0898
--- /dev/null
+++ b/core/java/android/text/style/SuggestionSpan.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.text.style;
+
+import android.content.Context;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.ParcelableSpan;
+import android.text.TextUtils;
+
+import java.util.Arrays;
+import java.util.Locale;
+
+/**
+ * Holds suggestion candidates of words under this span.
+ */
+public class SuggestionSpan implements ParcelableSpan {
+
+ /**
+ * Flag for indicating that the input is verbatim. TextView refers to this flag to determine
+ * how it displays a word with SuggestionSpan.
+ */
+ public static final int FLAG_VERBATIM = 0x0001;
+
+ private static final int SUGGESTIONS_MAX_SIZE = 5;
+
+ /*
+ * TODO: Needs to check the validity and add a feature that TextView will change
+ * the current IME to the other IME which is specified in SuggestionSpan.
+ * An IME needs to set the span by specifying the target IME and Subtype of SuggestionSpan.
+ * And the current IME might want to specify any IME as the target IME including other IMEs.
+ */
+
+ private final int mFlags;
+ private final String[] mSuggestions;
+ private final String mLocaleString;
+ private final String mOriginalString;
+ /*
+ * TODO: If switching IME is required, needs to add parameters for ids of InputMethodInfo
+ * and InputMethodSubtype.
+ */
+
+ /**
+ * @param context Context for the application
+ * @param suggestions Suggestions for the string under the span
+ * @param flags Additional flags indicating how this span is handled in TextView
+ */
+ public SuggestionSpan(Context context, String[] suggestions, int flags) {
+ this(context, null, suggestions, flags, null);
+ }
+
+ /**
+ * @param locale Locale of the suggestions
+ * @param suggestions Suggestions for the string under the span
+ * @param flags Additional flags indicating how this span is handled in TextView
+ */
+ public SuggestionSpan(Locale locale, String[] suggestions, int flags) {
+ this(null, locale, suggestions, flags, null);
+ }
+
+ /**
+ * @param context Context for the application
+ * @param locale locale Locale of the suggestions
+ * @param suggestions Suggestions for the string under the span
+ * @param flags Additional flags indicating how this span is handled in TextView
+ * @param originalString originalString for suggestions
+ */
+ public SuggestionSpan(Context context, Locale locale, String[] suggestions, int flags,
+ String originalString) {
+ final int N = Math.min(SUGGESTIONS_MAX_SIZE, suggestions.length);
+ mSuggestions = Arrays.copyOf(suggestions, N);
+ mFlags = flags;
+ if (context != null && locale == null) {
+ mLocaleString = context.getResources().getConfiguration().locale.toString();
+ } else {
+ mLocaleString = locale.toString();
+ }
+ mOriginalString = originalString;
+ }
+
+ public SuggestionSpan(Parcel src) {
+ mSuggestions = src.readStringArray();
+ mFlags = src.readInt();
+ mLocaleString = src.readString();
+ mOriginalString = src.readString();
+ }
+
+ /**
+ * @return suggestions
+ */
+ public String[] getSuggestions() {
+ return mSuggestions;
+ }
+
+ /**
+ * @return locale of suggestions
+ */
+ public String getLocale() {
+ return mLocaleString;
+ }
+
+ /**
+ * @return original string of suggestions
+ */
+ public String getOriginalString() {
+ return mOriginalString;
+ }
+
+ public int getFlags() {
+ return mFlags;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeStringArray(mSuggestions);
+ dest.writeInt(mFlags);
+ dest.writeString(mLocaleString);
+ dest.writeString(mOriginalString);
+ }
+
+ @Override
+ public int getSpanTypeId() {
+ return TextUtils.SUGGESTION_SPAN;
+ }
+
+ public static final Parcelable.Creator<SuggestionSpan> CREATOR =
+ new Parcelable.Creator<SuggestionSpan>() {
+ @Override
+ public SuggestionSpan createFromParcel(Parcel source) {
+ return new SuggestionSpan(source);
+ }
+
+ @Override
+ public SuggestionSpan[] newArray(int size) {
+ return new SuggestionSpan[size];
+ }
+ };
+}
diff --git a/core/java/android/text/style/TextAppearanceSpan.java b/core/java/android/text/style/TextAppearanceSpan.java
index de929e3..deed713 100644
--- a/core/java/android/text/style/TextAppearanceSpan.java
+++ b/core/java/android/text/style/TextAppearanceSpan.java
@@ -51,10 +51,9 @@ public class TextAppearanceSpan extends MetricAffectingSpan implements Parcelabl
* to determine the color. The <code>appearance</code> should be,
* for example, <code>android.R.style.TextAppearance_Small</code>,
* and the <code>colorList</code> should be, for example,
- * <code>android.R.styleable.Theme_textColorDim</code>.
+ * <code>android.R.styleable.Theme_textColorPrimary</code>.
*/
- public TextAppearanceSpan(Context context, int appearance,
- int colorList) {
+ public TextAppearanceSpan(Context context, int appearance, int colorList) {
ColorStateList textColor;
TypedArray a =
diff --git a/core/java/android/util/Config.java b/core/java/android/util/Config.java
deleted file mode 100644
index becb882..0000000
--- a/core/java/android/util/Config.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.util;
-
-/**
- * Build configuration. The constants in this class vary depending
- * on release vs. debug build.
- * {@more}
- */
-public final class Config {
- /** @hide */ public Config() {}
-
- /**
- * If this is a debug build, this field will be true.
- */
- public static final boolean DEBUG = ConfigBuildFlags.DEBUG;
-
- /*
- * Deprecated fields
- * TODO: Remove platform references to these and @hide them.
- */
-
- /**
- * @deprecated Use {@link #DEBUG} instead.
- */
- @Deprecated
- public static final boolean RELEASE = !DEBUG;
-
- /**
- * @deprecated Always false.
- */
- @Deprecated
- public static final boolean PROFILE = false;
-
- /**
- * @deprecated Always false.
- */
- @Deprecated
- public static final boolean LOGV = false;
-
- /**
- * @deprecated Always true.
- */
- @Deprecated
- public static final boolean LOGD = true;
-}
diff --git a/core/java/android/util/JsonReader.java b/core/java/android/util/JsonReader.java
index 8f44895..132b595 100644
--- a/core/java/android/util/JsonReader.java
+++ b/core/java/android/util/JsonReader.java
@@ -16,12 +16,13 @@
package android.util;
+import java.io.Closeable;
import java.io.EOFException;
import java.io.IOException;
import java.io.Reader;
-import java.io.Closeable;
import java.util.ArrayList;
import java.util.List;
+import libcore.internal.StringPool;
/**
* Reads a JSON (<a href="http://www.ietf.org/rfc/rfc4627.txt">RFC 4627</a>)
@@ -86,7 +87,11 @@ import java.util.List;
*
* public List<Message> readJsonStream(InputStream in) throws IOException {
* JsonReader reader = new JsonReader(new InputStreamReader(in, "UTF-8"));
- * return readMessagesArray(reader);
+ * try {
+ * return readMessagesArray(reader);
+ * } finally {
+ * reader.close();
+ * }
* }
*
* public List<Message> readMessagesArray(JsonReader reader) throws IOException {
@@ -173,6 +178,8 @@ public final class JsonReader implements Closeable {
private static final String TRUE = "true";
private static final String FALSE = "false";
+ private final StringPool stringPool = new StringPool();
+
/** The input JSON. */
private final Reader in;
@@ -832,7 +839,7 @@ public final class JsonReader implements Closeable {
if (skipping) {
return "skipped!";
} else if (builder == null) {
- return new String(buffer, start, pos - start - 1);
+ return stringPool.get(buffer, start, pos - start - 1);
} else {
builder.append(buffer, start, pos - start - 1);
return builder.toString();
@@ -930,7 +937,7 @@ public final class JsonReader implements Closeable {
} else if (skipping) {
result = "skipped!";
} else if (builder == null) {
- result = new String(buffer, pos, i);
+ result = stringPool.get(buffer, pos, i);
} else {
builder.append(buffer, pos, i);
result = builder.toString();
@@ -964,7 +971,7 @@ public final class JsonReader implements Closeable {
if (pos + 4 > limit && !fillBuffer(4)) {
throw syntaxError("Unterminated escape sequence");
}
- String hex = new String(buffer, pos, 4);
+ String hex = stringPool.get(buffer, pos, 4);
pos += 4;
return (char) Integer.parseInt(hex, 16);
@@ -1036,7 +1043,7 @@ public final class JsonReader implements Closeable {
value = FALSE;
return JsonToken.BOOLEAN;
} else {
- value = new String(buffer, valuePos, valueLength);
+ value = stringPool.get(buffer, valuePos, valueLength);
return decodeNumber(buffer, valuePos, valueLength);
}
}
diff --git a/core/java/android/util/LruCache.java b/core/java/android/util/LruCache.java
index 834dac3..5540000 100644
--- a/core/java/android/util/LruCache.java
+++ b/core/java/android/util/LruCache.java
@@ -304,7 +304,8 @@ public class LruCache<K, V> {
}
/**
- * Returns the number of times {@link #get} returned a value.
+ * Returns the number of times {@link #get} returned a value that was
+ * already present in the cache.
*/
public synchronized final int hitCount() {
return hitCount;
diff --git a/core/java/android/util/NtpTrustedTime.java b/core/java/android/util/NtpTrustedTime.java
new file mode 100644
index 0000000..5b19ecd
--- /dev/null
+++ b/core/java/android/util/NtpTrustedTime.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+import android.net.SntpClient;
+import android.os.SystemClock;
+
+/**
+ * {@link TrustedTime} that connects with a remote NTP server as its remote
+ * trusted time source.
+ *
+ * @hide
+ */
+public class NtpTrustedTime implements TrustedTime {
+ private String mNtpServer;
+ private long mNtpTimeout;
+
+ private boolean mHasCache;
+ private long mCachedNtpTime;
+ private long mCachedNtpElapsedRealtime;
+ private long mCachedNtpCertainty;
+
+ public NtpTrustedTime() {
+ }
+
+ public void setNtpServer(String server, long timeout) {
+ mNtpServer = server;
+ mNtpTimeout = timeout;
+ }
+
+ /** {@inheritDoc} */
+ public boolean forceRefresh() {
+ if (mNtpServer == null) {
+ // missing server, so no trusted time available
+ return false;
+ }
+
+ final SntpClient client = new SntpClient();
+ if (client.requestTime(mNtpServer, (int) mNtpTimeout)) {
+ mHasCache = true;
+ mCachedNtpTime = client.getNtpTime();
+ mCachedNtpElapsedRealtime = client.getNtpTimeReference();
+ mCachedNtpCertainty = client.getRoundTripTime() / 2;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /** {@inheritDoc} */
+ public boolean hasCache() {
+ return mHasCache;
+ }
+
+ /** {@inheritDoc} */
+ public long getCacheAge() {
+ if (mHasCache) {
+ return SystemClock.elapsedRealtime() - mCachedNtpElapsedRealtime;
+ } else {
+ return Long.MAX_VALUE;
+ }
+ }
+
+ /** {@inheritDoc} */
+ public long getCacheCertainty() {
+ if (mHasCache) {
+ return mCachedNtpCertainty;
+ } else {
+ return Long.MAX_VALUE;
+ }
+ }
+
+ /** {@inheritDoc} */
+ public long currentTimeMillis() {
+ if (!mHasCache) {
+ throw new IllegalStateException("Missing authoritative time source");
+ }
+
+ // current time is age after the last ntp cache; callers who
+ // want fresh values will hit makeAuthoritative() first.
+ return mCachedNtpTime + getCacheAge();
+ }
+}
diff --git a/core/java/android/util/TimeUtils.java b/core/java/android/util/TimeUtils.java
index 9042505..93299eb 100644
--- a/core/java/android/util/TimeUtils.java
+++ b/core/java/android/util/TimeUtils.java
@@ -19,7 +19,7 @@ package android.util;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
-import org.apache.harmony.luni.internal.util.ZoneInfoDB;
+import libcore.util.ZoneInfoDB;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
diff --git a/core/java/android/util/TrustedTime.java b/core/java/android/util/TrustedTime.java
new file mode 100644
index 0000000..263d782
--- /dev/null
+++ b/core/java/android/util/TrustedTime.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2011 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;
+
+/**
+ * Interface that provides trusted time information, possibly coming from an NTP
+ * server. Implementations may cache answers until {@link #forceRefresh()}.
+ *
+ * @hide
+ */
+public interface TrustedTime {
+ /**
+ * Force update with an external trusted time source, returning {@code true}
+ * when successful.
+ */
+ public boolean forceRefresh();
+
+ /**
+ * Check if this instance has cached a response from a trusted time source.
+ */
+ public boolean hasCache();
+
+ /**
+ * Return time since last trusted time source contact, or
+ * {@link Long#MAX_VALUE} if never contacted.
+ */
+ public long getCacheAge();
+
+ /**
+ * Return certainty of cached trusted time in milliseconds, or
+ * {@link Long#MAX_VALUE} if never contacted. Smaller values are more
+ * precise.
+ */
+ public long getCacheCertainty();
+
+ /**
+ * Return current time similar to {@link System#currentTimeMillis()},
+ * possibly using a cached authoritative time source.
+ */
+ public long currentTimeMillis();
+}
diff --git a/core/java/android/util/Xml.java b/core/java/android/util/Xml.java
index b0c33e5..041e8a8 100644
--- a/core/java/android/util/Xml.java
+++ b/core/java/android/util/Xml.java
@@ -16,23 +16,21 @@
package android.util;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.UnsupportedEncodingException;
+import org.apache.harmony.xml.ExpatReader;
+import org.kxml2.io.KXmlParser;
import org.xml.sax.ContentHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlSerializer;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.Reader;
-import java.io.StringReader;
-import java.io.UnsupportedEncodingException;
-
-import org.apache.harmony.xml.ExpatPullParser;
-import org.apache.harmony.xml.ExpatReader;
+import org.xmlpull.v1.XmlSerializer;
/**
* XML utility methods.
@@ -46,7 +44,7 @@ public class Xml {
* @see <a href="http://xmlpull.org/v1/doc/features.html#relaxed">
* specification</a>
*/
- public static String FEATURE_RELAXED = ExpatPullParser.FEATURE_RELAXED;
+ public static String FEATURE_RELAXED = "http://xmlpull.org/v1/doc/features.html#relaxed";
/**
* Parses the given xml string and fires events on the given SAX handler.
@@ -57,8 +55,7 @@ public class Xml {
XMLReader reader = new ExpatReader();
reader.setContentHandler(contentHandler);
reader.parse(new InputSource(new StringReader(xml)));
- }
- catch (IOException e) {
+ } catch (IOException e) {
throw new AssertionError(e);
}
}
@@ -88,16 +85,17 @@ public class Xml {
}
/**
- * Creates a new pull parser with namespace support.
- *
- * <p><b>Note:</b> This is actually slower than the SAX parser, and it's not
- * fully implemented. If you need a fast, mostly implemented pull parser,
- * use this. If you need a complete implementation, use KXML.
+ * Returns a new pull parser with namespace support.
*/
public static XmlPullParser newPullParser() {
- ExpatPullParser parser = new ExpatPullParser();
- parser.setNamespaceProcessingEnabled(true);
- return parser;
+ try {
+ KXmlParser parser = new KXmlParser();
+ parser.setFeature(XmlPullParser.FEATURE_PROCESS_DOCDECL, true);
+ parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
+ return parser;
+ } catch (XmlPullParserException e) {
+ throw new AssertionError();
+ }
}
/**
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index 14f2e9d..2b79a76 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -39,6 +39,12 @@ import android.text.TextUtils;
* An implementation of Canvas on top of OpenGL ES 2.0.
*/
class GLES20Canvas extends HardwareCanvas {
+ // Must match modifiers used in the JNI layer
+ private static final int MODIFIER_NONE = 0;
+ private static final int MODIFIER_SHADOW = 1;
+ private static final int MODIFIER_SHADER = 2;
+ private static final int MODIFIER_COLOR_FILTER = 4;
+
private final boolean mOpaque;
private int mRenderer;
@@ -154,8 +160,10 @@ class GLES20Canvas extends HardwareCanvas {
// Hardware layers
///////////////////////////////////////////////////////////////////////////
+ static native int nCreateTextureLayer(int[] layerInfo);
static native int nCreateLayer(int width, int height, boolean isOpaque, int[] layerInfo);
static native void nResizeLayer(int layerId, int width, int height, int[] layerInfo);
+ static native void nUpdateTextureLayer(int layerId, int width, int height, int surface);
static native void nDestroyLayer(int layerId);
static native void nDestroyLayerDeferred(int layerId);
@@ -253,20 +261,27 @@ class GLES20Canvas extends HardwareCanvas {
private static native boolean nDrawDisplayList(int renderer, int displayList,
int width, int height, Rect dirty);
+ @Override
+ void outputDisplayList(DisplayList displayList) {
+ nOutputDisplayList(mRenderer, ((GLES20DisplayList) displayList).mNativeDisplayList);
+ }
+
+ private static native void nOutputDisplayList(int renderer, int displayList);
+
///////////////////////////////////////////////////////////////////////////
// Hardware layer
///////////////////////////////////////////////////////////////////////////
void drawHardwareLayer(HardwareLayer layer, float x, float y, Paint paint) {
final GLES20Layer glLayer = (GLES20Layer) layer;
- boolean hasColorFilter = paint != null && setupColorFilter(paint);
+ int modifier = paint != null ? setupColorFilter(paint) : MODIFIER_NONE;
final int nativePaint = paint == null ? 0 : paint.mNativePaint;
nDrawLayer(mRenderer, glLayer.getLayer(), x, y, nativePaint);
- if (hasColorFilter) nResetModifiers(mRenderer);
+ if (modifier != MODIFIER_NONE) nResetModifiers(mRenderer, modifier);
}
private static native void nDrawLayer(int renderer, int layer, float x, float y, int paint);
-
+
void interrupt() {
nInterrupt(mRenderer);
}
@@ -455,10 +470,10 @@ class GLES20Canvas extends HardwareCanvas {
public int saveLayer(float left, float top, float right, float bottom, Paint paint,
int saveFlags) {
if (left < right && top < bottom) {
- boolean hasColorFilter = paint != null && setupColorFilter(paint);
+ int modifier = paint != null ? setupColorFilter(paint) : MODIFIER_NONE;
final int nativePaint = paint == null ? 0 : paint.mNativePaint;
int count = nSaveLayer(mRenderer, left, top, right, bottom, nativePaint, saveFlags);
- if (hasColorFilter) nResetModifiers(mRenderer);
+ if (modifier != MODIFIER_NONE) nResetModifiers(mRenderer, modifier);
return count;
}
return save(saveFlags);
@@ -527,10 +542,10 @@ class GLES20Canvas extends HardwareCanvas {
@Override
public void drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter,
Paint paint) {
- boolean hasModifier = setupModifiers(paint);
+ int modifiers = setupModifiers(paint);
nDrawArc(mRenderer, oval.left, oval.top, oval.right, oval.bottom, startAngle, sweepAngle,
useCenter, paint.mNativePaint);
- if (hasModifier) nResetModifiers(mRenderer);
+ if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
}
private static native void nDrawArc(int renderer, float left, float top,
@@ -545,11 +560,11 @@ class GLES20Canvas extends HardwareCanvas {
@Override
public void drawPatch(Bitmap bitmap, byte[] chunks, RectF dst, Paint paint) {
// Shaders are ignored when drawing patches
- boolean hasColorFilter = paint != null && setupColorFilter(paint);
+ int modifier = paint != null ? setupColorFilter(paint) : MODIFIER_NONE;
final int nativePaint = paint == null ? 0 : paint.mNativePaint;
nDrawPatch(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, chunks,
dst.left, dst.top, dst.right, dst.bottom, nativePaint);
- if (hasColorFilter) nResetModifiers(mRenderer);
+ if (modifier != MODIFIER_NONE) nResetModifiers(mRenderer, modifier);
}
private static native void nDrawPatch(int renderer, int bitmap, byte[] buffer, byte[] chunks,
@@ -558,10 +573,10 @@ class GLES20Canvas extends HardwareCanvas {
@Override
public void drawBitmap(Bitmap bitmap, float left, float top, Paint paint) {
// Shaders are ignored when drawing bitmaps
- boolean hasColorFilter = paint != null && setupColorFilter(paint);
+ int modifiers = paint != null ? setupModifiers(bitmap, paint) : MODIFIER_NONE;
final int nativePaint = paint == null ? 0 : paint.mNativePaint;
nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, left, top, nativePaint);
- if (hasColorFilter) nResetModifiers(mRenderer);
+ if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
}
private static native void nDrawBitmap(
@@ -570,11 +585,11 @@ class GLES20Canvas extends HardwareCanvas {
@Override
public void drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint) {
// Shaders are ignored when drawing bitmaps
- boolean hasColorFilter = paint != null && setupColorFilter(paint);
+ int modifiers = paint != null ? setupModifiers(bitmap, paint) : MODIFIER_NONE;
final int nativePaint = paint == null ? 0 : paint.mNativePaint;
nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer,
matrix.native_instance, nativePaint);
- if (hasColorFilter) nResetModifiers(mRenderer);
+ if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
}
private static native void nDrawBitmap(int renderer, int bitmap, byte[] buff,
@@ -583,7 +598,7 @@ class GLES20Canvas extends HardwareCanvas {
@Override
public void drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint) {
// Shaders are ignored when drawing bitmaps
- boolean hasColorFilter = paint != null && setupColorFilter(paint);
+ int modifiers = paint != null ? setupModifiers(bitmap, paint) : MODIFIER_NONE;
final int nativePaint = paint == null ? 0 : paint.mNativePaint;
int left, top, right, bottom;
@@ -600,17 +615,17 @@ class GLES20Canvas extends HardwareCanvas {
nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, left, top, right, bottom,
dst.left, dst.top, dst.right, dst.bottom, nativePaint);
- if (hasColorFilter) nResetModifiers(mRenderer);
+ if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
}
@Override
public void drawBitmap(Bitmap bitmap, Rect src, RectF dst, Paint paint) {
// Shaders are ignored when drawing bitmaps
- boolean hasColorFilter = paint != null && setupColorFilter(paint);
+ int modifiers = paint != null ? setupModifiers(bitmap, paint) : MODIFIER_NONE;
final int nativePaint = paint == null ? 0 : paint.mNativePaint;
nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, src.left, src.top, src.right,
src.bottom, dst.left, dst.top, dst.right, dst.bottom, nativePaint);
- if (hasColorFilter) nResetModifiers(mRenderer);
+ if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
}
private static native void nDrawBitmap(int renderer, int bitmap, byte[] buffer,
@@ -621,13 +636,13 @@ class GLES20Canvas extends HardwareCanvas {
public void drawBitmap(int[] colors, int offset, int stride, float x, float y,
int width, int height, boolean hasAlpha, Paint paint) {
// Shaders are ignored when drawing bitmaps
- boolean hasColorFilter = paint != null && setupColorFilter(paint);
+ int modifier = paint != null ? setupColorFilter(paint) : MODIFIER_NONE;
final Bitmap.Config config = hasAlpha ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565;
final Bitmap b = Bitmap.createBitmap(colors, offset, stride, width, height, config);
final int nativePaint = paint == null ? 0 : paint.mNativePaint;
nDrawBitmap(mRenderer, b.mNativeBitmap, b.mBuffer, x, y, nativePaint);
b.recycle();
- if (hasColorFilter) nResetModifiers(mRenderer);
+ if (modifier != MODIFIER_NONE) nResetModifiers(mRenderer, modifier);
}
@Override
@@ -655,11 +670,11 @@ class GLES20Canvas extends HardwareCanvas {
colors = null;
colorOffset = 0;
- boolean hasColorFilter = paint != null && setupColorFilter(paint);
+ int modifiers = paint != null ? setupModifiers(bitmap, paint) : MODIFIER_NONE;
final int nativePaint = paint == null ? 0 : paint.mNativePaint;
nDrawBitmapMesh(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, meshWidth, meshHeight,
verts, vertOffset, colors, colorOffset, nativePaint);
- if (hasColorFilter) nResetModifiers(mRenderer);
+ if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
}
private static native void nDrawBitmapMesh(int renderer, int bitmap, byte[] buffer,
@@ -668,9 +683,9 @@ class GLES20Canvas extends HardwareCanvas {
@Override
public void drawCircle(float cx, float cy, float radius, Paint paint) {
- boolean hasModifier = setupModifiers(paint);
+ int modifiers = setupModifiers(paint);
nDrawCircle(mRenderer, cx, cy, radius, paint.mNativePaint);
- if (hasModifier) nResetModifiers(mRenderer);
+ if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
}
private static native void nDrawCircle(int renderer, float cx, float cy,
@@ -702,9 +717,9 @@ class GLES20Canvas extends HardwareCanvas {
if ((offset | count) < 0 || offset + count > pts.length) {
throw new IllegalArgumentException("The lines array must contain 4 elements per line.");
}
- boolean hasModifier = setupModifiers(paint);
+ int modifiers = setupModifiers(paint);
nDrawLines(mRenderer, pts, offset, count, paint.mNativePaint);
- if (hasModifier) nResetModifiers(mRenderer);
+ if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
}
private static native void nDrawLines(int renderer, float[] points,
@@ -717,9 +732,9 @@ class GLES20Canvas extends HardwareCanvas {
@Override
public void drawOval(RectF oval, Paint paint) {
- boolean hasModifier = setupModifiers(paint);
+ int modifiers = setupModifiers(paint);
nDrawOval(mRenderer, oval.left, oval.top, oval.right, oval.bottom, paint.mNativePaint);
- if (hasModifier) nResetModifiers(mRenderer);
+ if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
}
private static native void nDrawOval(int renderer, float left, float top,
@@ -734,7 +749,7 @@ class GLES20Canvas extends HardwareCanvas {
@Override
public void drawPath(Path path, Paint paint) {
- boolean hasModifier = setupModifiers(paint);
+ int modifiers = setupModifiers(paint);
if (path.isSimplePath) {
if (path.rects != null) {
nDrawRects(mRenderer, path.rects.mNativeRegion, paint.mNativePaint);
@@ -742,7 +757,7 @@ class GLES20Canvas extends HardwareCanvas {
} else {
nDrawPath(mRenderer, path.mNativePath, paint.mNativePaint);
}
- if (hasModifier) nResetModifiers(mRenderer);
+ if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
}
private static native void nDrawPath(int renderer, int path, int paint);
@@ -767,19 +782,24 @@ class GLES20Canvas extends HardwareCanvas {
public void drawPoint(float x, float y, Paint paint) {
mPoint[0] = x;
mPoint[1] = y;
- drawPoints(mPoint, 0, 1, paint);
+ drawPoints(mPoint, 0, 2, paint);
}
@Override
- public void drawPoints(float[] pts, int offset, int count, Paint paint) {
- // TODO: Implement
+ public void drawPoints(float[] pts, Paint paint) {
+ drawPoints(pts, 0, pts.length, paint);
}
@Override
- public void drawPoints(float[] pts, Paint paint) {
- drawPoints(pts, 0, pts.length / 2, paint);
+ public void drawPoints(float[] pts, int offset, int count, Paint paint) {
+ int modifiers = setupModifiers(paint);
+ nDrawPoints(mRenderer, pts, offset, count, paint.mNativePaint);
+ if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
}
+ private static native void nDrawPoints(int renderer, float[] points,
+ int offset, int count, int paint);
+
@Override
public void drawPosText(char[] text, int index, int count, float[] pos, Paint paint) {
// TODO: Implement
@@ -792,9 +812,9 @@ class GLES20Canvas extends HardwareCanvas {
@Override
public void drawRect(float left, float top, float right, float bottom, Paint paint) {
- boolean hasModifier = setupModifiers(paint);
+ int modifiers = setupModifiers(paint);
nDrawRect(mRenderer, left, top, right, bottom, paint.mNativePaint);
- if (hasModifier) nResetModifiers(mRenderer);
+ if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
}
private static native void nDrawRect(int renderer, float left, float top,
@@ -817,10 +837,10 @@ class GLES20Canvas extends HardwareCanvas {
@Override
public void drawRoundRect(RectF rect, float rx, float ry, Paint paint) {
- boolean hasModifier = setupModifiers(paint);
+ int modifiers = setupModifiers(paint);
nDrawRoundRect(mRenderer, rect.left, rect.top, rect.right, rect.bottom,
rx, ry, paint.mNativePaint);
- if (hasModifier) nResetModifiers(mRenderer);
+ if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
}
private static native void nDrawRoundRect(int renderer, float left, float top,
@@ -832,11 +852,11 @@ class GLES20Canvas extends HardwareCanvas {
throw new IndexOutOfBoundsException();
}
- boolean hasModifier = setupModifiers(paint);
+ int modifiers = setupModifiers(paint);
try {
nDrawText(mRenderer, text, index, count, x, y, paint.mBidiFlags, paint.mNativePaint);
} finally {
- if (hasModifier) nResetModifiers(mRenderer);
+ if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
}
}
@@ -845,7 +865,7 @@ class GLES20Canvas extends HardwareCanvas {
@Override
public void drawText(CharSequence text, int start, int end, float x, float y, Paint paint) {
- boolean hasModifier = setupModifiers(paint);
+ int modifiers = setupModifiers(paint);
try {
if (text instanceof String || text instanceof SpannedString ||
text instanceof SpannableString) {
@@ -862,7 +882,7 @@ class GLES20Canvas extends HardwareCanvas {
TemporaryBuffer.recycle(buf);
}
} finally {
- if (hasModifier) nResetModifiers(mRenderer);
+ if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
}
}
@@ -872,11 +892,11 @@ class GLES20Canvas extends HardwareCanvas {
throw new IndexOutOfBoundsException();
}
- boolean hasModifier = setupModifiers(paint);
+ int modifiers = setupModifiers(paint);
try {
nDrawText(mRenderer, text, start, end, x, y, paint.mBidiFlags, paint.mNativePaint);
} finally {
- if (hasModifier) nResetModifiers(mRenderer);
+ if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
}
}
@@ -885,12 +905,12 @@ class GLES20Canvas extends HardwareCanvas {
@Override
public void drawText(String text, float x, float y, Paint paint) {
- boolean hasModifier = setupModifiers(paint);
+ int modifiers = setupModifiers(paint);
try {
nDrawText(mRenderer, text, 0, text.length(), x, y, paint.mBidiFlags,
paint.mNativePaint);
} finally {
- if (hasModifier) nResetModifiers(mRenderer);
+ if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
}
}
@@ -915,12 +935,12 @@ class GLES20Canvas extends HardwareCanvas {
throw new IllegalArgumentException("Unknown direction: " + dir);
}
- boolean hasModifier = setupModifiers(paint);
+ int modifiers = setupModifiers(paint);
try {
nDrawTextRun(mRenderer, text, index, count, contextIndex, contextCount, x, y, dir,
paint.mNativePaint);
} finally {
- if (hasModifier) nResetModifiers(mRenderer);
+ if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
}
}
@@ -934,7 +954,7 @@ class GLES20Canvas extends HardwareCanvas {
throw new IndexOutOfBoundsException();
}
- boolean hasModifier = setupModifiers(paint);
+ int modifiers = setupModifiers(paint);
try {
int flags = dir == 0 ? 0 : 1;
if (text instanceof String || text instanceof SpannedString ||
@@ -954,7 +974,7 @@ class GLES20Canvas extends HardwareCanvas {
TemporaryBuffer.recycle(buf);
}
} finally {
- if (hasModifier) nResetModifiers(mRenderer);
+ if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
}
}
@@ -968,43 +988,57 @@ class GLES20Canvas extends HardwareCanvas {
// TODO: Implement
}
- private boolean setupModifiers(Paint paint) {
- boolean hasModifier = false;
+ private int setupModifiers(Bitmap b, Paint paint) {
+ if (b.getConfig() == Bitmap.Config.ALPHA_8) {
+ return setupModifiers(paint);
+ }
+
+ final ColorFilter filter = paint.getColorFilter();
+ if (filter != null) {
+ nSetupColorFilter(mRenderer, filter.nativeColorFilter);
+ return MODIFIER_COLOR_FILTER;
+ }
+
+ return MODIFIER_NONE;
+ }
+
+ private int setupModifiers(Paint paint) {
+ int modifiers = MODIFIER_NONE;
if (paint.hasShadow) {
nSetupShadow(mRenderer, paint.shadowRadius, paint.shadowDx, paint.shadowDy,
paint.shadowColor);
- hasModifier = true;
+ modifiers |= MODIFIER_SHADOW;
}
final Shader shader = paint.getShader();
if (shader != null) {
nSetupShader(mRenderer, shader.native_shader);
- hasModifier = true;
+ modifiers |= MODIFIER_SHADER;
}
final ColorFilter filter = paint.getColorFilter();
if (filter != null) {
nSetupColorFilter(mRenderer, filter.nativeColorFilter);
- hasModifier = true;
+ modifiers |= MODIFIER_COLOR_FILTER;
}
- return hasModifier;
+ return modifiers;
}
- private boolean setupColorFilter(Paint paint) {
+ private int setupColorFilter(Paint paint) {
final ColorFilter filter = paint.getColorFilter();
if (filter != null) {
nSetupColorFilter(mRenderer, filter.nativeColorFilter);
- return true;
+ return MODIFIER_COLOR_FILTER;
}
- return false;
+ return MODIFIER_NONE;
}
-
+
private static native void nSetupShader(int renderer, int shader);
private static native void nSetupColorFilter(int renderer, int colorFilter);
private static native void nSetupShadow(int renderer, float radius,
float dx, float dy, int color);
- private static native void nResetModifiers(int renderer);
+ private static native void nResetModifiers(int renderer, int modifiers);
}
diff --git a/core/java/android/view/GLES20Layer.java b/core/java/android/view/GLES20Layer.java
index 6000a4a..bc191a6 100644
--- a/core/java/android/view/GLES20Layer.java
+++ b/core/java/android/view/GLES20Layer.java
@@ -14,39 +14,21 @@
* limitations under the License.
*/
-package android.view;
-import android.graphics.Canvas;
+package android.view;
/**
* An OpenGL ES 2.0 implementation of {@link HardwareLayer}.
*/
-class GLES20Layer extends HardwareLayer {
- private int mLayer;
-
- private int mLayerWidth;
- private int mLayerHeight;
-
- private final GLES20Canvas mCanvas;
+abstract class GLES20Layer extends HardwareLayer {
+ int mLayer;
+ Finalizer mFinalizer;
- @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"})
- private final Finalizer mFinalizer;
-
- GLES20Layer(int width, int height, boolean isOpaque) {
- super(width, height, isOpaque);
-
- int[] layerInfo = new int[2];
- mLayer = GLES20Canvas.nCreateLayer(width, height, isOpaque, layerInfo);
- if (mLayer != 0) {
- mLayerWidth = layerInfo[0];
- mLayerHeight = layerInfo[1];
+ GLES20Layer() {
+ }
- mCanvas = new GLES20Canvas(mLayer, !isOpaque);
- mFinalizer = new Finalizer(mLayer);
- } else {
- mCanvas = null;
- mFinalizer = null;
- }
+ GLES20Layer(int width, int height, boolean opaque) {
+ super(width, height, opaque);
}
/**
@@ -58,55 +40,14 @@ class GLES20Layer extends HardwareLayer {
return mLayer;
}
- @Override
- boolean isValid() {
- return mLayer != 0 && mLayerWidth > 0 && mLayerHeight > 0;
- }
-
- @Override
- void resize(int width, int height) {
- if (!isValid() || width <= 0 || height <= 0) return;
-
- mWidth = width;
- mHeight = height;
-
- if (width != mLayerWidth || height != mLayerHeight) {
- int[] layerInfo = new int[2];
-
- GLES20Canvas.nResizeLayer(mLayer, width, height, layerInfo);
-
- mLayerWidth = layerInfo[0];
- mLayerHeight = layerInfo[1];
- }
- }
-
- @Override
- HardwareCanvas getCanvas() {
- return mCanvas;
- }
-
- @Override
- void end(Canvas currentCanvas) {
- if (currentCanvas instanceof GLES20Canvas) {
- ((GLES20Canvas) currentCanvas).resume();
- }
- }
-
- @Override
- HardwareCanvas start(Canvas currentCanvas) {
- if (currentCanvas instanceof GLES20Canvas) {
- ((GLES20Canvas) currentCanvas).interrupt();
- }
- return getCanvas();
- }
-
+
@Override
void destroy() {
- mFinalizer.destroy();
+ if (mFinalizer != null) mFinalizer.destroy();
mLayer = 0;
}
- private static class Finalizer {
+ static class Finalizer {
private int mLayerId;
public Finalizer(int layerId) {
diff --git a/core/java/android/view/GLES20RenderLayer.java b/core/java/android/view/GLES20RenderLayer.java
new file mode 100644
index 0000000..7adac1c
--- /dev/null
+++ b/core/java/android/view/GLES20RenderLayer.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2011 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;
+
+import android.graphics.Canvas;
+
+/**
+ * An OpenGL ES 2.0 implementation of {@link HardwareLayer}. This
+ * implementation can be used a rendering target. It generates a
+ * {@link Canvas} that can be used to render into an FBO using OpenGL.
+ */
+class GLES20RenderLayer extends GLES20Layer {
+
+ private int mLayerWidth;
+ private int mLayerHeight;
+
+ private final GLES20Canvas mCanvas;
+
+ GLES20RenderLayer(int width, int height, boolean isOpaque) {
+ super(width, height, isOpaque);
+
+ int[] layerInfo = new int[2];
+ mLayer = GLES20Canvas.nCreateLayer(width, height, isOpaque, layerInfo);
+ if (mLayer != 0) {
+ mLayerWidth = layerInfo[0];
+ mLayerHeight = layerInfo[1];
+
+ mCanvas = new GLES20Canvas(mLayer, !isOpaque);
+ mFinalizer = new Finalizer(mLayer);
+ } else {
+ mCanvas = null;
+ mFinalizer = null;
+ }
+ }
+
+ @Override
+ boolean isValid() {
+ return mLayer != 0 && mLayerWidth > 0 && mLayerHeight > 0;
+ }
+
+ @Override
+ void resize(int width, int height) {
+ if (!isValid() || width <= 0 || height <= 0) return;
+
+ mWidth = width;
+ mHeight = height;
+
+ if (width != mLayerWidth || height != mLayerHeight) {
+ int[] layerInfo = new int[2];
+
+ GLES20Canvas.nResizeLayer(mLayer, width, height, layerInfo);
+
+ mLayerWidth = layerInfo[0];
+ mLayerHeight = layerInfo[1];
+ }
+ }
+
+ @Override
+ HardwareCanvas getCanvas() {
+ return mCanvas;
+ }
+
+ @Override
+ void end(Canvas currentCanvas) {
+ if (currentCanvas instanceof GLES20Canvas) {
+ ((GLES20Canvas) currentCanvas).resume();
+ }
+ }
+
+ @Override
+ HardwareCanvas start(Canvas currentCanvas) {
+ if (currentCanvas instanceof GLES20Canvas) {
+ ((GLES20Canvas) currentCanvas).interrupt();
+ }
+ return getCanvas();
+ }
+}
diff --git a/core/java/android/view/GLES20TextureLayer.java b/core/java/android/view/GLES20TextureLayer.java
new file mode 100644
index 0000000..fcf421b
--- /dev/null
+++ b/core/java/android/view/GLES20TextureLayer.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2011 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;
+
+import android.graphics.Canvas;
+import android.graphics.SurfaceTexture;
+
+/**
+ * An OpenGL ES 2.0 implementation of {@link HardwareLayer}. This
+ * implementation can be used as a texture. Rendering into this
+ * layer is not controlled by a {@link HardwareCanvas}.
+ */
+class GLES20TextureLayer extends GLES20Layer {
+ private int mTexture;
+ private SurfaceTexture mSurface;
+
+ GLES20TextureLayer() {
+ int[] layerInfo = new int[2];
+ mLayer = GLES20Canvas.nCreateTextureLayer(layerInfo);
+
+ if (mLayer != 0) {
+ mTexture = layerInfo[0];
+ mFinalizer = new Finalizer(mLayer);
+ } else {
+ mFinalizer = null;
+ }
+ }
+
+ @Override
+ boolean isValid() {
+ return mLayer != 0 && mTexture != 0;
+ }
+
+ @Override
+ void resize(int width, int height) {
+ }
+
+ @Override
+ HardwareCanvas getCanvas() {
+ return null;
+ }
+
+ @Override
+ HardwareCanvas start(Canvas currentCanvas) {
+ return null;
+ }
+
+ @Override
+ void end(Canvas currentCanvas) {
+ }
+
+ SurfaceTexture getSurfaceTexture() {
+ if (mSurface == null) {
+ mSurface = new SurfaceTexture(mTexture);
+ }
+ return mSurface;
+ }
+
+ void update(int width, int height, int surface) {
+ GLES20Canvas.nUpdateTextureLayer(mLayer, width, height, surface);
+ }
+}
diff --git a/core/java/android/view/GestureDetector.java b/core/java/android/view/GestureDetector.java
index 79b3d42..1ccc66f 100755..100644
--- a/core/java/android/view/GestureDetector.java
+++ b/core/java/android/view/GestureDetector.java
@@ -193,8 +193,10 @@ public class GestureDetector {
}
}
+ // TODO: ViewConfiguration
+ private int mBiggerTouchSlopSquare = 20 * 20;
+
private int mTouchSlopSquare;
- private int mLargeTouchSlopSquare;
private int mDoubleTapSlopSquare;
private int mMinimumFlingVelocity;
private int mMaximumFlingVelocity;
@@ -243,6 +245,13 @@ public class GestureDetector {
*/
private VelocityTracker mVelocityTracker;
+ /**
+ * Consistency verifier for debugging purposes.
+ */
+ private final InputEventConsistencyVerifier mInputEventConsistencyVerifier =
+ InputEventConsistencyVerifier.isInstrumentationEnabled() ?
+ new InputEventConsistencyVerifier(this, 0) : null;
+
private class GestureHandler extends Handler {
GestureHandler() {
super();
@@ -382,11 +391,10 @@ public class GestureDetector {
mIgnoreMultitouch = ignoreMultitouch;
// Fallback to support pre-donuts releases
- int touchSlop, largeTouchSlop, doubleTapSlop;
+ int touchSlop, doubleTapSlop;
if (context == null) {
//noinspection deprecation
touchSlop = ViewConfiguration.getTouchSlop();
- largeTouchSlop = touchSlop + 2;
doubleTapSlop = ViewConfiguration.getDoubleTapSlop();
//noinspection deprecation
mMinimumFlingVelocity = ViewConfiguration.getMinimumFlingVelocity();
@@ -394,13 +402,11 @@ public class GestureDetector {
} else {
final ViewConfiguration configuration = ViewConfiguration.get(context);
touchSlop = configuration.getScaledTouchSlop();
- largeTouchSlop = configuration.getScaledLargeTouchSlop();
doubleTapSlop = configuration.getScaledDoubleTapSlop();
mMinimumFlingVelocity = configuration.getScaledMinimumFlingVelocity();
mMaximumFlingVelocity = configuration.getScaledMaximumFlingVelocity();
}
mTouchSlopSquare = touchSlop * touchSlop;
- mLargeTouchSlopSquare = largeTouchSlop * largeTouchSlop;
mDoubleTapSlopSquare = doubleTapSlop * doubleTapSlop;
}
@@ -444,6 +450,10 @@ public class GestureDetector {
* else false.
*/
public boolean onTouchEvent(MotionEvent ev) {
+ if (mInputEventConsistencyVerifier != null) {
+ mInputEventConsistencyVerifier.onTouchEvent(ev, 0);
+ }
+
final int action = ev.getAction();
final float y = ev.getY();
final float x = ev.getX();
@@ -535,7 +545,7 @@ public class GestureDetector {
mHandler.removeMessages(SHOW_PRESS);
mHandler.removeMessages(LONG_PRESS);
}
- if (distance > mLargeTouchSlopSquare) {
+ if (distance > mBiggerTouchSlopSquare) {
mAlwaysInBiggerTapRegion = false;
}
} else if ((Math.abs(scrollX) >= 1) || (Math.abs(scrollY) >= 1)) {
@@ -580,8 +590,14 @@ public class GestureDetector {
mHandler.removeMessages(SHOW_PRESS);
mHandler.removeMessages(LONG_PRESS);
break;
+
case MotionEvent.ACTION_CANCEL:
cancel();
+ break;
+ }
+
+ if (!handled && mInputEventConsistencyVerifier != null) {
+ mInputEventConsistencyVerifier.onUnhandledEvent(ev, 0);
}
return handled;
}
diff --git a/core/java/android/view/HardwareCanvas.java b/core/java/android/view/HardwareCanvas.java
index caa7b74..23b3abc 100644
--- a/core/java/android/view/HardwareCanvas.java
+++ b/core/java/android/view/HardwareCanvas.java
@@ -64,6 +64,14 @@ public abstract class HardwareCanvas extends Canvas {
abstract boolean drawDisplayList(DisplayList displayList, int width, int height, Rect dirty);
/**
+ * Outputs the specified display list to the log. This method exists for use by
+ * tools to output display lists for selected nodes to the log.
+ *
+ * @param displayList The display list to be logged.
+ */
+ abstract void outputDisplayList(DisplayList displayList);
+
+ /**
* Draws the specified layer onto this canvas.
*
* @param layer The layer to composite on this canvas
diff --git a/core/java/android/view/HardwareLayer.java b/core/java/android/view/HardwareLayer.java
index d01b8ce..86dec3f 100644
--- a/core/java/android/view/HardwareLayer.java
+++ b/core/java/android/view/HardwareLayer.java
@@ -26,12 +26,24 @@ import android.graphics.Canvas;
* drawn several times.
*/
abstract class HardwareLayer {
+ /**
+ * Indicates an unknown dimension (width or height.)
+ */
+ static final int DIMENSION_UNDEFINED = -1;
+
int mWidth;
int mHeight;
final boolean mOpaque;
/**
+ * Creates a new hardware layer with undefined dimensions.
+ */
+ HardwareLayer() {
+ this(DIMENSION_UNDEFINED, DIMENSION_UNDEFINED, false);
+ }
+
+ /**
* Creates a new hardware layer at least as large as the supplied
* dimensions.
*
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index 8584bf2..845fbc3 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -20,7 +20,8 @@ package android.view;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
-import android.os.SystemClock;
+import android.graphics.SurfaceTexture;
+import android.os.*;
import android.util.EventLog;
import android.util.Log;
@@ -33,7 +34,7 @@ import javax.microedition.khronos.egl.EGLSurface;
import javax.microedition.khronos.opengles.GL;
/**
- * Interface for rendering a ViewRoot using hardware acceleration.
+ * Interface for rendering a ViewAncestor using hardware acceleration.
*
* @hide
*/
@@ -166,6 +167,14 @@ public abstract class HardwareRenderer {
abstract DisplayList createDisplayList(View v);
/**
+ * Creates a new hardware layer. A hardware layer built by calling this
+ * method will be treated as a texture layer, instead of as a render target.
+ *
+ * @return A hardware layer
+ */
+ abstract HardwareLayer createHardwareLayer();
+
+ /**
* Creates a new hardware layer.
*
* @param width The minimum width of the layer
@@ -175,10 +184,32 @@ public abstract class HardwareRenderer {
* @return A hardware layer
*/
abstract HardwareLayer createHardwareLayer(int width, int height, boolean isOpaque);
+
+ /**
+ * Creates a new {@link SurfaceTexture} that can be used to render into the
+ * specified hardware layer.
+ *
+ *
+ * @param layer The layer to render into using a {@link android.graphics.SurfaceTexture}
+ *
+ * @return A {@link SurfaceTexture}
+ */
+ abstract SurfaceTexture createSuraceTexture(HardwareLayer layer);
+
+ /**
+ * Updates the specified layer.
+ *
+ * @param layer The hardware layer to update
+ * @param width The layer's width
+ * @param height The layer's height
+ * @param surface The surface to update
+ */
+ abstract void updateTextureLayer(HardwareLayer layer, int width, int height,
+ SurfaceTexture surface);
/**
* Initializes the hardware renderer for the specified surface and setup the
- * renderer for drawing, if needed. This is invoked when the ViewRoot has
+ * renderer for drawing, if needed. This is invoked when the ViewAncestor has
* potentially lost the hardware renderer. The hardware renderer should be
* reinitialized and setup when the render {@link #isRequested()} and
* {@link #isEnabled()}.
@@ -256,9 +287,11 @@ public abstract class HardwareRenderer {
@SuppressWarnings({"deprecation"})
static abstract class GlRenderer extends HardwareRenderer {
- private static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
- private static final int EGL_SURFACE_TYPE = 0x3033;
- private static final int EGL_SWAP_BEHAVIOR_PRESERVED_BIT = 0x0400;
+ // These values are not exposed in our EGL APIs
+ static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
+ static final int EGL_SURFACE_TYPE = 0x3033;
+ static final int EGL_SWAP_BEHAVIOR_PRESERVED_BIT = 0x0400;
+ static final int EGL_OPENGL_ES2_BIT = 4;
private static final int SURFACE_STATE_ERROR = 0;
private static final int SURFACE_STATE_SUCCESS = 1;
@@ -290,7 +323,7 @@ public abstract class HardwareRenderer {
GlRenderer(int glVersion, boolean translucent) {
mGlVersion = glVersion;
mTranslucent = translucent;
- final String dirtyProperty = System.getProperty(RENDER_DIRTY_REGIONS_PROPERTY, "true");
+ final String dirtyProperty = SystemProperties.get(RENDER_DIRTY_REGIONS_PROPERTY, "true");
//noinspection PointlessBooleanExpression,ConstantConditions
mDirtyRegions = RENDER_DIRTY_REGIONS && "true".equalsIgnoreCase(dirtyProperty);
}
@@ -426,13 +459,12 @@ public abstract class HardwareRenderer {
getEGLErrorString(sEgl.eglGetError()));
}
- sEglConfig = getConfigChooser(mGlVersion).chooseConfig(sEgl, sEglDisplay);
+ sEglConfig = chooseEglConfig();
if (sEglConfig == null) {
// We tried to use EGL_SWAP_BEHAVIOR_PRESERVED_BIT, try again without
if (mDirtyRegions) {
mDirtyRegions = false;
-
- sEglConfig = getConfigChooser(mGlVersion).chooseConfig(sEgl, sEglDisplay);
+ sEglConfig = chooseEglConfig();
if (sEglConfig == null) {
throw new RuntimeException("eglConfig not initialized");
}
@@ -448,6 +480,21 @@ public abstract class HardwareRenderer {
sEglContext = createContext(sEgl, sEglDisplay, sEglConfig);
}
+ private EGLConfig chooseEglConfig() {
+ int[] configsCount = new int[1];
+ EGLConfig[] configs = new EGLConfig[1];
+ int[] configSpec = getConfig(mDirtyRegions);
+ if (!sEgl.eglChooseConfig(sEglDisplay, configSpec, configs, 1, configsCount)) {
+ throw new IllegalArgumentException("eglChooseConfig failed " +
+ getEGLErrorString(sEgl.eglGetError()));
+ } else if (configsCount[0] > 0) {
+ return configs[0];
+ }
+ return null;
+ }
+
+ abstract int[] getConfig(boolean dirtyRegions);
+
GL createEglSurface(SurfaceHolder holder) throws Surface.OutOfResourcesException {
// Check preconditions.
if (sEgl == null) {
@@ -559,15 +606,6 @@ public abstract class HardwareRenderer {
void onPostDraw() {
}
-
- /**
- * Defines the EGL configuration for this renderer.
- *
- * @return An {@link android.view.HardwareRenderer.GlRenderer.EglConfigChooser}.
- */
- EglConfigChooser getConfigChooser(int glVersion) {
- return new ComponentSizeChooser(glVersion, 8, 8, 8, 8, 0, 0, mDirtyRegions);
- }
@Override
void draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks,
@@ -644,8 +682,21 @@ public abstract class HardwareRenderer {
}
attachInfo.mIgnoreDirtyState = false;
-
+
+ final long swapBuffersStartTime;
+ if (ViewDebug.DEBUG_LATENCY) {
+ swapBuffersStartTime = System.nanoTime();
+ }
+
sEgl.eglSwapBuffers(sEglDisplay, mEglSurface);
+
+ if (ViewDebug.DEBUG_LATENCY) {
+ long now = System.nanoTime();
+ Log.d(LOG_TAG, "Latency: Spent "
+ + ((now - swapBuffersStartTime) * 0.000001f)
+ + "ms waiting for eglSwapBuffers()");
+ }
+
checkEglErrors();
}
}
@@ -667,134 +718,6 @@ public abstract class HardwareRenderer {
}
return SURFACE_STATE_SUCCESS;
}
-
- static abstract class EglConfigChooser {
- final int[] mConfigSpec;
- private final int mGlVersion;
-
- EglConfigChooser(int glVersion, int[] configSpec) {
- mGlVersion = glVersion;
- mConfigSpec = filterConfigSpec(configSpec);
- }
-
- EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
- int[] index = new int[1];
- if (!egl.eglChooseConfig(display, mConfigSpec, null, 0, index)) {
- throw new IllegalArgumentException("eglChooseConfig failed "
- + getEGLErrorString(egl.eglGetError()));
- }
-
- int numConfigs = index[0];
- if (numConfigs <= 0) {
- throw new IllegalArgumentException("No configs match configSpec");
- }
-
- EGLConfig[] configs = new EGLConfig[numConfigs];
- if (!egl.eglChooseConfig(display, mConfigSpec, configs, numConfigs, index)) {
- throw new IllegalArgumentException("eglChooseConfig failed "
- + getEGLErrorString(egl.eglGetError()));
- }
-
- EGLConfig config = chooseConfig(egl, display, configs);
- if (config == null) {
- throw new IllegalArgumentException("No config chosen");
- }
-
- return config;
- }
-
- abstract EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, EGLConfig[] configs);
-
- private int[] filterConfigSpec(int[] configSpec) {
- if (mGlVersion != 2) {
- return configSpec;
- }
- /* We know none of the subclasses define EGL_RENDERABLE_TYPE.
- * And we know the configSpec is well formed.
- */
- int len = configSpec.length;
- int[] newConfigSpec = new int[len + 2];
- System.arraycopy(configSpec, 0, newConfigSpec, 0, len - 1);
- newConfigSpec[len - 1] = EGL10.EGL_RENDERABLE_TYPE;
- newConfigSpec[len] = 4; /* EGL_OPENGL_ES2_BIT */
- newConfigSpec[len + 1] = EGL10.EGL_NONE;
- return newConfigSpec;
- }
- }
-
- /**
- * Choose a configuration with exactly the specified r,g,b,a sizes,
- * and at least the specified depth and stencil sizes.
- */
- static class ComponentSizeChooser extends EglConfigChooser {
- private int[] mValue;
-
- private final int mRedSize;
- private final int mGreenSize;
- private final int mBlueSize;
- private final int mAlphaSize;
- private final int mDepthSize;
- private final int mStencilSize;
- private final boolean mDirtyRegions;
-
- ComponentSizeChooser(int glVersion, int redSize, int greenSize, int blueSize,
- int alphaSize, int depthSize, int stencilSize, boolean dirtyRegions) {
- super(glVersion, new int[] {
- EGL10.EGL_RED_SIZE, redSize,
- EGL10.EGL_GREEN_SIZE, greenSize,
- EGL10.EGL_BLUE_SIZE, blueSize,
- EGL10.EGL_ALPHA_SIZE, alphaSize,
- EGL10.EGL_DEPTH_SIZE, depthSize,
- EGL10.EGL_STENCIL_SIZE, stencilSize,
- EGL_SURFACE_TYPE, EGL10.EGL_WINDOW_BIT |
- (dirtyRegions ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0),
- EGL10.EGL_NONE });
- mValue = new int[1];
- mRedSize = redSize;
- mGreenSize = greenSize;
- mBlueSize = blueSize;
- mAlphaSize = alphaSize;
- mDepthSize = depthSize;
- mStencilSize = stencilSize;
- mDirtyRegions = dirtyRegions;
- }
-
- @Override
- EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, EGLConfig[] configs) {
- for (EGLConfig config : configs) {
- int d = findConfigAttrib(egl, display, config, EGL10.EGL_DEPTH_SIZE, 0);
- int s = findConfigAttrib(egl, display, config, EGL10.EGL_STENCIL_SIZE, 0);
- if (d >= mDepthSize && s >= mStencilSize) {
- int r = findConfigAttrib(egl, display, config, EGL10.EGL_RED_SIZE, 0);
- int g = findConfigAttrib(egl, display, config, EGL10.EGL_GREEN_SIZE, 0);
- int b = findConfigAttrib(egl, display, config, EGL10.EGL_BLUE_SIZE, 0);
- int a = findConfigAttrib(egl, display, config, EGL10.EGL_ALPHA_SIZE, 0);
- boolean backBuffer;
- if (mDirtyRegions) {
- int surfaceType = findConfigAttrib(egl, display, config,
- EGL_SURFACE_TYPE, 0);
- backBuffer = (surfaceType & EGL_SWAP_BEHAVIOR_PRESERVED_BIT) != 0;
- } else {
- backBuffer = true;
- }
- if (r >= mRedSize && g >= mGreenSize && b >= mBlueSize && a >= mAlphaSize
- && backBuffer) {
- return config;
- }
- }
- }
- return null;
- }
-
- private int findConfigAttrib(EGL10 egl, EGLDisplay display, EGLConfig config,
- int attribute, int defaultValue) {
- if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) {
- return mValue[0];
- }
-
- return defaultValue;
- }
- }
}
/**
@@ -811,7 +734,23 @@ public abstract class HardwareRenderer {
GLES20Canvas createCanvas() {
return mGlCanvas = new GLES20Canvas(mTranslucent);
}
-
+
+ @Override
+ int[] getConfig(boolean dirtyRegions) {
+ return new int[] {
+ EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ EGL10.EGL_RED_SIZE, 8,
+ EGL10.EGL_GREEN_SIZE, 8,
+ EGL10.EGL_BLUE_SIZE, 8,
+ EGL10.EGL_ALPHA_SIZE, 8,
+ EGL10.EGL_DEPTH_SIZE, 0,
+ EGL10.EGL_STENCIL_SIZE, 0,
+ EGL_SURFACE_TYPE, EGL10.EGL_WINDOW_BIT |
+ (dirtyRegions ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0),
+ EGL10.EGL_NONE
+ };
+ }
+
@Override
boolean canDraw() {
return super.canDraw() && mGlCanvas != null;
@@ -842,10 +781,26 @@ public abstract class HardwareRenderer {
DisplayList createDisplayList(View v) {
return new GLES20DisplayList(v);
}
-
+
+ @Override
+ HardwareLayer createHardwareLayer() {
+ return new GLES20TextureLayer();
+ }
+
@Override
HardwareLayer createHardwareLayer(int width, int height, boolean isOpaque) {
- return new GLES20Layer(width, height, isOpaque);
+ return new GLES20RenderLayer(width, height, isOpaque);
+ }
+
+ @Override
+ SurfaceTexture createSuraceTexture(HardwareLayer layer) {
+ return ((GLES20TextureLayer) layer).getSurfaceTexture();
+ }
+
+ @Override
+ void updateTextureLayer(HardwareLayer layer, int width, int height,
+ SurfaceTexture surface) {
+ ((GLES20TextureLayer) layer).update(width, height, surface.mSurfaceTexture);
}
static HardwareRenderer create(boolean translucent) {
diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java
index 8cb68f9..bfc7c31 100755
--- a/core/java/android/view/InputDevice.java
+++ b/core/java/android/view/InputDevice.java
@@ -153,7 +153,14 @@ public final class InputDevice implements Parcelable {
* @see #SOURCE_CLASS_POINTER
*/
public static final int SOURCE_MOUSE = 0x00002000 | SOURCE_CLASS_POINTER;
-
+
+ /**
+ * The input source is a stylus pointing device.
+ *
+ * @see #SOURCE_CLASS_POINTER
+ */
+ public static final int SOURCE_STYLUS = 0x00004000 | SOURCE_CLASS_POINTER;
+
/**
* The input source is a trackball.
*
@@ -585,6 +592,7 @@ public final class InputDevice implements Parcelable {
appendSourceDescriptionIfApplicable(description, SOURCE_DPAD, "dpad");
appendSourceDescriptionIfApplicable(description, SOURCE_TOUCHSCREEN, "touchscreen");
appendSourceDescriptionIfApplicable(description, SOURCE_MOUSE, "mouse");
+ appendSourceDescriptionIfApplicable(description, SOURCE_STYLUS, "stylus");
appendSourceDescriptionIfApplicable(description, SOURCE_TRACKBALL, "trackball");
appendSourceDescriptionIfApplicable(description, SOURCE_TOUCHPAD, "touchpad");
appendSourceDescriptionIfApplicable(description, SOURCE_JOYSTICK, "joystick");
diff --git a/core/java/android/view/InputEvent.java b/core/java/android/view/InputEvent.java
index f6aeb39..01ddcc9 100755
--- a/core/java/android/view/InputEvent.java
+++ b/core/java/android/view/InputEvent.java
@@ -67,6 +67,52 @@ public abstract class InputEvent implements Parcelable {
*/
public abstract void setSource(int source);
+ /**
+ * Copies the event.
+ *
+ * @return A deep copy of the event.
+ * @hide
+ */
+ public abstract InputEvent copy();
+
+ /**
+ * Recycles the event.
+ * This method should only be used by the system since applications do not
+ * expect {@link KeyEvent} objects to be recycled, although {@link MotionEvent}
+ * objects are fine. See {@link KeyEvent#recycle()} for details.
+ * @hide
+ */
+ public abstract void recycle();
+
+ /**
+ * Gets a private flag that indicates when the system has detected that this input event
+ * may be inconsistent with respect to the sequence of previously delivered input events,
+ * such as when a key up event is sent but the key was not down or when a pointer
+ * move event is sent but the pointer is not down.
+ *
+ * @return True if this event is tainted.
+ * @hide
+ */
+ public abstract boolean isTainted();
+
+ /**
+ * Sets a private flag that indicates when the system has detected that this input event
+ * may be inconsistent with respect to the sequence of previously delivered input events,
+ * such as when a key up event is sent but the key was not down or when a pointer
+ * move event is sent but the pointer is not down.
+ *
+ * @param tainted True if this event is tainted.
+ * @hide
+ */
+ public abstract void setTainted(boolean tainted);
+
+ /**
+ * Returns the time (in ns) when this specific event was generated.
+ * The value is in nanosecond precision but it may not have nanosecond accuracy.
+ * @hide
+ */
+ public abstract long getEventTimeNano();
+
public int describeContents() {
return 0;
}
diff --git a/core/java/android/view/InputEventConsistencyVerifier.java b/core/java/android/view/InputEventConsistencyVerifier.java
new file mode 100644
index 0000000..e14b975
--- /dev/null
+++ b/core/java/android/view/InputEventConsistencyVerifier.java
@@ -0,0 +1,730 @@
+/*
+ * Copyright (C) 2010 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;
+
+import android.os.Build;
+import android.util.Log;
+
+/**
+ * Checks whether a sequence of input events is self-consistent.
+ * Logs a description of each problem detected.
+ * <p>
+ * When a problem is detected, the event is tainted. This mechanism prevents the same
+ * error from being reported multiple times.
+ * </p>
+ *
+ * @hide
+ */
+public final class InputEventConsistencyVerifier {
+ private static final boolean IS_ENG_BUILD = "eng".equals(Build.TYPE);
+
+ // The number of recent events to log when a problem is detected.
+ // Can be set to 0 to disable logging recent events but the runtime overhead of
+ // this feature is negligible on current hardware.
+ private static final int RECENT_EVENTS_TO_LOG = 5;
+
+ // The object to which the verifier is attached.
+ private final Object mCaller;
+
+ // Consistency verifier flags.
+ private final int mFlags;
+
+ // Tag for logging which a client can set to help distinguish the output
+ // from different verifiers since several can be active at the same time.
+ // If not provided defaults to the simple class name.
+ private final String mLogTag;
+
+ // The most recently checked event and the nesting level at which it was checked.
+ // This is only set when the verifier is called from a nesting level greater than 0
+ // so that the verifier can detect when it has been asked to verify the same event twice.
+ // It does not make sense to examine the contents of the last event since it may have
+ // been recycled.
+ private InputEvent mLastEvent;
+ private int mLastNestingLevel;
+
+ // Copy of the most recent events.
+ private InputEvent[] mRecentEvents;
+ private boolean[] mRecentEventsUnhandled;
+ private int mMostRecentEventIndex;
+
+ // Current event and its type.
+ private InputEvent mCurrentEvent;
+ private String mCurrentEventType;
+
+ // Linked list of key state objects.
+ private KeyState mKeyStateList;
+
+ // Current state of the trackball.
+ private boolean mTrackballDown;
+ private boolean mTrackballUnhandled;
+
+ // Bitfield of pointer ids that are currently down.
+ // Assumes that the largest possible pointer id is 31, which is potentially subject to change.
+ // (See MAX_POINTER_ID in frameworks/base/include/ui/Input.h)
+ private int mTouchEventStreamPointers;
+
+ // The device id and source of the current stream of touch events.
+ private int mTouchEventStreamDeviceId = -1;
+ private int mTouchEventStreamSource;
+
+ // Set to true when we discover that the touch event stream is inconsistent.
+ // Reset on down or cancel.
+ private boolean mTouchEventStreamIsTainted;
+
+ // Set to true if the touch event stream is partially unhandled.
+ private boolean mTouchEventStreamUnhandled;
+
+ // Set to true if we received hover enter.
+ private boolean mHoverEntered;
+
+ // The current violation message.
+ private StringBuilder mViolationMessage;
+
+ /**
+ * Indicates that the verifier is intended to act on raw device input event streams.
+ * Disables certain checks for invariants that are established by the input dispatcher
+ * itself as it delivers input events, such as key repeating behavior.
+ */
+ public static final int FLAG_RAW_DEVICE_INPUT = 1 << 0;
+
+ /**
+ * Creates an input consistency verifier.
+ * @param caller The object to which the verifier is attached.
+ * @param flags Flags to the verifier, or 0 if none.
+ */
+ public InputEventConsistencyVerifier(Object caller, int flags) {
+ this(caller, flags, InputEventConsistencyVerifier.class.getSimpleName());
+ }
+
+ /**
+ * Creates an input consistency verifier.
+ * @param caller The object to which the verifier is attached.
+ * @param flags Flags to the verifier, or 0 if none.
+ * @param logTag Tag for logging. If null defaults to the short class name.
+ */
+ public InputEventConsistencyVerifier(Object caller, int flags, String logTag) {
+ this.mCaller = caller;
+ this.mFlags = flags;
+ this.mLogTag = (logTag != null) ? logTag : "InputEventConsistencyVerifier";
+ }
+
+ /**
+ * Determines whether the instrumentation should be enabled.
+ * @return True if it should be enabled.
+ */
+ public static boolean isInstrumentationEnabled() {
+ return IS_ENG_BUILD;
+ }
+
+ /**
+ * Resets the state of the input event consistency verifier.
+ */
+ public void reset() {
+ mLastEvent = null;
+ mLastNestingLevel = 0;
+ mTrackballDown = false;
+ mTrackballUnhandled = false;
+ mTouchEventStreamPointers = 0;
+ mTouchEventStreamIsTainted = false;
+ mTouchEventStreamUnhandled = false;
+ mHoverEntered = false;
+
+ while (mKeyStateList != null) {
+ final KeyState state = mKeyStateList;
+ mKeyStateList = state.next;
+ state.recycle();
+ }
+ }
+
+ /**
+ * Checks an arbitrary input event.
+ * @param event The event.
+ * @param nestingLevel The nesting level: 0 if called from the base class,
+ * or 1 from a subclass. If the event was already checked by this consistency verifier
+ * at a higher nesting level, it will not be checked again. Used to handle the situation
+ * where a subclass dispatching method delegates to its superclass's dispatching method
+ * and both dispatching methods call into the consistency verifier.
+ */
+ public void onInputEvent(InputEvent event, int nestingLevel) {
+ if (event instanceof KeyEvent) {
+ final KeyEvent keyEvent = (KeyEvent)event;
+ onKeyEvent(keyEvent, nestingLevel);
+ } else {
+ final MotionEvent motionEvent = (MotionEvent)event;
+ if (motionEvent.isTouchEvent()) {
+ onTouchEvent(motionEvent, nestingLevel);
+ } else if ((motionEvent.getSource() & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
+ onTrackballEvent(motionEvent, nestingLevel);
+ } else {
+ onGenericMotionEvent(motionEvent, nestingLevel);
+ }
+ }
+ }
+
+ /**
+ * Checks a key event.
+ * @param event The event.
+ * @param nestingLevel The nesting level: 0 if called from the base class,
+ * or 1 from a subclass. If the event was already checked by this consistency verifier
+ * at a higher nesting level, it will not be checked again. Used to handle the situation
+ * where a subclass dispatching method delegates to its superclass's dispatching method
+ * and both dispatching methods call into the consistency verifier.
+ */
+ public void onKeyEvent(KeyEvent event, int nestingLevel) {
+ if (!startEvent(event, nestingLevel, "KeyEvent")) {
+ return;
+ }
+
+ try {
+ ensureMetaStateIsNormalized(event.getMetaState());
+
+ final int action = event.getAction();
+ final int deviceId = event.getDeviceId();
+ final int source = event.getSource();
+ final int keyCode = event.getKeyCode();
+ switch (action) {
+ case KeyEvent.ACTION_DOWN: {
+ KeyState state = findKeyState(deviceId, source, keyCode, /*remove*/ false);
+ if (state != null) {
+ // If the key is already down, ensure it is a repeat.
+ // We don't perform this check when processing raw device input
+ // because the input dispatcher itself is responsible for setting
+ // the key repeat count before it delivers input events.
+ if (state.unhandled) {
+ state.unhandled = false;
+ } else if ((mFlags & FLAG_RAW_DEVICE_INPUT) == 0
+ && event.getRepeatCount() == 0) {
+ problem("ACTION_DOWN but key is already down and this event "
+ + "is not a key repeat.");
+ }
+ } else {
+ addKeyState(deviceId, source, keyCode);
+ }
+ break;
+ }
+ case KeyEvent.ACTION_UP: {
+ KeyState state = findKeyState(deviceId, source, keyCode, /*remove*/ true);
+ if (state == null) {
+ problem("ACTION_UP but key was not down.");
+ } else {
+ state.recycle();
+ }
+ break;
+ }
+ case KeyEvent.ACTION_MULTIPLE:
+ break;
+ default:
+ problem("Invalid action " + KeyEvent.actionToString(action)
+ + " for key event.");
+ break;
+ }
+ } finally {
+ finishEvent(false);
+ }
+ }
+
+ /**
+ * Checks a trackball event.
+ * @param event The event.
+ * @param nestingLevel The nesting level: 0 if called from the base class,
+ * or 1 from a subclass. If the event was already checked by this consistency verifier
+ * at a higher nesting level, it will not be checked again. Used to handle the situation
+ * where a subclass dispatching method delegates to its superclass's dispatching method
+ * and both dispatching methods call into the consistency verifier.
+ */
+ public void onTrackballEvent(MotionEvent event, int nestingLevel) {
+ if (!startEvent(event, nestingLevel, "TrackballEvent")) {
+ return;
+ }
+
+ try {
+ ensureMetaStateIsNormalized(event.getMetaState());
+
+ final int action = event.getAction();
+ final int source = event.getSource();
+ if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
+ switch (action) {
+ case MotionEvent.ACTION_DOWN:
+ if (mTrackballDown && !mTrackballUnhandled) {
+ problem("ACTION_DOWN but trackball is already down.");
+ } else {
+ mTrackballDown = true;
+ mTrackballUnhandled = false;
+ }
+ ensureHistorySizeIsZeroForThisAction(event);
+ ensurePointerCountIsOneForThisAction(event);
+ break;
+ case MotionEvent.ACTION_UP:
+ if (!mTrackballDown) {
+ problem("ACTION_UP but trackball is not down.");
+ } else {
+ mTrackballDown = false;
+ mTrackballUnhandled = false;
+ }
+ ensureHistorySizeIsZeroForThisAction(event);
+ ensurePointerCountIsOneForThisAction(event);
+ break;
+ case MotionEvent.ACTION_MOVE:
+ ensurePointerCountIsOneForThisAction(event);
+ break;
+ default:
+ problem("Invalid action " + MotionEvent.actionToString(action)
+ + " for trackball event.");
+ break;
+ }
+
+ if (mTrackballDown && event.getPressure() <= 0) {
+ problem("Trackball is down but pressure is not greater than 0.");
+ } else if (!mTrackballDown && event.getPressure() != 0) {
+ problem("Trackball is up but pressure is not equal to 0.");
+ }
+ } else {
+ problem("Source was not SOURCE_CLASS_TRACKBALL.");
+ }
+ } finally {
+ finishEvent(false);
+ }
+ }
+
+ /**
+ * Checks a touch event.
+ * @param event The event.
+ * @param nestingLevel The nesting level: 0 if called from the base class,
+ * or 1 from a subclass. If the event was already checked by this consistency verifier
+ * at a higher nesting level, it will not be checked again. Used to handle the situation
+ * where a subclass dispatching method delegates to its superclass's dispatching method
+ * and both dispatching methods call into the consistency verifier.
+ */
+ public void onTouchEvent(MotionEvent event, int nestingLevel) {
+ if (!startEvent(event, nestingLevel, "TouchEvent")) {
+ return;
+ }
+
+ final int action = event.getAction();
+ final boolean newStream = action == MotionEvent.ACTION_DOWN
+ || action == MotionEvent.ACTION_CANCEL;
+ if (mTouchEventStreamIsTainted || mTouchEventStreamUnhandled) {
+ if (newStream) {
+ mTouchEventStreamIsTainted = false;
+ mTouchEventStreamUnhandled = false;
+ mTouchEventStreamPointers = 0;
+ } else {
+ finishEvent(mTouchEventStreamIsTainted);
+ return;
+ }
+ }
+
+ try {
+ ensureMetaStateIsNormalized(event.getMetaState());
+
+ final int deviceId = event.getDeviceId();
+ final int source = event.getSource();
+
+ if (!newStream && mTouchEventStreamDeviceId != -1
+ && (mTouchEventStreamDeviceId != deviceId
+ || mTouchEventStreamSource != source)) {
+ problem("Touch event stream contains events from multiple sources: "
+ + "previous device id " + mTouchEventStreamDeviceId
+ + ", previous source " + Integer.toHexString(mTouchEventStreamSource)
+ + ", new device id " + deviceId
+ + ", new source " + Integer.toHexString(source));
+ }
+ mTouchEventStreamDeviceId = deviceId;
+ mTouchEventStreamSource = source;
+
+ final int pointerCount = event.getPointerCount();
+ if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
+ switch (action) {
+ case MotionEvent.ACTION_DOWN:
+ if (mTouchEventStreamPointers != 0) {
+ problem("ACTION_DOWN but pointers are already down. "
+ + "Probably missing ACTION_UP from previous gesture.");
+ }
+ ensureHistorySizeIsZeroForThisAction(event);
+ ensurePointerCountIsOneForThisAction(event);
+ mTouchEventStreamPointers = 1 << event.getPointerId(0);
+ break;
+ case MotionEvent.ACTION_UP:
+ ensureHistorySizeIsZeroForThisAction(event);
+ ensurePointerCountIsOneForThisAction(event);
+ mTouchEventStreamPointers = 0;
+ mTouchEventStreamIsTainted = false;
+ break;
+ case MotionEvent.ACTION_MOVE: {
+ final int expectedPointerCount =
+ Integer.bitCount(mTouchEventStreamPointers);
+ if (pointerCount != expectedPointerCount) {
+ problem("ACTION_MOVE contained " + pointerCount
+ + " pointers but there are currently "
+ + expectedPointerCount + " pointers down.");
+ mTouchEventStreamIsTainted = true;
+ }
+ break;
+ }
+ case MotionEvent.ACTION_CANCEL:
+ mTouchEventStreamPointers = 0;
+ mTouchEventStreamIsTainted = false;
+ break;
+ case MotionEvent.ACTION_OUTSIDE:
+ if (mTouchEventStreamPointers != 0) {
+ problem("ACTION_OUTSIDE but pointers are still down.");
+ }
+ ensureHistorySizeIsZeroForThisAction(event);
+ ensurePointerCountIsOneForThisAction(event);
+ mTouchEventStreamIsTainted = false;
+ break;
+ default: {
+ final int actionMasked = event.getActionMasked();
+ final int actionIndex = event.getActionIndex();
+ if (actionMasked == MotionEvent.ACTION_POINTER_DOWN) {
+ if (mTouchEventStreamPointers == 0) {
+ problem("ACTION_POINTER_DOWN but no other pointers were down.");
+ mTouchEventStreamIsTainted = true;
+ }
+ if (actionIndex < 0 || actionIndex >= pointerCount) {
+ problem("ACTION_POINTER_DOWN index is " + actionIndex
+ + " but the pointer count is " + pointerCount + ".");
+ mTouchEventStreamIsTainted = true;
+ } else {
+ final int id = event.getPointerId(actionIndex);
+ final int idBit = 1 << id;
+ if ((mTouchEventStreamPointers & idBit) != 0) {
+ problem("ACTION_POINTER_DOWN specified pointer id " + id
+ + " which is already down.");
+ mTouchEventStreamIsTainted = true;
+ } else {
+ mTouchEventStreamPointers |= idBit;
+ }
+ }
+ ensureHistorySizeIsZeroForThisAction(event);
+ } else if (actionMasked == MotionEvent.ACTION_POINTER_UP) {
+ if (actionIndex < 0 || actionIndex >= pointerCount) {
+ problem("ACTION_POINTER_UP index is " + actionIndex
+ + " but the pointer count is " + pointerCount + ".");
+ mTouchEventStreamIsTainted = true;
+ } else {
+ final int id = event.getPointerId(actionIndex);
+ final int idBit = 1 << id;
+ if ((mTouchEventStreamPointers & idBit) == 0) {
+ problem("ACTION_POINTER_UP specified pointer id " + id
+ + " which is not currently down.");
+ mTouchEventStreamIsTainted = true;
+ } else {
+ mTouchEventStreamPointers &= ~idBit;
+ }
+ }
+ ensureHistorySizeIsZeroForThisAction(event);
+ } else {
+ problem("Invalid action " + MotionEvent.actionToString(action)
+ + " for touch event.");
+ }
+ break;
+ }
+ }
+ } else {
+ problem("Source was not SOURCE_CLASS_POINTER.");
+ }
+ } finally {
+ finishEvent(false);
+ }
+ }
+
+ /**
+ * Checks a generic motion event.
+ * @param event The event.
+ * @param nestingLevel The nesting level: 0 if called from the base class,
+ * or 1 from a subclass. If the event was already checked by this consistency verifier
+ * at a higher nesting level, it will not be checked again. Used to handle the situation
+ * where a subclass dispatching method delegates to its superclass's dispatching method
+ * and both dispatching methods call into the consistency verifier.
+ */
+ public void onGenericMotionEvent(MotionEvent event, int nestingLevel) {
+ if (!startEvent(event, nestingLevel, "GenericMotionEvent")) {
+ return;
+ }
+
+ try {
+ ensureMetaStateIsNormalized(event.getMetaState());
+
+ final int action = event.getAction();
+ final int source = event.getSource();
+ if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
+ switch (action) {
+ case MotionEvent.ACTION_HOVER_ENTER:
+ ensurePointerCountIsOneForThisAction(event);
+ mHoverEntered = true;
+ break;
+ case MotionEvent.ACTION_HOVER_MOVE:
+ ensurePointerCountIsOneForThisAction(event);
+ break;
+ case MotionEvent.ACTION_HOVER_EXIT:
+ ensurePointerCountIsOneForThisAction(event);
+ if (!mHoverEntered) {
+ problem("ACTION_HOVER_EXIT without prior ACTION_HOVER_ENTER");
+ }
+ mHoverEntered = false;
+ break;
+ case MotionEvent.ACTION_SCROLL:
+ ensureHistorySizeIsZeroForThisAction(event);
+ ensurePointerCountIsOneForThisAction(event);
+ break;
+ default:
+ problem("Invalid action for generic pointer event.");
+ break;
+ }
+ } else if ((source & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
+ switch (action) {
+ case MotionEvent.ACTION_MOVE:
+ ensurePointerCountIsOneForThisAction(event);
+ break;
+ default:
+ problem("Invalid action for generic joystick event.");
+ break;
+ }
+ }
+ } finally {
+ finishEvent(false);
+ }
+ }
+
+ /**
+ * Notifies the verifier that a given event was unhandled and the rest of the
+ * trace for the event should be ignored.
+ * This method should only be called if the event was previously checked by
+ * the consistency verifier using {@link #onInputEvent} and other methods.
+ * @param event The event.
+ * @param nestingLevel The nesting level: 0 if called from the base class,
+ * or 1 from a subclass. If the event was already checked by this consistency verifier
+ * at a higher nesting level, it will not be checked again. Used to handle the situation
+ * where a subclass dispatching method delegates to its superclass's dispatching method
+ * and both dispatching methods call into the consistency verifier.
+ */
+ public void onUnhandledEvent(InputEvent event, int nestingLevel) {
+ if (nestingLevel != mLastNestingLevel) {
+ return;
+ }
+
+ if (mRecentEventsUnhandled != null) {
+ mRecentEventsUnhandled[mMostRecentEventIndex] = true;
+ }
+
+ if (event instanceof KeyEvent) {
+ final KeyEvent keyEvent = (KeyEvent)event;
+ final int deviceId = keyEvent.getDeviceId();
+ final int source = keyEvent.getSource();
+ final int keyCode = keyEvent.getKeyCode();
+ final KeyState state = findKeyState(deviceId, source, keyCode, /*remove*/ false);
+ if (state != null) {
+ state.unhandled = true;
+ }
+ } else {
+ final MotionEvent motionEvent = (MotionEvent)event;
+ if (motionEvent.isTouchEvent()) {
+ mTouchEventStreamUnhandled = true;
+ } else if ((motionEvent.getSource() & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
+ if (mTrackballDown) {
+ mTrackballUnhandled = true;
+ }
+ }
+ }
+ }
+
+ private void ensureMetaStateIsNormalized(int metaState) {
+ final int normalizedMetaState = KeyEvent.normalizeMetaState(metaState);
+ if (normalizedMetaState != metaState) {
+ problem(String.format("Metastate not normalized. Was 0x%08x but expected 0x%08x.",
+ metaState, normalizedMetaState));
+ }
+ }
+
+ private void ensurePointerCountIsOneForThisAction(MotionEvent event) {
+ final int pointerCount = event.getPointerCount();
+ if (pointerCount != 1) {
+ problem("Pointer count is " + pointerCount + " but it should always be 1 for "
+ + MotionEvent.actionToString(event.getAction()));
+ }
+ }
+
+ private void ensureHistorySizeIsZeroForThisAction(MotionEvent event) {
+ final int historySize = event.getHistorySize();
+ if (historySize != 0) {
+ problem("History size is " + historySize + " but it should always be 0 for "
+ + MotionEvent.actionToString(event.getAction()));
+ }
+ }
+
+ private boolean startEvent(InputEvent event, int nestingLevel, String eventType) {
+ // Ignore the event if it is already tainted.
+ if (event.isTainted()) {
+ return false;
+ }
+
+ // Ignore the event if we already checked it at a higher nesting level.
+ if (event == mLastEvent && nestingLevel < mLastNestingLevel) {
+ return false;
+ }
+
+ if (nestingLevel > 0) {
+ mLastEvent = event;
+ mLastNestingLevel = nestingLevel;
+ } else {
+ mLastEvent = null;
+ mLastNestingLevel = 0;
+ }
+
+ mCurrentEvent = event;
+ mCurrentEventType = eventType;
+ return true;
+ }
+
+ private void finishEvent(boolean tainted) {
+ if (mViolationMessage != null && mViolationMessage.length() != 0) {
+ mViolationMessage.append("\n in ").append(mCaller);
+ mViolationMessage.append("\n ");
+ appendEvent(mViolationMessage, 0, mCurrentEvent, false);
+
+ if (RECENT_EVENTS_TO_LOG != 0 && mRecentEvents != null) {
+ mViolationMessage.append("\n -- recent events --");
+ for (int i = 0; i < RECENT_EVENTS_TO_LOG; i++) {
+ final int index = (mMostRecentEventIndex + RECENT_EVENTS_TO_LOG - i)
+ % RECENT_EVENTS_TO_LOG;
+ final InputEvent event = mRecentEvents[index];
+ if (event == null) {
+ break;
+ }
+ mViolationMessage.append("\n ");
+ appendEvent(mViolationMessage, i + 1, event, mRecentEventsUnhandled[index]);
+ }
+ }
+
+ Log.d(mLogTag, mViolationMessage.toString());
+ mViolationMessage.setLength(0);
+ tainted = true;
+ }
+
+ if (tainted) {
+ // Taint the event so that we do not generate additional violations from it
+ // further downstream.
+ mCurrentEvent.setTainted(true);
+ }
+
+ if (RECENT_EVENTS_TO_LOG != 0) {
+ if (mRecentEvents == null) {
+ mRecentEvents = new InputEvent[RECENT_EVENTS_TO_LOG];
+ mRecentEventsUnhandled = new boolean[RECENT_EVENTS_TO_LOG];
+ }
+ final int index = (mMostRecentEventIndex + 1) % RECENT_EVENTS_TO_LOG;
+ mMostRecentEventIndex = index;
+ if (mRecentEvents[index] != null) {
+ mRecentEvents[index].recycle();
+ }
+ mRecentEvents[index] = mCurrentEvent.copy();
+ mRecentEventsUnhandled[index] = false;
+ }
+
+ mCurrentEvent = null;
+ mCurrentEventType = null;
+ }
+
+ private static void appendEvent(StringBuilder message, int index,
+ InputEvent event, boolean unhandled) {
+ message.append(index).append(": sent at ").append(event.getEventTimeNano());
+ message.append(", ");
+ if (unhandled) {
+ message.append("(unhandled) ");
+ }
+ message.append(event);
+ }
+
+ private void problem(String message) {
+ if (mViolationMessage == null) {
+ mViolationMessage = new StringBuilder();
+ }
+ if (mViolationMessage.length() == 0) {
+ mViolationMessage.append(mCurrentEventType).append(": ");
+ } else {
+ mViolationMessage.append("\n ");
+ }
+ mViolationMessage.append(message);
+ }
+
+ private KeyState findKeyState(int deviceId, int source, int keyCode, boolean remove) {
+ KeyState last = null;
+ KeyState state = mKeyStateList;
+ while (state != null) {
+ if (state.deviceId == deviceId && state.source == source
+ && state.keyCode == keyCode) {
+ if (remove) {
+ if (last != null) {
+ last.next = state.next;
+ } else {
+ mKeyStateList = state.next;
+ }
+ state.next = null;
+ }
+ return state;
+ }
+ last = state;
+ state = state.next;
+ }
+ return null;
+ }
+
+ private void addKeyState(int deviceId, int source, int keyCode) {
+ KeyState state = KeyState.obtain(deviceId, source, keyCode);
+ state.next = mKeyStateList;
+ mKeyStateList = state;
+ }
+
+ private static final class KeyState {
+ private static Object mRecycledListLock = new Object();
+ private static KeyState mRecycledList;
+
+ public KeyState next;
+ public int deviceId;
+ public int source;
+ public int keyCode;
+ public boolean unhandled;
+
+ private KeyState() {
+ }
+
+ public static KeyState obtain(int deviceId, int source, int keyCode) {
+ KeyState state;
+ synchronized (mRecycledListLock) {
+ state = mRecycledList;
+ if (state != null) {
+ mRecycledList = state.next;
+ } else {
+ state = new KeyState();
+ }
+ }
+ state.deviceId = deviceId;
+ state.source = source;
+ state.keyCode = keyCode;
+ state.unhandled = false;
+ return state;
+ }
+
+ public void recycle() {
+ synchronized (mRecycledListLock) {
+ next = mRecycledList;
+ mRecycledList = next;
+ }
+ }
+ }
+}
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index 81d5a6e..5dbda90 100755
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -566,6 +566,19 @@ public class KeyEvent extends InputEvent implements Parcelable {
public static final int KEYCODE_BUTTON_15 = 202;
/** Key code constant: Generic Game Pad Button #16.*/
public static final int KEYCODE_BUTTON_16 = 203;
+ /** Key code constant: Language Switch key.
+ * Toggles the current input language such as switching between English and Japanese on
+ * a QWERTY keyboard. On some devices, the same function may be performed by
+ * pressing Shift+Spacebar. */
+ public static final int KEYCODE_LANGUAGE_SWITCH = 204;
+ /** Key code constant: Manner Mode key.
+ * Toggles silent or vibrate mode on and off to make the device behave more politely
+ * in certain settings such as on a crowded train. On some devices, the key may only
+ * operate when long-pressed. */
+ public static final int KEYCODE_MANNER_MODE = 205;
+ /** Key code constant: 3D Mode key.
+ * Toggles the display between 2D and 3D mode. */
+ public static final int KEYCODE_3D_MODE = 206;
private static final int LAST_KEYCODE = KEYCODE_BUTTON_16;
@@ -791,6 +804,9 @@ public class KeyEvent extends InputEvent implements Parcelable {
names.append(KEYCODE_BUTTON_14, "KEYCODE_BUTTON_14");
names.append(KEYCODE_BUTTON_15, "KEYCODE_BUTTON_15");
names.append(KEYCODE_BUTTON_16, "KEYCODE_BUTTON_16");
+ names.append(KEYCODE_LANGUAGE_SWITCH, "KEYCODE_LANGUAGE_SWITCH");
+ names.append(KEYCODE_MANNER_MODE, "KEYCODE_MANNER_MODE");
+ names.append(KEYCODE_3D_MODE, "KEYCODE_3D_MODE");
};
// Symbolic names of all metakeys in bit order from least significant to most significant.
@@ -1154,7 +1170,18 @@ public class KeyEvent extends InputEvent implements Parcelable {
* @hide
*/
public static final int FLAG_START_TRACKING = 0x40000000;
-
+
+ /**
+ * Private flag that indicates when the system has detected that this key event
+ * may be inconsistent with respect to the sequence of previously delivered key events,
+ * such as when a key up event is sent but the key was not down.
+ *
+ * @hide
+ * @see #isTainted
+ * @see #setTainted
+ */
+ public static final int FLAG_TAINTED = 0x80000000;
+
/**
* Returns the maximum keycode.
*/
@@ -1519,6 +1546,33 @@ public class KeyEvent extends InputEvent implements Parcelable {
}
/**
+ * Obtains a (potentially recycled) copy of another key event.
+ *
+ * @hide
+ */
+ public static KeyEvent obtain(KeyEvent other) {
+ KeyEvent ev = obtain();
+ ev.mDownTime = other.mDownTime;
+ ev.mEventTime = other.mEventTime;
+ ev.mAction = other.mAction;
+ ev.mKeyCode = other.mKeyCode;
+ ev.mRepeatCount = other.mRepeatCount;
+ ev.mMetaState = other.mMetaState;
+ ev.mDeviceId = other.mDeviceId;
+ ev.mScanCode = other.mScanCode;
+ ev.mFlags = other.mFlags;
+ ev.mSource = other.mSource;
+ ev.mCharacters = other.mCharacters;
+ return ev;
+ }
+
+ /** @hide */
+ @Override
+ public KeyEvent copy() {
+ return obtain(this);
+ }
+
+ /**
* Recycles a key event.
* Key events should only be recycled if they are owned by the system since user
* code expects them to be essentially immutable, "tracking" notwithstanding.
@@ -1619,7 +1673,19 @@ public class KeyEvent extends InputEvent implements Parcelable {
event.mFlags = flags;
return event;
}
-
+
+ /** @hide */
+ @Override
+ public final boolean isTainted() {
+ return (mFlags & FLAG_TAINTED) != 0;
+ }
+
+ /** @hide */
+ @Override
+ public final void setTainted(boolean tainted) {
+ mFlags = tainted ? mFlags | FLAG_TAINTED : mFlags & ~FLAG_TAINTED;
+ }
+
/**
* Don't use in new code, instead explicitly check
* {@link #getAction()}.
@@ -1741,12 +1807,33 @@ public class KeyEvent extends InputEvent implements Parcelable {
* @see #META_CAPS_LOCK_ON
* @see #META_NUM_LOCK_ON
* @see #META_SCROLL_LOCK_ON
+ * @see #getModifiers
*/
public final int getMetaState() {
return mMetaState;
}
/**
+ * Returns the state of the modifier keys.
+ * <p>
+ * For the purposes of this function, {@link #KEYCODE_CAPS_LOCK},
+ * {@link #KEYCODE_SCROLL_LOCK}, and {@link #KEYCODE_NUM_LOCK} are
+ * not considered modifier keys. Consequently, this function specifically masks out
+ * {@link #META_CAPS_LOCK_ON}, {@link #META_SCROLL_LOCK_ON} and {@link #META_NUM_LOCK_ON}.
+ * </p><p>
+ * The value returned consists of the meta state (from {@link #getMetaState})
+ * normalized using {@link #normalizeMetaState(int)} and then masked with
+ * {@link #getModifierMetaStateMask} so that only valid modifier bits are retained.
+ * </p>
+ *
+ * @return An integer in which each bit set to 1 represents a pressed modifier key.
+ * @see #getMetaState
+ */
+ public final int getModifiers() {
+ return normalizeMetaState(mMetaState) & META_MODIFIER_MASK;
+ }
+
+ /**
* Returns the flags for this key event.
*
* @see #FLAG_WOKE_HERE
@@ -2253,6 +2340,12 @@ public class KeyEvent extends InputEvent implements Parcelable {
return mEventTime;
}
+ /** @hide */
+ @Override
+ public final long getEventTimeNano() {
+ return mEventTime * 1000000L;
+ }
+
/**
* Renamed to {@link #getDeviceId}.
*
@@ -2579,15 +2672,22 @@ public class KeyEvent extends InputEvent implements Parcelable {
@Override
public String toString() {
- return "KeyEvent{action=" + actionToString(mAction)
- + " keycode=" + keyCodeToString(mKeyCode)
- + " scancode=" + mScanCode
- + " metaState=" + metaStateToString(mMetaState)
- + " flags=0x" + Integer.toHexString(mFlags)
- + " repeat=" + mRepeatCount
- + " device=" + mDeviceId
- + " source=0x" + Integer.toHexString(mSource)
- + "}";
+ StringBuilder msg = new StringBuilder();
+ msg.append("KeyEvent { action=").append(actionToString(mAction));
+ msg.append(", keyCode=").append(keyCodeToString(mKeyCode));
+ msg.append(", scanCode=").append(mScanCode);
+ if (mCharacters != null) {
+ msg.append(", characters=\"").append(mCharacters).append("\"");
+ }
+ msg.append(", metaState=").append(metaStateToString(mMetaState));
+ msg.append(", flags=0x").append(Integer.toHexString(mFlags));
+ msg.append(", repeatCount=").append(mRepeatCount);
+ msg.append(", eventTime=").append(mEventTime);
+ msg.append(", downTime=").append(mDownTime);
+ msg.append(", deviceId=").append(mDeviceId);
+ msg.append(", source=0x").append(Integer.toHexString(mSource));
+ msg.append(" }");
+ return msg.toString();
}
/**
diff --git a/core/java/android/view/LayoutInflater.java b/core/java/android/view/LayoutInflater.java
index 81346b4..332a0fa 100644
--- a/core/java/android/view/LayoutInflater.java
+++ b/core/java/android/view/LayoutInflater.java
@@ -16,6 +16,10 @@
package android.view;
+import android.graphics.Canvas;
+import android.os.Handler;
+import android.os.Message;
+import android.widget.FrameLayout;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -83,6 +87,7 @@ public abstract class LayoutInflater {
private static final String TAG_MERGE = "merge";
private static final String TAG_INCLUDE = "include";
+ private static final String TAG_1995 = "blink";
private static final String TAG_REQUEST_FOCUS = "requestFocus";
/**
@@ -454,7 +459,12 @@ public abstract class LayoutInflater {
rInflate(parser, root, attrs, false);
} else {
// Temp is the root view that was found in the xml
- View temp = createViewFromTag(root, name, attrs);
+ View temp;
+ if (TAG_1995.equals(name)) {
+ temp = new BlinkLayout(mContext, attrs);
+ } else {
+ temp = createViewFromTag(root, name, attrs);
+ }
ViewGroup.LayoutParams params = null;
@@ -605,10 +615,9 @@ public abstract class LayoutInflater {
* Throw an exception because the specified class is not allowed to be inflated.
*/
private void failNotAllowed(String name, String prefix, AttributeSet attrs) {
- InflateException ie = new InflateException(attrs.getPositionDescription()
+ throw new InflateException(attrs.getPositionDescription()
+ ": Class not allowed to be inflated "
+ (prefix != null ? (prefix + name) : name));
- throw ie;
}
/**
@@ -720,6 +729,12 @@ public abstract class LayoutInflater {
parseInclude(parser, parent, attrs);
} else if (TAG_MERGE.equals(name)) {
throw new InflateException("<merge /> must be the root element");
+ } else if (TAG_1995.equals(name)) {
+ final View view = new BlinkLayout(mContext, attrs);
+ final ViewGroup viewGroup = (ViewGroup) parent;
+ final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
+ rInflate(parser, view, attrs, true);
+ viewGroup.addView(view, params);
} else {
final View view = createViewFromTag(parent, name, attrs);
final ViewGroup viewGroup = (ViewGroup) parent;
@@ -847,5 +862,64 @@ public abstract class LayoutInflater {
parser.getDepth() > currentDepth) && type != XmlPullParser.END_DOCUMENT) {
// Empty
}
- }
+ }
+
+ private static class BlinkLayout extends FrameLayout {
+ private static final int MESSAGE_BLINK = 0x42;
+ private static final int BLINK_DELAY = 500;
+
+ private boolean mBlink;
+ private boolean mBlinkState;
+ private final Handler mHandler;
+
+ public BlinkLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mHandler = new Handler(new Handler.Callback() {
+ @Override
+ public boolean handleMessage(Message msg) {
+ if (msg.what == MESSAGE_BLINK) {
+ if (mBlink) {
+ mBlinkState = !mBlinkState;
+ makeBlink();
+ }
+ invalidate();
+ return true;
+ }
+ return false;
+ }
+ });
+ }
+
+ private void makeBlink() {
+ Message message = mHandler.obtainMessage(MESSAGE_BLINK);
+ mHandler.sendMessageDelayed(message, BLINK_DELAY);
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+
+ mBlink = true;
+ mBlinkState = true;
+
+ makeBlink();
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+
+ mBlink = false;
+ mBlinkState = true;
+
+ mHandler.removeMessages(MESSAGE_BLINK);
+ }
+
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ if (mBlinkState) {
+ super.dispatchDraw(canvas);
+ }
+ }
+ }
}
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index a17db5d..82fd581 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -23,53 +23,60 @@ import android.os.SystemClock;
import android.util.SparseArray;
/**
- * Object used to report movement (mouse, pen, finger, trackball) events. This
- * class may hold either absolute or relative movements, depending on what
- * it is being used for.
+ * Object used to report movement (mouse, pen, finger, trackball) events.
+ * Motion events may hold either absolute or relative movements and other data,
+ * depending on the type of device.
+ *
+ * <h3>Overview</h3>
* <p>
- * On pointing devices with source class {@link InputDevice#SOURCE_CLASS_POINTER}
- * such as touch screens, the pointer coordinates specify absolute
- * positions such as view X/Y coordinates. Each complete gesture is represented
- * by a sequence of motion events with actions that describe pointer state transitions
- * and movements. A gesture starts with a motion event with {@link #ACTION_DOWN}
- * that provides the location of the first pointer down. As each additional
- * pointer that goes down or up, the framework will generate a motion event with
- * {@link #ACTION_POINTER_DOWN} or {@link #ACTION_POINTER_UP} accordingly.
- * Pointer movements are described by motion events with {@link #ACTION_MOVE}.
- * Finally, a gesture end either when the final pointer goes up as represented
- * by a motion event with {@link #ACTION_UP} or when gesture is canceled
- * with {@link #ACTION_CANCEL}.
+ * Motion events describe movements in terms of an action code and a set of axis values.
+ * The action code specifies the state change that occurred such as a pointer going
+ * down or up. The axis values describe the position and other movement properties.
* </p><p>
- * Some pointing devices such as mice may support vertical and/or horizontal scrolling.
- * A scroll event is reported as a generic motion event with {@link #ACTION_SCROLL} that
- * includes the relative scroll offset in the {@link #AXIS_VSCROLL} and
- * {@link #AXIS_HSCROLL} axes. See {@link #getAxisValue(int)} for information
- * about retrieving these additional axes.
+ * For example, when the user first touches the screen, the system delivers a touch
+ * event to the appropriate {@link View} with the action code {@link #ACTION_DOWN}
+ * and a set of axis values that include the X and Y coordinates of the touch and
+ * information about the pressure, size and orientation of the contact area.
* </p><p>
- * On trackball devices with source class {@link InputDevice#SOURCE_CLASS_TRACKBALL},
- * the pointer coordinates specify relative movements as X/Y deltas.
- * A trackball gesture consists of a sequence of movements described by motion
- * events with {@link #ACTION_MOVE} interspersed with occasional {@link #ACTION_DOWN}
- * or {@link #ACTION_UP} motion events when the trackball button is pressed or released.
+ * Some devices can report multiple movement traces at the same time. Multi-touch
+ * screens emit one movement trace for each finger. The individual fingers or
+ * other objects that generate movement traces are referred to as <em>pointers</em>.
+ * Motion events contain information about all of the pointers that are currently active
+ * even if some of them have not moved since the last event was delivered.
* </p><p>
- * On joystick devices with source class {@link InputDevice#SOURCE_CLASS_JOYSTICK},
- * the pointer coordinates specify the absolute position of the joystick axes.
- * The joystick axis values are normalized to a range of -1.0 to 1.0 where 0.0 corresponds
- * to the center position. More information about the set of available axes and the
- * range of motion can be obtained using {@link InputDevice#getMotionRange}.
- * Some common joystick axes are {@link #AXIS_X}, {@link #AXIS_Y},
- * {@link #AXIS_HAT_X}, {@link #AXIS_HAT_Y}, {@link #AXIS_Z} and {@link #AXIS_RZ}.
- * </p><p>
- * Motion events always report movements for all pointers at once. The number
- * of pointers only ever changes by one as individual pointers go up and down,
+ * The number of pointers only ever changes by one as individual pointers go up and down,
* except when the gesture is canceled.
* </p><p>
- * The order in which individual pointers appear within a motion event can change
- * from one event to the next. Use the {@link #getPointerId(int)} method to obtain a
- * pointer id to track pointers across motion events in a gesture. Then for
- * successive motion events, use the {@link #findPointerIndex(int)} method to obtain
- * the pointer index for a given pointer id in that motion event.
+ * Each pointer has a unique id that is assigned when it first goes down
+ * (indicated by {@link #ACTION_DOWN} or {@link #ACTION_POINTER_DOWN}). A pointer id
+ * remains valid until the pointer eventually goes up (indicated by {@link #ACTION_UP}
+ * or {@link #ACTION_POINTER_UP}) or when the gesture is canceled (indicated by
+ * {@link #ACTION_CANCEL}).
* </p><p>
+ * The MotionEvent class provides many methods to query the position and other properties of
+ * pointers, such as {@link #getX(int)}, {@link #getY(int)}, {@link #getAxisValue},
+ * {@link #getPointerId(int)}, {@link #getToolType(int)}, and many others. Most of these
+ * methods accept the pointer index as a parameter rather than the pointer id.
+ * The pointer index of each pointer in the event ranges from 0 to one less than the value
+ * returned by {@link #getPointerCount()}.
+ * </p><p>
+ * The order in which individual pointers appear within a motion event is undefined.
+ * Thus the pointer index of a pointer can change from one event to the next but
+ * the pointer id of a pointer is guaranteed to remain constant as long as the pointer
+ * remains active. Use the {@link #getPointerId(int)} method to obtain the
+ * pointer id of a pointer to track it across all subsequent motion events in a gesture.
+ * Then for successive motion events, use the {@link #findPointerIndex(int)} method
+ * to obtain the pointer index for a given pointer id in that motion event.
+ * </p><p>
+ * Mouse and stylus buttons can be retrieved using {@link #getButtonState()}. It is a
+ * good idea to check the button state while handling {@link #ACTION_DOWN} as part
+ * of a touch event. The application may choose to perform some different action
+ * if the touch event starts due to a secondary button click, such as presenting a
+ * context menu.
+ * </p>
+ *
+ * <h3>Batching</h3>
+ * <p>
* For efficiency, motion events with {@link #ACTION_MOVE} may batch together
* multiple movement samples within a single object. The most current
* pointer coordinates are available using {@link #getX(int)} and {@link #getY(int)}.
@@ -98,42 +105,104 @@ import android.util.SparseArray;
* ev.getPointerId(p), ev.getX(p), ev.getY(p));
* }
* }
- * </code></pre></p><p>
- * In general, the framework cannot guarantee that the motion events it delivers
- * to a view always constitute a complete motion sequences since some events may be dropped
- * or modified by containing views before they are delivered. The view implementation
- * should be prepared to handle {@link #ACTION_CANCEL} and should tolerate anomalous
- * situations such as receiving a new {@link #ACTION_DOWN} without first having
- * received an {@link #ACTION_UP} for the prior gesture.
+ * </code></pre></p>
+ *
+ * <h3>Device Types</h3>
+ * <p>
+ * The interpretation of the contents of a MotionEvent varies significantly depending
+ * on the source class of the device.
+ * </p><p>
+ * On pointing devices with source class {@link InputDevice#SOURCE_CLASS_POINTER}
+ * such as touch screens, the pointer coordinates specify absolute
+ * positions such as view X/Y coordinates. Each complete gesture is represented
+ * by a sequence of motion events with actions that describe pointer state transitions
+ * and movements. A gesture starts with a motion event with {@link #ACTION_DOWN}
+ * that provides the location of the first pointer down. As each additional
+ * pointer that goes down or up, the framework will generate a motion event with
+ * {@link #ACTION_POINTER_DOWN} or {@link #ACTION_POINTER_UP} accordingly.
+ * Pointer movements are described by motion events with {@link #ACTION_MOVE}.
+ * Finally, a gesture end either when the final pointer goes up as represented
+ * by a motion event with {@link #ACTION_UP} or when gesture is canceled
+ * with {@link #ACTION_CANCEL}.
+ * </p><p>
+ * Some pointing devices such as mice may support vertical and/or horizontal scrolling.
+ * A scroll event is reported as a generic motion event with {@link #ACTION_SCROLL} that
+ * includes the relative scroll offset in the {@link #AXIS_VSCROLL} and
+ * {@link #AXIS_HSCROLL} axes. See {@link #getAxisValue(int)} for information
+ * about retrieving these additional axes.
+ * </p><p>
+ * On trackball devices with source class {@link InputDevice#SOURCE_CLASS_TRACKBALL},
+ * the pointer coordinates specify relative movements as X/Y deltas.
+ * A trackball gesture consists of a sequence of movements described by motion
+ * events with {@link #ACTION_MOVE} interspersed with occasional {@link #ACTION_DOWN}
+ * or {@link #ACTION_UP} motion events when the trackball button is pressed or released.
+ * </p><p>
+ * On joystick devices with source class {@link InputDevice#SOURCE_CLASS_JOYSTICK},
+ * the pointer coordinates specify the absolute position of the joystick axes.
+ * The joystick axis values are normalized to a range of -1.0 to 1.0 where 0.0 corresponds
+ * to the center position. More information about the set of available axes and the
+ * range of motion can be obtained using {@link InputDevice#getMotionRange}.
+ * Some common joystick axes are {@link #AXIS_X}, {@link #AXIS_Y},
+ * {@link #AXIS_HAT_X}, {@link #AXIS_HAT_Y}, {@link #AXIS_Z} and {@link #AXIS_RZ}.
* </p><p>
* Refer to {@link InputDevice} for more information about how different kinds of
* input devices and sources represent pointer coordinates.
* </p>
+ *
+ * <h3>Consistency Guarantees</h3>
+ * <p>
+ * Motion events are always delivered to views as a consistent stream of events.
+ * What constitutes a consistent stream varies depending on the type of device.
+ * For touch events, consistency implies that pointers go down one at a time,
+ * move around as a group and then go up one at a time or are canceled.
+ * </p><p>
+ * While the framework tries to deliver consistent streams of motion events to
+ * views, it cannot guarantee it. Some events may be dropped or modified by
+ * containing views in the application before they are delivered thereby making
+ * the stream of events inconsistent. Views should always be prepared to
+ * handle {@link #ACTION_CANCEL} and should tolerate anomalous
+ * situations such as receiving a new {@link #ACTION_DOWN} without first having
+ * received an {@link #ACTION_UP} for the prior gesture.
+ * </p>
*/
public final class MotionEvent extends InputEvent implements Parcelable {
private static final long NS_PER_MS = 1000000;
private static final boolean TRACK_RECYCLED_LOCATION = false;
-
+
+ /**
+ * An invalid pointer id.
+ *
+ * This value (-1) can be used as a placeholder to indicate that a pointer id
+ * has not been assigned or is not available. It cannot appear as
+ * a pointer id inside a {@link MotionEvent}.
+ */
+ public static final int INVALID_POINTER_ID = -1;
+
/**
* Bit mask of the parts of the action code that are the action itself.
*/
public static final int ACTION_MASK = 0xff;
/**
- * Constant for {@link #getAction}: A pressed gesture has started, the
+ * Constant for {@link #getActionMasked}: A pressed gesture has started, the
* motion contains the initial starting location.
+ * <p>
+ * This is also a good time to check the button state to distinguish
+ * secondary and tertiary button clicks and handle them appropriately.
+ * Use {@link #getButtonState} to retrieve the button state.
+ * </p>
*/
public static final int ACTION_DOWN = 0;
/**
- * Constant for {@link #getAction}: A pressed gesture has finished, the
+ * Constant for {@link #getActionMasked}: A pressed gesture has finished, the
* motion contains the final release location as well as any intermediate
* points since the last down or move event.
*/
public static final int ACTION_UP = 1;
/**
- * Constant for {@link #getAction}: A change has happened during a
+ * Constant for {@link #getActionMasked}: A change has happened during a
* press gesture (between {@link #ACTION_DOWN} and {@link #ACTION_UP}).
* The motion contains the most recent point, as well as any intermediate
* points since the last down or move event.
@@ -141,37 +210,49 @@ public final class MotionEvent extends InputEvent implements Parcelable {
public static final int ACTION_MOVE = 2;
/**
- * Constant for {@link #getAction}: The current gesture has been aborted.
+ * Constant for {@link #getActionMasked}: The current gesture has been aborted.
* You will not receive any more points in it. You should treat this as
* an up event, but not perform any action that you normally would.
*/
public static final int ACTION_CANCEL = 3;
/**
- * Constant for {@link #getAction}: A movement has happened outside of the
+ * Constant for {@link #getActionMasked}: A movement has happened outside of the
* normal bounds of the UI element. This does not provide a full gesture,
* but only the initial location of the movement/touch.
*/
public static final int ACTION_OUTSIDE = 4;
/**
- * A non-primary pointer has gone down. The bits in
- * {@link #ACTION_POINTER_ID_MASK} indicate which pointer changed.
+ * Constant for {@link #getActionMasked}: A non-primary pointer has gone down.
+ * <p>
+ * Use {@link #getActionIndex} to retrieve the index of the pointer that changed.
+ * </p><p>
+ * The index is encoded in the {@link #ACTION_POINTER_INDEX_MASK} bits of the
+ * unmasked action returned by {@link #getAction}.
+ * </p>
*/
public static final int ACTION_POINTER_DOWN = 5;
/**
- * A non-primary pointer has gone up. The bits in
- * {@link #ACTION_POINTER_ID_MASK} indicate which pointer changed.
+ * Constant for {@link #getActionMasked}: A non-primary pointer has gone up.
+ * <p>
+ * Use {@link #getActionIndex} to retrieve the index of the pointer that changed.
+ * </p><p>
+ * The index is encoded in the {@link #ACTION_POINTER_INDEX_MASK} bits of the
+ * unmasked action returned by {@link #getAction}.
+ * </p>
*/
public static final int ACTION_POINTER_UP = 6;
/**
- * Constant for {@link #getAction}: A change happened but the pointer
+ * Constant for {@link #getActionMasked}: A change happened but the pointer
* is not down (unlike {@link #ACTION_MOVE}). The motion contains the most
* recent point, as well as any intermediate points since the last
* hover move event.
* <p>
+ * This action is always delivered to the window or view under the pointer.
+ * </p><p>
* This action is not a touch event so it is delivered to
* {@link View#onGenericMotionEvent(MotionEvent)} rather than
* {@link View#onTouchEvent(MotionEvent)}.
@@ -180,13 +261,14 @@ public final class MotionEvent extends InputEvent implements Parcelable {
public static final int ACTION_HOVER_MOVE = 7;
/**
- * Constant for {@link #getAction}: The motion event contains relative
+ * Constant for {@link #getActionMasked}: The motion event contains relative
* vertical and/or horizontal scroll offsets. Use {@link #getAxisValue(int)}
* to retrieve the information from {@link #AXIS_VSCROLL} and {@link #AXIS_HSCROLL}.
* The pointer may or may not be down when this event is dispatched.
- * This action is always delivered to the winder under the pointer, which
- * may not be the window currently touched.
* <p>
+ * This action is always delivered to the window or view under the pointer, which
+ * may not be the window or view currently touched.
+ * </p><p>
* This action is not a touch event so it is delivered to
* {@link View#onGenericMotionEvent(MotionEvent)} rather than
* {@link View#onTouchEvent(MotionEvent)}.
@@ -195,21 +277,51 @@ public final class MotionEvent extends InputEvent implements Parcelable {
public static final int ACTION_SCROLL = 8;
/**
+ * Constant for {@link #getActionMasked}: The pointer is not down but has entered the
+ * boundaries of a window or view.
+ * <p>
+ * This action is always delivered to the window or view under the pointer.
+ * </p><p>
+ * This action is not a touch event so it is delivered to
+ * {@link View#onGenericMotionEvent(MotionEvent)} rather than
+ * {@link View#onTouchEvent(MotionEvent)}.
+ * </p>
+ */
+ public static final int ACTION_HOVER_ENTER = 9;
+
+ /**
+ * Constant for {@link #getActionMasked}: The pointer is not down but has exited the
+ * boundaries of a window or view.
+ * <p>
+ * This action is always delivered to the window or view that was previously under the pointer.
+ * </p><p>
+ * This action is not a touch event so it is delivered to
+ * {@link View#onGenericMotionEvent(MotionEvent)} rather than
+ * {@link View#onTouchEvent(MotionEvent)}.
+ * </p>
+ */
+ public static final int ACTION_HOVER_EXIT = 10;
+
+ /**
* Bits in the action code that represent a pointer index, used with
* {@link #ACTION_POINTER_DOWN} and {@link #ACTION_POINTER_UP}. Shifting
* down by {@link #ACTION_POINTER_INDEX_SHIFT} provides the actual pointer
* index where the data for the pointer going up or down can be found; you can
* get its identifier with {@link #getPointerId(int)} and the actual
* data with {@link #getX(int)} etc.
+ *
+ * @see #getActionIndex
*/
public static final int ACTION_POINTER_INDEX_MASK = 0xff00;
/**
* Bit shift for the action bits holding the pointer index as
* defined by {@link #ACTION_POINTER_INDEX_MASK}.
+ *
+ * @see #getActionIndex
*/
public static final int ACTION_POINTER_INDEX_SHIFT = 8;
-
+
/**
* @deprecated Use {@link #ACTION_POINTER_INDEX_MASK} to retrieve the
* data index associated with {@link #ACTION_POINTER_DOWN}.
@@ -279,6 +391,17 @@ public final class MotionEvent extends InputEvent implements Parcelable {
public static final int FLAG_WINDOW_IS_OBSCURED = 0x1;
/**
+ * Private flag that indicates when the system has detected that this motion event
+ * may be inconsistent with respect to the sequence of previously delivered motion events,
+ * such as when a pointer move event is sent but the pointer is not down.
+ *
+ * @hide
+ * @see #isTainted
+ * @see #setTainted
+ */
+ public static final int FLAG_TAINTED = 0x80000000;
+
+ /**
* Flag indicating the motion event intersected the top edge of the screen.
*/
public static final int EDGE_TOP = 0x00000001;
@@ -299,7 +422,7 @@ public final class MotionEvent extends InputEvent implements Parcelable {
public static final int EDGE_RIGHT = 0x00000008;
/**
- * Constant used to identify the X axis of a motion event.
+ * Axis constant: X axis of a motion event.
* <p>
* <ul>
* <li>For a touch screen, reports the absolute X screen position of the center of
@@ -324,7 +447,7 @@ public final class MotionEvent extends InputEvent implements Parcelable {
public static final int AXIS_X = 0;
/**
- * Constant used to identify the Y axis of a motion event.
+ * Axis constant: Y axis of a motion event.
* <p>
* <ul>
* <li>For a touch screen, reports the absolute Y screen position of the center of
@@ -349,7 +472,7 @@ public final class MotionEvent extends InputEvent implements Parcelable {
public static final int AXIS_Y = 1;
/**
- * Constant used to identify the Pressure axis of a motion event.
+ * Axis constant: Pressure axis of a motion event.
* <p>
* <ul>
* <li>For a touch screen or touch pad, reports the approximate pressure applied to the surface
@@ -371,7 +494,7 @@ public final class MotionEvent extends InputEvent implements Parcelable {
public static final int AXIS_PRESSURE = 2;
/**
- * Constant used to identify the Size axis of a motion event.
+ * Axis constant: Size axis of a motion event.
* <p>
* <ul>
* <li>For a touch screen or touch pad, reports the approximate size of the contact area in
@@ -391,7 +514,7 @@ public final class MotionEvent extends InputEvent implements Parcelable {
public static final int AXIS_SIZE = 3;
/**
- * Constant used to identify the TouchMajor axis of a motion event.
+ * Axis constant: TouchMajor axis of a motion event.
* <p>
* <ul>
* <li>For a touch screen, reports the length of the major axis of an ellipse that
@@ -412,7 +535,7 @@ public final class MotionEvent extends InputEvent implements Parcelable {
public static final int AXIS_TOUCH_MAJOR = 4;
/**
- * Constant used to identify the TouchMinor axis of a motion event.
+ * Axis constant: TouchMinor axis of a motion event.
* <p>
* <ul>
* <li>For a touch screen, reports the length of the minor axis of an ellipse that
@@ -435,7 +558,7 @@ public final class MotionEvent extends InputEvent implements Parcelable {
public static final int AXIS_TOUCH_MINOR = 5;
/**
- * Constant used to identify the ToolMajor axis of a motion event.
+ * Axis constant: ToolMajor axis of a motion event.
* <p>
* <ul>
* <li>For a touch screen, reports the length of the major axis of an ellipse that
@@ -460,7 +583,7 @@ public final class MotionEvent extends InputEvent implements Parcelable {
public static final int AXIS_TOOL_MAJOR = 6;
/**
- * Constant used to identify the ToolMinor axis of a motion event.
+ * Axis constant: ToolMinor axis of a motion event.
* <p>
* <ul>
* <li>For a touch screen, reports the length of the minor axis of an ellipse that
@@ -485,7 +608,7 @@ public final class MotionEvent extends InputEvent implements Parcelable {
public static final int AXIS_TOOL_MINOR = 7;
/**
- * Constant used to identify the Orientation axis of a motion event.
+ * Axis constant: Orientation axis of a motion event.
* <p>
* <ul>
* <li>For a touch screen or touch pad, reports the orientation of the finger
@@ -507,7 +630,7 @@ public final class MotionEvent extends InputEvent implements Parcelable {
public static final int AXIS_ORIENTATION = 8;
/**
- * Constant used to identify the Vertical Scroll axis of a motion event.
+ * Axis constant: Vertical Scroll axis of a motion event.
* <p>
* <ul>
* <li>For a mouse, reports the relative movement of the vertical scroll wheel.
@@ -525,7 +648,7 @@ public final class MotionEvent extends InputEvent implements Parcelable {
public static final int AXIS_VSCROLL = 9;
/**
- * Constant used to identify the Horizontal Scroll axis of a motion event.
+ * Axis constant: Horizontal Scroll axis of a motion event.
* <p>
* <ul>
* <li>For a mouse, reports the relative movement of the horizontal scroll wheel.
@@ -543,7 +666,7 @@ public final class MotionEvent extends InputEvent implements Parcelable {
public static final int AXIS_HSCROLL = 10;
/**
- * Constant used to identify the Z axis of a motion event.
+ * Axis constant: Z axis of a motion event.
* <p>
* <ul>
* <li>For a joystick, reports the absolute Z position of the joystick.
@@ -561,7 +684,7 @@ public final class MotionEvent extends InputEvent implements Parcelable {
public static final int AXIS_Z = 11;
/**
- * Constant used to identify the X Rotation axis of a motion event.
+ * Axis constant: X Rotation axis of a motion event.
* <p>
* <ul>
* <li>For a joystick, reports the absolute rotation angle about the X axis.
@@ -577,7 +700,7 @@ public final class MotionEvent extends InputEvent implements Parcelable {
public static final int AXIS_RX = 12;
/**
- * Constant used to identify the Y Rotation axis of a motion event.
+ * Axis constant: Y Rotation axis of a motion event.
* <p>
* <ul>
* <li>For a joystick, reports the absolute rotation angle about the Y axis.
@@ -593,7 +716,7 @@ public final class MotionEvent extends InputEvent implements Parcelable {
public static final int AXIS_RY = 13;
/**
- * Constant used to identify the Z Rotation axis of a motion event.
+ * Axis constant: Z Rotation axis of a motion event.
* <p>
* <ul>
* <li>For a joystick, reports the absolute rotation angle about the Z axis.
@@ -611,7 +734,7 @@ public final class MotionEvent extends InputEvent implements Parcelable {
public static final int AXIS_RZ = 14;
/**
- * Constant used to identify the Hat X axis of a motion event.
+ * Axis constant: Hat X axis of a motion event.
* <p>
* <ul>
* <li>For a joystick, reports the absolute X position of the directional hat control.
@@ -627,7 +750,7 @@ public final class MotionEvent extends InputEvent implements Parcelable {
public static final int AXIS_HAT_X = 15;
/**
- * Constant used to identify the Hat Y axis of a motion event.
+ * Axis constant: Hat Y axis of a motion event.
* <p>
* <ul>
* <li>For a joystick, reports the absolute Y position of the directional hat control.
@@ -643,7 +766,7 @@ public final class MotionEvent extends InputEvent implements Parcelable {
public static final int AXIS_HAT_Y = 16;
/**
- * Constant used to identify the Left Trigger axis of a motion event.
+ * Axis constant: Left Trigger axis of a motion event.
* <p>
* <ul>
* <li>For a joystick, reports the absolute position of the left trigger control.
@@ -659,7 +782,7 @@ public final class MotionEvent extends InputEvent implements Parcelable {
public static final int AXIS_LTRIGGER = 17;
/**
- * Constant used to identify the Right Trigger axis of a motion event.
+ * Axis constant: Right Trigger axis of a motion event.
* <p>
* <ul>
* <li>For a joystick, reports the absolute position of the right trigger control.
@@ -675,7 +798,7 @@ public final class MotionEvent extends InputEvent implements Parcelable {
public static final int AXIS_RTRIGGER = 18;
/**
- * Constant used to identify the Throttle axis of a motion event.
+ * Axis constant: Throttle axis of a motion event.
* <p>
* <ul>
* <li>For a joystick, reports the absolute position of the throttle control.
@@ -691,7 +814,7 @@ public final class MotionEvent extends InputEvent implements Parcelable {
public static final int AXIS_THROTTLE = 19;
/**
- * Constant used to identify the Rudder axis of a motion event.
+ * Axis constant: Rudder axis of a motion event.
* <p>
* <ul>
* <li>For a joystick, reports the absolute position of the rudder control.
@@ -707,7 +830,7 @@ public final class MotionEvent extends InputEvent implements Parcelable {
public static final int AXIS_RUDDER = 20;
/**
- * Constant used to identify the Wheel axis of a motion event.
+ * Axis constant: Wheel axis of a motion event.
* <p>
* <ul>
* <li>For a joystick, reports the absolute position of the steering wheel control.
@@ -723,7 +846,7 @@ public final class MotionEvent extends InputEvent implements Parcelable {
public static final int AXIS_WHEEL = 21;
/**
- * Constant used to identify the Gas axis of a motion event.
+ * Axis constant: Gas axis of a motion event.
* <p>
* <ul>
* <li>For a joystick, reports the absolute position of the gas (accelerator) control.
@@ -740,7 +863,7 @@ public final class MotionEvent extends InputEvent implements Parcelable {
public static final int AXIS_GAS = 22;
/**
- * Constant used to identify the Brake axis of a motion event.
+ * Axis constant: Brake axis of a motion event.
* <p>
* <ul>
* <li>For a joystick, reports the absolute position of the brake control.
@@ -756,7 +879,24 @@ public final class MotionEvent extends InputEvent implements Parcelable {
public static final int AXIS_BRAKE = 23;
/**
- * Constant used to identify the Generic 1 axis of a motion event.
+ * Axis constant: Distance axis of a motion event.
+ * <p>
+ * <ul>
+ * <li>For a stylus, reports the distance of the stylus from the screen.
+ * The value is normalized to a range from 0.0 (direct contact) to 1.0 (furthest measurable
+ * distance).
+ * </ul>
+ * </p>
+ *
+ * @see #getAxisValue(int, int)
+ * @see #getHistoricalAxisValue(int, int, int)
+ * @see MotionEvent.PointerCoords#getAxisValue(int)
+ * @see InputDevice#getMotionRange
+ */
+ public static final int AXIS_DISTANCE = 24;
+
+ /**
+ * Axis constant: Generic 1 axis of a motion event.
* The interpretation of a generic axis is device-specific.
*
* @see #getAxisValue(int, int)
@@ -767,7 +907,7 @@ public final class MotionEvent extends InputEvent implements Parcelable {
public static final int AXIS_GENERIC_1 = 32;
/**
- * Constant used to identify the Generic 2 axis of a motion event.
+ * Axis constant: Generic 2 axis of a motion event.
* The interpretation of a generic axis is device-specific.
*
* @see #getAxisValue(int, int)
@@ -778,7 +918,7 @@ public final class MotionEvent extends InputEvent implements Parcelable {
public static final int AXIS_GENERIC_2 = 33;
/**
- * Constant used to identify the Generic 3 axis of a motion event.
+ * Axis constant: Generic 3 axis of a motion event.
* The interpretation of a generic axis is device-specific.
*
* @see #getAxisValue(int, int)
@@ -789,7 +929,7 @@ public final class MotionEvent extends InputEvent implements Parcelable {
public static final int AXIS_GENERIC_3 = 34;
/**
- * Constant used to identify the Generic 4 axis of a motion event.
+ * Axis constant: Generic 4 axis of a motion event.
* The interpretation of a generic axis is device-specific.
*
* @see #getAxisValue(int, int)
@@ -800,7 +940,7 @@ public final class MotionEvent extends InputEvent implements Parcelable {
public static final int AXIS_GENERIC_4 = 35;
/**
- * Constant used to identify the Generic 5 axis of a motion event.
+ * Axis constant: Generic 5 axis of a motion event.
* The interpretation of a generic axis is device-specific.
*
* @see #getAxisValue(int, int)
@@ -811,7 +951,7 @@ public final class MotionEvent extends InputEvent implements Parcelable {
public static final int AXIS_GENERIC_5 = 36;
/**
- * Constant used to identify the Generic 6 axis of a motion event.
+ * Axis constant: Generic 6 axis of a motion event.
* The interpretation of a generic axis is device-specific.
*
* @see #getAxisValue(int, int)
@@ -822,7 +962,7 @@ public final class MotionEvent extends InputEvent implements Parcelable {
public static final int AXIS_GENERIC_6 = 37;
/**
- * Constant used to identify the Generic 7 axis of a motion event.
+ * Axis constant: Generic 7 axis of a motion event.
* The interpretation of a generic axis is device-specific.
*
* @see #getAxisValue(int, int)
@@ -833,7 +973,7 @@ public final class MotionEvent extends InputEvent implements Parcelable {
public static final int AXIS_GENERIC_7 = 38;
/**
- * Constant used to identify the Generic 8 axis of a motion event.
+ * Axis constant: Generic 8 axis of a motion event.
* The interpretation of a generic axis is device-specific.
*
* @see #getAxisValue(int, int)
@@ -844,7 +984,7 @@ public final class MotionEvent extends InputEvent implements Parcelable {
public static final int AXIS_GENERIC_8 = 39;
/**
- * Constant used to identify the Generic 9 axis of a motion event.
+ * Axis constant: Generic 9 axis of a motion event.
* The interpretation of a generic axis is device-specific.
*
* @see #getAxisValue(int, int)
@@ -855,7 +995,7 @@ public final class MotionEvent extends InputEvent implements Parcelable {
public static final int AXIS_GENERIC_9 = 40;
/**
- * Constant used to identify the Generic 10 axis of a motion event.
+ * Axis constant: Generic 10 axis of a motion event.
* The interpretation of a generic axis is device-specific.
*
* @see #getAxisValue(int, int)
@@ -866,7 +1006,7 @@ public final class MotionEvent extends InputEvent implements Parcelable {
public static final int AXIS_GENERIC_10 = 41;
/**
- * Constant used to identify the Generic 11 axis of a motion event.
+ * Axis constant: Generic 11 axis of a motion event.
* The interpretation of a generic axis is device-specific.
*
* @see #getAxisValue(int, int)
@@ -877,7 +1017,7 @@ public final class MotionEvent extends InputEvent implements Parcelable {
public static final int AXIS_GENERIC_11 = 42;
/**
- * Constant used to identify the Generic 12 axis of a motion event.
+ * Axis constant: Generic 12 axis of a motion event.
* The interpretation of a generic axis is device-specific.
*
* @see #getAxisValue(int, int)
@@ -888,7 +1028,7 @@ public final class MotionEvent extends InputEvent implements Parcelable {
public static final int AXIS_GENERIC_12 = 43;
/**
- * Constant used to identify the Generic 13 axis of a motion event.
+ * Axis constant: Generic 13 axis of a motion event.
* The interpretation of a generic axis is device-specific.
*
* @see #getAxisValue(int, int)
@@ -899,7 +1039,7 @@ public final class MotionEvent extends InputEvent implements Parcelable {
public static final int AXIS_GENERIC_13 = 44;
/**
- * Constant used to identify the Generic 14 axis of a motion event.
+ * Axis constant: Generic 14 axis of a motion event.
* The interpretation of a generic axis is device-specific.
*
* @see #getAxisValue(int, int)
@@ -910,7 +1050,7 @@ public final class MotionEvent extends InputEvent implements Parcelable {
public static final int AXIS_GENERIC_14 = 45;
/**
- * Constant used to identify the Generic 15 axis of a motion event.
+ * Axis constant: Generic 15 axis of a motion event.
* The interpretation of a generic axis is device-specific.
*
* @see #getAxisValue(int, int)
@@ -921,7 +1061,7 @@ public final class MotionEvent extends InputEvent implements Parcelable {
public static final int AXIS_GENERIC_15 = 46;
/**
- * Constant used to identify the Generic 16 axis of a motion event.
+ * Axis constant: Generic 16 axis of a motion event.
* The interpretation of a generic axis is device-specific.
*
* @see #getAxisValue(int, int)
@@ -937,7 +1077,7 @@ public final class MotionEvent extends InputEvent implements Parcelable {
// Symbolic names of all axes.
private static final SparseArray<String> AXIS_SYMBOLIC_NAMES = new SparseArray<String>();
- private static void populateAxisSymbolicNames() {
+ static {
SparseArray<String> names = AXIS_SYMBOLIC_NAMES;
names.append(AXIS_X, "AXIS_X");
names.append(AXIS_Y, "AXIS_Y");
@@ -963,6 +1103,7 @@ public final class MotionEvent extends InputEvent implements Parcelable {
names.append(AXIS_WHEEL, "AXIS_WHEEL");
names.append(AXIS_GAS, "AXIS_GAS");
names.append(AXIS_BRAKE, "AXIS_BRAKE");
+ names.append(AXIS_DISTANCE, "AXIS_DISTANCE");
names.append(AXIS_GENERIC_1, "AXIS_GENERIC_1");
names.append(AXIS_GENERIC_2, "AXIS_GENERIC_2");
names.append(AXIS_GENERIC_3, "AXIS_GENERIC_3");
@@ -981,8 +1122,169 @@ public final class MotionEvent extends InputEvent implements Parcelable {
names.append(AXIS_GENERIC_16, "AXIS_GENERIC_16");
}
+ /**
+ * Button constant: Primary button (left mouse button, stylus tip).
+ *
+ * @see #getButtonState
+ */
+ public static final int BUTTON_PRIMARY = 1 << 0;
+
+ /**
+ * Button constant: Secondary button (right mouse button, stylus barrel).
+ *
+ * @see #getButtonState
+ */
+ public static final int BUTTON_SECONDARY = 1 << 1;
+
+ /**
+ * Button constant: Tertiary button (middle mouse button).
+ *
+ * @see #getButtonState
+ */
+ public static final int BUTTON_TERTIARY = 1 << 2;
+
+ /**
+ * Button constant: Back button pressed (mouse back button).
+ * <p>
+ * The system may send a {@link KeyEvent#KEYCODE_BACK} key press to the application
+ * when this button is pressed.
+ * </p>
+ *
+ * @see #getButtonState
+ */
+ public static final int BUTTON_BACK = 1 << 3;
+
+ /**
+ * Button constant: Forward button pressed (mouse forward button).
+ * <p>
+ * The system may send a {@link KeyEvent#KEYCODE_FORWARD} key press to the application
+ * when this button is pressed.
+ * </p>
+ *
+ * @see #getButtonState
+ */
+ public static final int BUTTON_FORWARD = 1 << 4;
+
+ /**
+ * Button constant: Eraser button pressed (stylus end).
+ *
+ * @see #getButtonState
+ */
+ public static final int BUTTON_ERASER = 1 << 5;
+
+ // NOTE: If you add a new axis here you must also add it to:
+ // native/include/android/input.h
+
+ // Symbolic names of all button states in bit order from least significant
+ // to most significant.
+ private static final String[] BUTTON_SYMBOLIC_NAMES = new String[] {
+ "BUTTON_PRIMARY",
+ "BUTTON_SECONDARY",
+ "BUTTON_TERTIARY",
+ "BUTTON_BACK",
+ "BUTTON_FORWARD",
+ "BUTTON_ERASER",
+ "0x00000040",
+ "0x00000080",
+ "0x00000100",
+ "0x00000200",
+ "0x00000400",
+ "0x00000800",
+ "0x00001000",
+ "0x00002000",
+ "0x00004000",
+ "0x00008000",
+ "0x00010000",
+ "0x00020000",
+ "0x00040000",
+ "0x00080000",
+ "0x00100000",
+ "0x00200000",
+ "0x00400000",
+ "0x00800000",
+ "0x01000000",
+ "0x02000000",
+ "0x04000000",
+ "0x08000000",
+ "0x10000000",
+ "0x20000000",
+ "0x40000000",
+ "0x80000000",
+ };
+
+ /**
+ * Tool type constant: Unknown tool type.
+ * This constant is used when the tool type is not known or is not relevant,
+ * such as for a trackball or other non-pointing device.
+ *
+ * @see #getToolType
+ */
+ public static final int TOOL_TYPE_UNKNOWN = 0;
+
+ /**
+ * Tool type constant: The tool is a finger directly touching the display.
+ *
+ * This is a <em>direct</em> positioning tool.
+ *
+ * @see #getToolType
+ */
+ public static final int TOOL_TYPE_FINGER = 1;
+
+ /**
+ * Tool type constant: The tool is a stylus directly touching the display
+ * or hovering slightly above it.
+ *
+ * This is a <em>direct</em> positioning tool.
+ *
+ * @see #getToolType
+ */
+ public static final int TOOL_TYPE_STYLUS = 2;
+
+ /**
+ * Tool type constant: The tool is a mouse or trackpad that translates
+ * relative motions into cursor movements on the display.
+ *
+ * This is an <em>indirect</em> positioning tool.
+ *
+ * @see #getToolType
+ */
+ public static final int TOOL_TYPE_MOUSE = 3;
+
+ /**
+ * Tool type constant: The tool is a finger on a touch pad that is not
+ * directly attached to the display. Finger movements on the touch pad
+ * may be translated into touches on the display, possibly with visual feedback.
+ *
+ * This is an <em>indirect</em> positioning tool.
+ *
+ * @see #getToolType
+ */
+ public static final int TOOL_TYPE_INDIRECT_FINGER = 4;
+
+ /**
+ * Tool type constant: The tool is a stylus on a digitizer tablet that is not
+ * attached to the display. Stylus movements on the digitizer may be translated
+ * into touches on the display, possibly with visual feedback.
+ *
+ * This is an <em>indirect</em> positioning tool.
+ *
+ * @see #getToolType
+ */
+ public static final int TOOL_TYPE_INDIRECT_STYLUS = 5;
+
+ // NOTE: If you add a new tool type here you must also add it to:
+ // native/include/android/input.h
+
+ // Symbolic names of all tool types.
+ private static final SparseArray<String> TOOL_TYPE_SYMBOLIC_NAMES = new SparseArray<String>();
static {
- populateAxisSymbolicNames();
+ SparseArray<String> names = TOOL_TYPE_SYMBOLIC_NAMES;
+ names.append(TOOL_TYPE_UNKNOWN, "TOOL_TYPE_UNKNOWN");
+ names.append(TOOL_TYPE_FINGER, "TOOL_TYPE_FINGER");
+ names.append(TOOL_TYPE_STYLUS, "TOOL_TYPE_STYLUS");
+ names.append(TOOL_TYPE_MOUSE, "TOOL_TYPE_MOUSE");
+ names.append(TOOL_TYPE_INDIRECT_FINGER, "TOOL_TYPE_INDIRECT_FINGER");
+ names.append(TOOL_TYPE_INDIRECT_STYLUS, "TOOL_TYPE_INDIRECT_STYLUS");
}
// Private value for history pos that obtains the current sample.
@@ -995,10 +1297,23 @@ public final class MotionEvent extends InputEvent implements Parcelable {
// Shared temporary objects used when translating coordinates supplied by
// the caller into single element PointerCoords and pointer id arrays.
- // Must lock gTmpPointerCoords prior to use.
- private static final PointerCoords[] gTmpPointerCoords =
- new PointerCoords[] { new PointerCoords() };
- private static final int[] gTmpPointerIds = new int[] { 0 /*always 0*/ };
+ private static final Object gSharedTempLock = new Object();
+ private static PointerCoords[] gSharedTempPointerCoords;
+ private static PointerProperties[] gSharedTempPointerProperties;
+ private static int[] gSharedTempPointerIndexMap;
+
+ private static final void ensureSharedTempPointerCapacity(int desiredCapacity) {
+ if (gSharedTempPointerCoords == null
+ || gSharedTempPointerCoords.length < desiredCapacity) {
+ int capacity = gSharedTempPointerCoords != null ? gSharedTempPointerCoords.length : 8;
+ while (capacity < desiredCapacity) {
+ capacity *= 2;
+ }
+ gSharedTempPointerCoords = PointerCoords.createArray(capacity);
+ gSharedTempPointerProperties = PointerProperties.createArray(capacity);
+ gSharedTempPointerIndexMap = new int[capacity];
+ }
+ }
// Pointer to the native MotionEvent object that contains the actual data.
private int mNativePtr;
@@ -1008,10 +1323,11 @@ public final class MotionEvent extends InputEvent implements Parcelable {
private boolean mRecycled;
private static native int nativeInitialize(int nativePtr,
- int deviceId, int source, int action, int flags, int edgeFlags, int metaState,
+ int deviceId, int source, int action, int flags, int edgeFlags,
+ int metaState, int buttonState,
float xOffset, float yOffset, float xPrecision, float yPrecision,
long downTimeNanos, long eventTimeNanos,
- int pointerCount, int[] pointerIds, PointerCoords[] pointerCoords);
+ int pointerCount, PointerProperties[] pointerIds, PointerCoords[] pointerCoords);
private static native int nativeCopy(int destNativePtr, int sourceNativePtr,
boolean keepHistory);
private static native void nativeDispose(int nativePtr);
@@ -1025,16 +1341,22 @@ public final class MotionEvent extends InputEvent implements Parcelable {
private static native void nativeSetAction(int nativePtr, int action);
private static native boolean nativeIsTouchEvent(int nativePtr);
private static native int nativeGetFlags(int nativePtr);
+ private static native void nativeSetFlags(int nativePtr, int flags);
private static native int nativeGetEdgeFlags(int nativePtr);
private static native void nativeSetEdgeFlags(int nativePtr, int action);
private static native int nativeGetMetaState(int nativePtr);
+ private static native int nativeGetButtonState(int nativePtr);
private static native void nativeOffsetLocation(int nativePtr, float deltaX, float deltaY);
+ private static native float nativeGetXOffset(int nativePtr);
+ private static native float nativeGetYOffset(int nativePtr);
private static native float nativeGetXPrecision(int nativePtr);
private static native float nativeGetYPrecision(int nativePtr);
private static native long nativeGetDownTimeNanos(int nativePtr);
+ private static native void nativeSetDownTimeNanos(int nativePtr, long downTime);
private static native int nativeGetPointerCount(int nativePtr);
private static native int nativeGetPointerId(int nativePtr, int pointerIndex);
+ private static native int nativeGetToolType(int nativePtr, int pointerIndex);
private static native int nativeFindPointerIndex(int nativePtr, int pointerId);
private static native int nativeGetHistorySize(int nativePtr);
@@ -1045,6 +1367,8 @@ public final class MotionEvent extends InputEvent implements Parcelable {
int axis, int pointerIndex, int historyPos);
private static native void nativeGetPointerCoords(int nativePtr,
int pointerIndex, int historyPos, PointerCoords outPointerCoords);
+ private static native void nativeGetPointerProperties(int nativePtr,
+ int pointerIndex, PointerProperties outPointerProperties);
private static native void nativeScale(int nativePtr, float scale);
private static native void nativeTransform(int nativePtr, Matrix matrix);
@@ -1086,19 +1410,21 @@ public final class MotionEvent extends InputEvent implements Parcelable {
/**
* 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
* 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
* must be obtained from {@link SystemClock#uptimeMillis()}.
* @param action The kind of action being performed, such as {@link #ACTION_DOWN}.
- * @param pointers The number of points that will be in this event.
- * @param pointerIds An array of <em>pointers</em> values providing
- * an identifier for each pointer.
- * @param pointerCoords An array of <em>pointers</em> values providing
+ * @param pointerCount The number of pointers that will be in this event.
+ * @param pointerProperties An array of <em>pointerCount</em> values providing
+ * a {@link PointerProperties} property object for each pointer, which must
+ * include the pointer identifier.
+ * @param pointerCoords An array of <em>pointerCount</em> values providing
* a {@link PointerCoords} coordinate object for each pointer.
* @param metaState The state of any meta / modifier keys that were in effect when
* the event was generated.
+ * @param buttonState The state of buttons that are pressed.
* @param xPrecision The precision of the X coordinate being reported.
* @param yPrecision The precision of the Y coordinate being reported.
* @param deviceId The id for the device that this event came from. An id of
@@ -1110,21 +1436,69 @@ public final class MotionEvent extends InputEvent implements Parcelable {
* @param flags The motion event flags.
*/
static public MotionEvent obtain(long downTime, long eventTime,
- int action, int pointers, int[] pointerIds, PointerCoords[] pointerCoords,
- int metaState, float xPrecision, float yPrecision, int deviceId,
+ int action, int pointerCount, PointerProperties[] pointerProperties,
+ PointerCoords[] pointerCoords, int metaState, int buttonState,
+ float xPrecision, float yPrecision, int deviceId,
int edgeFlags, int source, int flags) {
MotionEvent ev = obtain();
ev.mNativePtr = nativeInitialize(ev.mNativePtr,
- deviceId, source, action, flags, edgeFlags, metaState,
+ deviceId, source, action, flags, edgeFlags, metaState, buttonState,
0, 0, xPrecision, yPrecision,
downTime * NS_PER_MS, eventTime * NS_PER_MS,
- pointers, pointerIds, pointerCoords);
+ pointerCount, pointerProperties, pointerCoords);
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
+ * 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
+ * must be obtained from {@link SystemClock#uptimeMillis()}.
+ * @param action The kind of action being performed, such as {@link #ACTION_DOWN}.
+ * @param pointerCount The number of pointers that will be in this event.
+ * @param pointerIds An array of <em>pointerCount</em> values providing
+ * an identifier for each pointer.
+ * @param pointerCoords An array of <em>pointerCount</em> values providing
+ * a {@link PointerCoords} coordinate object for each pointer.
+ * @param metaState The state of any meta / modifier keys that were in effect when
+ * the event was generated.
+ * @param xPrecision The precision of the X coordinate being reported.
+ * @param yPrecision The precision of the Y coordinate being reported.
+ * @param deviceId 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
+ * numbers are arbitrary and you shouldn't depend on the values.
+ * @param edgeFlags A bitfield indicating which edges, if any, were touched by this
+ * MotionEvent.
+ * @param source The source of this event.
+ * @param flags The motion event flags.
+ *
+ * @deprecated Use {@link #obtain(long, long, int, int, PointerProperties[], PointerCoords[], int, int, float, float, int, int, int, int)}
+ * instead.
+ */
+ @Deprecated
+ static public MotionEvent obtain(long downTime, long eventTime,
+ int action, int pointerCount, int[] pointerIds, PointerCoords[] pointerCoords,
+ int metaState, float xPrecision, float yPrecision, int deviceId,
+ int edgeFlags, int source, int flags) {
+ synchronized (gSharedTempLock) {
+ ensureSharedTempPointerCapacity(pointerCount);
+ final PointerProperties[] pp = gSharedTempPointerProperties;
+ for (int i = 0; i < pointerCount; i++) {
+ pp[i].clear();
+ pp[i].id = pointerIds[i];
+ }
+ return obtain(downTime, eventTime, action, pointerCount, pp,
+ pointerCoords, metaState, 0, xPrecision, yPrecision, deviceId,
+ edgeFlags, source, flags);
+ }
+ }
+
+ /**
+ * 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
* a stream of position events. This must be obtained from {@link SystemClock#uptimeMillis()}.
@@ -1154,20 +1528,25 @@ public final class MotionEvent extends InputEvent implements Parcelable {
static public MotionEvent obtain(long downTime, long eventTime, int action,
float x, float y, float pressure, float size, int metaState,
float xPrecision, float yPrecision, int deviceId, int edgeFlags) {
- synchronized (gTmpPointerCoords) {
- final PointerCoords pc = gTmpPointerCoords[0];
- pc.clear();
- pc.x = x;
- pc.y = y;
- pc.pressure = pressure;
- pc.size = size;
-
- MotionEvent ev = obtain();
+ MotionEvent ev = obtain();
+ synchronized (gSharedTempLock) {
+ ensureSharedTempPointerCapacity(1);
+ final PointerProperties[] pp = gSharedTempPointerProperties;
+ pp[0].clear();
+ pp[0].id = 0;
+
+ final PointerCoords pc[] = gSharedTempPointerCoords;
+ pc[0].clear();
+ pc[0].x = x;
+ pc[0].y = y;
+ pc[0].pressure = pressure;
+ pc[0].size = size;
+
ev.mNativePtr = nativeInitialize(ev.mNativePtr,
- deviceId, InputDevice.SOURCE_UNKNOWN, action, 0, edgeFlags, metaState,
+ deviceId, InputDevice.SOURCE_UNKNOWN, action, 0, edgeFlags, metaState, 0,
0, 0, xPrecision, yPrecision,
downTime * NS_PER_MS, eventTime * NS_PER_MS,
- 1, gTmpPointerIds, gTmpPointerCoords);
+ 1, pp, pc);
return ev;
}
}
@@ -1181,7 +1560,7 @@ public final class MotionEvent extends InputEvent implements Parcelable {
* @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, such as {@link #ACTION_DOWN}.
- * @param pointers The number of pointers that are active in this event.
+ * @param pointerCount The number of pointers that are active in this event.
* @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
@@ -1207,7 +1586,7 @@ public final class MotionEvent extends InputEvent implements Parcelable {
*/
@Deprecated
static public MotionEvent obtain(long downTime, long eventTime, int action,
- int pointers, float x, float y, float pressure, float size, int metaState,
+ int pointerCount, float x, float y, float pressure, float size, int metaState,
float xPrecision, float yPrecision, int deviceId, int edgeFlags) {
return obtain(downTime, eventTime, action, x, y, pressure, size,
metaState, xPrecision, yPrecision, deviceId, edgeFlags);
@@ -1261,6 +1640,12 @@ public final class MotionEvent extends InputEvent implements Parcelable {
return ev;
}
+ /** @hide */
+ @Override
+ public MotionEvent copy() {
+ return obtain(this);
+ }
+
/**
* Recycle the MotionEvent, to be re-used by a later caller. After calling
* this function you must not ever touch the event again.
@@ -1354,9 +1739,9 @@ public final class MotionEvent extends InputEvent implements Parcelable {
/**
* Returns true if this motion event is a touch event.
* <p>
- * Specifically excludes pointer events with action {@link #ACTION_HOVER_MOVE}
- * or {@link #ACTION_SCROLL} because they are not actually touch events
- * (the pointer is not down).
+ * Specifically excludes pointer events with action {@link #ACTION_HOVER_MOVE},
+ * {@link #ACTION_HOVER_ENTER}, {@link #ACTION_HOVER_EXIT}, or {@link #ACTION_SCROLL}
+ * because they are not actually touch events (the pointer is not down).
* </p>
* @return True if this motion event is a touch event.
* @hide
@@ -1374,6 +1759,20 @@ public final class MotionEvent extends InputEvent implements Parcelable {
return nativeGetFlags(mNativePtr);
}
+ /** @hide */
+ @Override
+ public final boolean isTainted() {
+ final int flags = getFlags();
+ return (flags & FLAG_TAINTED) != 0;
+ }
+
+ /** @hide */
+ @Override
+ public final void setTainted(boolean tainted) {
+ final int flags = getFlags();
+ nativeSetFlags(mNativePtr, tainted ? flags | FLAG_TAINTED : flags & ~FLAG_TAINTED);
+ }
+
/**
* Returns the time (in ms) when the user originally pressed down to start
* a stream of position events.
@@ -1383,6 +1782,16 @@ public final class MotionEvent extends InputEvent implements Parcelable {
}
/**
+ * Sets the time (in ms) when the user originally pressed down to start
+ * a stream of position events.
+ *
+ * @hide
+ */
+ public final void setDownTime(long downTime) {
+ nativeSetDownTimeNanos(mNativePtr, downTime * NS_PER_MS);
+ }
+
+ /**
* Returns the time (in ms) when this specific event was generated.
*/
public final long getEventTime() {
@@ -1521,7 +1930,27 @@ public final class MotionEvent extends InputEvent implements Parcelable {
public final int getPointerId(int pointerIndex) {
return nativeGetPointerId(mNativePtr, pointerIndex);
}
-
+
+ /**
+ * Gets the tool type of a pointer for the given pointer index.
+ * The tool type indicates the type of tool used to make contact such
+ * as a finger or stylus, if known.
+ *
+ * @param pointerIndex Raw index of pointer to retrieve. Value may be from 0
+ * (the first pointer that is down) to {@link #getPointerCount()}-1.
+ * @return The tool type of the pointer.
+ *
+ * @see #TOOL_TYPE_UNKNOWN
+ * @see #TOOL_TYPE_FINGER
+ * @see #TOOL_TYPE_STYLUS
+ * @see #TOOL_TYPE_MOUSE
+ * @see #TOOL_TYPE_INDIRECT_FINGER
+ * @see #TOOL_TYPE_INDIRECT_STYLUS
+ */
+ public final int getToolType(int pointerIndex) {
+ return nativeGetToolType(mNativePtr, pointerIndex);
+ }
+
/**
* Given a pointer identifier, find the index of its data in the event.
*
@@ -1533,7 +1962,7 @@ public final class MotionEvent extends InputEvent implements Parcelable {
public final int findPointerIndex(int pointerId) {
return nativeFindPointerIndex(mNativePtr, pointerId);
}
-
+
/**
* Returns the X coordinate of this event for the given pointer
* <em>index</em> (use {@link #getPointerId(int)} to find the pointer
@@ -1709,6 +2138,21 @@ public final class MotionEvent extends InputEvent implements Parcelable {
}
/**
+ * Populates a {@link PointerProperties} object with pointer properties for
+ * the specified pointer index.
+ *
+ * @param pointerIndex Raw index of pointer to retrieve. Value may be from 0
+ * (the first pointer that is down) to {@link #getPointerCount()}-1.
+ * @param outPointerProperties The pointer properties object to populate.
+ *
+ * @see PointerProperties
+ */
+ public final void getPointerProperties(int pointerIndex,
+ PointerProperties outPointerProperties) {
+ nativeGetPointerProperties(mNativePtr, pointerIndex, outPointerProperties);
+ }
+
+ /**
* Returns the state of any meta / modifier keys that were in effect when
* the event was generated. This is the same values as those
* returned by {@link KeyEvent#getMetaState() KeyEvent.getMetaState}.
@@ -1723,6 +2167,22 @@ public final class MotionEvent extends InputEvent implements Parcelable {
}
/**
+ * Gets the state of all buttons that are pressed such as a mouse or stylus button.
+ *
+ * @return The button state.
+ *
+ * @see #BUTTON_PRIMARY
+ * @see #BUTTON_SECONDARY
+ * @see #BUTTON_TERTIARY
+ * @see #BUTTON_FORWARD
+ * @see #BUTTON_BACK
+ * @see #BUTTON_ERASER
+ */
+ public final int getButtonState() {
+ return nativeGetButtonState(mNativePtr);
+ }
+
+ /**
* Returns the original raw X coordinate of this event. For touch
* events on the screen, this is the original location of the event
* on the screen, before it had been adjusted for the containing window
@@ -2236,14 +2696,16 @@ public final class MotionEvent extends InputEvent implements Parcelable {
*/
public final void addBatch(long eventTime, float x, float y,
float pressure, float size, int metaState) {
- synchronized (gTmpPointerCoords) {
- final PointerCoords pc = gTmpPointerCoords[0];
- pc.clear();
- pc.x = x;
- pc.y = y;
- pc.pressure = pressure;
- pc.size = size;
- nativeAddBatch(mNativePtr, eventTime * NS_PER_MS, gTmpPointerCoords, metaState);
+ synchronized (gSharedTempLock) {
+ ensureSharedTempPointerCapacity(1);
+ final PointerCoords[] pc = gSharedTempPointerCoords;
+ pc[0].clear();
+ pc[0].x = x;
+ pc[0].y = y;
+ pc[0].pressure = pressure;
+ pc[0].size = size;
+
+ nativeAddBatch(mNativePtr, eventTime * NS_PER_MS, pc, metaState);
}
}
@@ -2262,30 +2724,187 @@ public final class MotionEvent extends InputEvent implements Parcelable {
nativeAddBatch(mNativePtr, eventTime * NS_PER_MS, pointerCoords, metaState);
}
+ /**
+ * Returns true if all points in the motion event are completely within the specified bounds.
+ * @hide
+ */
+ public final boolean isWithinBoundsNoHistory(float left, float top,
+ float right, float bottom) {
+ final int pointerCount = nativeGetPointerCount(mNativePtr);
+ for (int i = 0; i < pointerCount; i++) {
+ final float x = nativeGetAxisValue(mNativePtr, AXIS_X, i, HISTORY_CURRENT);
+ final float y = nativeGetAxisValue(mNativePtr, AXIS_Y, i, HISTORY_CURRENT);
+ if (x < left || x > right || y < top || y > bottom) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private static final float clamp(float value, float low, float high) {
+ if (value < low) {
+ return low;
+ } else if (value > high) {
+ return high;
+ }
+ return value;
+ }
+
+ /**
+ * Returns a new motion events whose points have been clamped to the specified bounds.
+ * @hide
+ */
+ public final MotionEvent clampNoHistory(float left, float top, float right, float bottom) {
+ MotionEvent ev = obtain();
+ synchronized (gSharedTempLock) {
+ final int pointerCount = nativeGetPointerCount(mNativePtr);
+
+ ensureSharedTempPointerCapacity(pointerCount);
+ final PointerProperties[] pp = gSharedTempPointerProperties;
+ final PointerCoords[] pc = gSharedTempPointerCoords;
+
+ for (int i = 0; i < pointerCount; i++) {
+ nativeGetPointerProperties(mNativePtr, i, pp[i]);
+ nativeGetPointerCoords(mNativePtr, i, HISTORY_CURRENT, pc[i]);
+ pc[i].x = clamp(pc[i].x, left, right);
+ pc[i].y = clamp(pc[i].y, top, bottom);
+ }
+ ev.mNativePtr = nativeInitialize(ev.mNativePtr,
+ nativeGetDeviceId(mNativePtr), nativeGetSource(mNativePtr),
+ nativeGetAction(mNativePtr), nativeGetFlags(mNativePtr),
+ nativeGetEdgeFlags(mNativePtr), nativeGetMetaState(mNativePtr),
+ nativeGetButtonState(mNativePtr),
+ nativeGetXOffset(mNativePtr), nativeGetYOffset(mNativePtr),
+ nativeGetXPrecision(mNativePtr), nativeGetYPrecision(mNativePtr),
+ nativeGetDownTimeNanos(mNativePtr),
+ nativeGetEventTimeNanos(mNativePtr, HISTORY_CURRENT),
+ pointerCount, pp, pc);
+ return ev;
+ }
+ }
+
+ /**
+ * Gets an integer where each pointer id present in the event is marked as a bit.
+ * @hide
+ */
+ public final int getPointerIdBits() {
+ int idBits = 0;
+ final int pointerCount = nativeGetPointerCount(mNativePtr);
+ for (int i = 0; i < pointerCount; i++) {
+ idBits |= 1 << nativeGetPointerId(mNativePtr, i);
+ }
+ return idBits;
+ }
+
+ /**
+ * Splits a motion event such that it includes only a subset of pointer ids.
+ * @hide
+ */
+ public final MotionEvent split(int idBits) {
+ MotionEvent ev = obtain();
+ synchronized (gSharedTempLock) {
+ final int oldPointerCount = nativeGetPointerCount(mNativePtr);
+ ensureSharedTempPointerCapacity(oldPointerCount);
+ final PointerProperties[] pp = gSharedTempPointerProperties;
+ final PointerCoords[] pc = gSharedTempPointerCoords;
+ final int[] map = gSharedTempPointerIndexMap;
+
+ final int oldAction = nativeGetAction(mNativePtr);
+ final int oldActionMasked = oldAction & ACTION_MASK;
+ final int oldActionPointerIndex = (oldAction & ACTION_POINTER_INDEX_MASK)
+ >> ACTION_POINTER_INDEX_SHIFT;
+ int newActionPointerIndex = -1;
+ int newPointerCount = 0;
+ int newIdBits = 0;
+ for (int i = 0; i < oldPointerCount; i++) {
+ nativeGetPointerProperties(mNativePtr, i, pp[newPointerCount]);
+ final int idBit = 1 << pp[newPointerCount].id;
+ if ((idBit & idBits) != 0) {
+ if (i == oldActionPointerIndex) {
+ newActionPointerIndex = newPointerCount;
+ }
+ map[newPointerCount] = i;
+ newPointerCount += 1;
+ newIdBits |= idBit;
+ }
+ }
+
+ if (newPointerCount == 0) {
+ throw new IllegalArgumentException("idBits did not match any ids in the event");
+ }
+
+ final int newAction;
+ if (oldActionMasked == ACTION_POINTER_DOWN || oldActionMasked == ACTION_POINTER_UP) {
+ if (newActionPointerIndex < 0) {
+ // An unrelated pointer changed.
+ newAction = ACTION_MOVE;
+ } else if (newPointerCount == 1) {
+ // The first/last pointer went down/up.
+ newAction = oldActionMasked == ACTION_POINTER_DOWN
+ ? ACTION_DOWN : ACTION_UP;
+ } else {
+ // A secondary pointer went down/up.
+ newAction = oldActionMasked
+ | (newActionPointerIndex << ACTION_POINTER_INDEX_SHIFT);
+ }
+ } else {
+ // Simple up/down/cancel/move or other motion action.
+ newAction = oldAction;
+ }
+
+ final int historySize = nativeGetHistorySize(mNativePtr);
+ for (int h = 0; h <= historySize; h++) {
+ final int historyPos = h == historySize ? HISTORY_CURRENT : h;
+
+ for (int i = 0; i < newPointerCount; i++) {
+ nativeGetPointerCoords(mNativePtr, map[i], historyPos, pc[i]);
+ }
+
+ final long eventTimeNanos = nativeGetEventTimeNanos(mNativePtr, historyPos);
+ if (h == 0) {
+ ev.mNativePtr = nativeInitialize(ev.mNativePtr,
+ nativeGetDeviceId(mNativePtr), nativeGetSource(mNativePtr),
+ newAction, nativeGetFlags(mNativePtr),
+ nativeGetEdgeFlags(mNativePtr), nativeGetMetaState(mNativePtr),
+ nativeGetButtonState(mNativePtr),
+ nativeGetXOffset(mNativePtr), nativeGetYOffset(mNativePtr),
+ nativeGetXPrecision(mNativePtr), nativeGetYPrecision(mNativePtr),
+ nativeGetDownTimeNanos(mNativePtr), eventTimeNanos,
+ newPointerCount, pp, pc);
+ } else {
+ nativeAddBatch(ev.mNativePtr, eventTimeNanos, pc, 0);
+ }
+ }
+ return ev;
+ }
+ }
+
@Override
public String toString() {
- return "MotionEvent{" + Integer.toHexString(System.identityHashCode(this))
- + " pointerId=" + getPointerId(0)
- + " action=" + actionToString(getAction())
- + " x=" + getX()
- + " y=" + getY()
- + " pressure=" + getPressure()
- + " size=" + getSize()
- + " touchMajor=" + getTouchMajor()
- + " touchMinor=" + getTouchMinor()
- + " toolMajor=" + getToolMajor()
- + " toolMinor=" + getToolMinor()
- + " orientation=" + getOrientation()
- + " meta=" + KeyEvent.metaStateToString(getMetaState())
- + " pointerCount=" + getPointerCount()
- + " historySize=" + getHistorySize()
- + " flags=0x" + Integer.toHexString(getFlags())
- + " edgeFlags=0x" + Integer.toHexString(getEdgeFlags())
- + " device=" + getDeviceId()
- + " source=0x" + Integer.toHexString(getSource())
- + (getPointerCount() > 1 ?
- " pointerId2=" + getPointerId(1) + " x2=" + getX(1) + " y2=" + getY(1) : "")
- + "}";
+ StringBuilder msg = new StringBuilder();
+ msg.append("MotionEvent { action=").append(actionToString(getAction()));
+
+ final int pointerCount = getPointerCount();
+ for (int i = 0; i < pointerCount; i++) {
+ msg.append(", id[").append(i).append("]=").append(getPointerId(i));
+ msg.append(", x[").append(i).append("]=").append(getX(i));
+ msg.append(", y[").append(i).append("]=").append(getY(i));
+ msg.append(", toolType[").append(i).append("]=").append(
+ toolTypeToString(getToolType(i)));
+ }
+
+ msg.append(", buttonState=").append(KeyEvent.metaStateToString(getButtonState()));
+ msg.append(", metaState=").append(KeyEvent.metaStateToString(getMetaState()));
+ msg.append(", flags=0x").append(Integer.toHexString(getFlags()));
+ msg.append(", edgeFlags=0x").append(Integer.toHexString(getEdgeFlags()));
+ msg.append(", pointerCount=").append(pointerCount);
+ msg.append(", historySize=").append(getHistorySize());
+ msg.append(", eventTime=").append(getEventTime());
+ msg.append(", downTime=").append(getDownTime());
+ msg.append(", deviceId=").append(getDeviceId());
+ msg.append(", source=0x").append(Integer.toHexString(getSource()));
+ msg.append(" }");
+ return msg.toString();
}
/**
@@ -2313,6 +2932,10 @@ public final class MotionEvent extends InputEvent implements Parcelable {
return "ACTION_HOVER_MOVE";
case ACTION_SCROLL:
return "ACTION_SCROLL";
+ case ACTION_HOVER_ENTER:
+ return "ACTION_HOVER_ENTER";
+ case ACTION_HOVER_EXIT:
+ return "ACTION_HOVER_EXIT";
}
int index = (action & ACTION_POINTER_INDEX_MASK) >> ACTION_POINTER_INDEX_SHIFT;
switch (action & ACTION_MASK) {
@@ -2364,6 +2987,55 @@ public final class MotionEvent extends InputEvent implements Parcelable {
}
}
+ /**
+ * Returns a string that represents the symbolic name of the specified combined
+ * button state flags such as "0", "BUTTON_PRIMARY",
+ * "BUTTON_PRIMARY|BUTTON_SECONDARY" or an equivalent numeric constant such as "0x10000000"
+ * if unknown.
+ *
+ * @param buttonState The button state.
+ * @return The symbolic name of the specified combined button state flags.
+ * @hide
+ */
+ public static String buttonStateToString(int buttonState) {
+ if (buttonState == 0) {
+ return "0";
+ }
+ StringBuilder result = null;
+ int i = 0;
+ while (buttonState != 0) {
+ final boolean isSet = (buttonState & 1) != 0;
+ buttonState >>>= 1; // unsigned shift!
+ if (isSet) {
+ final String name = BUTTON_SYMBOLIC_NAMES[i];
+ if (result == null) {
+ if (buttonState == 0) {
+ return name;
+ }
+ result = new StringBuilder(name);
+ } else {
+ result.append('|');
+ result.append(name);
+ }
+ }
+ i += 1;
+ }
+ return result.toString();
+ }
+
+ /**
+ * Returns a string that represents the symbolic name of the specified tool type
+ * such as "TOOL_TYPE_FINGER" or an equivalent numeric constant such as "42" if unknown.
+ *
+ * @param toolType The tool type.
+ * @return The symbolic name of the specified tool type.
+ * @hide
+ */
+ public static String toolTypeToString(int toolType) {
+ String symbolicName = TOOL_TYPE_SYMBOLIC_NAMES.get(toolType);
+ return symbolicName != null ? symbolicName : Integer.toString(toolType);
+ }
+
public static final Parcelable.Creator<MotionEvent> CREATOR
= new Parcelable.Creator<MotionEvent>() {
public MotionEvent createFromParcel(Parcel in) {
@@ -2391,8 +3063,9 @@ public final class MotionEvent extends InputEvent implements Parcelable {
/**
* Transfer object for pointer coordinates.
*
- * Objects of this type can be used to manufacture new {@link MotionEvent} objects
- * and to query pointer coordinate information in bulk.
+ * Objects of this type can be used to specify the pointer coordinates when
+ * creating new {@link MotionEvent} objects and to query pointer coordinates
+ * in bulk.
*
* Refer to {@link InputDevice} for information about how different kinds of
* input devices and sources represent pointer coordinates.
@@ -2418,6 +3091,15 @@ public final class MotionEvent extends InputEvent implements Parcelable {
copyFrom(other);
}
+ /** @hide */
+ public static PointerCoords[] createArray(int size) {
+ PointerCoords[] array = new PointerCoords[size];
+ for (int i = 0; i < size; i++) {
+ array[i] = new PointerCoords();
+ }
+ return array;
+ }
+
/**
* The X component of the pointer movement.
*
@@ -2678,4 +3360,71 @@ public final class MotionEvent extends InputEvent implements Parcelable {
}
}
}
+
+ /**
+ * Transfer object for pointer properties.
+ *
+ * Objects of this type can be used to specify the pointer id and tool type
+ * when creating new {@link MotionEvent} objects and to query pointer properties in bulk.
+ */
+ public static final class PointerProperties {
+ /**
+ * Creates a pointer properties object with an invalid pointer id.
+ */
+ public PointerProperties() {
+ clear();
+ }
+
+ /**
+ * Creates a pointer properties object as a copy of the contents of
+ * another pointer properties object.
+ * @param other
+ */
+ public PointerProperties(PointerProperties other) {
+ copyFrom(other);
+ }
+
+ /** @hide */
+ public static PointerProperties[] createArray(int size) {
+ PointerProperties[] array = new PointerProperties[size];
+ for (int i = 0; i < size; i++) {
+ array[i] = new PointerProperties();
+ }
+ return array;
+ }
+
+ /**
+ * The pointer id.
+ * Initially set to {@link #INVALID_POINTER_ID} (-1).
+ *
+ * @see MotionEvent#getPointerId(int)
+ */
+ public int id;
+
+ /**
+ * The pointer tool type.
+ * Initially set to 0.
+ *
+ * @see MotionEvent#getToolType(int)
+ */
+ public int toolType;
+
+ /**
+ * Resets the pointer properties to their initial values.
+ */
+ public void clear() {
+ id = INVALID_POINTER_ID;
+ toolType = TOOL_TYPE_UNKNOWN;
+ }
+
+ /**
+ * Copies the contents of another pointer properties object.
+ *
+ * @param other The pointer properties object to copy.
+ */
+ public void copyFrom(PointerProperties other) {
+ id = other.id;
+ toolType = other.toolType;
+ }
+ }
}
diff --git a/core/java/android/view/OrientationEventListener.java b/core/java/android/view/OrientationEventListener.java
index 391ba1e..cd48a4f 100755
--- a/core/java/android/view/OrientationEventListener.java
+++ b/core/java/android/view/OrientationEventListener.java
@@ -21,7 +21,6 @@ import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
-import android.util.Config;
import android.util.Log;
/**
@@ -31,7 +30,7 @@ import android.util.Log;
public abstract class OrientationEventListener {
private static final String TAG = "OrientationEventListener";
private static final boolean DEBUG = false;
- private static final boolean localLOGV = DEBUG ? Config.LOGD : Config.LOGV;
+ private static final boolean localLOGV = false;
private int mOrientation = ORIENTATION_UNKNOWN;
private SensorManager mSensorManager;
private boolean mEnabled = false;
diff --git a/core/config/ndebug/android/util/ConfigBuildFlags.java b/core/java/android/view/PointerIcon.aidl
index 2345a40..b09340b 100644
--- a/core/config/ndebug/android/util/ConfigBuildFlags.java
+++ b/core/java/android/view/PointerIcon.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009 The Android Open Source Project
+ * Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,11 +14,6 @@
* limitations under the License.
*/
-package android.util;
+package android.view;
-/**
- * Static flags set by the build process.
- */
-/* package */ final class ConfigBuildFlags {
- /* package */ static final boolean DEBUG = false;
-}
+parcelable PointerIcon;
diff --git a/core/java/android/view/PointerIcon.java b/core/java/android/view/PointerIcon.java
new file mode 100644
index 0000000..bb7ed41
--- /dev/null
+++ b/core/java/android/view/PointerIcon.java
@@ -0,0 +1,435 @@
+/*
+ * Copyright (C) 2011 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;
+
+import com.android.internal.util.XmlUtils;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+
+/**
+ * Represents an icon that can be used as a mouse pointer.
+ * <p>
+ * Pointer icons can be provided either by the system using system styles,
+ * or by applications using bitmaps or application resources.
+ * </p>
+ *
+ * @hide
+ */
+public final class PointerIcon implements Parcelable {
+ private static final String TAG = "PointerIcon";
+
+ /** Style constant: Custom icon with a user-supplied bitmap. */
+ public static final int STYLE_CUSTOM = -1;
+
+ /** Style constant: Null icon. It has no bitmap. */
+ public static final int STYLE_NULL = 0;
+
+ /** Style constant: Arrow icon. (Default mouse pointer) */
+ public static final int STYLE_ARROW = 1000;
+
+ /** {@hide} Style constant: Spot hover icon for touchpads. */
+ public static final int STYLE_SPOT_HOVER = 2000;
+
+ /** {@hide} Style constant: Spot touch icon for touchpads. */
+ public static final int STYLE_SPOT_TOUCH = 2001;
+
+ /** {@hide} Style constant: Spot anchor icon for touchpads. */
+ public static final int STYLE_SPOT_ANCHOR = 2002;
+
+ // OEM private styles should be defined starting at this range to avoid
+ // conflicts with any system styles that may be defined in the future.
+ private static final int STYLE_OEM_FIRST = 10000;
+
+ // The default pointer icon.
+ private static final int STYLE_DEFAULT = STYLE_ARROW;
+
+ private static final PointerIcon gNullIcon = new PointerIcon(STYLE_NULL);
+
+ private final int mStyle;
+ private int mSystemIconResourceId;
+ private Bitmap mBitmap;
+ private float mHotSpotX;
+ private float mHotSpotY;
+
+ private PointerIcon(int style) {
+ mStyle = style;
+ }
+
+ /**
+ * Gets a special pointer icon that has no bitmap.
+ *
+ * @return The null pointer icon.
+ *
+ * @see #STYLE_NULL
+ */
+ public static PointerIcon getNullIcon() {
+ return gNullIcon;
+ }
+
+ /**
+ * Gets the default pointer icon.
+ *
+ * @param context The context.
+ * @return The default pointer icon.
+ *
+ * @throws IllegalArgumentException if context is null.
+ */
+ public static PointerIcon getDefaultIcon(Context context) {
+ return getSystemIcon(context, STYLE_DEFAULT);
+ }
+
+ /**
+ * Gets a system pointer icon for the given style.
+ * If style is not recognized, returns the default pointer icon.
+ *
+ * @param context The context.
+ * @param style The pointer icon style.
+ * @return The pointer icon.
+ *
+ * @throws IllegalArgumentException if context is null.
+ */
+ public static PointerIcon getSystemIcon(Context context, int style) {
+ if (context == null) {
+ throw new IllegalArgumentException("context must not be null");
+ }
+
+ if (style == STYLE_NULL) {
+ return gNullIcon;
+ }
+
+ int styleIndex = getSystemIconStyleIndex(style);
+ if (styleIndex == 0) {
+ styleIndex = getSystemIconStyleIndex(STYLE_DEFAULT);
+ }
+
+ TypedArray a = context.obtainStyledAttributes(null,
+ com.android.internal.R.styleable.Pointer,
+ com.android.internal.R.attr.pointerStyle, 0);
+ int resourceId = a.getResourceId(styleIndex, -1);
+ a.recycle();
+
+ if (resourceId == -1) {
+ Log.w(TAG, "Missing theme resources for pointer icon style " + style);
+ return style == STYLE_DEFAULT ? gNullIcon : getSystemIcon(context, STYLE_DEFAULT);
+ }
+
+ PointerIcon icon = new PointerIcon(style);
+ if ((resourceId & 0xff000000) == 0x01000000) {
+ icon.mSystemIconResourceId = resourceId;
+ } else {
+ icon.loadResource(context.getResources(), resourceId);
+ }
+ return icon;
+ }
+
+ /**
+ * Creates a custom pointer from the given bitmap and hotspot information.
+ *
+ * @param bitmap The bitmap for the icon.
+ * @param hotspotX The X offset of the pointer icon hotspot in the bitmap.
+ * Must be within the [0, bitmap.getWidth()) range.
+ * @param hotspotY The Y offset of the pointer icon hotspot in the bitmap.
+ * Must be within the [0, bitmap.getHeight()) range.
+ * @return A pointer icon for this bitmap.
+ *
+ * @throws IllegalArgumentException if bitmap is null, or if the x/y hotspot
+ * parameters are invalid.
+ */
+ public static PointerIcon createCustomIcon(Bitmap bitmap, float hotSpotX, float hotSpotY) {
+ if (bitmap == null) {
+ throw new IllegalArgumentException("bitmap must not be null");
+ }
+ validateHotSpot(bitmap, hotSpotX, hotSpotY);
+
+ PointerIcon icon = new PointerIcon(STYLE_CUSTOM);
+ icon.mBitmap = bitmap;
+ icon.mHotSpotX = hotSpotX;
+ icon.mHotSpotY = hotSpotY;
+ return icon;
+ }
+
+ /**
+ * Loads a custom pointer icon from an XML resource.
+ * <p>
+ * The XML resource should have the following form:
+ * <code>
+ * &lt;?xml version="1.0" encoding="utf-8"?&gt;
+ * &lt;pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
+ * android:bitmap="@drawable/my_pointer_bitmap"
+ * android:hotSpotX="24"
+ * android:hotSpotY="24" /&gt;
+ * </code>
+ * </p>
+ *
+ * @param resources The resources object.
+ * @param resourceId The resource id.
+ * @return The pointer icon.
+ *
+ * @throws IllegalArgumentException if resources is null.
+ * @throws Resources.NotFoundException if the resource was not found or the drawable
+ * linked in the resource was not found.
+ */
+ public static PointerIcon loadCustomIcon(Resources resources, int resourceId) {
+ if (resources == null) {
+ throw new IllegalArgumentException("resources must not be null");
+ }
+
+ PointerIcon icon = new PointerIcon(STYLE_CUSTOM);
+ icon.loadResource(resources, resourceId);
+ return icon;
+ }
+
+ /**
+ * Loads the bitmap and hotspot information for a pointer icon, if it is not already loaded.
+ * Returns a pointer icon (not necessarily the same instance) with the information filled in.
+ *
+ * @param context The context.
+ * @return The loaded pointer icon.
+ *
+ * @throws IllegalArgumentException if context is null.
+ * @see #isLoaded()
+ * @hide
+ */
+ public PointerIcon load(Context context) {
+ if (context == null) {
+ throw new IllegalArgumentException("context must not be null");
+ }
+
+ if (mSystemIconResourceId == 0 || mBitmap != null) {
+ return this;
+ }
+
+ PointerIcon result = new PointerIcon(mStyle);
+ result.mSystemIconResourceId = mSystemIconResourceId;
+ result.loadResource(context.getResources(), mSystemIconResourceId);
+ return result;
+ }
+
+ /**
+ * Returns true if the pointer icon style is {@link #STYLE_NULL}.
+ *
+ * @return True if the pointer icon style is {@link #STYLE_NULL}.
+ */
+ public boolean isNullIcon() {
+ return mStyle == STYLE_NULL;
+ }
+
+ /**
+ * Returns true if the pointer icon has been loaded and its bitmap and hotspot
+ * information are available.
+ *
+ * @return True if the pointer icon is loaded.
+ * @see #load(Context)
+ */
+ public boolean isLoaded() {
+ return mBitmap != null || mStyle == STYLE_NULL;
+ }
+
+ /**
+ * Gets the style of the pointer icon.
+ *
+ * @return The pointer icon style.
+ */
+ public int getStyle() {
+ return mStyle;
+ }
+
+ /**
+ * Gets the bitmap of the pointer icon.
+ *
+ * @return The pointer icon bitmap, or null if the style is {@link #STYLE_NULL}.
+ *
+ * @throws IllegalStateException if the bitmap is not loaded.
+ * @see #isLoaded()
+ * @see #load(Context)
+ */
+ public Bitmap getBitmap() {
+ throwIfIconIsNotLoaded();
+ return mBitmap;
+ }
+
+ /**
+ * Gets the X offset of the pointer icon hotspot.
+ *
+ * @return The hotspot X offset.
+ *
+ * @throws IllegalStateException if the bitmap is not loaded.
+ * @see #isLoaded()
+ * @see #load(Context)
+ */
+ public float getHotSpotX() {
+ throwIfIconIsNotLoaded();
+ return mHotSpotX;
+ }
+
+ /**
+ * Gets the Y offset of the pointer icon hotspot.
+ *
+ * @return The hotspot Y offset.
+ *
+ * @throws IllegalStateException if the bitmap is not loaded.
+ * @see #isLoaded()
+ * @see #load(Context)
+ */
+ public float getHotSpotY() {
+ throwIfIconIsNotLoaded();
+ return mHotSpotY;
+ }
+
+ private void throwIfIconIsNotLoaded() {
+ if (!isLoaded()) {
+ throw new IllegalStateException("The icon is not loaded.");
+ }
+ }
+
+ public static final Parcelable.Creator<PointerIcon> CREATOR
+ = new Parcelable.Creator<PointerIcon>() {
+ public PointerIcon createFromParcel(Parcel in) {
+ int style = in.readInt();
+ if (style == STYLE_NULL) {
+ return getNullIcon();
+ }
+
+ int systemIconResourceId = in.readInt();
+ if (systemIconResourceId != 0) {
+ PointerIcon icon = new PointerIcon(style);
+ icon.mSystemIconResourceId = systemIconResourceId;
+ return icon;
+ }
+
+ Bitmap bitmap = Bitmap.CREATOR.createFromParcel(in);
+ float hotSpotX = in.readFloat();
+ float hotSpotY = in.readFloat();
+ return PointerIcon.createCustomIcon(bitmap, hotSpotX, hotSpotY);
+ }
+
+ public PointerIcon[] newArray(int size) {
+ return new PointerIcon[size];
+ }
+ };
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(mStyle);
+
+ if (mStyle != STYLE_NULL) {
+ out.writeInt(mSystemIconResourceId);
+ if (mSystemIconResourceId == 0) {
+ mBitmap.writeToParcel(out, flags);
+ out.writeFloat(mHotSpotX);
+ out.writeFloat(mHotSpotY);
+ }
+ }
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+
+ if (other == null || !(other instanceof PointerIcon)) {
+ return false;
+ }
+
+ PointerIcon otherIcon = (PointerIcon) other;
+ if (mStyle != otherIcon.mStyle
+ || mSystemIconResourceId != otherIcon.mSystemIconResourceId) {
+ return false;
+ }
+
+ if (mSystemIconResourceId == 0 && (mBitmap != otherIcon.mBitmap
+ || mHotSpotX != otherIcon.mHotSpotX
+ || mHotSpotY != otherIcon.mHotSpotY)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ private void loadResource(Resources resources, int resourceId) {
+ XmlResourceParser parser = resources.getXml(resourceId);
+ final int bitmapRes;
+ final float hotSpotX;
+ final float hotSpotY;
+ try {
+ XmlUtils.beginDocument(parser, "pointer-icon");
+
+ TypedArray a = resources.obtainAttributes(
+ parser, com.android.internal.R.styleable.PointerIcon);
+ bitmapRes = a.getResourceId(com.android.internal.R.styleable.PointerIcon_bitmap, 0);
+ hotSpotX = a.getFloat(com.android.internal.R.styleable.PointerIcon_hotSpotX, 0);
+ hotSpotY = a.getFloat(com.android.internal.R.styleable.PointerIcon_hotSpotY, 0);
+ a.recycle();
+ } catch (Exception ex) {
+ throw new IllegalArgumentException("Exception parsing pointer icon resource.", ex);
+ } finally {
+ parser.close();
+ }
+
+ if (bitmapRes == 0) {
+ throw new IllegalArgumentException("<pointer-icon> is missing bitmap attribute.");
+ }
+
+ Drawable drawable = resources.getDrawable(bitmapRes);
+ if (!(drawable instanceof BitmapDrawable)) {
+ throw new IllegalArgumentException("<pointer-icon> bitmap attribute must "
+ + "refer to a bitmap drawable.");
+ }
+
+ // Set the properties now that we have successfully loaded the icon.
+ mBitmap = ((BitmapDrawable)drawable).getBitmap();
+ mHotSpotX = hotSpotX;
+ mHotSpotY = hotSpotY;
+ }
+
+ private static void validateHotSpot(Bitmap bitmap, float hotSpotX, float hotSpotY) {
+ if (hotSpotX < 0 || hotSpotX >= bitmap.getWidth()) {
+ throw new IllegalArgumentException("x hotspot lies outside of the bitmap area");
+ }
+ if (hotSpotY < 0 || hotSpotY >= bitmap.getHeight()) {
+ throw new IllegalArgumentException("y hotspot lies outside of the bitmap area");
+ }
+ }
+
+ private static int getSystemIconStyleIndex(int style) {
+ switch (style) {
+ case STYLE_ARROW:
+ return com.android.internal.R.styleable.Pointer_pointerIconArrow;
+ case STYLE_SPOT_HOVER:
+ return com.android.internal.R.styleable.Pointer_pointerIconSpotHover;
+ case STYLE_SPOT_TOUCH:
+ return com.android.internal.R.styleable.Pointer_pointerIconSpotTouch;
+ case STYLE_SPOT_ANCHOR:
+ return com.android.internal.R.styleable.Pointer_pointerIconSpotAnchor;
+ default:
+ return 0;
+ }
+ }
+}
diff --git a/core/java/android/view/ScaleGestureDetector.java b/core/java/android/view/ScaleGestureDetector.java
index d638e70..5e07e1a 100644
--- a/core/java/android/view/ScaleGestureDetector.java
+++ b/core/java/android/view/ScaleGestureDetector.java
@@ -163,6 +163,13 @@ public class ScaleGestureDetector {
private int mActiveId1;
private boolean mActive0MostRecent;
+ /**
+ * Consistency verifier for debugging purposes.
+ */
+ private final InputEventConsistencyVerifier mInputEventConsistencyVerifier =
+ InputEventConsistencyVerifier.isInstrumentationEnabled() ?
+ new InputEventConsistencyVerifier(this, 0) : null;
+
public ScaleGestureDetector(Context context, OnScaleGestureListener listener) {
ViewConfiguration config = ViewConfiguration.get(context);
mContext = context;
@@ -171,16 +178,20 @@ public class ScaleGestureDetector {
}
public boolean onTouchEvent(MotionEvent event) {
+ if (mInputEventConsistencyVerifier != null) {
+ mInputEventConsistencyVerifier.onTouchEvent(event, 0);
+ }
+
final int action = event.getActionMasked();
- boolean handled = true;
if (action == MotionEvent.ACTION_DOWN) {
reset(); // Start fresh
}
- if (mInvalidGesture) return false;
-
- if (!mGestureInProgress) {
+ boolean handled = true;
+ if (mInvalidGesture) {
+ handled = false;
+ } else if (!mGestureInProgress) {
switch (action) {
case MotionEvent.ACTION_DOWN: {
mActiveId0 = event.getPointerId(0);
@@ -456,6 +467,10 @@ public class ScaleGestureDetector {
break;
}
}
+
+ if (!handled && mInputEventConsistencyVerifier != null) {
+ mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
+ }
return handled;
}
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 87b3d79..a98c669 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -34,7 +34,6 @@ import android.os.RemoteException;
import android.os.SystemClock;
import android.os.ParcelFileDescriptor;
import android.util.AttributeSet;
-import android.util.Config;
import android.util.Log;
import java.lang.ref.WeakReference;
@@ -84,7 +83,7 @@ import java.util.concurrent.locks.ReentrantLock;
public class SurfaceView extends View {
static private final String TAG = "SurfaceView";
static private final boolean DEBUG = false;
- static private final boolean localLOGV = DEBUG ? true : Config.LOGV;
+ static private final boolean localLOGV = DEBUG ? true : false;
final ArrayList<SurfaceHolder.Callback> mCallbacks
= new ArrayList<SurfaceHolder.Callback>();
@@ -428,7 +427,7 @@ public class SurfaceView extends View {
if (!mHaveFrame) {
return;
}
- ViewRoot viewRoot = (ViewRoot) getRootView().getParent();
+ ViewAncestor viewRoot = (ViewAncestor) getRootView().getParent();
if (viewRoot != null) {
mTranslator = viewRoot.mTranslator;
}
diff --git a/core/java/android/view/TextureView.java b/core/java/android/view/TextureView.java
new file mode 100644
index 0000000..755ecf5
--- /dev/null
+++ b/core/java/android/view/TextureView.java
@@ -0,0 +1,334 @@
+/*
+ * Copyright (C) 2011 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;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.SurfaceTexture;
+import android.util.AttributeSet;
+import android.util.Log;
+
+/**
+ * <p>A TextureView can be used to display a content stream. Such a content
+ * stream can for instance be a video or an OpenGL scene. The content stream
+ * can come from the application's process as well as a remote process.</p>
+ *
+ * <p>TextureView can only be used in a hardware accelerated window. When
+ * rendered in software, TextureView will draw nothing.</p>
+ *
+ * <p>Unlike {@link SurfaceView}, TextureView does not create a separate
+ * window but behaves as a regular View. This key difference allows a
+ * TextureView to be moved, transformed, animated, etc. For instance, you
+ * can make a TextureView semi-translucent by calling
+ * <code>myView.setAlpha(0.5f)</code>.</p>
+ *
+ * <p>Using a TextureView is simple: all you need to do is get its
+ * {@link SurfaceTexture}. The {@link SurfaceTexture} can then be used to
+ * render content. The following example demonstrates how to render the
+ * camera preview into a TextureView:</p>
+ *
+ * <pre>
+ * public class LiveCameraActivity extends Activity implements TextureView.SurfaceTextureListener {
+ * private Camera mCamera;
+ * private TextureView mTextureView;
+ *
+ * protected void onCreate(Bundle savedInstanceState) {
+ * super.onCreate(savedInstanceState);
+ *
+ * mTextureView = new TextureView(this);
+ * mTextureView.setSurfaceTextureListener(this);
+ *
+ * setContentView(mTextureView);
+ * }
+ *
+ * protected void onDestroy() {
+ * super.onDestroy();
+ *
+ * mCamera.stopPreview();
+ * mCamera.release();
+ * }
+ *
+ * public void onSurfaceTextureAvailable(SurfaceTexture surface) {
+ * mCamera = Camera.open();
+ *
+ * try {
+ * mCamera.setPreviewTexture(surface);
+ * mCamera.startPreview();
+ * } catch (IOException ioe) {
+ * // Something bad happened
+ * }
+ * }
+ *
+ * public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
+ * // Ignored, Camera does all the work for us
+ * }
+ * }
+ * </pre>
+ *
+ * <p>A TextureView's SurfaceTexture can be obtained either by invoking
+ * {@link #getSurfaceTexture()} or by using a {@link SurfaceTextureListener}.
+ * It is important to know that a SurfaceTexture is available only after the
+ * TextureView is attached to a window (and {@link #onAttachedToWindow()} has
+ * been invoked.) It is therefore highly recommended you use a listener to
+ * be notified when the SurfaceTexture becomes available.</p>
+ *
+ * @see SurfaceView
+ * @see SurfaceTexture
+ */
+public class TextureView extends View {
+ private HardwareLayer mLayer;
+ private SurfaceTexture mSurface;
+ private SurfaceTextureListener mListener;
+
+ private final Runnable mUpdateLayerAction = new Runnable() {
+ @Override
+ public void run() {
+ updateLayer();
+ }
+ };
+ private SurfaceTexture.OnFrameAvailableListener mUpdateListener;
+
+ /**
+ * Creates a new TextureView.
+ *
+ * @param context The context to associate this view with.
+ */
+ public TextureView(Context context) {
+ super(context);
+ init();
+ }
+
+ /**
+ * Creates a new TextureView.
+ *
+ * @param context The context to associate this view with.
+ * @param attrs The attributes of the XML tag that is inflating the view.
+ */
+ @SuppressWarnings({"UnusedDeclaration"})
+ public TextureView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init();
+ }
+
+ /**
+ * Creates a new TextureView.
+ *
+ * @param context The context to associate this view with.
+ * @param attrs The attributes of the XML tag that is inflating the view.
+ * @param defStyle The default style to apply to this view. If 0, no style
+ * will be applied (beyond what is included in the theme). This may
+ * either be an attribute resource, whose value will be retrieved
+ * from the current theme, or an explicit style resource.
+ */
+ @SuppressWarnings({"UnusedDeclaration"})
+ public TextureView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ init();
+ }
+
+ private void init() {
+ mLayerPaint = new Paint();
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+
+ if (!isHardwareAccelerated()) {
+ Log.w("TextureView", "A TextureView or a subclass can only be "
+ + "used with hardware acceleration enabled.");
+ }
+ }
+
+ /**
+ * The layer type of a TextureView is ignored since a TextureView is always
+ * considered to act as a hardware layer. The optional paint supplied to this
+ * method will however be taken into account when rendering the content of
+ * this TextureView.
+ *
+ * @param layerType The ype of layer to use with this view, must be one of
+ * {@link #LAYER_TYPE_NONE}, {@link #LAYER_TYPE_SOFTWARE} or
+ * {@link #LAYER_TYPE_HARDWARE}
+ * @param paint The paint used to compose the layer. This argument is optional
+ * and can be null. It is ignored when the layer type is
+ * {@link #LAYER_TYPE_NONE}
+ */
+ @Override
+ public void setLayerType(int layerType, Paint paint) {
+ if (paint != mLayerPaint) {
+ mLayerPaint = paint;
+ invalidate();
+ }
+ }
+
+ /**
+ * Always returns {@link #LAYER_TYPE_HARDWARE}.
+ */
+ @Override
+ public int getLayerType() {
+ return LAYER_TYPE_HARDWARE;
+ }
+
+ /**
+ * Calling this method has no effect.
+ */
+ @Override
+ public void buildLayer() {
+ }
+
+ /**
+ * Subclasses of TextureView cannot do their own rendering
+ * with the {@link Canvas} object.
+ *
+ * @param canvas The Canvas to which the View is rendered.
+ */
+ @Override
+ public final void draw(Canvas canvas) {
+ super.draw(canvas);
+ }
+
+ /**
+ * Subclasses of TextureView cannot do their own rendering
+ * with the {@link Canvas} object.
+ *
+ * @param canvas The Canvas to which the View is rendered.
+ */
+ @Override
+ protected final void onDraw(Canvas canvas) {
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(w, h, oldw, oldh);
+ if (mSurface != null) {
+ nSetDefaultBufferSize(mSurface.mSurfaceTexture, getWidth(), getHeight());
+ }
+ }
+
+ @Override
+ HardwareLayer getHardwareLayer() {
+ if (mAttachInfo == null || mAttachInfo.mHardwareRenderer == null) {
+ return null;
+ }
+
+ if (mLayer == null) {
+ mLayer = mAttachInfo.mHardwareRenderer.createHardwareLayer();
+ mSurface = mAttachInfo.mHardwareRenderer.createSuraceTexture(mLayer);
+ nSetDefaultBufferSize(mSurface.mSurfaceTexture, getWidth(), getHeight());
+
+ mUpdateListener = new SurfaceTexture.OnFrameAvailableListener() {
+ @Override
+ public void onFrameAvailable(SurfaceTexture surfaceTexture) {
+ // Per SurfaceTexture's documentation, the callback may be invoked
+ // from an arbitrary thread
+ post(mUpdateLayerAction);
+ }
+ };
+ mSurface.setOnFrameAvailableListener(mUpdateListener);
+
+ if (mListener != null) {
+ mListener.onSurfaceTextureAvailable(mSurface);
+ }
+ }
+
+ return mLayer;
+ }
+
+ @Override
+ protected void onVisibilityChanged(View changedView, int visibility) {
+ super.onVisibilityChanged(changedView, visibility);
+
+ if (mSurface != null) {
+ // When the view becomes invisible, stop updating it, it's a waste of CPU
+ // To cancel updates, the easiest thing to do is simply to remove the
+ // updates listener
+ if (visibility == VISIBLE) {
+ mSurface.setOnFrameAvailableListener(mUpdateListener);
+ updateLayer();
+ } else {
+ mSurface.setOnFrameAvailableListener(null);
+ }
+ }
+ }
+
+ private void updateLayer() {
+ if (mAttachInfo == null || mAttachInfo.mHardwareRenderer == null) {
+ return;
+ }
+
+ mAttachInfo.mHardwareRenderer.updateTextureLayer(mLayer, getWidth(), getHeight(), mSurface);
+
+ invalidate();
+ }
+
+ /**
+ * Returns the {@link SurfaceTexture} used by this view. This method
+ * may return null if the view is not attached to a window.
+ */
+ public SurfaceTexture getSurfaceTexture() {
+ return mSurface;
+ }
+
+ /**
+ * Returns the {@link SurfaceTextureListener} currently associated with this
+ * texture view.
+ *
+ * @see #setSurfaceTextureListener(android.view.TextureView.SurfaceTextureListener)
+ * @see SurfaceTextureListener
+ */
+ public SurfaceTextureListener getSurfaceTextureListener() {
+ return mListener;
+ }
+
+ /**
+ * Sets the {@link SurfaceTextureListener} used to listen to surface
+ * texture events.
+ *
+ * @see #getSurfaceTextureListener()
+ * @see SurfaceTextureListener
+ */
+ public void setSurfaceTextureListener(SurfaceTextureListener listener) {
+ mListener = listener;
+ }
+
+ /**
+ * This listener can be used to be notified when the surface texture
+ * associated with this texture view is available.
+ */
+ public static interface SurfaceTextureListener {
+ /**
+ * Invoked when a {@link TextureView}'s SurfaceTexture is ready for use.
+ *
+ * @param surface The surface returned by
+ * {@link android.view.TextureView#getSurfaceTexture()}
+ */
+ public void onSurfaceTextureAvailable(SurfaceTexture surface);
+
+ /**
+ * Invoked when the {@link SurfaceTexture}'s buffers size changed.
+ *
+ * @param surface The surface returned by
+ * {@link android.view.TextureView#getSurfaceTexture()}
+ * @param width The new width of the surface
+ * @param height The new height of the surface
+ */
+ public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height);
+ }
+
+ private static native void nSetDefaultBufferSize(int surfaceTexture, int width, int height);
+}
diff --git a/core/java/android/view/VelocityTracker.java b/core/java/android/view/VelocityTracker.java
index 4ab2881..fccef2b 100644
--- a/core/java/android/view/VelocityTracker.java
+++ b/core/java/android/view/VelocityTracker.java
@@ -16,8 +16,6 @@
package android.view;
-import android.util.Config;
-import android.util.Log;
import android.util.Poolable;
import android.util.Pool;
import android.util.Pools;
@@ -25,24 +23,15 @@ import android.util.PoolableManager;
/**
* Helper for tracking the velocity of touch events, for implementing
- * flinging and other such gestures. Use {@link #obtain} to retrieve a
- * new instance of the class when you are going to begin tracking, put
- * the motion events you receive into it with {@link #addMovement(MotionEvent)},
- * and when you want to determine the velocity call
- * {@link #computeCurrentVelocity(int)} and then {@link #getXVelocity()}
- * and {@link #getXVelocity()}.
+ * flinging and other such gestures.
+ *
+ * Use {@link #obtain} to retrieve a new instance of the class when you are going
+ * to begin tracking. Put the motion events you receive into it with
+ * {@link #addMovement(MotionEvent)}. When you want to determine the velocity call
+ * {@link #computeCurrentVelocity(int)} and then call {@link #getXVelocity(int)}
+ * and {@link #getXVelocity(int)} to retrieve the velocity for each pointer id.
*/
public final class VelocityTracker implements Poolable<VelocityTracker> {
- private static final String TAG = "VelocityTracker";
- private static final boolean DEBUG = false;
- private static final boolean localLOGV = DEBUG || Config.LOGV;
-
- private static final int NUM_PAST = 10;
- private static final int MAX_AGE_MILLISECONDS = 200;
-
- private static final int POINTER_POOL_CAPACITY = 20;
- private static final int INVALID_POINTER = -1;
-
private static final Pool<VelocityTracker> sPool = Pools.synchronizedPool(
Pools.finitePool(new PoolableManager<VelocityTracker>() {
public VelocityTracker newInstance() {
@@ -56,31 +45,20 @@ public final class VelocityTracker implements Poolable<VelocityTracker> {
element.clear();
}
}, 2));
-
- private static Pointer sRecycledPointerListHead;
- private static int sRecycledPointerCount;
-
- private static final class Pointer {
- public Pointer next;
-
- public int id;
- public float xVelocity;
- public float yVelocity;
-
- public final float[] pastX = new float[NUM_PAST];
- public final float[] pastY = new float[NUM_PAST];
- public final long[] pastTime = new long[NUM_PAST]; // uses Long.MIN_VALUE as a sentinel
-
- public int generation;
- }
-
- private Pointer mPointerListHead; // sorted by id in increasing order
- private int mLastTouchIndex;
- private int mGeneration;
- private int mActivePointerId;
+ private static final int ACTIVE_POINTER_ID = -1;
+
+ private int mPtr;
private VelocityTracker mNext;
+ private static native int nativeInitialize();
+ private static native void nativeDispose(int ptr);
+ private static native void nativeClear(int ptr);
+ private static native void nativeAddMovement(int ptr, MotionEvent event);
+ private static native void nativeComputeCurrentVelocity(int ptr, int units, float maxVelocity);
+ private static native float nativeGetXVelocity(int ptr, int id);
+ private static native float nativeGetYVelocity(int ptr, int id);
+
/**
* Retrieve a new VelocityTracker object to watch the velocity of a
* motion. Be sure to call {@link #recycle} when done. You should
@@ -116,18 +94,26 @@ public final class VelocityTracker implements Poolable<VelocityTracker> {
}
private VelocityTracker() {
- clear();
+ mPtr = nativeInitialize();
}
-
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ if (mPtr != 0) {
+ nativeDispose(mPtr);
+ mPtr = 0;
+ }
+ } finally {
+ super.finalize();
+ }
+ }
+
/**
* Reset the velocity tracker back to its initial state.
*/
public void clear() {
- releasePointerList(mPointerListHead);
-
- mPointerListHead = null;
- mLastTouchIndex = 0;
- mActivePointerId = INVALID_POINTER;
+ nativeClear(mPtr);
}
/**
@@ -137,110 +123,13 @@ public final class VelocityTracker implements Poolable<VelocityTracker> {
* final {@link MotionEvent#ACTION_UP}. You can, however, call this
* for whichever events you desire.
*
- * @param ev The MotionEvent you received and would like to track.
+ * @param event The MotionEvent you received and would like to track.
*/
- public void addMovement(MotionEvent ev) {
- final int historySize = ev.getHistorySize();
- final int pointerCount = ev.getPointerCount();
- final int lastTouchIndex = mLastTouchIndex;
- final int nextTouchIndex = (lastTouchIndex + 1) % NUM_PAST;
- final int finalTouchIndex = (nextTouchIndex + historySize) % NUM_PAST;
- final int generation = mGeneration++;
-
- mLastTouchIndex = finalTouchIndex;
-
- // Update pointer data.
- Pointer previousPointer = null;
- for (int i = 0; i < pointerCount; i++){
- final int pointerId = ev.getPointerId(i);
-
- // Find the pointer data for this pointer id.
- // This loop is optimized for the common case where pointer ids in the event
- // are in sorted order. However, we check for this case explicitly and
- // perform a full linear scan from the start if needed.
- Pointer nextPointer;
- if (previousPointer == null || pointerId < previousPointer.id) {
- previousPointer = null;
- nextPointer = mPointerListHead;
- } else {
- nextPointer = previousPointer.next;
- }
-
- final Pointer pointer;
- for (;;) {
- if (nextPointer != null) {
- final int nextPointerId = nextPointer.id;
- if (nextPointerId == pointerId) {
- pointer = nextPointer;
- break;
- }
- if (nextPointerId < pointerId) {
- nextPointer = nextPointer.next;
- continue;
- }
- }
-
- // Pointer went down. Add it to the list.
- // Write a sentinel at the end of the pastTime trace so we will be able to
- // tell when the trace started.
- if (mActivePointerId == INVALID_POINTER) {
- // Congratulations! You're the new active pointer!
- mActivePointerId = pointerId;
- }
- pointer = obtainPointer();
- pointer.id = pointerId;
- pointer.pastTime[lastTouchIndex] = Long.MIN_VALUE;
- pointer.next = nextPointer;
- if (previousPointer == null) {
- mPointerListHead = pointer;
- } else {
- previousPointer.next = pointer;
- }
- break;
- }
-
- pointer.generation = generation;
- previousPointer = pointer;
-
- final float[] pastX = pointer.pastX;
- final float[] pastY = pointer.pastY;
- final long[] pastTime = pointer.pastTime;
-
- for (int j = 0; j < historySize; j++) {
- final int touchIndex = (nextTouchIndex + j) % NUM_PAST;
- pastX[touchIndex] = ev.getHistoricalX(i, j);
- pastY[touchIndex] = ev.getHistoricalY(i, j);
- pastTime[touchIndex] = ev.getHistoricalEventTime(j);
- }
- pastX[finalTouchIndex] = ev.getX(i);
- pastY[finalTouchIndex] = ev.getY(i);
- pastTime[finalTouchIndex] = ev.getEventTime();
- }
-
- // Find removed pointers.
- previousPointer = null;
- for (Pointer pointer = mPointerListHead; pointer != null; ) {
- final Pointer nextPointer = pointer.next;
- final int pointerId = pointer.id;
- if (pointer.generation != generation) {
- // Pointer went up. Remove it from the list.
- if (previousPointer == null) {
- mPointerListHead = nextPointer;
- } else {
- previousPointer.next = nextPointer;
- }
- releasePointer(pointer);
-
- if (pointerId == mActivePointerId) {
- // Pick a new active pointer. How is arbitrary.
- mActivePointerId = mPointerListHead != null ?
- mPointerListHead.id : INVALID_POINTER;
- }
- } else {
- previousPointer = pointer;
- }
- pointer = nextPointer;
+ public void addMovement(MotionEvent event) {
+ if (event == null) {
+ throw new IllegalArgumentException("event must not be null");
}
+ nativeAddMovement(mPtr, event);
}
/**
@@ -250,7 +139,7 @@ public final class VelocityTracker implements Poolable<VelocityTracker> {
* @see #computeCurrentVelocity(int, float)
*/
public void computeCurrentVelocity(int units) {
- computeCurrentVelocity(units, Float.MAX_VALUE);
+ nativeComputeCurrentVelocity(mPtr, units, Float.MAX_VALUE);
}
/**
@@ -267,78 +156,7 @@ public final class VelocityTracker implements Poolable<VelocityTracker> {
* must be positive.
*/
public void computeCurrentVelocity(int units, float maxVelocity) {
- final int lastTouchIndex = mLastTouchIndex;
-
- for (Pointer pointer = mPointerListHead; pointer != null; pointer = pointer.next) {
- final long[] pastTime = pointer.pastTime;
-
- // Search backwards in time for oldest acceptable time.
- // Stop at the beginning of the trace as indicated by the sentinel time Long.MIN_VALUE.
- int oldestTouchIndex = lastTouchIndex;
- int numTouches = 1;
- final long minTime = pastTime[lastTouchIndex] - MAX_AGE_MILLISECONDS;
- while (numTouches < NUM_PAST) {
- final int nextOldestTouchIndex = (oldestTouchIndex + NUM_PAST - 1) % NUM_PAST;
- final long nextOldestTime = pastTime[nextOldestTouchIndex];
- if (nextOldestTime < minTime) { // also handles end of trace sentinel
- break;
- }
- oldestTouchIndex = nextOldestTouchIndex;
- numTouches += 1;
- }
-
- // If we have a lot of samples, skip the last received sample since it is
- // probably pretty noisy compared to the sum of all of the traces already acquired.
- if (numTouches > 3) {
- numTouches -= 1;
- }
-
- // Kind-of stupid.
- final float[] pastX = pointer.pastX;
- final float[] pastY = pointer.pastY;
-
- final float oldestX = pastX[oldestTouchIndex];
- final float oldestY = pastY[oldestTouchIndex];
- final long oldestTime = pastTime[oldestTouchIndex];
-
- float accumX = 0;
- float accumY = 0;
-
- for (int i = 1; i < numTouches; i++) {
- final int touchIndex = (oldestTouchIndex + i) % NUM_PAST;
- final int duration = (int)(pastTime[touchIndex] - oldestTime);
-
- if (duration == 0) continue;
-
- float delta = pastX[touchIndex] - oldestX;
- float velocity = (delta / duration) * units; // pixels/frame.
- accumX = (accumX == 0) ? velocity : (accumX + velocity) * .5f;
-
- delta = pastY[touchIndex] - oldestY;
- velocity = (delta / duration) * units; // pixels/frame.
- accumY = (accumY == 0) ? velocity : (accumY + velocity) * .5f;
- }
-
- if (accumX < -maxVelocity) {
- accumX = - maxVelocity;
- } else if (accumX > maxVelocity) {
- accumX = maxVelocity;
- }
-
- if (accumY < -maxVelocity) {
- accumY = - maxVelocity;
- } else if (accumY > maxVelocity) {
- accumY = maxVelocity;
- }
-
- pointer.xVelocity = accumX;
- pointer.yVelocity = accumY;
-
- if (localLOGV) {
- Log.v(TAG, "Pointer " + pointer.id
- + ": Y velocity=" + accumX +" X velocity=" + accumY + " N=" + numTouches);
- }
- }
+ nativeComputeCurrentVelocity(mPtr, units, maxVelocity);
}
/**
@@ -348,8 +166,7 @@ public final class VelocityTracker implements Poolable<VelocityTracker> {
* @return The previously computed X velocity.
*/
public float getXVelocity() {
- Pointer pointer = getPointer(mActivePointerId);
- return pointer != null ? pointer.xVelocity : 0;
+ return nativeGetXVelocity(mPtr, ACTIVE_POINTER_ID);
}
/**
@@ -359,8 +176,7 @@ public final class VelocityTracker implements Poolable<VelocityTracker> {
* @return The previously computed Y velocity.
*/
public float getYVelocity() {
- Pointer pointer = getPointer(mActivePointerId);
- return pointer != null ? pointer.yVelocity : 0;
+ return nativeGetYVelocity(mPtr, ACTIVE_POINTER_ID);
}
/**
@@ -371,8 +187,7 @@ public final class VelocityTracker implements Poolable<VelocityTracker> {
* @return The previously computed X velocity.
*/
public float getXVelocity(int id) {
- Pointer pointer = getPointer(id);
- return pointer != null ? pointer.xVelocity : 0;
+ return nativeGetXVelocity(mPtr, id);
}
/**
@@ -383,68 +198,6 @@ public final class VelocityTracker implements Poolable<VelocityTracker> {
* @return The previously computed Y velocity.
*/
public float getYVelocity(int id) {
- Pointer pointer = getPointer(id);
- return pointer != null ? pointer.yVelocity : 0;
- }
-
- private Pointer getPointer(int id) {
- for (Pointer pointer = mPointerListHead; pointer != null; pointer = pointer.next) {
- if (pointer.id == id) {
- return pointer;
- }
- }
- return null;
- }
-
- private static Pointer obtainPointer() {
- synchronized (sPool) {
- if (sRecycledPointerCount != 0) {
- Pointer element = sRecycledPointerListHead;
- sRecycledPointerCount -= 1;
- sRecycledPointerListHead = element.next;
- element.next = null;
- return element;
- }
- }
- return new Pointer();
- }
-
- private static void releasePointer(Pointer pointer) {
- synchronized (sPool) {
- if (sRecycledPointerCount < POINTER_POOL_CAPACITY) {
- pointer.next = sRecycledPointerListHead;
- sRecycledPointerCount += 1;
- sRecycledPointerListHead = pointer;
- }
- }
- }
-
- private static void releasePointerList(Pointer pointer) {
- if (pointer != null) {
- synchronized (sPool) {
- int count = sRecycledPointerCount;
- if (count >= POINTER_POOL_CAPACITY) {
- return;
- }
-
- Pointer tail = pointer;
- for (;;) {
- count += 1;
- if (count >= POINTER_POOL_CAPACITY) {
- break;
- }
-
- Pointer next = tail.next;
- if (next == null) {
- break;
- }
- tail = next;
- }
-
- tail.next = sRecycledPointerListHead;
- sRecycledPointerCount = count;
- sRecycledPointerListHead = pointer;
- }
- }
+ return nativeGetYVelocity(mPtr, id);
}
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 8e3e699..d5f573c 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -49,7 +49,6 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.os.RemoteException;
import android.os.SystemClock;
-import android.os.SystemProperties;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Pool;
@@ -128,11 +127,11 @@ import java.util.concurrent.CopyOnWriteArrayList;
* that will be notified when something interesting happens to the view. For
* example, all views will let you set a listener to be notified when the view
* gains or loses focus. You can register such a listener using
- * {@link #setOnFocusChangeListener}. Other view subclasses offer more
- * specialized listeners. For example, a Button exposes a listener to notify
- * clients when the button is clicked.</li>
+ * {@link #setOnFocusChangeListener(android.view.View.OnFocusChangeListener)}.
+ * Other view subclasses offer more specialized listeners. For example, a Button
+ * exposes a listener to notify clients when the button is clicked.</li>
* <li><strong>Set visibility:</strong> You can hide or show views using
- * {@link #setVisibility}.</li>
+ * {@link #setVisibility(int)}.</li>
* </ul>
* </p>
* <p><em>
@@ -173,61 +172,61 @@ import java.util.concurrent.CopyOnWriteArrayList;
*
* <tr>
* <td rowspan="3">Layout</td>
- * <td><code>{@link #onMeasure}</code></td>
+ * <td><code>{@link #onMeasure(int, int)}</code></td>
* <td>Called to determine the size requirements for this view and all
* of its children.
* </td>
* </tr>
* <tr>
- * <td><code>{@link #onLayout}</code></td>
+ * <td><code>{@link #onLayout(boolean, int, int, int, int)}</code></td>
* <td>Called when this view should assign a size and position to all
* of its children.
* </td>
* </tr>
* <tr>
- * <td><code>{@link #onSizeChanged}</code></td>
+ * <td><code>{@link #onSizeChanged(int, int, int, int)}</code></td>
* <td>Called when the size of this view has changed.
* </td>
* </tr>
*
* <tr>
* <td>Drawing</td>
- * <td><code>{@link #onDraw}</code></td>
+ * <td><code>{@link #onDraw(android.graphics.Canvas)}</code></td>
* <td>Called when the view should render its content.
* </td>
* </tr>
*
* <tr>
* <td rowspan="4">Event processing</td>
- * <td><code>{@link #onKeyDown}</code></td>
+ * <td><code>{@link #onKeyDown(int, KeyEvent)}</code></td>
* <td>Called when a new key event occurs.
* </td>
* </tr>
* <tr>
- * <td><code>{@link #onKeyUp}</code></td>
+ * <td><code>{@link #onKeyUp(int, KeyEvent)}</code></td>
* <td>Called when a key up event occurs.
* </td>
* </tr>
* <tr>
- * <td><code>{@link #onTrackballEvent}</code></td>
+ * <td><code>{@link #onTrackballEvent(MotionEvent)}</code></td>
* <td>Called when a trackball motion event occurs.
* </td>
* </tr>
* <tr>
- * <td><code>{@link #onTouchEvent}</code></td>
+ * <td><code>{@link #onTouchEvent(MotionEvent)}</code></td>
* <td>Called when a touch screen motion event occurs.
* </td>
* </tr>
*
* <tr>
* <td rowspan="2">Focus</td>
- * <td><code>{@link #onFocusChanged}</code></td>
+ * <td><code>{@link #onFocusChanged(boolean, int, android.graphics.Rect)}</code></td>
* <td>Called when the view gains or loses focus.
* </td>
* </tr>
*
* <tr>
- * <td><code>{@link #onWindowFocusChanged}</code></td>
+ * <td><code>{@link #onWindowFocusChanged(boolean)}</code></td>
* <td>Called when the window containing the view gains or loses focus.
* </td>
* </tr>
@@ -246,7 +245,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
* </tr>
*
* <tr>
- * <td><code>{@link #onWindowVisibilityChanged}</code></td>
+ * <td><code>{@link #onWindowVisibilityChanged(int)}</code></td>
* <td>Called when the visibility of the window containing the view
* has changed.
* </td>
@@ -562,15 +561,15 @@ import java.util.concurrent.CopyOnWriteArrayList;
* As a remedy, the framework offers a touch filtering mechanism that can be used to
* improve the security of views that provide access to sensitive functionality.
* </p><p>
- * To enable touch filtering, call {@link #setFilterTouchesWhenObscured} or set the
+ * To enable touch filtering, call {@link #setFilterTouchesWhenObscured(boolean)} or set the
* android:filterTouchesWhenObscured layout attribute to true. When enabled, the framework
* will discard touches that are received whenever the view's window is obscured by
* another visible window. As a result, the view will not receive touches whenever a
* toast, dialog or other window appears above the view's window.
* </p><p>
* For more fine-grained control over security, consider overriding the
- * {@link #onFilterTouchEventForSecurity} method to implement your own security policy.
- * See also {@link MotionEvent#FLAG_WINDOW_IS_OBSCURED}.
+ * {@link #onFilterTouchEventForSecurity(MotionEvent)} method to implement your own
+ * security policy. See also {@link MotionEvent#FLAG_WINDOW_IS_OBSCURED}.
* </p>
*
* @attr ref android.R.styleable#View_alpha
@@ -668,19 +667,19 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
private static final int FITS_SYSTEM_WINDOWS = 0x00000002;
/**
- * This view is visible. Use with {@link #setVisibility}.
+ * This view is visible. Use with {@link #setVisibility(int)}.
*/
public static final int VISIBLE = 0x00000000;
/**
* This view is invisible, but it still takes up space for layout purposes.
- * Use with {@link #setVisibility}.
+ * Use with {@link #setVisibility(int)}.
*/
public static final int INVISIBLE = 0x00000004;
/**
* This view is invisible, and it doesn't take any space for layout
- * purposes. Use with {@link #setVisibility}.
+ * purposes. Use with {@link #setVisibility(int)}.
*/
public static final int GONE = 0x00000008;
@@ -714,10 +713,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
static final int ENABLED_MASK = 0x00000020;
/**
- * This view won't draw. {@link #onDraw} won't be called and further
- * optimizations
- * will be performed. It is okay to have this flag set and a background.
- * Use with DRAW_MASK when calling setFlags.
+ * This view won't draw. {@link #onDraw(android.graphics.Canvas)} won't be
+ * called and further optimizations will be performed. It is okay to have
+ * this flag set and a background. Use with DRAW_MASK when calling setFlags.
* {@hide}
*/
static final int WILL_NOT_DRAW = 0x00000080;
@@ -951,6 +949,43 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
static final int PARENT_SAVE_DISABLED_MASK = 0x20000000;
/**
+ * Horizontal direction of this view is from Left to Right.
+ * Use with {@link #setHorizontalDirection}.
+ * {@hide}
+ */
+ public static final int HORIZONTAL_DIRECTION_LTR = 0x00000000;
+
+ /**
+ * Horizontal direction of this view is from Right to Left.
+ * Use with {@link #setHorizontalDirection}.
+ * {@hide}
+ */
+ public static final int HORIZONTAL_DIRECTION_RTL = 0x40000000;
+
+ /**
+ * Horizontal direction of this view is inherited from its parent.
+ * Use with {@link #setHorizontalDirection}.
+ * {@hide}
+ */
+ public static final int HORIZONTAL_DIRECTION_INHERIT = 0x80000000;
+
+ /**
+ * Horizontal direction of this view is from deduced from the default language
+ * script for the locale. Use with {@link #setHorizontalDirection}.
+ * {@hide}
+ */
+ public static final int HORIZONTAL_DIRECTION_LOCALE = 0xC0000000;
+
+ /**
+ * Mask for use with setFlags indicating bits used for horizontalDirection.
+ * {@hide}
+ */
+ static final int HORIZONTAL_DIRECTION_MASK = 0xC0000000;
+
+ private static final int[] HORIZONTAL_DIRECTION_FLAGS = { HORIZONTAL_DIRECTION_LTR,
+ HORIZONTAL_DIRECTION_RTL, HORIZONTAL_DIRECTION_INHERIT, HORIZONTAL_DIRECTION_LOCALE};
+
+ /**
* View flag indicating whether {@link #addFocusables(ArrayList, int, int)}
* should add all focusable Views regardless if they are focusable in touch mode.
*/
@@ -963,34 +998,34 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
public static final int FOCUSABLES_TOUCH_MODE = 0x00000001;
/**
- * Use with {@link #focusSearch}. Move focus to the previous selectable
+ * Use with {@link #focusSearch(int)}. Move focus to the previous selectable
* item.
*/
public static final int FOCUS_BACKWARD = 0x00000001;
/**
- * Use with {@link #focusSearch}. Move focus to the next selectable
+ * Use with {@link #focusSearch(int)}. Move focus to the next selectable
* item.
*/
public static final int FOCUS_FORWARD = 0x00000002;
/**
- * Use with {@link #focusSearch}. Move focus to the left.
+ * Use with {@link #focusSearch(int)}. Move focus to the left.
*/
public static final int FOCUS_LEFT = 0x00000011;
/**
- * Use with {@link #focusSearch}. Move focus up.
+ * Use with {@link #focusSearch(int)}. Move focus up.
*/
public static final int FOCUS_UP = 0x00000021;
/**
- * Use with {@link #focusSearch}. Move focus to the right.
+ * Use with {@link #focusSearch(int)}. Move focus to the right.
*/
public static final int FOCUS_RIGHT = 0x00000042;
/**
- * Use with {@link #focusSearch}. Move focus down.
+ * Use with {@link #focusSearch(int)}. Move focus down.
*/
public static final int FOCUS_DOWN = 0x00000082;
@@ -1304,6 +1339,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
static final int VIEW_STATE_PRESSED = 1 << 4;
static final int VIEW_STATE_ACTIVATED = 1 << 5;
static final int VIEW_STATE_ACCELERATED = 1 << 6;
+ static final int VIEW_STATE_HOVERED = 1 << 7;
+ static final int VIEW_STATE_DRAG_CAN_ACCEPT = 1 << 8;
+ static final int VIEW_STATE_DRAG_HOVERED = 1 << 9;
static final int[] VIEW_STATE_IDS = new int[] {
R.attr.state_window_focused, VIEW_STATE_WINDOW_FOCUSED,
@@ -1313,6 +1351,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
R.attr.state_pressed, VIEW_STATE_PRESSED,
R.attr.state_activated, VIEW_STATE_ACTIVATED,
R.attr.state_accelerated, VIEW_STATE_ACCELERATED,
+ R.attr.state_hovered, VIEW_STATE_HOVERED,
+ R.attr.state_drag_can_accept, VIEW_STATE_DRAG_CAN_ACCEPT,
+ R.attr.state_drag_hovered, VIEW_STATE_DRAG_HOVERED,
};
static {
@@ -1623,6 +1664,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
private static final int AWAKEN_SCROLL_BARS_ON_ATTACH = 0x08000000;
/**
+ * Indicates that the view has received HOVER_ENTER. Cleared on HOVER_EXIT.
+ * @hide
+ */
+ private static final int HOVERED = 0x10000000;
+
+ /**
* Indicates that pivotX or pivotY were explicitly set and we should not assume the center
* for transform operations
*
@@ -1643,6 +1690,34 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
*/
static final int INVALIDATED = 0x80000000;
+ /* Masks for mPrivateFlags2 */
+
+ /**
+ * Indicates that this view has reported that it can accept the current drag's content.
+ * Cleared when the drag operation concludes.
+ * @hide
+ */
+ static final int DRAG_CAN_ACCEPT = 0x00000001;
+
+ /**
+ * Indicates that this view is currently directly under the drag location in a
+ * drag-and-drop operation involving content that it can accept. Cleared when
+ * the drag exits the view, or when the drag operation concludes.
+ * @hide
+ */
+ static final int DRAG_HOVERED = 0x00000002;
+
+ /**
+ * Indicates whether the view is drawn in right-to-left direction.
+ *
+ * @hide
+ */
+ static final int RESOLVED_LAYOUT_RTL = 0x00000004;
+
+ /* End of masks for mPrivateFlags2 */
+
+ static final int DRAG_MASK = DRAG_CAN_ACCEPT | DRAG_HOVERED;
+
/**
* Always allow a user to over-scroll this view, provided it is a
* view that can scroll.
@@ -1814,6 +1889,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
@ViewDebug.FlagToString(mask = DIRTY_MASK, equals = DIRTY, name = "DIRTY")
})
int mPrivateFlags;
+ int mPrivateFlags2;
/**
* This view's request for the visibility of the status bar.
@@ -2243,12 +2319,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
private ViewPropertyAnimator mAnimator = null;
/**
- * Cache drag/drop state
- *
- */
- boolean mCanAcceptDrop;
-
- /**
* Flag indicating that a drag can cross window boundaries. When
* {@link #startDrag(ClipData, DragShadowBuilder, Object, int)} is called
* with this flag set, all visible applications will be able to participate
@@ -2356,6 +2426,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
Rect mLocalDirtyRect;
/**
+ * Consistency verifier for debugging purposes.
+ * @hide
+ */
+ protected final InputEventConsistencyVerifier mInputEventConsistencyVerifier =
+ InputEventConsistencyVerifier.isInstrumentationEnabled() ?
+ new InputEventConsistencyVerifier(this, 0) : null;
+
+ /**
* Simple constructor to use when creating a view from code.
*
* @param context The Context the view is running in, through which it can
@@ -2562,6 +2640,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
viewFlagMasks |= VISIBILITY_MASK;
}
break;
+ case com.android.internal.R.styleable.View_horizontalDirection:
+ final int layoutDirection = a.getInt(attr, 0);
+ if (layoutDirection != 0) {
+ viewFlagValues |= HORIZONTAL_DIRECTION_FLAGS[layoutDirection];
+ viewFlagMasks |= HORIZONTAL_DIRECTION_MASK;
+ }
+ break;
case com.android.internal.R.styleable.View_drawingCacheQuality:
final int cacheQuality = a.getInt(attr, 0);
if (cacheQuality != 0) {
@@ -2799,8 +2884,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
/**
* Set the size of the faded edge used to indicate that more content in this
* view is available. Will not change whether the fading edge is enabled; use
- * {@link #setVerticalFadingEdgeEnabled} or {@link #setHorizontalFadingEdgeEnabled}
- * to enable the fading edge for the vertical or horizontal fading edges.
+ * {@link #setVerticalFadingEdgeEnabled(boolean)} or
+ * {@link #setHorizontalFadingEdgeEnabled(boolean)} to enable the fading edge
+ * for the vertical or horizontal fading edges.
*
* @param length The size in pixels of the faded edge used to indicate that more
* content in this view is visible.
@@ -3137,6 +3223,23 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
}
/**
+ * Performs button-related actions during a touch down event.
+ *
+ * @param event The event.
+ * @return True if the down was consumed.
+ *
+ * @hide
+ */
+ protected boolean performButtonActionOnTouchDown(MotionEvent event) {
+ if ((event.getButtonState() & MotionEvent.BUTTON_SECONDARY) != 0) {
+ if (showContextMenu(event.getX(), event.getY(), event.getMetaState())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
* Bring up the context menu for this view.
*
* @return Whether a context menu was displayed.
@@ -3146,6 +3249,20 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
}
/**
+ * Bring up the context menu for this view, referring to the item under the specified point.
+ *
+ * @param x The referenced x coordinate.
+ * @param y The referenced y coordinate.
+ * @param metaState The keyboard modifiers that were pressed.
+ * @return Whether a context menu was displayed.
+ *
+ * @hide
+ */
+ public boolean showContextMenu(float x, float y, int metaState) {
+ return showContextMenu();
+ }
+
+ /**
* Start an action mode.
*
* @param callback Callback that will control the lifecycle of the action mode
@@ -3193,7 +3310,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
}
/**
- * Give this view focus. This will cause {@link #onFocusChanged} to be called.
+ * Give this view focus. This will cause
+ * {@link #onFocusChanged(boolean, int, android.graphics.Rect)} to be called.
*
* Note: this does not check whether this {@link View} should get focus, it just
* gives it focus no matter what. It should only be called internally by framework
@@ -3278,7 +3396,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
/**
* Called when this view wants to give up focus. This will cause
- * {@link #onFocusChanged} to be called.
+ * {@link #onFocusChanged(boolean, int, android.graphics.Rect)} to be called.
*/
public void clearFocus() {
if (DBG) {
@@ -3404,7 +3522,21 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
}
/**
- * {@inheritDoc}
+ * Sends an accessibility event of the given type. If accessiiblity is
+ * not enabled this method has no effect. The default implementation calls
+ * {@link #onInitializeAccessibilityEvent(AccessibilityEvent)} first
+ * to populate information about the event source (this View), then calls
+ * {@link #dispatchPopulateAccessibilityEvent(AccessibilityEvent)} to
+ * populate the text content of the event source including its descendants,
+ * and last calls
+ * {@link ViewParent#requestSendAccessibilityEvent(View, AccessibilityEvent)}
+ * on its parent to resuest sending of the event to interested parties.
+ *
+ * @param eventType The type of the event to send.
+ *
+ * @see #onInitializeAccessibilityEvent(AccessibilityEvent)
+ * @see #dispatchPopulateAccessibilityEvent(AccessibilityEvent)
+ * @see ViewParent#requestSendAccessibilityEvent(View, AccessibilityEvent)
*/
public void sendAccessibilityEvent(int eventType) {
if (AccessibilityManager.getInstance(mContext).isEnabled()) {
@@ -3413,12 +3545,93 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
}
/**
- * {@inheritDoc}
+ * This method behaves exactly as {@link #sendAccessibilityEvent(int)} but
+ * takes as an argument an empty {@link AccessibilityEvent} and does not
+ * perfrom a check whether accessibility is enabled.
+ *
+ * @param event The event to send.
+ *
+ * @see #sendAccessibilityEvent(int)
*/
public void sendAccessibilityEventUnchecked(AccessibilityEvent event) {
if (!isShown()) {
return;
}
+ onInitializeAccessibilityEvent(event);
+ dispatchPopulateAccessibilityEvent(event);
+ // In the beginning we called #isShown(), so we know that getParent() is not null.
+ getParent().requestSendAccessibilityEvent(this, event);
+ }
+
+ /**
+ * Dispatches an {@link AccessibilityEvent} to the {@link View} first and then
+ * to its children for adding their text content to the event. Note that the
+ * event text is populated in a separate dispatch path since we add to the
+ * event not only the text of the source but also the text of all its descendants.
+ * </p>
+ * A typical implementation will call
+ * {@link #onPopulateAccessibilityEvent(AccessibilityEvent)} on the this view
+ * and then call the {@link #dispatchPopulateAccessibilityEvent(AccessibilityEvent)}
+ * on each child. Override this method if custom population of the event text
+ * content is required.
+ *
+ * @param event The event.
+ *
+ * @return True if the event population was completed.
+ */
+ public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
+ onPopulateAccessibilityEvent(event);
+ return false;
+ }
+
+ /**
+ * Called from {@link #dispatchPopulateAccessibilityEvent(AccessibilityEvent)}
+ * giving a chance to this View to populate the accessibility event with its
+ * text content. While the implementation is free to modify other event
+ * attributes this should be performed in
+ * {@link #onInitializeAccessibilityEvent(AccessibilityEvent)}.
+ * <p>
+ * Example: Adding formatted date string to an accessibility event in addition
+ * to the text added by the super implementation.
+ * </p><p><pre><code>
+ * public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
+ * super.onPopulateAccessibilityEvent(event);
+ * final int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_WEEKDAY;
+ * String selectedDateUtterance = DateUtils.formatDateTime(mContext,
+ * mCurrentDate.getTimeInMillis(), flags);
+ * event.getText().add(selectedDateUtterance);
+ * }
+ * </code></pre></p>
+ *
+ * @param event The accessibility event which to populate.
+ *
+ * @see #sendAccessibilityEvent(int)
+ * @see #dispatchPopulateAccessibilityEvent(AccessibilityEvent)
+ */
+ public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
+
+ }
+
+ /**
+ * Initializes an {@link AccessibilityEvent} with information about the
+ * the type of the event and this View which is the event source. In other
+ * words, the source of an accessibility event is the view whose state
+ * change triggered firing the event.
+ * <p>
+ * Example: Setting the password property of an event in addition
+ * to properties set by the super implementation.
+ * </p><p><pre><code>
+ * public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ * super.onInitializeAccessibilityEvent(event);
+ * event.setPassword(true);
+ * }
+ * </code></pre></p>
+ * @param event The event to initialeze.
+ *
+ * @see #sendAccessibilityEvent(int)
+ * @see #dispatchPopulateAccessibilityEvent(AccessibilityEvent)
+ */
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
event.setClassName(getClass().getName());
event.setPackageName(getContext().getPackageName());
event.setEnabled(isEnabled());
@@ -3431,22 +3644,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
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;
}
/**
@@ -3924,11 +4121,47 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
}
/**
+ * Returns the horizontal direction for this view.
+ *
+ * @return One of {@link #HORIZONTAL_DIRECTION_LTR},
+ * {@link #HORIZONTAL_DIRECTION_RTL},
+ * {@link #HORIZONTAL_DIRECTION_INHERIT} or
+ * {@link #HORIZONTAL_DIRECTION_LOCALE}.
+ * @attr ref android.R.styleable#View_horizontalDirection
+ * @hide
+ */
+ @ViewDebug.ExportedProperty(mapping = {
+ @ViewDebug.IntToString(from = HORIZONTAL_DIRECTION_LTR, to = "LTR"),
+ @ViewDebug.IntToString(from = HORIZONTAL_DIRECTION_RTL, to = "RTL"),
+ @ViewDebug.IntToString(from = HORIZONTAL_DIRECTION_INHERIT, to = "INHERIT"),
+ @ViewDebug.IntToString(from = HORIZONTAL_DIRECTION_LOCALE, to = "LOCALE")
+ })
+ public int getHorizontalDirection() {
+ return mViewFlags & HORIZONTAL_DIRECTION_MASK;
+ }
+
+ /**
+ * Set the horizontal direction for this view.
+ *
+ * @param horizontalDirection One of {@link #HORIZONTAL_DIRECTION_LTR},
+ * {@link #HORIZONTAL_DIRECTION_RTL},
+ * {@link #HORIZONTAL_DIRECTION_INHERIT} or
+ * {@link #HORIZONTAL_DIRECTION_LOCALE}.
+ * @attr ref android.R.styleable#View_horizontalDirection
+ * @hide
+ */
+ @RemotableViewMethod
+ public void setHorizontalDirection(int horizontalDirection) {
+ setFlags(horizontalDirection, HORIZONTAL_DIRECTION_MASK);
+ }
+
+ /**
* If this view doesn't do any drawing on its own, set this flag to
* allow further optimizations. By default, this flag is not set on
* View, but could be set on some View subclasses such as ViewGroup.
*
- * Typically, if you override {@link #onDraw} you should clear this flag.
+ * Typically, if you override {@link #onDraw(android.graphics.Canvas)}
+ * you should clear this flag.
*
* @param willNotDraw whether or not this View draw on its own
*/
@@ -4057,7 +4290,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
* {@link #setPressed(boolean)} is explicitly called, only clickable views can enter
* the pressed state.
*
- * @see #setPressed
+ * @see #setPressed(boolean)
* @see #isClickable()
* @see #setClickable(boolean)
*
@@ -4084,7 +4317,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
* Controls whether the saving of this view's state is
* enabled (that is, whether its {@link #onSaveInstanceState} method
* will be called). Note that even if freezing is enabled, the
- * view still must have an id assigned to it (via {@link #setId setId()})
+ * view still must have an id assigned to it (via {@link #setId(int)})
* for its state to be saved. This flag can only disable the
* saving of this view; any child views may still have their state saved.
*
@@ -4355,7 +4588,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
* false), or if it is focusable and it is not focusable in touch mode
* ({@link #isFocusableInTouchMode}) while the device is in touch mode.
*
- * See also {@link #focusSearch}, which is what you call to say that you
+ * See also {@link #focusSearch(int)}, which is what you call to say that you
* have focus, and you want your parent to look for the next one.
*
* This is equivalent to calling {@link #requestFocus(int, Rect)} with arguments
@@ -4376,7 +4609,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
* false), or if it is focusable and it is not focusable in touch mode
* ({@link #isFocusableInTouchMode}) while the device is in touch mode.
*
- * See also {@link #focusSearch}, which is what you call to say that you
+ * See also {@link #focusSearch(int)}, which is what you call to say that you
* have focus, and you want your parent to look for the next one.
*
* This is equivalent to calling {@link #requestFocus(int, Rect)} with
@@ -4406,7 +4639,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
* {@link android.view.ViewGroup#getDescendantFocusability()} equal to
* {@link ViewGroup#FOCUS_BLOCK_DESCENDANTS}.
*
- * See also {@link #focusSearch}, which is what you call to say that you
+ * See also {@link #focusSearch(int)}, which is what you call to say that you
* have focus, and you want your parent to look for the next one.
*
* You may wish to override this method if your custom {@link View} has an internal
@@ -4440,10 +4673,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
return true;
}
- /** Gets the ViewRoot, or null if not attached. */
- /*package*/ ViewRoot getViewRoot() {
+ /** Gets the ViewAncestor, or null if not attached. */
+ /*package*/ ViewAncestor getViewAncestor() {
View root = getRootView();
- return root != null ? (ViewRoot)root.getParent() : null;
+ return root != null ? (ViewAncestor)root.getParent() : null;
}
/**
@@ -4459,7 +4692,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
public final boolean requestFocusFromTouch() {
// Leave touch mode if we need to
if (isInTouchMode()) {
- ViewRoot viewRoot = getViewRoot();
+ ViewAncestor viewRoot = getViewAncestor();
if (viewRoot != null) {
viewRoot.ensureTouchMode(false);
}
@@ -4516,22 +4749,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
}
/**
- * capture information of this view for later analysis: developement only
- * check dynamic switch to make sure we only dump view
- * when ViewDebug.SYSTEM_PROPERTY_CAPTURE_VIEW) is set
- */
- private static void captureViewInfo(String subTag, View v) {
- if (v == null || SystemProperties.getInt(ViewDebug.SYSTEM_PROPERTY_CAPTURE_VIEW, 0) == 0) {
- return;
- }
- ViewDebug.dumpCapturedView(subTag, v);
- }
-
- /**
* Return the global {@link KeyEvent.DispatcherState KeyEvent.DispatcherState}
* for this view's window. Returns null if the view is not currently attached
* to the window. Normally you will not need to use this directly, but
- * just use the standard high-level event callbacks like {@link #onKeyDown}.
+ * just use the standard high-level event callbacks like
+ * {@link #onKeyDown(int, KeyEvent)}.
*/
public KeyEvent.DispatcherState getKeyDispatcherState() {
return mAttachInfo != null ? mAttachInfo.mKeyDispatchState : null;
@@ -4562,21 +4784,26 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
* @return True if the event was handled, false otherwise.
*/
public boolean dispatchKeyEvent(KeyEvent event) {
- // If any attached key listener a first crack at the event.
-
- //noinspection SimplifiableIfStatement,deprecation
- if (android.util.Config.LOGV) {
- captureViewInfo("captureViewKeyEvent", this);
+ if (mInputEventConsistencyVerifier != null) {
+ mInputEventConsistencyVerifier.onKeyEvent(event, 0);
}
+ // Give any attached key listener a first crack at the event.
//noinspection SimplifiableIfStatement
if (mOnKeyListener != null && (mViewFlags & ENABLED_MASK) == ENABLED
&& mOnKeyListener.onKey(this, event.getKeyCode(), event)) {
return true;
}
- return event.dispatch(this, mAttachInfo != null
- ? mAttachInfo.mKeyDispatchState : null, this);
+ if (event.dispatch(this, mAttachInfo != null
+ ? mAttachInfo.mKeyDispatchState : null, this)) {
+ return true;
+ }
+
+ if (mInputEventConsistencyVerifier != null) {
+ mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
+ }
+ return false;
}
/**
@@ -4597,16 +4824,26 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
* @return True if the event was handled by the view, false otherwise.
*/
public boolean dispatchTouchEvent(MotionEvent event) {
- if (!onFilterTouchEventForSecurity(event)) {
- return false;
+ if (mInputEventConsistencyVerifier != null) {
+ mInputEventConsistencyVerifier.onTouchEvent(event, 0);
}
- //noinspection SimplifiableIfStatement
- if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&
- mOnTouchListener.onTouch(this, event)) {
- return true;
+ if (onFilterTouchEventForSecurity(event)) {
+ //noinspection SimplifiableIfStatement
+ if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&
+ mOnTouchListener.onTouch(this, event)) {
+ return true;
+ }
+
+ if (onTouchEvent(event)) {
+ return true;
+ }
+ }
+
+ if (mInputEventConsistencyVerifier != null) {
+ mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
}
- return onTouchEvent(event);
+ return false;
}
/**
@@ -4634,8 +4871,19 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
* @return True if the event was handled by the view, false otherwise.
*/
public boolean dispatchTrackballEvent(MotionEvent event) {
+ if (mInputEventConsistencyVerifier != null) {
+ mInputEventConsistencyVerifier.onTrackballEvent(event, 0);
+ }
+
//Log.i("view", "view=" + this + ", " + event.toString());
- return onTrackballEvent(event);
+ if (onTrackballEvent(event)) {
+ return true;
+ }
+
+ if (mInputEventConsistencyVerifier != null) {
+ mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
+ }
+ return false;
}
/**
@@ -4643,28 +4891,101 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
* <p>
* Generic motion events with source class {@link InputDevice#SOURCE_CLASS_POINTER}
* are delivered to the view under the pointer. All other generic motion events are
- * delivered to the focused view.
+ * delivered to the focused view. Hover events are handled specially and are delivered
+ * to {@link #onHoverEvent(MotionEvent)}.
* </p>
*
* @param event The motion event to be dispatched.
* @return True if the event was handled by the view, false otherwise.
*/
public boolean dispatchGenericMotionEvent(MotionEvent event) {
+ if (mInputEventConsistencyVerifier != null) {
+ mInputEventConsistencyVerifier.onGenericMotionEvent(event, 0);
+ }
+
+ final int source = event.getSource();
+ if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
+ final int action = event.getAction();
+ if (action == MotionEvent.ACTION_HOVER_ENTER
+ || action == MotionEvent.ACTION_HOVER_MOVE
+ || action == MotionEvent.ACTION_HOVER_EXIT) {
+ if (dispatchHoverEvent(event)) {
+ return true;
+ }
+ } else if (dispatchGenericPointerEvent(event)) {
+ return true;
+ }
+ } else if (dispatchGenericFocusedEvent(event)) {
+ return true;
+ }
+
//noinspection SimplifiableIfStatement
if (mOnGenericMotionListener != null && (mViewFlags & ENABLED_MASK) == ENABLED
&& mOnGenericMotionListener.onGenericMotion(this, event)) {
return true;
}
- return onGenericMotionEvent(event);
+ if (onGenericMotionEvent(event)) {
+ return true;
+ }
+
+ if (mInputEventConsistencyVerifier != null) {
+ mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
+ }
+ return false;
+ }
+
+ /**
+ * Dispatch a hover event.
+ * <p>
+ * Do not call this method directly.
+ * Call {@link #dispatchGenericMotionEvent(MotionEvent)} instead.
+ * </p>
+ *
+ * @param event The motion event to be dispatched.
+ * @return True if the event was handled by the view, false otherwise.
+ * @hide
+ */
+ protected boolean dispatchHoverEvent(MotionEvent event) {
+ return onHoverEvent(event);
+ }
+
+ /**
+ * Dispatch a generic motion event to the view under the first pointer.
+ * <p>
+ * Do not call this method directly.
+ * Call {@link #dispatchGenericMotionEvent(MotionEvent)} instead.
+ * </p>
+ *
+ * @param event The motion event to be dispatched.
+ * @return True if the event was handled by the view, false otherwise.
+ * @hide
+ */
+ protected boolean dispatchGenericPointerEvent(MotionEvent event) {
+ return false;
+ }
+
+ /**
+ * Dispatch a generic motion event to the currently focused view.
+ * <p>
+ * Do not call this method directly.
+ * Call {@link #dispatchGenericMotionEvent(MotionEvent)} instead.
+ * </p>
+ *
+ * @param event The motion event to be dispatched.
+ * @return True if the event was handled by the view, false otherwise.
+ * @hide
+ */
+ protected boolean dispatchGenericFocusedEvent(MotionEvent event) {
+ return false;
}
/**
* Dispatch a pointer event.
* <p>
- * Dispatches touch related pointer events to {@link #onTouchEvent} and all
- * other events to {@link #onGenericMotionEvent}. This separation of concerns
- * reinforces the invariant that {@link #onTouchEvent} is really about touches
+ * Dispatches touch related pointer events to {@link #onTouchEvent(MotionEvent)} and all
+ * other events to {@link #onGenericMotionEvent(MotionEvent)}. This separation of concerns
+ * reinforces the invariant that {@link #onTouchEvent(MotionEvent)} is really about touches
* and should not be expected to handle other pointing device features.
* </p>
*
@@ -4789,7 +5110,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
*
* @param visibility The new visibility of the window.
*
- * @see #onWindowVisibilityChanged
+ * @see #onWindowVisibilityChanged(int)
*/
public void dispatchWindowVisibilityChanged(int visibility) {
onWindowVisibilityChanged(visibility);
@@ -4865,7 +5186,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
*
* @param newConfig The new resource configuration.
*
- * @see #onConfigurationChanged
+ * @see #onConfigurationChanged(android.content.res.Configuration)
*/
public void dispatchConfigurationChanged(Configuration newConfig) {
onConfigurationChanged(newConfig);
@@ -4926,7 +5247,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
if (mAttachInfo != null) {
return mAttachInfo.mInTouchMode;
} else {
- return ViewRoot.isInTouchMode();
+ return ViewAncestor.isInTouchMode();
}
}
@@ -4981,9 +5302,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
(mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) &&
(event.getRepeatCount() == 0)) {
setPressed(true);
- if ((mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) {
- postCheckForLongClick(0);
- }
+ checkForLongClick(0);
return true;
}
break;
@@ -5223,15 +5542,80 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
* </code>
*
* @param event The generic motion event being processed.
- *
- * @return Return true if you have consumed the event, false if you haven't.
- * The default implementation always returns false.
+ * @return True if the event was handled, false otherwise.
*/
public boolean onGenericMotionEvent(MotionEvent event) {
return false;
}
/**
+ * Implement this method to handle hover events.
+ * <p>
+ * Hover events are pointer events with action {@link MotionEvent#ACTION_HOVER_ENTER},
+ * {@link MotionEvent#ACTION_HOVER_MOVE}, or {@link MotionEvent#ACTION_HOVER_EXIT}.
+ * </p><p>
+ * The view receives hover enter as the pointer enters the bounds of the view and hover
+ * exit as the pointer exits the bound of the view or just before the pointer goes down
+ * (which implies that {@link #onTouchEvent(MotionEvent)} will be called soon).
+ * </p><p>
+ * If the view would like to handle the hover event itself and prevent its children
+ * from receiving hover, it should return true from this method. If this method returns
+ * true and a child has already received a hover enter event, the child will
+ * automatically receive a hover exit event.
+ * </p><p>
+ * The default implementation sets the hovered state of the view if the view is
+ * clickable.
+ * </p>
+ *
+ * @param event The motion event that describes the hover.
+ * @return True if this view handled the hover event and does not want its children
+ * to receive the hover event.
+ */
+ public boolean onHoverEvent(MotionEvent event) {
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_HOVER_ENTER:
+ setHovered(true);
+ break;
+
+ case MotionEvent.ACTION_HOVER_EXIT:
+ setHovered(false);
+ break;
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns true if the view is currently hovered.
+ *
+ * @return True if the view is currently hovered.
+ */
+ public boolean isHovered() {
+ return (mPrivateFlags & HOVERED) != 0;
+ }
+
+ /**
+ * Sets whether the view is currently hovered.
+ *
+ * @param hovered True if the view is hovered.
+ */
+ public void setHovered(boolean hovered) {
+ if (hovered) {
+ if ((mPrivateFlags & HOVERED) == 0) {
+ mPrivateFlags |= HOVERED;
+ refreshDrawableState();
+ sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_HOVER_ENTER);
+ }
+ } else {
+ if ((mPrivateFlags & HOVERED) != 0) {
+ mPrivateFlags &= ~HOVERED;
+ refreshDrawableState();
+ sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_HOVER_EXIT);
+ }
+ }
+ }
+
+ /**
* Implement this method to handle touch screen motion events.
*
* @param event The motion event.
@@ -5241,6 +5625,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
final int viewFlags = mViewFlags;
if ((viewFlags & ENABLED_MASK) == DISABLED) {
+ if (event.getAction() == MotionEvent.ACTION_UP && (mPrivateFlags & PRESSED) != 0) {
+ mPrivateFlags &= ~PRESSED;
+ refreshDrawableState();
+ }
// A disabled view that is clickable still consumes the touch
// events, it just doesn't respond to them.
return (((viewFlags & CLICKABLE) == CLICKABLE ||
@@ -5309,12 +5697,37 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
break;
case MotionEvent.ACTION_DOWN:
- if (mPendingCheckForTap == null) {
- mPendingCheckForTap = new CheckForTap();
- }
- mPrivateFlags |= PREPRESSED;
mHasPerformedLongPress = false;
- postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
+
+ if (performButtonActionOnTouchDown(event)) {
+ break;
+ }
+
+ // Walk up the hierarchy to determine if we're inside a scrolling container.
+ boolean isInScrollingContainer = false;
+ ViewParent p = getParent();
+ while (p != null && p instanceof ViewGroup) {
+ if (((ViewGroup) p).shouldDelayChildPressedState()) {
+ isInScrollingContainer = true;
+ break;
+ }
+ p = p.getParent();
+ }
+
+ // For views inside a scrolling container, delay the pressed feedback for
+ // a short period in case this is a scroll.
+ if (isInScrollingContainer) {
+ mPrivateFlags |= PREPRESSED;
+ if (mPendingCheckForTap == null) {
+ mPendingCheckForTap = new CheckForTap();
+ }
+ postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
+ } else {
+ // Not inside a scrolling container, so show the feedback right away
+ mPrivateFlags |= PRESSED;
+ refreshDrawableState();
+ checkForLongClick(0);
+ }
break;
case MotionEvent.ACTION_CANCEL:
@@ -5544,6 +5957,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
mParent.recomputeViewAttributes(this);
}
}
+
+ if ((changed & HORIZONTAL_DIRECTION_MASK) != 0) {
+ requestLayout();
+ }
}
/**
@@ -5700,7 +6117,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
/**
* Return the full width measurement information for this view as computed
- * by the most recent call to {@link #measure}. This result is a bit mask
+ * by the most recent call to {@link #measure(int, int)}. This result is a bit mask
* as defined by {@link #MEASURED_SIZE_MASK} and {@link #MEASURED_STATE_TOO_SMALL}.
* This should be used during measurement and layout calculations only. Use
* {@link #getWidth()} to see how wide a view is after layout.
@@ -5724,7 +6141,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
/**
* Return the full height measurement information for this view as computed
- * by the most recent call to {@link #measure}. This result is a bit mask
+ * by the most recent call to {@link #measure(int, int)}. This result is a bit mask
* as defined by {@link #MEASURED_SIZE_MASK} and {@link #MEASURED_STATE_TOO_SMALL}.
* This should be used during measurement and layout calculations only. Use
* {@link #getHeight()} to see how wide a view is after layout.
@@ -6693,9 +7110,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
* When a view has focus and the user navigates away from it, the next view is searched for
* starting from the rectangle filled in by this method.
*
- * By default, the rectange is the {@link #getDrawingRect})of the view. However, if your
- * view maintains some idea of internal selection, such as a cursor, or a selected row
- * or column, you should override this method and fill in a more specific rectangle.
+ * By default, the rectange is the {@link #getDrawingRect(android.graphics.Rect)})
+ * of the view. However, if your view maintains some idea of internal selection,
+ * such as a cursor, or a selected row or column, you should override this method and
+ * fill in a more specific rectangle.
*
* @param r The rectangle to fill in, in this view's coordinates.
*/
@@ -7062,9 +7480,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
/**
* Mark the the area defined by dirty as needing to be drawn. If the view is
- * visible, {@link #onDraw} will be called at some point in the future.
- * This must be called from a UI thread. To call from a non-UI thread, call
- * {@link #postInvalidate()}.
+ * visible, {@link #onDraw(android.graphics.Canvas)} will be called at some point
+ * in the future. This must be called from a UI thread. To call from a non-UI
+ * thread, call {@link #postInvalidate()}.
*
* WARNING: This method is destructive to dirty.
* @param dirty the rectangle representing the bounds of the dirty region
@@ -7085,7 +7503,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
if (!HardwareRenderer.RENDER_DIRTY_REGIONS) {
if (p != null && ai != null && ai.mHardwareAccelerated) {
// fast-track for GL-enabled applications; just invalidate the whole hierarchy
- // with a null dirty rect, which tells the ViewRoot to redraw everything
+ // with a null dirty rect, which tells the ViewAncestor to redraw everything
p.invalidateChild(this, null);
return;
}
@@ -7104,9 +7522,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
/**
* Mark the the area defined by the rect (l,t,r,b) as needing to be drawn.
* The coordinates of the dirty rect are relative to the view.
- * If the view is visible, {@link #onDraw} will be called at some point
- * in the future. This must be called from a UI thread. To call
- * from a non-UI thread, call {@link #postInvalidate()}.
+ * If the view is visible, {@link #onDraw(android.graphics.Canvas)}
+ * will be called at some point in the future. This must be called from
+ * a UI thread. To call from a non-UI thread, call {@link #postInvalidate()}.
* @param l the left position of the dirty region
* @param t the top position of the dirty region
* @param r the right position of the dirty region
@@ -7128,7 +7546,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
if (!HardwareRenderer.RENDER_DIRTY_REGIONS) {
if (p != null && ai != null && ai.mHardwareAccelerated) {
// fast-track for GL-enabled applications; just invalidate the whole hierarchy
- // with a null dirty rect, which tells the ViewRoot to redraw everything
+ // with a null dirty rect, which tells the ViewAncestor to redraw everything
p.invalidateChild(this, null);
return;
}
@@ -7144,9 +7562,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
}
/**
- * Invalidate the whole view. If the view is visible, {@link #onDraw} will
- * be called at some point in the future. This must be called from a
- * UI thread. To call from a non-UI thread, call {@link #postInvalidate()}.
+ * Invalidate the whole view. If the view is visible,
+ * {@link #onDraw(android.graphics.Canvas)} will be called at some point in
+ * the future. This must be called from a UI thread. To call from a non-UI thread,
+ * call {@link #postInvalidate()}.
*/
public void invalidate() {
invalidate(true);
@@ -7183,7 +7602,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
if (!HardwareRenderer.RENDER_DIRTY_REGIONS) {
if (p != null && ai != null && ai.mHardwareAccelerated) {
// fast-track for GL-enabled applications; just invalidate the whole hierarchy
- // with a null dirty rect, which tells the ViewRoot to redraw everything
+ // with a null dirty rect, which tells the ViewAncestor to redraw everything
p.invalidateChild(this, null);
return;
}
@@ -7212,8 +7631,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
mPrivateFlags &= ~DRAWN;
mPrivateFlags |= INVALIDATED;
mPrivateFlags &= ~DRAWING_CACHE_VALID;
- if (mParent != null && mAttachInfo != null && mAttachInfo.mHardwareAccelerated) {
- mParent.invalidateChild(this, null);
+ if (mParent != null && mAttachInfo != null) {
+ if (mAttachInfo.mHardwareAccelerated) {
+ mParent.invalidateChild(this, null);
+ } else {
+ final Rect r = mAttachInfo.mTmpInvalRect;
+ r.set(0, 0, mRight - mLeft, mBottom - mTop);
+ // Don't call invalidate -- we don't want to internally scroll
+ // our own bounds
+ mParent.invalidateChild(this, r);
+ }
}
}
}
@@ -7319,11 +7746,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
*/
public boolean post(Runnable action) {
Handler handler;
- if (mAttachInfo != null) {
- handler = mAttachInfo.mHandler;
+ AttachInfo attachInfo = mAttachInfo;
+ if (attachInfo != null) {
+ handler = attachInfo.mHandler;
} else {
// Assume that post will succeed later
- ViewRoot.getRunQueue().post(action);
+ ViewAncestor.getRunQueue().post(action);
return true;
}
@@ -7348,11 +7776,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
*/
public boolean postDelayed(Runnable action, long delayMillis) {
Handler handler;
- if (mAttachInfo != null) {
- handler = mAttachInfo.mHandler;
+ AttachInfo attachInfo = mAttachInfo;
+ if (attachInfo != null) {
+ handler = attachInfo.mHandler;
} else {
// Assume that post will succeed later
- ViewRoot.getRunQueue().postDelayed(action, delayMillis);
+ ViewAncestor.getRunQueue().postDelayed(action, delayMillis);
return true;
}
@@ -7371,11 +7800,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
*/
public boolean removeCallbacks(Runnable action) {
Handler handler;
- if (mAttachInfo != null) {
- handler = mAttachInfo.mHandler;
+ AttachInfo attachInfo = mAttachInfo;
+ if (attachInfo != null) {
+ handler = attachInfo.mHandler;
} else {
// Assume that post will succeed later
- ViewRoot.getRunQueue().removeCallbacks(action);
+ ViewAncestor.getRunQueue().removeCallbacks(action);
return true;
}
@@ -7419,11 +7849,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
public void postInvalidateDelayed(long delayMilliseconds) {
// We try only with the AttachInfo because there's no point in invalidating
// if we are not attached to our window
- if (mAttachInfo != null) {
+ AttachInfo attachInfo = mAttachInfo;
+ if (attachInfo != null) {
Message msg = Message.obtain();
msg.what = AttachInfo.INVALIDATE_MSG;
msg.obj = this;
- mAttachInfo.mHandler.sendMessageDelayed(msg, delayMilliseconds);
+ attachInfo.mHandler.sendMessageDelayed(msg, delayMilliseconds);
}
}
@@ -7443,7 +7874,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
// We try only with the AttachInfo because there's no point in invalidating
// if we are not attached to our window
- if (mAttachInfo != null) {
+ AttachInfo attachInfo = mAttachInfo;
+ if (attachInfo != null) {
final AttachInfo.InvalidateInfo info = AttachInfo.InvalidateInfo.acquire();
info.target = this;
info.left = left;
@@ -7454,7 +7886,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
final Message msg = Message.obtain();
msg.what = AttachInfo.INVALIDATE_RECT_MSG;
msg.obj = info;
- mAttachInfo.mHandler.sendMessageDelayed(msg, delayMilliseconds);
+ attachInfo.mHandler.sendMessageDelayed(msg, delayMilliseconds);
}
}
@@ -8046,9 +8478,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
/**
* This is called when the view is attached to a window. At this point it
* has a Surface and will start drawing. Note that this function is
- * guaranteed to be called before {@link #onDraw}, however it may be called
- * any time before the first onDraw -- including before or after
- * {@link #onMeasure}.
+ * guaranteed to be called before {@link #onDraw(android.graphics.Canvas)},
+ * however it may be called any time before the first onDraw -- including
+ * before or after {@link #onMeasure(int, int)}.
*
* @see #onDetachedFromWindow()
*/
@@ -8061,6 +8493,23 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
mPrivateFlags &= ~AWAKEN_SCROLL_BARS_ON_ATTACH;
}
jumpDrawablesToCurrentState();
+
+ // We are supposing here that the parent directionality will be resolved before its children
+ // View horizontalDirection public attribute resolution to an internal var.
+ // Resolving the layout direction. LTR is set initially.
+ mPrivateFlags2 &= ~RESOLVED_LAYOUT_RTL;
+ switch (getHorizontalDirection()) {
+ case HORIZONTAL_DIRECTION_INHERIT:
+ // If this is root view, no need to look at parent's layout dir.
+ if (mParent != null && mParent instanceof ViewGroup &&
+ ((ViewGroup) mParent).isLayoutRtl()) {
+ mPrivateFlags2 |= RESOLVED_LAYOUT_RTL;
+ }
+ break;
+ case HORIZONTAL_DIRECTION_RTL:
+ mPrivateFlags2 |= RESOLVED_LAYOUT_RTL;
+ break;
+ }
}
/**
@@ -8221,24 +8670,24 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
*
* @param container The SparseArray in which to save the view's state.
*
- * @see #restoreHierarchyState
- * @see #dispatchSaveInstanceState
- * @see #onSaveInstanceState
+ * @see #restoreHierarchyState(android.util.SparseArray)
+ * @see #dispatchSaveInstanceState(android.util.SparseArray)
+ * @see #onSaveInstanceState()
*/
public void saveHierarchyState(SparseArray<Parcelable> container) {
dispatchSaveInstanceState(container);
}
/**
- * Called by {@link #saveHierarchyState} to store the state for this view and its children.
- * May be overridden to modify how freezing happens to a view's children; for example, some
- * views may want to not store state for their children.
+ * Called by {@link #saveHierarchyState(android.util.SparseArray)} to store the state for
+ * this view and its children. May be overridden to modify how freezing happens to a
+ * view's children; for example, some views may want to not store state for their children.
*
* @param container The SparseArray in which to save the view's state.
*
- * @see #dispatchRestoreInstanceState
- * @see #saveHierarchyState
- * @see #onSaveInstanceState
+ * @see #dispatchRestoreInstanceState(android.util.SparseArray)
+ * @see #saveHierarchyState(android.util.SparseArray)
+ * @see #onSaveInstanceState()
*/
protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
if (mID != NO_ID && (mViewFlags & SAVE_DISABLED_MASK) == 0) {
@@ -8272,9 +8721,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
* @return Returns a Parcelable object containing the view's current dynamic
* state, or null if there is nothing interesting to save. The
* default implementation returns null.
- * @see #onRestoreInstanceState
- * @see #saveHierarchyState
- * @see #dispatchSaveInstanceState
+ * @see #onRestoreInstanceState(android.os.Parcelable)
+ * @see #saveHierarchyState(android.util.SparseArray)
+ * @see #dispatchSaveInstanceState(android.util.SparseArray)
* @see #setSaveEnabled(boolean)
*/
protected Parcelable onSaveInstanceState() {
@@ -8287,24 +8736,25 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
*
* @param container The SparseArray which holds previously frozen states.
*
- * @see #saveHierarchyState
- * @see #dispatchRestoreInstanceState
- * @see #onRestoreInstanceState
+ * @see #saveHierarchyState(android.util.SparseArray)
+ * @see #dispatchRestoreInstanceState(android.util.SparseArray)
+ * @see #onRestoreInstanceState(android.os.Parcelable)
*/
public void restoreHierarchyState(SparseArray<Parcelable> container) {
dispatchRestoreInstanceState(container);
}
/**
- * Called by {@link #restoreHierarchyState} to retrieve the state for this view and its
- * children. May be overridden to modify how restoreing happens to a view's children; for
- * example, some views may want to not store state for their children.
+ * Called by {@link #restoreHierarchyState(android.util.SparseArray)} to retrieve the
+ * state for this view and its children. May be overridden to modify how restoring
+ * happens to a view's children; for example, some views may want to not store state
+ * for their children.
*
* @param container The SparseArray which holds previously saved state.
*
- * @see #dispatchSaveInstanceState
- * @see #restoreHierarchyState
- * @see #onRestoreInstanceState
+ * @see #dispatchSaveInstanceState(android.util.SparseArray)
+ * @see #restoreHierarchyState(android.util.SparseArray)
+ * @see #onRestoreInstanceState(android.os.Parcelable)
*/
protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
if (mID != NO_ID) {
@@ -8330,9 +8780,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
* @param state The frozen state that had previously been returned by
* {@link #onSaveInstanceState}.
*
- * @see #onSaveInstanceState
- * @see #restoreHierarchyState
- * @see #dispatchRestoreInstanceState
+ * @see #onSaveInstanceState()
+ * @see #restoreHierarchyState(android.util.SparseArray)
+ * @see #dispatchRestoreInstanceState(android.util.SparseArray)
*/
protected void onRestoreInstanceState(Parcelable state) {
mPrivateFlags |= SAVE_STATE_CALLED;
@@ -8448,6 +8898,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
// Destroy any previous software drawing cache if needed
switch (mLayerType) {
+ case LAYER_TYPE_HARDWARE:
+ if (mHardwareLayer != null) {
+ mHardwareLayer.destroy();
+ mHardwareLayer = null;
+ }
+ // fall through - unaccelerated views may use software layer mechanism instead
case LAYER_TYPE_SOFTWARE:
if (mDrawingCache != null) {
mDrawingCache.recycle();
@@ -8459,12 +8915,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
mUnscaledDrawingCache = null;
}
break;
- case LAYER_TYPE_HARDWARE:
- if (mHardwareLayer != null) {
- mHardwareLayer.destroy();
- mHardwareLayer = null;
- }
- break;
default:
break;
}
@@ -9218,9 +9668,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
/**
* Manually render this view (and all of its children) to the given Canvas.
* The view must have already done a full layout before this function is
- * called. When implementing a view, implement {@link #onDraw} instead of
- * overriding this method. If you do need to override this method, call
- * the superclass version.
+ * called. When implementing a view, implement
+ * {@link #onDraw(android.graphics.Canvas)} instead of overriding this method.
+ * If you do need to override this method, call the superclass version.
*
* @param canvas The Canvas to which the View is rendered.
*/
@@ -9433,8 +9883,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
* optimize the drawing of the fading edges. If you do return a non-zero color, the alpha
* should be set to 0xFF.
*
- * @see #setVerticalFadingEdgeEnabled
- * @see #setHorizontalFadingEdgeEnabled
+ * @see #setVerticalFadingEdgeEnabled(boolean)
+ * @see #setHorizontalFadingEdgeEnabled(boolean)
*
* @return The known solid color background for this view, or 0 if the color may vary
*/
@@ -9547,6 +9997,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
}
/**
+ * <p>Indicates whether or not this view's layout is right-to-left. This is resolved from
+ * layout attribute and/or the inherited value from the parent.</p>
+ *
+ * @return true if the layout is right-to-left.
+ */
+ public boolean isLayoutRtl() {
+ return (mPrivateFlags2 & RESOLVED_LAYOUT_RTL) == RESOLVED_LAYOUT_RTL;
+ }
+
+ /**
* Assign a size and position to a view and all of its
* descendants
*
@@ -9774,8 +10234,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
* @return boolean If true than the Drawable is being displayed in the
* view; else false and it is not allowed to animate.
*
- * @see #unscheduleDrawable
- * @see #drawableStateChanged
+ * @see #unscheduleDrawable(android.graphics.drawable.Drawable)
+ * @see #drawableStateChanged()
*/
protected boolean verifyDrawable(Drawable who) {
return who == mBGDrawable;
@@ -9788,7 +10248,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
* <p>Be sure to call through to the superclass when overriding this
* function.
*
- * @see Drawable#setState
+ * @see Drawable#setState(int[])
*/
protected void drawableStateChanged() {
Drawable d = mBGDrawable;
@@ -9821,9 +10281,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
*
* @return The current drawable state
*
- * @see Drawable#setState
- * @see #drawableStateChanged
- * @see #onCreateDrawableState
+ * @see Drawable#setState(int[])
+ * @see #drawableStateChanged()
+ * @see #onCreateDrawableState(int)
*/
public final int[] getDrawableState() {
if ((mDrawableState != null) && ((mPrivateFlags & DRAWABLE_STATE_DIRTY) == 0)) {
@@ -9848,7 +10308,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
* @return Returns an array holding the current {@link Drawable} state of
* the view.
*
- * @see #mergeDrawableStates
+ * @see #mergeDrawableStates(int[], int[])
*/
protected int[] onCreateDrawableState(int extraSpace) {
if ((mViewFlags & DUPLICATE_PARENT_STATE) == DUPLICATE_PARENT_STATE &&
@@ -9867,12 +10327,18 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
if ((privateFlags & SELECTED) != 0) viewStateIndex |= VIEW_STATE_SELECTED;
if (hasWindowFocus()) viewStateIndex |= VIEW_STATE_WINDOW_FOCUSED;
if ((privateFlags & ACTIVATED) != 0) viewStateIndex |= VIEW_STATE_ACTIVATED;
- if (mAttachInfo != null && mAttachInfo.mHardwareAccelerationRequested) {
+ if (mAttachInfo != null && mAttachInfo.mHardwareAccelerationRequested &&
+ HardwareRenderer.isAvailable()) {
// This is set if HW acceleration is requested, even if the current
// process doesn't allow it. This is just to allow app preview
// windows to better match their app.
viewStateIndex |= VIEW_STATE_ACCELERATED;
}
+ if ((privateFlags & HOVERED) != 0) viewStateIndex |= VIEW_STATE_HOVERED;
+
+ final int privateFlags2 = mPrivateFlags2;
+ if ((privateFlags2 & DRAG_CAN_ACCEPT) != 0) viewStateIndex |= VIEW_STATE_DRAG_CAN_ACCEPT;
+ if ((privateFlags2 & DRAG_HOVERED) != 0) viewStateIndex |= VIEW_STATE_DRAG_HOVERED;
drawableState = VIEW_STATE_SETS[viewStateIndex];
@@ -9906,10 +10372,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
/**
* Merge your own state values in <var>additionalState</var> into the base
* state values <var>baseState</var> that were returned by
- * {@link #onCreateDrawableState}.
+ * {@link #onCreateDrawableState(int)}.
*
* @param baseState The base state values returned by
- * {@link #onCreateDrawableState}, which will be modified to also hold your
+ * {@link #onCreateDrawableState(int)}, which will be modified to also hold your
* own additional state values.
*
* @param additionalState The additional state values you would like
@@ -9918,7 +10384,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
* @return As a convenience, the <var>baseState</var> array you originally
* passed into the function is returned.
*
- * @see #onCreateDrawableState
+ * @see #onCreateDrawableState(int)
*/
protected static int[] mergeDrawableStates(int[] baseState, int[] additionalState) {
final int N = baseState.length;
@@ -10348,9 +10814,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
viewParent = view.mParent;
}
- if (viewParent instanceof ViewRoot) {
+ if (viewParent instanceof ViewAncestor) {
// *cough*
- final ViewRoot vr = (ViewRoot)viewParent;
+ final ViewAncestor vr = (ViewAncestor)viewParent;
location[1] -= vr.mCurScrollY;
}
}
@@ -10437,8 +10903,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
* number.
*
* @see #NO_ID
- * @see #getId
- * @see #findViewById
+ * @see #getId()
+ * @see #findViewById(int)
*
* @param id a number used to identify the view
*
@@ -10477,8 +10943,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
* @return a positive integer used to identify the view or {@link #NO_ID}
* if the view has no ID
*
- * @see #setId
- * @see #findViewById
+ * @see #setId(int)
+ * @see #findViewById(int)
* @attr ref android.R.styleable#View_id
*/
@ViewDebug.CapturedViewProperty
@@ -11160,7 +11626,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
* therefore all View objects remove themselves from the global transparent
* region (passed as a parameter to this function).
*
- * @param region The transparent region for this ViewRoot (window).
+ * @param region The transparent region for this ViewAncestor (window).
*
* @return Returns true if the effective visibility of the view at this
* point is opaque, regardless of the transparent region; returns false
@@ -11341,6 +11807,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
*
* @return The View object associate with this builder object.
*/
+ @SuppressWarnings({"JavadocReference"})
final public View getView() {
return mView.get();
}
@@ -11466,7 +11933,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
surface.unlockCanvasAndPost(canvas);
}
- final ViewRoot root = getViewRoot();
+ final ViewAncestor root = getViewAncestor();
// Cache the local state object for delivery with DragEvents
root.setLocalDragState(myLocalState);
@@ -11540,6 +12007,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
return onDragEvent(event);
}
+ boolean canAcceptDrag() {
+ return (mPrivateFlags2 & DRAG_CAN_ACCEPT) != 0;
+ }
+
/**
* This needs to be a better API (NOT ON VIEW) before it is exposed. If
* it is ever exposed at all.
@@ -11550,7 +12021,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
/**
* Given a Drawable whose bounds have been set to draw into this view,
- * update a Region being computed for {@link #gatherTransparentRegion} so
+ * update a Region being computed for
+ * {@link #gatherTransparentRegion(android.graphics.Region)} so
* that any non-transparent parts of the Drawable are removed from the
* given transparent region.
*
@@ -11597,15 +12069,17 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
}
}
- private void postCheckForLongClick(int delayOffset) {
- mHasPerformedLongPress = false;
+ private void checkForLongClick(int delayOffset) {
+ if ((mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) {
+ mHasPerformedLongPress = false;
- if (mPendingCheckForLongPress == null) {
- mPendingCheckForLongPress = new CheckForLongPress();
+ if (mPendingCheckForLongPress == null) {
+ mPendingCheckForLongPress = new CheckForLongPress();
+ }
+ mPendingCheckForLongPress.rememberWindowAttachCount();
+ postDelayed(mPendingCheckForLongPress,
+ ViewConfiguration.getLongPressTimeout() - delayOffset);
}
- mPendingCheckForLongPress.rememberWindowAttachCount();
- postDelayed(mPendingCheckForLongPress,
- ViewConfiguration.getLongPressTimeout() - delayOffset);
}
/**
@@ -11917,9 +12391,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
mPrivateFlags &= ~PREPRESSED;
mPrivateFlags |= PRESSED;
refreshDrawableState();
- if ((mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) {
- postCheckForLongClick(ViewConfiguration.getTapTimeout());
- }
+ checkForLongClick(ViewConfiguration.getTapTimeout());
}
}
@@ -12084,12 +12556,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
* Interface definition for a callback to be invoked when the status bar changes
* visibility.
*
- * @see #setOnSystemUiVisibilityChangeListener
+ * @see View#setOnSystemUiVisibilityChangeListener(android.view.View.OnSystemUiVisibilityChangeListener)
*/
public interface OnSystemUiVisibilityChangeListener {
/**
* Called when the status bar changes visibility because of a call to
- * {@link #setSystemUiVisibility}.
+ * {@link View#setSystemUiVisibility(int)}.
*
* @param visibility {@link #STATUS_BAR_VISIBLE} or {@link #STATUS_BAR_HIDDEN}.
*/
@@ -12248,7 +12720,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
boolean mScalingRequired;
/**
- * If set, ViewRoot doesn't use its lame animation for when the window resizes.
+ * If set, ViewAncestor doesn't use its lame animation for when the window resizes.
*/
boolean mTurnOffWindowResizeAnim;
@@ -12327,7 +12799,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
boolean mInTouchMode;
/**
- * Indicates that ViewRoot should trigger a global layout change
+ * Indicates that ViewAncestor should trigger a global layout change
* the next time it performs a traversal
*/
boolean mRecomputeGlobalAttributes;
@@ -12389,7 +12861,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
Canvas mCanvas;
/**
- * A Handler supplied by a view's {@link android.view.ViewRoot}. This
+ * A Handler supplied by a view's {@link android.view.ViewAncestor}. This
* handler can be used to pump events in the UI events queue.
*/
final Handler mHandler;
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewAncestor.java
index 2ba28c6..8085ea8 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewAncestor.java
@@ -49,7 +49,6 @@ import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.util.AndroidRuntimeException;
-import android.util.Config;
import android.util.DisplayMetrics;
import android.util.EventLog;
import android.util.Log;
@@ -83,9 +82,9 @@ import java.util.ArrayList;
* {@hide}
*/
@SuppressWarnings({"EmptyCatchBlock", "PointlessBooleanExpression"})
-public final class ViewRoot extends Handler implements ViewParent,
+public final class ViewAncestor extends Handler implements ViewParent,
View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks {
- private static final String TAG = "ViewRoot";
+ private static final String TAG = "ViewAncestor";
private static final boolean DBG = false;
private static final boolean SHOW_FPS = false;
private static final boolean LOCAL_LOGV = false;
@@ -186,6 +185,8 @@ public final class ViewRoot extends Handler implements ViewParent,
final Rect mVisRect; // used to retrieve visible rect of focused view.
boolean mTraversalScheduled;
+ long mLastTraversalFinishedTimeNanos;
+ long mLastDrawDurationNanos;
boolean mWillDrawSoon;
boolean mLayoutRequested;
boolean mFirst;
@@ -250,6 +251,13 @@ public final class ViewRoot extends Handler implements ViewParent,
private final int mDensity;
+ /**
+ * Consistency verifier for debugging purposes.
+ */
+ protected final InputEventConsistencyVerifier mInputEventConsistencyVerifier =
+ InputEventConsistencyVerifier.isInstrumentationEnabled() ?
+ new InputEventConsistencyVerifier(this, 0) : null;
+
public static IWindowSession getWindowSession(Looper mainLooper) {
synchronized (mStaticInit) {
if (!mInitialized) {
@@ -265,7 +273,7 @@ public final class ViewRoot extends Handler implements ViewParent,
}
}
- public ViewRoot(Context context) {
+ public ViewAncestor(Context context) {
super();
if (MEASURE_LATENCY) {
@@ -500,9 +508,14 @@ public final class ViewRoot extends Handler implements ViewParent,
final boolean hardwareAccelerated =
(attrs.flags & WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) != 0;
- if (attrs != null && hardwareAccelerated) {
+ if (hardwareAccelerated) {
+ if (!HardwareRenderer.isAvailable()) {
+ mAttachInfo.mHardwareAccelerationRequested = true;
+ return;
+ }
+
// Only enable hardware acceleration if we are not in the system process
- // The window manager creates ViewRoots to display animated preview windows
+ // The window manager creates ViewAncestors to display animated preview windows
// of launching apps and we don't want those to be hardware accelerated
final boolean systemHwAccelerated =
@@ -523,8 +536,6 @@ public final class ViewRoot extends Handler implements ViewParent,
mAttachInfo.mHardwareRenderer = HardwareRenderer.createGlRenderer(2, translucent);
mAttachInfo.mHardwareAccelerated = mAttachInfo.mHardwareAccelerationRequested
= mAttachInfo.mHardwareRenderer != null;
- } else if (HardwareRenderer.isAvailable()) {
- mAttachInfo.mHardwareAccelerationRequested = true;
}
}
}
@@ -660,6 +671,14 @@ public final class ViewRoot extends Handler implements ViewParent,
public void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
+
+ if (ViewDebug.DEBUG_LATENCY && mLastTraversalFinishedTimeNanos != 0) {
+ final long now = System.nanoTime();
+ Log.d(TAG, "Latency: Scheduled traversal, it has been "
+ + ((now - mLastTraversalFinishedTimeNanos) * 0.000001f)
+ + "ms since the last traversal finished.");
+ }
+
sendEmptyMessage(DO_TRAVERSAL);
}
}
@@ -1252,7 +1271,7 @@ public final class ViewRoot extends Handler implements ViewParent,
}
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
- if (Config.DEBUG && ViewDebug.consistencyCheckEnabled) {
+ if (false && ViewDebug.consistencyCheckEnabled) {
if (!host.dispatchConsistencyCheck(ViewDebug.CONSISTENCY_LAYOUT)) {
throw new IllegalStateException("The view hierarchy is an inconsistent state,"
+ "please refer to the logs with the tag "
@@ -1378,8 +1397,18 @@ public final class ViewRoot extends Handler implements ViewParent,
if (!cancelDraw && !newSurface) {
mFullRedrawNeeded = false;
+
+ final long drawStartTime;
+ if (ViewDebug.DEBUG_LATENCY) {
+ drawStartTime = System.nanoTime();
+ }
+
draw(fullRedrawNeeded);
+ if (ViewDebug.DEBUG_LATENCY) {
+ mLastDrawDurationNanos = System.nanoTime() - drawStartTime;
+ }
+
if ((relayoutResult&WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0
|| mReportNextDraw) {
if (LOCAL_LOGV) {
@@ -1478,6 +1507,20 @@ public final class ViewRoot extends Handler implements ViewParent,
}
}
+ /**
+ * @hide
+ */
+ void outputDisplayList(View view) {
+ if (mAttachInfo != null && mAttachInfo.mHardwareCanvas != null) {
+
+ HardwareCanvas canvas = (HardwareCanvas) mAttachInfo.mHardwareCanvas;
+ DisplayList displayList = view.getDisplayList();
+ if (displayList != null) {
+ canvas.outputDisplayList(displayList);
+ }
+ }
+ }
+
private void draw(boolean fullRedrawNeeded) {
Surface surface = mSurface;
if (surface == null || !surface.isValid()) {
@@ -1590,8 +1633,20 @@ public final class ViewRoot extends Handler implements ViewParent,
int right = dirty.right;
int bottom = dirty.bottom;
+ final long lockCanvasStartTime;
+ if (ViewDebug.DEBUG_LATENCY) {
+ lockCanvasStartTime = System.nanoTime();
+ }
+
canvas = surface.lockCanvas(dirty);
+ if (ViewDebug.DEBUG_LATENCY) {
+ long now = System.nanoTime();
+ Log.d(TAG, "Latency: Spent "
+ + ((now - lockCanvasStartTime) * 0.000001f)
+ + "ms waiting for surface.lockCanvas()");
+ }
+
if (left != dirty.left || top != dirty.top || right != dirty.right ||
bottom != dirty.bottom) {
mAttachInfo.mIgnoreDirtyState = true;
@@ -1668,7 +1723,7 @@ public final class ViewRoot extends Handler implements ViewParent,
mAttachInfo.mIgnoreDirtyState = false;
}
- if (Config.DEBUG && ViewDebug.consistencyCheckEnabled) {
+ if (false && ViewDebug.consistencyCheckEnabled) {
mView.dispatchConsistencyCheck(ViewDebug.CONSISTENCY_DRAWING);
}
@@ -1997,11 +2052,27 @@ public final class ViewRoot extends Handler implements ViewParent,
break;
case DO_TRAVERSAL:
if (mProfile) {
- Debug.startMethodTracing("ViewRoot");
+ Debug.startMethodTracing("ViewAncestor");
+ }
+
+ final long traversalStartTime;
+ if (ViewDebug.DEBUG_LATENCY) {
+ traversalStartTime = System.nanoTime();
+ mLastDrawDurationNanos = 0;
}
performTraversals();
+ if (ViewDebug.DEBUG_LATENCY) {
+ long now = System.nanoTime();
+ Log.d(TAG, "Latency: Spent "
+ + ((now - traversalStartTime) * 0.000001f)
+ + "ms in performTraversals(), with "
+ + (mLastDrawDurationNanos * 0.000001f)
+ + "ms of that time in draw()");
+ mLastTraversalFinishedTimeNanos = now;
+ }
+
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
@@ -2169,25 +2240,68 @@ public final class ViewRoot extends Handler implements ViewParent,
}
}
- private void startInputEvent(InputQueue.FinishedCallback finishedCallback) {
+ private void startInputEvent(InputEvent event, InputQueue.FinishedCallback finishedCallback) {
if (mFinishedCallback != null) {
Slog.w(TAG, "Received a new input event from the input queue but there is "
+ "already an unfinished input event in progress.");
}
+ if (ViewDebug.DEBUG_LATENCY) {
+ mInputEventReceiveTimeNanos = System.nanoTime();
+ mInputEventDeliverTimeNanos = 0;
+ mInputEventDeliverPostImeTimeNanos = 0;
+ }
+
mFinishedCallback = finishedCallback;
}
- private void finishInputEvent(boolean handled) {
+ private void finishInputEvent(InputEvent event, boolean handled) {
if (LOCAL_LOGV) Log.v(TAG, "Telling window manager input event is finished");
- if (mFinishedCallback != null) {
- mFinishedCallback.finished(handled);
- mFinishedCallback = null;
- } else {
+ if (mFinishedCallback == null) {
Slog.w(TAG, "Attempted to tell the input queue that the current input event "
+ "is finished but there is no input event actually in progress.");
+ return;
}
+
+ if (ViewDebug.DEBUG_LATENCY) {
+ final long now = System.nanoTime();
+ final long eventTime = event.getEventTimeNano();
+ final StringBuilder msg = new StringBuilder();
+ msg.append("Latency: Spent ");
+ msg.append((now - mInputEventReceiveTimeNanos) * 0.000001f);
+ msg.append("ms processing ");
+ if (event instanceof KeyEvent) {
+ final KeyEvent keyEvent = (KeyEvent)event;
+ msg.append("key event, action=");
+ msg.append(KeyEvent.actionToString(keyEvent.getAction()));
+ } else {
+ final MotionEvent motionEvent = (MotionEvent)event;
+ msg.append("motion event, action=");
+ msg.append(MotionEvent.actionToString(motionEvent.getAction()));
+ msg.append(", historySize=");
+ msg.append(motionEvent.getHistorySize());
+ }
+ msg.append(", handled=");
+ msg.append(handled);
+ msg.append(", received at +");
+ msg.append((mInputEventReceiveTimeNanos - eventTime) * 0.000001f);
+ if (mInputEventDeliverTimeNanos != 0) {
+ msg.append("ms, delivered at +");
+ msg.append((mInputEventDeliverTimeNanos - eventTime) * 0.000001f);
+ }
+ if (mInputEventDeliverPostImeTimeNanos != 0) {
+ msg.append("ms, delivered post IME at +");
+ msg.append((mInputEventDeliverPostImeTimeNanos - eventTime) * 0.000001f);
+ }
+ msg.append("ms, finished at +");
+ msg.append((now - eventTime) * 0.000001f);
+ msg.append("ms.");
+ Log.d(TAG, msg.toString());
+ }
+
+ mFinishedCallback.finished(handled);
+ mFinishedCallback = null;
}
/**
@@ -2312,6 +2426,18 @@ public final class ViewRoot extends Handler implements ViewParent,
}
private void deliverPointerEvent(MotionEvent event, boolean sendDone) {
+ if (ViewDebug.DEBUG_LATENCY) {
+ mInputEventDeliverTimeNanos = System.nanoTime();
+ }
+
+ if (mInputEventConsistencyVerifier != null) {
+ if (event.isTouchEvent()) {
+ mInputEventConsistencyVerifier.onTouchEvent(event, 0);
+ } else {
+ mInputEventConsistencyVerifier.onGenericMotionEvent(event, 0);
+ }
+ }
+
// If there is no view, then the event will not be handled.
if (mView == null || !mAdded) {
finishMotionEvent(event, sendDone, false);
@@ -2328,7 +2454,7 @@ public final class ViewRoot extends Handler implements ViewParent,
if (isDown) {
ensureTouchMode(true);
}
- if(Config.LOGV) {
+ if(false) {
captureMotionLog("captureDispatchPointer", event);
}
@@ -2406,7 +2532,7 @@ public final class ViewRoot extends Handler implements ViewParent,
private void finishMotionEvent(MotionEvent event, boolean sendDone, boolean handled) {
event.recycle();
if (sendDone) {
- finishInputEvent(handled);
+ finishInputEvent(event, handled);
}
if (LOCAL_LOGV || WATCH_POINTER) {
if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
@@ -2416,8 +2542,16 @@ public final class ViewRoot extends Handler implements ViewParent,
}
private void deliverTrackballEvent(MotionEvent event, boolean sendDone) {
+ if (ViewDebug.DEBUG_LATENCY) {
+ mInputEventDeliverTimeNanos = System.nanoTime();
+ }
+
if (DEBUG_TRACKBALL) Log.v(TAG, "Motion event:" + event);
+ if (mInputEventConsistencyVerifier != null) {
+ mInputEventConsistencyVerifier.onTrackballEvent(event, 0);
+ }
+
// If there is no view, then the event will not be handled.
if (mView == null || !mAdded) {
finishMotionEvent(event, sendDone, false);
@@ -2546,6 +2680,14 @@ public final class ViewRoot extends Handler implements ViewParent,
}
private void deliverGenericMotionEvent(MotionEvent event, boolean sendDone) {
+ if (ViewDebug.DEBUG_LATENCY) {
+ mInputEventDeliverTimeNanos = System.nanoTime();
+ }
+
+ if (mInputEventConsistencyVerifier != null) {
+ mInputEventConsistencyVerifier.onGenericMotionEvent(event, 0);
+ }
+
final int source = event.getSource();
final boolean isJoystick = (source & InputDevice.SOURCE_CLASS_JOYSTICK) != 0;
@@ -2781,6 +2923,14 @@ public final class ViewRoot extends Handler implements ViewParent,
}
private void deliverKeyEvent(KeyEvent event, boolean sendDone) {
+ if (ViewDebug.DEBUG_LATENCY) {
+ mInputEventDeliverTimeNanos = System.nanoTime();
+ }
+
+ if (mInputEventConsistencyVerifier != null) {
+ mInputEventConsistencyVerifier.onKeyEvent(event, 0);
+ }
+
// If there is no view, then the event will not be handled.
if (mView == null || !mAdded) {
finishKeyEvent(event, sendDone, false);
@@ -2827,6 +2977,10 @@ public final class ViewRoot extends Handler implements ViewParent,
}
private void deliverKeyEventPostIme(KeyEvent event, boolean sendDone) {
+ if (ViewDebug.DEBUG_LATENCY) {
+ mInputEventDeliverPostImeTimeNanos = System.nanoTime();
+ }
+
// If the view went away, then the event will not be handled.
if (mView == null || !mAdded) {
finishKeyEvent(event, sendDone, false);
@@ -2839,7 +2993,7 @@ public final class ViewRoot extends Handler implements ViewParent,
return;
}
- if (Config.LOGV) {
+ if (false) {
captureKeyLog("captureDispatchKeyEvent", event);
}
@@ -2940,7 +3094,7 @@ public final class ViewRoot extends Handler implements ViewParent,
private void finishKeyEvent(KeyEvent event, boolean sendDone, boolean handled) {
if (sendDone) {
- finishInputEvent(handled);
+ finishInputEvent(event, handled);
}
}
@@ -3231,16 +3385,19 @@ public final class ViewRoot extends Handler implements ViewParent,
sendMessage(msg);
}
+ private long mInputEventReceiveTimeNanos;
+ private long mInputEventDeliverTimeNanos;
+ private long mInputEventDeliverPostImeTimeNanos;
private InputQueue.FinishedCallback mFinishedCallback;
private final InputHandler mInputHandler = new InputHandler() {
public void handleKey(KeyEvent event, InputQueue.FinishedCallback finishedCallback) {
- startInputEvent(finishedCallback);
+ startInputEvent(event, finishedCallback);
dispatchKey(event, true);
}
public void handleMotion(MotionEvent event, InputQueue.FinishedCallback finishedCallback) {
- startInputEvent(finishedCallback);
+ startInputEvent(event, finishedCallback);
dispatchMotion(event, true);
}
};
@@ -3387,6 +3544,14 @@ public final class ViewRoot extends Handler implements ViewParent,
public void childDrawableStateChanged(View child) {
}
+ public boolean requestSendAccessibilityEvent(View child, AccessibilityEvent event) {
+ if (mView == null) {
+ return false;
+ }
+ AccessibilityManager.getInstance(child.mContext).sendAccessibilityEvent(event);
+ return true;
+ }
+
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
@@ -3395,7 +3560,7 @@ public final class ViewRoot extends Handler implements ViewParent,
}
public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
- // ViewRoot never intercepts touch event, so this can be a no-op
+ // ViewAncestor never intercepts touch event, so this can be a no-op
}
public boolean requestChildRectangleOnScreen(View child, Rect rectangle,
@@ -3444,14 +3609,14 @@ public final class ViewRoot extends Handler implements ViewParent,
}
static class InputMethodCallback extends IInputMethodCallback.Stub {
- private WeakReference<ViewRoot> mViewRoot;
+ private WeakReference<ViewAncestor> mViewAncestor;
- public InputMethodCallback(ViewRoot viewRoot) {
- mViewRoot = new WeakReference<ViewRoot>(viewRoot);
+ public InputMethodCallback(ViewAncestor viewRoot) {
+ mViewAncestor = new WeakReference<ViewAncestor>(viewRoot);
}
public void finishedEvent(int seq, boolean handled) {
- final ViewRoot viewRoot = mViewRoot.get();
+ final ViewAncestor viewRoot = mViewAncestor.get();
if (viewRoot != null) {
viewRoot.dispatchFinishedEvent(seq, handled);
}
@@ -3463,36 +3628,36 @@ public final class ViewRoot extends Handler implements ViewParent,
}
static class W extends IWindow.Stub {
- private final WeakReference<ViewRoot> mViewRoot;
+ private final WeakReference<ViewAncestor> mViewAncestor;
- W(ViewRoot viewRoot) {
- mViewRoot = new WeakReference<ViewRoot>(viewRoot);
+ W(ViewAncestor viewRoot) {
+ mViewAncestor = new WeakReference<ViewAncestor>(viewRoot);
}
public void resized(int w, int h, Rect coveredInsets, Rect visibleInsets,
boolean reportDraw, Configuration newConfig) {
- final ViewRoot viewRoot = mViewRoot.get();
+ final ViewAncestor viewRoot = mViewAncestor.get();
if (viewRoot != null) {
viewRoot.dispatchResized(w, h, coveredInsets, visibleInsets, reportDraw, newConfig);
}
}
public void dispatchAppVisibility(boolean visible) {
- final ViewRoot viewRoot = mViewRoot.get();
+ final ViewAncestor viewRoot = mViewAncestor.get();
if (viewRoot != null) {
viewRoot.dispatchAppVisibility(visible);
}
}
public void dispatchGetNewSurface() {
- final ViewRoot viewRoot = mViewRoot.get();
+ final ViewAncestor viewRoot = mViewAncestor.get();
if (viewRoot != null) {
viewRoot.dispatchGetNewSurface();
}
}
public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) {
- final ViewRoot viewRoot = mViewRoot.get();
+ final ViewAncestor viewRoot = mViewAncestor.get();
if (viewRoot != null) {
viewRoot.windowFocusChanged(hasFocus, inTouchMode);
}
@@ -3512,7 +3677,7 @@ public final class ViewRoot extends Handler implements ViewParent,
}
public void executeCommand(String command, String parameters, ParcelFileDescriptor out) {
- final ViewRoot viewRoot = mViewRoot.get();
+ final ViewAncestor viewRoot = mViewAncestor.get();
if (viewRoot != null) {
final View view = viewRoot.mView;
if (view != null) {
@@ -3543,7 +3708,7 @@ public final class ViewRoot extends Handler implements ViewParent,
}
public void closeSystemDialogs(String reason) {
- final ViewRoot viewRoot = mViewRoot.get();
+ final ViewAncestor viewRoot = mViewAncestor.get();
if (viewRoot != null) {
viewRoot.dispatchCloseSystemDialogs(reason);
}
@@ -3571,7 +3736,7 @@ public final class ViewRoot extends Handler implements ViewParent,
/* Drag/drop */
public void dispatchDragEvent(DragEvent event) {
- final ViewRoot viewRoot = mViewRoot.get();
+ final ViewAncestor viewRoot = mViewAncestor.get();
if (viewRoot != null) {
viewRoot.dispatchDragEvent(event);
}
@@ -3579,7 +3744,7 @@ public final class ViewRoot extends Handler implements ViewParent,
@Override
public void dispatchSystemUiVisibilityChanged(int visibility) {
- final ViewRoot viewRoot = mViewRoot.get();
+ final ViewAncestor viewRoot = mViewAncestor.get();
if (viewRoot != null) {
viewRoot.dispatchSystemUiVisibilityChanged(visibility);
}
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index 2a74eb7..36bb046 100755..100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -19,7 +19,6 @@ package android.view;
import android.app.AppGlobals;
import android.content.Context;
import android.content.res.Configuration;
-import android.os.Bundle;
import android.provider.Settings;
import android.util.DisplayMetrics;
import android.util.SparseArray;
@@ -96,7 +95,7 @@ public class ViewConfiguration {
* is a tap or a scroll. If the user does not move within this interval, it is
* considered to be a tap.
*/
- private static final int TAP_TIMEOUT = 115;
+ private static final int TAP_TIMEOUT = 180;
/**
* Defines the duration in milliseconds we will wait to see if a touch event
@@ -129,12 +128,6 @@ public class ViewConfiguration {
private static final int TOUCH_SLOP = 16;
/**
- * Distance a touch can wander before we think the user is the first touch
- * in a sequence of double tap
- */
- private static final int LARGE_TOUCH_SLOP = 18;
-
- /**
* Distance a touch can wander before we think the user is attempting a paged scroll
* (in dips)
*/
@@ -162,6 +155,13 @@ public class ViewConfiguration {
private static final int MAXIMUM_FLING_VELOCITY = 8000;
/**
+ * Distance between a touch up event denoting the end of a touch exploration
+ * gesture and the touch up event of a subsequent tap for the latter tap to be
+ * considered as a tap i.e. to perform a click.
+ */
+ private static final int TOUCH_EXPLORATION_TAP_SLOP = 80;
+
+ /**
* The maximum size of View's drawing cache, expressed in bytes. This size
* should be at least equal to the size of the screen in ARGB888 format.
*/
@@ -189,9 +189,9 @@ public class ViewConfiguration {
private final int mMaximumFlingVelocity;
private final int mScrollbarSize;
private final int mTouchSlop;
- private final int mLargeTouchSlop;
private final int mPagingTouchSlop;
private final int mDoubleTapSlop;
+ private final int mScaledTouchExplorationTapSlop;
private final int mWindowTouchSlop;
private final int mMaximumDrawingCacheSize;
private final int mOverscrollDistance;
@@ -211,9 +211,9 @@ public class ViewConfiguration {
mMaximumFlingVelocity = MAXIMUM_FLING_VELOCITY;
mScrollbarSize = SCROLL_BAR_SIZE;
mTouchSlop = TOUCH_SLOP;
- mLargeTouchSlop = LARGE_TOUCH_SLOP;
mPagingTouchSlop = PAGING_TOUCH_SLOP;
mDoubleTapSlop = DOUBLE_TAP_SLOP;
+ mScaledTouchExplorationTapSlop = TOUCH_EXPLORATION_TAP_SLOP;
mWindowTouchSlop = WINDOW_TOUCH_SLOP;
//noinspection deprecation
mMaximumDrawingCacheSize = MAXIMUM_DRAWING_CACHE_SIZE;
@@ -248,9 +248,9 @@ public class ViewConfiguration {
mMaximumFlingVelocity = (int) (density * MAXIMUM_FLING_VELOCITY + 0.5f);
mScrollbarSize = (int) (density * SCROLL_BAR_SIZE + 0.5f);
mTouchSlop = (int) (sizeAndDensity * TOUCH_SLOP + 0.5f);
- mLargeTouchSlop = (int) (sizeAndDensity * LARGE_TOUCH_SLOP + 0.5f);
mPagingTouchSlop = (int) (sizeAndDensity * PAGING_TOUCH_SLOP + 0.5f);
mDoubleTapSlop = (int) (sizeAndDensity * DOUBLE_TAP_SLOP + 0.5f);
+ mScaledTouchExplorationTapSlop = (int) (density * TOUCH_EXPLORATION_TAP_SLOP + 0.5f);
mWindowTouchSlop = (int) (sizeAndDensity * WINDOW_TOUCH_SLOP + 0.5f);
// Size of the screen in bytes, in ARGB_8888 format
@@ -425,14 +425,6 @@ public class ViewConfiguration {
}
/**
- * @return Distance a touch can wander before we think the user is the first touch
- * in a sequence of double tap
- */
- public int getScaledLargeTouchSlop() {
- return mLargeTouchSlop;
- }
-
- /**
* @return Distance a touch can wander before we think the user is scrolling a full page
* in dips
*/
@@ -461,6 +453,17 @@ public class ViewConfiguration {
}
/**
+ * @return Distance between a touch up event denoting the end of a touch exploration
+ * gesture and the touch up event of a subsequent tap for the latter tap to be
+ * considered as a tap i.e. to perform a click.
+ *
+ * @hide
+ */
+ public int getScaledTouchExplorationTapSlop() {
+ return mScaledTouchExplorationTapSlop;
+ }
+
+ /**
* @return Distance a touch must be outside the bounds of a window for it
* to be counted as outside the window for purposes of dismissing that
* window.
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index c19a107..4aa8727 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -16,7 +16,6 @@
package android.view;
-import android.util.Config;
import android.util.Log;
import android.util.DisplayMetrics;
import android.content.res.Resources;
@@ -93,21 +92,6 @@ public class ViewDebug {
public static final boolean TRACE_RECYCLER = false;
/**
- * Enables or disables motion events tracing. Any invoker of
- * {@link #trace(View, MotionEvent, MotionEventTraceType)} should first check
- * that this value is set to true as not to affect performance.
- *
- * @hide
- */
- public static final boolean TRACE_MOTION_EVENTS = 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
*/
@@ -141,8 +125,24 @@ public class ViewDebug {
public static final boolean DEBUG_DRAG = false;
/**
+ * Enables logging of factors that affect the latency and responsiveness of an application.
+ *
+ * Logs the relative difference between the time an event was created and the time it
+ * was delivered.
+ *
+ * Logs the time spent waiting for Surface.lockCanvas() or eglSwapBuffers().
+ * This is time that the event loop spends blocked and unresponsive. Ideally, drawing
+ * and animations should be perfectly synchronized with VSYNC so that swap buffers
+ * is instantaneous.
+ *
+ * Logs the time spent in ViewRoot.performTraversals() or ViewRoot.draw().
+ * @hide
+ */
+ public static final boolean DEBUG_LATENCY = false;
+
+ /**
* <p>Enables or disables views consistency check. Even when this property is enabled,
- * view consistency checks happen only if {@link android.util.Config#DEBUG} is set
+ * view consistency checks happen only if {@link false} is set
* to true. The value of this property can be configured externally in one of the
* following files:</p>
* <ul>
@@ -155,12 +155,6 @@ public class ViewDebug {
@Debug.DebugProperty
public static boolean consistencyCheckEnabled = false;
- static {
- if (Config.DEBUG) {
- Debug.setFieldsOn(ViewDebug.class, true);
- }
- }
-
/**
* This annotation can be used to mark fields and methods to be dumped by
* the view server. Only non-void methods with no arguments can be annotated
@@ -360,6 +354,7 @@ public class ViewDebug {
private static final String REMOTE_COMMAND_REQUEST_LAYOUT = "REQUEST_LAYOUT";
private static final String REMOTE_PROFILE = "PROFILE";
private static final String REMOTE_COMMAND_CAPTURE_LAYERS = "CAPTURE_LAYERS";
+ private static final String REMOTE_COMMAND_OUTPUT_DISPLAYLIST = "OUTPUT_DISPLAYLIST";
private static HashMap<Class<?>, Field[]> sFieldsForClasses;
private static HashMap<Class<?>, Method[]> sMethodsForClasses;
@@ -380,7 +375,7 @@ public class ViewDebug {
}
private static BufferedWriter sHierarchyTraces;
- private static ViewRoot sHierarhcyRoot;
+ private static ViewAncestor sHierarhcyRoot;
private static String sHierarchyTracePrefix;
/**
@@ -408,21 +403,6 @@ public class ViewDebug {
private static String sRecyclerTracePrefix;
/**
- * Defines the type of motion events trace to output to the motion events traces file.
- *
- * @hide
- */
- public enum MotionEventTraceType {
- DISPATCH,
- ON_INTERCEPT,
- ON_TOUCH
- }
-
- private static BufferedWriter sMotionEventTraces;
- private static ViewRoot sMotionEventRoot;
- private static String sMotionEventTracePrefix;
-
- /**
* Returns the number of instanciated Views.
*
* @return The number of Views instanciated in the current process.
@@ -434,14 +414,14 @@ public class ViewDebug {
}
/**
- * Returns the number of instanciated ViewRoots.
+ * Returns the number of instanciated ViewAncestors.
*
- * @return The number of ViewRoots instanciated in the current process.
+ * @return The number of ViewAncestors instanciated in the current process.
*
* @hide
*/
- public static long getViewRootInstanceCount() {
- return Debug.countInstancesOfClass(ViewRoot.class);
+ public static long getViewAncestorInstanceCount() {
+ return Debug.countInstancesOfClass(ViewAncestor.class);
}
/**
@@ -558,6 +538,7 @@ public class ViewDebug {
recyclerDump = new File(recyclerDump, sRecyclerTracePrefix + ".traces");
try {
if (recyclerDump.exists()) {
+ //noinspection ResultOfMethodCallIgnored
recyclerDump.delete();
}
final FileOutputStream file = new FileOutputStream(recyclerDump);
@@ -655,7 +636,7 @@ public class ViewDebug {
return;
}
- sHierarhcyRoot = (ViewRoot) view.getRootView().getParent();
+ sHierarhcyRoot = (ViewAncestor) view.getRootView().getParent();
}
/**
@@ -716,146 +697,6 @@ public class ViewDebug {
sHierarhcyRoot = null;
}
- /**
- * Outputs a trace to the currently opened traces file. The trace contains the class name
- * and instance's hashcode of the specified view as well as the supplied trace type.
- *
- * @param view the view to trace
- * @param event the event of the trace
- * @param type the type of the trace
- *
- * @hide
- */
- public static void trace(View view, MotionEvent event, MotionEventTraceType type) {
- if (sMotionEventTraces == null) {
- return;
- }
-
- try {
- sMotionEventTraces.write(type.name());
- sMotionEventTraces.write(' ');
- sMotionEventTraces.write(event.getAction());
- sMotionEventTraces.write(' ');
- sMotionEventTraces.write(view.getClass().getName());
- sMotionEventTraces.write('@');
- sMotionEventTraces.write(Integer.toHexString(view.hashCode()));
- sHierarchyTraces.newLine();
- } catch (IOException e) {
- Log.w("View", "Error while dumping trace of event " + event + " for view " + view);
- }
- }
-
- /**
- * Starts tracing the motion events for the hierarchy of the specificy view.
- * The trace is identified by a prefix, used to build the traces files names:
- * <code>/EXTERNAL/motion-events/PREFIX.traces</code> and
- * <code>/EXTERNAL/motion-events/PREFIX.tree</code>.
- *
- * Only one view hierarchy can be traced at the same time. After calling this method, any
- * other invocation will result in a <code>IllegalStateException</code> unless
- * {@link #stopMotionEventTracing()} is invoked before.
- *
- * Calling this method creates the file <code>/EXTERNAL/motion-events/PREFIX.traces</code>
- * containing all the traces (or method calls) relative to the specified view's hierarchy.
- *
- * This method will return immediately if TRACE_HIERARCHY is false.
- *
- * @param prefix the traces files name prefix
- * @param view the view whose hierarchy must be traced
- *
- * @see #stopMotionEventTracing()
- * @see #trace(View, MotionEvent, android.view.ViewDebug.MotionEventTraceType)
- *
- * @hide
- */
- public static void startMotionEventTracing(String prefix, View view) {
- //noinspection PointlessBooleanExpression,ConstantConditions
- if (!TRACE_MOTION_EVENTS) {
- return;
- }
-
- if (sMotionEventRoot != null) {
- throw new IllegalStateException("You must call stopMotionEventTracing() before running" +
- " a new trace!");
- }
-
- File hierarchyDump = new File(Environment.getExternalStorageDirectory(), "motion-events/");
- //noinspection ResultOfMethodCallIgnored
- hierarchyDump.mkdirs();
-
- hierarchyDump = new File(hierarchyDump, prefix + ".traces");
- sMotionEventTracePrefix = prefix;
-
- try {
- sMotionEventTraces = new BufferedWriter(new FileWriter(hierarchyDump), 32 * 1024);
- } catch (IOException e) {
- Log.e("View", "Could not dump view hierarchy");
- return;
- }
-
- sMotionEventRoot = (ViewRoot) view.getRootView().getParent();
- }
-
- /**
- * Stops the current motion events tracing. This method closes the file
- * <code>/EXTERNAL/motion-events/PREFIX.traces</code>.
- *
- * Calling this method creates the file <code>/EXTERNAL/motion-events/PREFIX.tree</code>
- * containing the view hierarchy of the view supplied to
- * {@link #startMotionEventTracing(String, View)}.
- *
- * This method will return immediately if TRACE_HIERARCHY is false.
- *
- * @see #startMotionEventTracing(String, View)
- * @see #trace(View, MotionEvent, android.view.ViewDebug.MotionEventTraceType)
- *
- * @hide
- */
- public static void stopMotionEventTracing() {
- //noinspection PointlessBooleanExpression,ConstantConditions
- if (!TRACE_MOTION_EVENTS) {
- return;
- }
-
- if (sMotionEventRoot == null || sMotionEventTraces == null) {
- throw new IllegalStateException("You must call startMotionEventTracing() before" +
- " stopMotionEventTracing()!");
- }
-
- try {
- sMotionEventTraces.close();
- } catch (IOException e) {
- Log.e("View", "Could not write view traces");
- }
- sMotionEventTraces = null;
-
- File hierarchyDump = new File(Environment.getExternalStorageDirectory(), "motion-events/");
- //noinspection ResultOfMethodCallIgnored
- hierarchyDump.mkdirs();
- hierarchyDump = new File(hierarchyDump, sMotionEventTracePrefix + ".tree");
-
- BufferedWriter out;
- try {
- out = new BufferedWriter(new FileWriter(hierarchyDump), 8 * 1024);
- } catch (IOException e) {
- Log.e("View", "Could not dump view hierarchy");
- return;
- }
-
- View view = sMotionEventRoot.getView();
- if (view instanceof ViewGroup) {
- ViewGroup group = (ViewGroup) view;
- dumpViewHierarchy(group, out, 0);
- try {
- out.close();
- } catch (IOException e) {
- Log.e("View", "Could not dump view hierarchy");
- }
- }
-
- sHierarhcyRoot = null;
- }
-
static void dispatchCommand(View view, String command, String parameters,
OutputStream clientStream) throws IOException {
@@ -870,6 +711,8 @@ public class ViewDebug {
final String[] params = parameters.split(" ");
if (REMOTE_COMMAND_CAPTURE.equalsIgnoreCase(command)) {
capture(view, clientStream, params[0]);
+ } else if (REMOTE_COMMAND_OUTPUT_DISPLAYLIST.equalsIgnoreCase(command)) {
+ outputDisplayList(view, params[0]);
} else if (REMOTE_COMMAND_INVALIDATE.equalsIgnoreCase(command)) {
invalidate(view, params[0]);
} else if (REMOTE_COMMAND_REQUEST_LAYOUT.equalsIgnoreCase(command)) {
@@ -1051,8 +894,10 @@ public class ViewDebug {
try {
T[] data = operation.pre();
long start = Debug.threadCpuTimeNanos();
+ //noinspection unchecked
operation.run(data);
duration[0] = Debug.threadCpuTimeNanos() - start;
+ //noinspection unchecked
operation.post(data);
} finally {
latch.countDown();
@@ -1141,6 +986,11 @@ public class ViewDebug {
}
}
+ private static void outputDisplayList(View root, String parameter) throws IOException {
+ final View view = findView(root, parameter);
+ view.getViewAncestor().outputDisplayList(view);
+ }
+
private static void capture(View root, final OutputStream clientStream, String parameter)
throws IOException {
@@ -1178,12 +1028,7 @@ public class ViewDebug {
cache[0] = captureView.createSnapshot(
Bitmap.Config.ARGB_8888, 0, skpiChildren);
} catch (OutOfMemoryError e) {
- try {
- cache[0] = captureView.createSnapshot(
- Bitmap.Config.ARGB_4444, 0, skpiChildren);
- } catch (OutOfMemoryError e2) {
- Log.w("View", "Out of memory for bitmap");
- }
+ Log.w("View", "Out of memory for bitmap");
} finally {
latch.countDown();
}
@@ -1293,7 +1138,6 @@ public class ViewDebug {
}
final HashMap<Class<?>, Field[]> map = sFieldsForClasses;
- final HashMap<AccessibleObject, ExportedProperty> annotations = sAnnotations;
Field[] fields = map.get(klass);
if (fields != null) {
@@ -1309,7 +1153,7 @@ public class ViewDebug {
if (field.isAnnotationPresent(ExportedProperty.class)) {
field.setAccessible(true);
foundFields.add(field);
- annotations.put(field, field.getAnnotation(ExportedProperty.class));
+ sAnnotations.put(field, field.getAnnotation(ExportedProperty.class));
}
}
@@ -1328,7 +1172,6 @@ public class ViewDebug {
}
final HashMap<Class<?>, Method[]> map = sMethodsForClasses;
- final HashMap<AccessibleObject, ExportedProperty> annotations = sAnnotations;
Method[] methods = map.get(klass);
if (methods != null) {
@@ -1346,7 +1189,7 @@ public class ViewDebug {
method.getReturnType() != Void.class) {
method.setAccessible(true);
foundMethods.add(method);
- annotations.put(method, method.getAnnotation(ExportedProperty.class));
+ sAnnotations.put(method, method.getAnnotation(ExportedProperty.class));
}
}
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 8dc86ac..6937573 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -129,11 +129,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
// First touch target in the linked list of touch targets.
private TouchTarget mFirstTouchTarget;
- // Temporary arrays for splitting pointers.
- private int[] mTmpPointerIndexMap;
- private int[] mTmpPointerIds;
- private MotionEvent.PointerCoords[] mTmpPointerCoords;
-
// For debugging only. You can see these in hierarchyviewer.
@SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"})
@ViewDebug.ExportedProperty(category = "events")
@@ -147,6 +142,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
@ViewDebug.ExportedProperty(category = "events")
private float mLastTouchDownY;
+ // Child which last received ACTION_HOVER_ENTER and ACTION_HOVER_MOVE.
+ private View mHoveredChild;
+
/**
* Internal flags.
*
@@ -533,7 +531,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
// note: knowing that mFocused is non-null is not a good enough reason
// to break the traversal since in that case we'd actually have to find
// the focused view and make sure it wasn't FOCUS_AFTER_DESCENDANTS and
- // an ancestor of v; this will get checked for at ViewRoot
+ // an ancestor of v; this will get checked for at ViewAncestor
&& !(isFocused() && getDescendantFocusability() != FOCUS_AFTER_DESCENDANTS)) {
mParent.focusableViewAvailable(v);
}
@@ -583,6 +581,36 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
/**
* {@inheritDoc}
*/
+ public boolean requestSendAccessibilityEvent(View child, AccessibilityEvent event) {
+ ViewParent parent = getParent();
+ if (parent == null) {
+ return false;
+ }
+ final boolean propagate = onRequestSendAccessibilityEvent(child, event);
+ //noinspection SimplifiableIfStatement
+ if (!propagate) {
+ return false;
+ }
+ return parent.requestSendAccessibilityEvent(this, event);
+ }
+
+ /**
+ * Called when a child has requested sending an {@link AccessibilityEvent} and
+ * gives an opportunity to its parent to augment the event.
+ *
+ * @param child The child which requests sending the event.
+ * @param event The event to be sent.
+ * @return True if the event should be sent.
+ *
+ * @see #requestSendAccessibilityEvent(View, AccessibilityEvent)
+ */
+ public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) {
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
@Override
public boolean dispatchUnhandledMove(View focused, int direction) {
return mFocused != null &&
@@ -904,7 +932,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
final float tx = event.mX;
final float ty = event.mY;
- ViewRoot root = getViewRoot();
+ ViewAncestor root = getViewAncestor();
// Dispatch down the view hierarchy
switch (event.mAction) {
@@ -926,6 +954,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
final View[] children = mChildren;
for (int i = 0; i < count; i++) {
final View child = children[i];
+ child.mPrivateFlags2 &= ~View.DRAG_MASK;
if (child.getVisibility() == VISIBLE) {
final boolean handled = notifyChildOfDrag(children[i]);
if (handled) {
@@ -946,6 +975,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
for (View child : mDragNotifiedChildren) {
// If a child was notified about an ongoing drag, it's told that it's over
child.dispatchDragEvent(event);
+ child.mPrivateFlags2 &= ~View.DRAG_MASK;
+ child.refreshDrawableState();
}
mDragNotifiedChildren.clear();
@@ -976,8 +1007,11 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
final int action = event.mAction;
// If we've dragged off of a child view, send it the EXITED message
if (mCurrentDragView != null) {
+ final View view = mCurrentDragView;
event.mAction = DragEvent.ACTION_DRAG_EXITED;
- mCurrentDragView.dispatchDragEvent(event);
+ view.dispatchDragEvent(event);
+ view.mPrivateFlags2 &= ~View.DRAG_HOVERED;
+ view.refreshDrawableState();
}
mCurrentDragView = target;
@@ -985,6 +1019,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
if (target != null) {
event.mAction = DragEvent.ACTION_DRAG_ENTERED;
target.dispatchDragEvent(event);
+ target.mPrivateFlags2 |= View.DRAG_HOVERED;
+ target.refreshDrawableState();
}
event.mAction = action; // restore the event's original state
}
@@ -1015,7 +1051,11 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
case DragEvent.ACTION_DRAG_EXITED: {
if (mCurrentDragView != null) {
- mCurrentDragView.dispatchDragEvent(event);
+ final View view = mCurrentDragView;
+ view.dispatchDragEvent(event);
+ view.mPrivateFlags2 &= ~View.DRAG_HOVERED;
+ view.refreshDrawableState();
+
mCurrentDragView = null;
}
} break;
@@ -1053,7 +1093,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
final View[] children = mChildren;
for (int i = count - 1; i >= 0; i--) {
final View child = children[i];
- if (!child.mCanAcceptDrop) {
+ if (!child.canAcceptDrag()) {
continue;
}
@@ -1069,11 +1109,16 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
Log.d(View.VIEW_LOG_TAG, "Sending drag-started to view: " + child);
}
+ boolean canAccept = false;
if (! mDragNotifiedChildren.contains(child)) {
mDragNotifiedChildren.add(child);
- child.mCanAcceptDrop = child.dispatchDragEvent(mCurrentDrag);
+ canAccept = child.dispatchDragEvent(mCurrentDrag);
+ if (canAccept && !child.canAcceptDrag()) {
+ child.mPrivateFlags2 |= View.DRAG_CAN_ACCEPT;
+ child.refreshDrawableState();
+ }
}
- return child.mCanAcceptDrop;
+ return canAccept;
}
@Override
@@ -1106,10 +1151,22 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
*/
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
+ if (mInputEventConsistencyVerifier != null) {
+ mInputEventConsistencyVerifier.onKeyEvent(event, 1);
+ }
+
if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) {
- return super.dispatchKeyEvent(event);
+ if (super.dispatchKeyEvent(event)) {
+ return true;
+ }
} else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) {
- return mFocused.dispatchKeyEvent(event);
+ if (mFocused.dispatchKeyEvent(event)) {
+ return true;
+ }
+ }
+
+ if (mInputEventConsistencyVerifier != null) {
+ mInputEventConsistencyVerifier.onUnhandledEvent(event, 1);
}
return false;
}
@@ -1132,21 +1189,70 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
*/
@Override
public boolean dispatchTrackballEvent(MotionEvent event) {
+ if (mInputEventConsistencyVerifier != null) {
+ mInputEventConsistencyVerifier.onTrackballEvent(event, 1);
+ }
+
if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) {
- return super.dispatchTrackballEvent(event);
+ if (super.dispatchTrackballEvent(event)) {
+ return true;
+ }
} else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) {
- return mFocused.dispatchTrackballEvent(event);
+ if (mFocused.dispatchTrackballEvent(event)) {
+ return true;
+ }
+ }
+
+ if (mInputEventConsistencyVerifier != null) {
+ mInputEventConsistencyVerifier.onUnhandledEvent(event, 1);
}
return false;
}
- /**
- * {@inheritDoc}
- */
+ /** @hide */
@Override
- public boolean dispatchGenericMotionEvent(MotionEvent event) {
- if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
- // Send the event to the child under the pointer.
+ protected boolean dispatchHoverEvent(MotionEvent event) {
+ // Send the hover enter or hover move event to the view group first.
+ // If it handles the event then a hovered child should receive hover exit.
+ boolean handled = false;
+ final boolean interceptHover;
+ final int action = event.getAction();
+ if (action == MotionEvent.ACTION_HOVER_EXIT) {
+ interceptHover = true;
+ } else {
+ handled = super.dispatchHoverEvent(event);
+ interceptHover = handled;
+ }
+
+ // Send successive hover events to the hovered child as long as the pointer
+ // remains within the child's bounds.
+ MotionEvent eventNoHistory = event;
+ if (mHoveredChild != null) {
+ final float x = event.getX();
+ final float y = event.getY();
+
+ if (interceptHover
+ || !isTransformedTouchPointInView(x, y, mHoveredChild, null)) {
+ // Pointer exited the child.
+ // Send it a hover exit with only the most recent coordinates. We could
+ // try to find the exact point in history when the pointer left the view
+ // but it is not worth the effort.
+ eventNoHistory = obtainMotionEventNoHistoryOrSelf(eventNoHistory);
+ eventNoHistory.setAction(MotionEvent.ACTION_HOVER_EXIT);
+ handled |= dispatchTransformedGenericPointerEvent(eventNoHistory, mHoveredChild);
+ eventNoHistory.setAction(action);
+ mHoveredChild = null;
+ } else {
+ // Pointer is still within the child.
+ //noinspection ConstantConditions
+ handled |= dispatchTransformedGenericPointerEvent(event, mHoveredChild);
+ }
+ }
+
+ // Find a new hovered child if needed.
+ if (!interceptHover && mHoveredChild == null
+ && (action == MotionEvent.ACTION_HOVER_ENTER
+ || action == MotionEvent.ACTION_HOVER_MOVE)) {
final int childrenCount = mChildrenCount;
if (childrenCount != 0) {
final View[] children = mChildren;
@@ -1155,45 +1261,100 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
for (int i = childrenCount - 1; i >= 0; i--) {
final View child = children[i];
- if ((child.mViewFlags & VISIBILITY_MASK) != VISIBLE
- && child.getAnimation() == null) {
- // Skip invisible child unless it is animating.
+ if (!canViewReceivePointerEvents(child)
+ || !isTransformedTouchPointInView(x, y, child, null)) {
continue;
}
- if (!isTransformedTouchPointInView(x, y, child, null)) {
- // Scroll point is out of child's bounds.
- continue;
+ // Found the hovered child.
+ mHoveredChild = child;
+ if (action == MotionEvent.ACTION_HOVER_MOVE) {
+ // Pointer was moving within the view group and entered the child.
+ // Send it a hover enter and hover move with only the most recent
+ // coordinates. We could try to find the exact point in history when
+ // the pointer entered the view but it is not worth the effort.
+ eventNoHistory = obtainMotionEventNoHistoryOrSelf(eventNoHistory);
+ eventNoHistory.setAction(MotionEvent.ACTION_HOVER_ENTER);
+ handled |= dispatchTransformedGenericPointerEvent(eventNoHistory, child);
+ eventNoHistory.setAction(action);
+
+ handled |= dispatchTransformedGenericPointerEvent(eventNoHistory, child);
+ } else { /* must be ACTION_HOVER_ENTER */
+ // Pointer entered the child.
+ handled |= dispatchTransformedGenericPointerEvent(event, child);
}
+ break;
+ }
+ }
+ }
- final float offsetX = mScrollX - child.mLeft;
- final float offsetY = mScrollY - child.mTop;
- final boolean handled;
- if (!child.hasIdentityMatrix()) {
- MotionEvent transformedEvent = MotionEvent.obtain(event);
- transformedEvent.offsetLocation(offsetX, offsetY);
- transformedEvent.transform(child.getInverseMatrix());
- handled = child.dispatchGenericMotionEvent(transformedEvent);
- transformedEvent.recycle();
- } else {
- event.offsetLocation(offsetX, offsetY);
- handled = child.dispatchGenericMotionEvent(event);
- event.offsetLocation(-offsetX, -offsetY);
- }
+ // Recycle the copy of the event that we made.
+ if (eventNoHistory != event) {
+ eventNoHistory.recycle();
+ }
- if (handled) {
- return true;
- }
+ // Send hover exit to the view group. If there was a child, we will already have
+ // sent the hover exit to it.
+ if (action == MotionEvent.ACTION_HOVER_EXIT) {
+ handled |= super.dispatchHoverEvent(event);
+ }
+
+ // Done.
+ return handled;
+ }
+
+ @Override
+ public boolean onHoverEvent(MotionEvent event) {
+ // Handle the event only if leaf. This guarantees that
+ // the leafs (or any custom class that returns true from
+ // this method) will get a change to process the hover.
+ //noinspection SimplifiableIfStatement
+ if (getChildCount() == 0) {
+ return super.onHoverEvent(event);
+ }
+ return false;
+ }
+
+ private static MotionEvent obtainMotionEventNoHistoryOrSelf(MotionEvent event) {
+ if (event.getHistorySize() == 0) {
+ return event;
+ }
+ return MotionEvent.obtainNoHistory(event);
+ }
+
+ /** @hide */
+ @Override
+ protected boolean dispatchGenericPointerEvent(MotionEvent event) {
+ // Send the event to the child under the pointer.
+ final int childrenCount = mChildrenCount;
+ if (childrenCount != 0) {
+ final View[] children = mChildren;
+ final float x = event.getX();
+ final float y = event.getY();
+
+ for (int i = childrenCount - 1; i >= 0; i--) {
+ final View child = children[i];
+ if (!canViewReceivePointerEvents(child)
+ || !isTransformedTouchPointInView(x, y, child, null)) {
+ continue;
}
- }
- // No child handled the event. Send it to this view group.
- return super.dispatchGenericMotionEvent(event);
+ if (dispatchTransformedGenericPointerEvent(event, child)) {
+ return true;
+ }
+ }
}
+ // No child handled the event. Send it to this view group.
+ return super.dispatchGenericPointerEvent(event);
+ }
+
+ /** @hide */
+ @Override
+ protected boolean dispatchGenericFocusedEvent(MotionEvent event) {
// Send the event to the focused child or to this view group if it has focus.
if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) {
- return super.dispatchGenericMotionEvent(event);
+ return super.dispatchGenericFocusedEvent(event);
} else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) {
return mFocused.dispatchGenericMotionEvent(event);
}
@@ -1201,166 +1362,193 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
}
/**
+ * Dispatches a generic pointer event to a child, taking into account
+ * transformations that apply to the child.
+ *
+ * @param event The event to send.
+ * @param child The view to send the event to.
+ * @return {@code true} if the child handled the event.
+ */
+ private boolean dispatchTransformedGenericPointerEvent(MotionEvent event, View child) {
+ final float offsetX = mScrollX - child.mLeft;
+ final float offsetY = mScrollY - child.mTop;
+
+ boolean handled;
+ if (!child.hasIdentityMatrix()) {
+ MotionEvent transformedEvent = MotionEvent.obtain(event);
+ transformedEvent.offsetLocation(offsetX, offsetY);
+ transformedEvent.transform(child.getInverseMatrix());
+ handled = child.dispatchGenericMotionEvent(transformedEvent);
+ transformedEvent.recycle();
+ } else {
+ event.offsetLocation(offsetX, offsetY);
+ handled = child.dispatchGenericMotionEvent(event);
+ event.offsetLocation(-offsetX, -offsetY);
+ }
+ return handled;
+ }
+
+ /**
* {@inheritDoc}
*/
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
- if (!onFilterTouchEventForSecurity(ev)) {
- return false;
- }
-
- final int action = ev.getAction();
- final int actionMasked = action & MotionEvent.ACTION_MASK;
-
- // Handle an initial down.
- if (actionMasked == MotionEvent.ACTION_DOWN
- || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
- // Throw away all previous state when starting a new touch gesture.
- // The framework may have dropped the up or cancel event for the previous gesture
- // due to an app switch, ANR, or some other state change.
- cancelAndClearTouchTargets(ev);
- resetTouchState();
+ if (mInputEventConsistencyVerifier != null) {
+ mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
}
- // Check for interception.
- final boolean intercepted;
- if (actionMasked == MotionEvent.ACTION_DOWN
- || mFirstTouchTarget != null) {
- final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
- if (!disallowIntercept) {
- intercepted = onInterceptTouchEvent(ev);
- ev.setAction(action); // restore action in case onInterceptTouchEvent() changed it
- } else {
- intercepted = false;
+ boolean handled = false;
+ if (onFilterTouchEventForSecurity(ev)) {
+ final int action = ev.getAction();
+ final int actionMasked = action & MotionEvent.ACTION_MASK;
+
+ // Handle an initial down.
+ if (actionMasked == MotionEvent.ACTION_DOWN) {
+ // Throw away all previous state when starting a new touch gesture.
+ // The framework may have dropped the up or cancel event for the previous gesture
+ // due to an app switch, ANR, or some other state change.
+ cancelAndClearTouchTargets(ev);
+ resetTouchState();
}
- } else {
- // There are no touch targets and this action is not an initial down
- // so this view group continues to intercept touches.
- intercepted = true;
- }
-
- // Check for cancelation.
- final boolean canceled = resetCancelNextUpFlag(this)
- || actionMasked == MotionEvent.ACTION_CANCEL;
- // Update list of touch targets for pointer down, if needed.
- final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
- TouchTarget newTouchTarget = null;
- boolean alreadyDispatchedToNewTouchTarget = false;
- if (!canceled && !intercepted) {
+ // Check for interception.
+ final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN
- || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
- || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
- final int actionIndex = ev.getActionIndex(); // always 0 for down
- final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)
- : TouchTarget.ALL_POINTER_IDS;
-
- // Clean up earlier touch targets for this pointer id in case they
- // have become out of sync.
- removePointersFromTouchTargets(idBitsToAssign);
-
- final int childrenCount = mChildrenCount;
- if (childrenCount != 0) {
- // Find a child that can receive the event. Scan children from front to back.
- final View[] children = mChildren;
- final float x = ev.getX(actionIndex);
- final float y = ev.getY(actionIndex);
-
- for (int i = childrenCount - 1; i >= 0; i--) {
- final View child = children[i];
- if ((child.mViewFlags & VISIBILITY_MASK) != VISIBLE
- && child.getAnimation() == null) {
- // Skip invisible child unless it is animating.
- continue;
- }
+ || mFirstTouchTarget != null) {
+ final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
+ if (!disallowIntercept) {
+ intercepted = onInterceptTouchEvent(ev);
+ ev.setAction(action); // restore action in case it was changed
+ } else {
+ intercepted = false;
+ }
+ } else {
+ // There are no touch targets and this action is not an initial down
+ // so this view group continues to intercept touches.
+ intercepted = true;
+ }
- if (!isTransformedTouchPointInView(x, y, child, null)) {
- // New pointer is out of child's bounds.
- continue;
- }
+ // Check for cancelation.
+ final boolean canceled = resetCancelNextUpFlag(this)
+ || actionMasked == MotionEvent.ACTION_CANCEL;
+
+ // Update list of touch targets for pointer down, if needed.
+ final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
+ TouchTarget newTouchTarget = null;
+ boolean alreadyDispatchedToNewTouchTarget = false;
+ if (!canceled && !intercepted) {
+ if (actionMasked == MotionEvent.ACTION_DOWN
+ || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
+ || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
+ final int actionIndex = ev.getActionIndex(); // always 0 for down
+ final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)
+ : TouchTarget.ALL_POINTER_IDS;
+
+ // Clean up earlier touch targets for this pointer id in case they
+ // have become out of sync.
+ removePointersFromTouchTargets(idBitsToAssign);
+
+ final int childrenCount = mChildrenCount;
+ if (childrenCount != 0) {
+ // Find a child that can receive the event.
+ // Scan children from front to back.
+ final View[] children = mChildren;
+ final float x = ev.getX(actionIndex);
+ final float y = ev.getY(actionIndex);
+
+ for (int i = childrenCount - 1; i >= 0; i--) {
+ final View child = children[i];
+ if (!canViewReceivePointerEvents(child)
+ || !isTransformedTouchPointInView(x, y, child, null)) {
+ continue;
+ }
- newTouchTarget = getTouchTarget(child);
- if (newTouchTarget != null) {
- // Child is already receiving touch within its bounds.
- // Give it the new pointer in addition to the ones it is handling.
- newTouchTarget.pointerIdBits |= idBitsToAssign;
- break;
- }
+ newTouchTarget = getTouchTarget(child);
+ if (newTouchTarget != null) {
+ // Child is already receiving touch within its bounds.
+ // Give it the new pointer in addition to the ones it is handling.
+ newTouchTarget.pointerIdBits |= idBitsToAssign;
+ break;
+ }
- resetCancelNextUpFlag(child);
- if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
- // Child wants to receive touch within its bounds.
- mLastTouchDownTime = ev.getDownTime();
- mLastTouchDownIndex = i;
- mLastTouchDownX = ev.getX();
- mLastTouchDownY = ev.getY();
- newTouchTarget = addTouchTarget(child, idBitsToAssign);
- alreadyDispatchedToNewTouchTarget = true;
- break;
+ resetCancelNextUpFlag(child);
+ if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
+ // Child wants to receive touch within its bounds.
+ mLastTouchDownTime = ev.getDownTime();
+ mLastTouchDownIndex = i;
+ mLastTouchDownX = ev.getX();
+ mLastTouchDownY = ev.getY();
+ newTouchTarget = addTouchTarget(child, idBitsToAssign);
+ alreadyDispatchedToNewTouchTarget = true;
+ break;
+ }
}
}
- }
- if (newTouchTarget == null && mFirstTouchTarget != null) {
- // Did not find a child to receive the event.
- // Assign the pointer to the least recently added target.
- newTouchTarget = mFirstTouchTarget;
- while (newTouchTarget.next != null) {
- newTouchTarget = newTouchTarget.next;
+ if (newTouchTarget == null && mFirstTouchTarget != null) {
+ // Did not find a child to receive the event.
+ // Assign the pointer to the least recently added target.
+ newTouchTarget = mFirstTouchTarget;
+ while (newTouchTarget.next != null) {
+ newTouchTarget = newTouchTarget.next;
+ }
+ newTouchTarget.pointerIdBits |= idBitsToAssign;
}
- newTouchTarget.pointerIdBits |= idBitsToAssign;
}
}
- }
- // Dispatch to touch targets.
- boolean handled = false;
- if (mFirstTouchTarget == null) {
- // No touch targets so treat this as an ordinary view.
- handled = dispatchTransformedTouchEvent(ev, canceled, null,
- TouchTarget.ALL_POINTER_IDS);
- } else {
- // Dispatch to touch targets, excluding the new touch target if we already
- // dispatched to it. Cancel touch targets if necessary.
- TouchTarget predecessor = null;
- TouchTarget target = mFirstTouchTarget;
- while (target != null) {
- final TouchTarget next = target.next;
- if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
- handled = true;
- } else {
- final boolean cancelChild = resetCancelNextUpFlag(target.child) || intercepted;
- if (dispatchTransformedTouchEvent(ev, cancelChild,
- target.child, target.pointerIdBits)) {
+ // Dispatch to touch targets.
+ if (mFirstTouchTarget == null) {
+ // No touch targets so treat this as an ordinary view.
+ handled = dispatchTransformedTouchEvent(ev, canceled, null,
+ TouchTarget.ALL_POINTER_IDS);
+ } else {
+ // Dispatch to touch targets, excluding the new touch target if we already
+ // dispatched to it. Cancel touch targets if necessary.
+ TouchTarget predecessor = null;
+ TouchTarget target = mFirstTouchTarget;
+ while (target != null) {
+ final TouchTarget next = target.next;
+ if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
handled = true;
- }
- if (cancelChild) {
- if (predecessor == null) {
- mFirstTouchTarget = next;
- } else {
- predecessor.next = next;
+ } else {
+ final boolean cancelChild = resetCancelNextUpFlag(target.child)
+ || intercepted;
+ if (dispatchTransformedTouchEvent(ev, cancelChild,
+ target.child, target.pointerIdBits)) {
+ handled = true;
+ }
+ if (cancelChild) {
+ if (predecessor == null) {
+ mFirstTouchTarget = next;
+ } else {
+ predecessor.next = next;
+ }
+ target.recycle();
+ target = next;
+ continue;
}
- target.recycle();
- target = next;
- continue;
}
+ predecessor = target;
+ target = next;
}
- predecessor = target;
- target = next;
}
- }
- // Update list of touch targets for pointer up or cancel, if needed.
- if (canceled
- || actionMasked == MotionEvent.ACTION_UP
- || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
- resetTouchState();
- } else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) {
- final int actionIndex = ev.getActionIndex();
- final int idBitsToRemove = 1 << ev.getPointerId(actionIndex);
- removePointersFromTouchTargets(idBitsToRemove);
+ // Update list of touch targets for pointer up or cancel, if needed.
+ if (canceled
+ || actionMasked == MotionEvent.ACTION_UP
+ || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
+ resetTouchState();
+ } else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) {
+ final int actionIndex = ev.getActionIndex();
+ final int idBitsToRemove = 1 << ev.getPointerId(actionIndex);
+ removePointersFromTouchTargets(idBitsToRemove);
+ }
}
+ if (!handled && mInputEventConsistencyVerifier != null) {
+ mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1);
+ }
return handled;
}
@@ -1476,6 +1664,15 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
}
/**
+ * Returns true if a child view can receive pointer events.
+ * @hide
+ */
+ private static boolean canViewReceivePointerEvents(View child) {
+ return (child.mViewFlags & VISIBILITY_MASK) == VISIBLE
+ || child.getAnimation() != null;
+ }
+
+ /**
* Returns true if a child view contains the specified point when transformed
* into its coordinate space.
* Child must not be null.
@@ -1524,141 +1721,38 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
}
// Calculate the number of pointers to deliver.
- final int oldPointerCount = event.getPointerCount();
- int newPointerCount = 0;
- if (desiredPointerIdBits == TouchTarget.ALL_POINTER_IDS) {
- newPointerCount = oldPointerCount;
- } else {
- for (int i = 0; i < oldPointerCount; i++) {
- final int pointerId = event.getPointerId(i);
- final int pointerIdBit = 1 << pointerId;
- if ((pointerIdBit & desiredPointerIdBits) != 0) {
- newPointerCount += 1;
- }
- }
- }
+ final int oldPointerIdBits = event.getPointerIdBits();
+ final int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits;
// If for some reason we ended up in an inconsistent state where it looks like we
// might produce a motion event with no pointers in it, then drop the event.
- if (newPointerCount == 0) {
+ if (newPointerIdBits == 0) {
return false;
}
// If the number of pointers is the same and we don't need to perform any fancy
// irreversible transformations, then we can reuse the motion event for this
// dispatch as long as we are careful to revert any changes we make.
- final boolean reuse = newPointerCount == oldPointerCount
- && (child == null || child.hasIdentityMatrix());
- if (reuse) {
- if (child == null) {
- handled = super.dispatchTouchEvent(event);
- } else {
- final float offsetX = mScrollX - child.mLeft;
- final float offsetY = mScrollY - child.mTop;
- event.offsetLocation(offsetX, offsetY);
-
- handled = child.dispatchTouchEvent(event);
-
- event.offsetLocation(-offsetX, -offsetY);
- }
- return handled;
- }
-
- // Make a copy of the event.
- // If the number of pointers is different, then we need to filter out irrelevant pointers
- // as we make a copy of the motion event.
- MotionEvent transformedEvent;
- if (newPointerCount == oldPointerCount) {
- transformedEvent = MotionEvent.obtain(event);
- } else {
- growTmpPointerArrays(newPointerCount);
- final int[] newPointerIndexMap = mTmpPointerIndexMap;
- final int[] newPointerIds = mTmpPointerIds;
- final MotionEvent.PointerCoords[] newPointerCoords = mTmpPointerCoords;
-
- int newPointerIndex = 0;
- int oldPointerIndex = 0;
- while (newPointerIndex < newPointerCount) {
- final int pointerId = event.getPointerId(oldPointerIndex);
- final int pointerIdBits = 1 << pointerId;
- if ((pointerIdBits & desiredPointerIdBits) != 0) {
- newPointerIndexMap[newPointerIndex] = oldPointerIndex;
- newPointerIds[newPointerIndex] = pointerId;
- if (newPointerCoords[newPointerIndex] == null) {
- newPointerCoords[newPointerIndex] = new MotionEvent.PointerCoords();
- }
-
- newPointerIndex += 1;
- }
- oldPointerIndex += 1;
- }
-
- final int newAction;
- if (cancel) {
- newAction = MotionEvent.ACTION_CANCEL;
- } else {
- final int oldMaskedAction = oldAction & MotionEvent.ACTION_MASK;
- if (oldMaskedAction == MotionEvent.ACTION_POINTER_DOWN
- || oldMaskedAction == MotionEvent.ACTION_POINTER_UP) {
- final int changedPointerId = event.getPointerId(
- (oldAction & MotionEvent.ACTION_POINTER_INDEX_MASK)
- >> MotionEvent.ACTION_POINTER_INDEX_SHIFT);
- final int changedPointerIdBits = 1 << changedPointerId;
- if ((changedPointerIdBits & desiredPointerIdBits) != 0) {
- if (newPointerCount == 1) {
- // The first/last pointer went down/up.
- newAction = oldMaskedAction == MotionEvent.ACTION_POINTER_DOWN
- ? MotionEvent.ACTION_DOWN : MotionEvent.ACTION_UP;
- } else {
- // A secondary pointer went down/up.
- int newChangedPointerIndex = 0;
- while (newPointerIds[newChangedPointerIndex] != changedPointerId) {
- newChangedPointerIndex += 1;
- }
- newAction = oldMaskedAction | (newChangedPointerIndex
- << MotionEvent.ACTION_POINTER_INDEX_SHIFT);
- }
- } else {
- // An unrelated pointer changed.
- newAction = MotionEvent.ACTION_MOVE;
- }
+ // Otherwise we need to make a copy.
+ final MotionEvent transformedEvent;
+ if (newPointerIdBits == oldPointerIdBits) {
+ if (child == null || child.hasIdentityMatrix()) {
+ if (child == null) {
+ handled = super.dispatchTouchEvent(event);
} else {
- // Simple up/down/cancel/move motion action.
- newAction = oldMaskedAction;
- }
- }
+ final float offsetX = mScrollX - child.mLeft;
+ final float offsetY = mScrollY - child.mTop;
+ event.offsetLocation(offsetX, offsetY);
- transformedEvent = null;
- final int historySize = event.getHistorySize();
- for (int historyIndex = 0; historyIndex <= historySize; historyIndex++) {
- for (newPointerIndex = 0; newPointerIndex < newPointerCount; newPointerIndex++) {
- final MotionEvent.PointerCoords c = newPointerCoords[newPointerIndex];
- oldPointerIndex = newPointerIndexMap[newPointerIndex];
- if (historyIndex != historySize) {
- event.getHistoricalPointerCoords(oldPointerIndex, historyIndex, c);
- } else {
- event.getPointerCoords(oldPointerIndex, c);
- }
- }
+ handled = child.dispatchTouchEvent(event);
- final long eventTime;
- if (historyIndex != historySize) {
- eventTime = event.getHistoricalEventTime(historyIndex);
- } else {
- eventTime = event.getEventTime();
- }
-
- if (transformedEvent == null) {
- transformedEvent = MotionEvent.obtain(
- event.getDownTime(), eventTime, newAction,
- newPointerCount, newPointerIds, newPointerCoords,
- event.getMetaState(), event.getXPrecision(), event.getYPrecision(),
- event.getDeviceId(), event.getEdgeFlags(), event.getSource(),
- event.getFlags());
- } else {
- transformedEvent.addBatch(eventTime, newPointerCoords, 0);
+ event.offsetLocation(-offsetX, -offsetY);
}
+ return handled;
}
+ transformedEvent = MotionEvent.obtain(event);
+ } else {
+ transformedEvent = event.split(newPointerIdBits);
}
// Perform any necessary transformations and dispatch.
@@ -1681,36 +1775,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
}
/**
- * Enlarge the temporary pointer arrays for splitting pointers.
- * May discard contents (but keeps PointerCoords objects to avoid reallocating them).
- */
- private void growTmpPointerArrays(int desiredCapacity) {
- final MotionEvent.PointerCoords[] oldTmpPointerCoords = mTmpPointerCoords;
- int capacity;
- if (oldTmpPointerCoords != null) {
- capacity = oldTmpPointerCoords.length;
- if (desiredCapacity <= capacity) {
- return;
- }
- } else {
- capacity = 4;
- }
-
- while (capacity < desiredCapacity) {
- capacity *= 2;
- }
-
- mTmpPointerIndexMap = new int[capacity];
- mTmpPointerIds = new int[capacity];
- mTmpPointerCoords = new MotionEvent.PointerCoords[capacity];
-
- if (oldTmpPointerCoords != null) {
- System.arraycopy(oldTmpPointerCoords, 0, mTmpPointerCoords, 0,
- oldTmpPointerCoords.length);
- }
- }
-
- /**
* Enable or disable the splitting of MotionEvents to multiple children during touch event
* dispatch. This behavior is enabled by default for applications that target an
* SDK version of {@link Build.VERSION_CODES#HONEYCOMB} or newer.
@@ -1818,7 +1882,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
* @see #FOCUS_BEFORE_DESCENDANTS
* @see #FOCUS_AFTER_DESCENDANTS
* @see #FOCUS_BLOCK_DESCENDANTS
- * @see #onRequestFocusInDescendants
+ * @see #onRequestFocusInDescendants(int, android.graphics.Rect)
*/
@Override
public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
@@ -1931,11 +1995,16 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
@Override
public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
- boolean populated = false;
+ // We first get a chance to populate the event.
+ onPopulateAccessibilityEvent(event);
+ // Let our children have a shot in populating the event.
for (int i = 0, count = getChildCount(); i < count; i++) {
- populated |= getChildAt(i).dispatchPopulateAccessibilityEvent(event);
+ boolean handled = getChildAt(i).dispatchPopulateAccessibilityEvent(event);
+ if (handled) {
+ return handled;
+ }
}
- return populated;
+ return false;
}
/**
@@ -1975,7 +2044,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
public void setPadding(int left, int top, int right, int bottom) {
super.setPadding(left, top, right, bottom);
- if ((mPaddingLeft | mPaddingTop | mPaddingRight | mPaddingRight) != 0) {
+ if ((mPaddingLeft | mPaddingTop | mPaddingRight | mPaddingBottom) != 0) {
mGroupFlags |= FLAG_PADDING_NOT_NULL;
} else {
mGroupFlags &= ~FLAG_PADDING_NOT_NULL;
@@ -1999,10 +2068,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
}
/**
- * Perform dispatching of a {@link #saveHierarchyState freeze()} to only this view,
- * not to its children. For use when overriding
- * {@link #dispatchSaveInstanceState dispatchFreeze()} to allow subclasses to freeze
- * their own state but not the state of their children.
+ * Perform dispatching of a {@link #saveHierarchyState(android.util.SparseArray)} freeze()}
+ * to only this view, not to its children. For use when overriding
+ * {@link #dispatchSaveInstanceState(android.util.SparseArray)} dispatchFreeze()} to allow
+ * subclasses to freeze their own state but not the state of their children.
*
* @param container the container
*/
@@ -2027,10 +2096,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
}
/**
- * Perform dispatching of a {@link #restoreHierarchyState thaw()} to only this view,
- * not to its children. For use when overriding
- * {@link #dispatchRestoreInstanceState dispatchThaw()} to allow subclasses to thaw
- * their own state but not the state of their children.
+ * Perform dispatching of a {@link #restoreHierarchyState(android.util.SparseArray)}
+ * to only this view, not to its children. For use when overriding
+ * {@link #dispatchRestoreInstanceState(android.util.SparseArray)} to allow
+ * subclasses to thaw their own state but not the state of their children.
*
* @param container the container
*/
@@ -3244,6 +3313,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
mTransition.removeChild(this, view);
}
+ if (view == mHoveredChild) {
+ mHoveredChild = null;
+ }
+
boolean clearChildFocus = false;
if (view == mFocused) {
view.clearFocusForRemoval();
@@ -3307,6 +3380,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
final OnHierarchyChangeListener onHierarchyChangeListener = mOnHierarchyChangeListener;
final boolean notifyListener = onHierarchyChangeListener != null;
final View focused = mFocused;
+ final View hoveredChild = mHoveredChild;
final boolean detach = mAttachInfo != null;
View clearChildFocus = null;
@@ -3320,6 +3394,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
mTransition.removeChild(this, view);
}
+ if (view == hoveredChild) {
+ mHoveredChild = null;
+ }
+
if (view == focused) {
view.clearFocusForRemoval();
clearChildFocus = view;
@@ -3377,6 +3455,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
final OnHierarchyChangeListener listener = mOnHierarchyChangeListener;
final boolean notify = listener != null;
final View focused = mFocused;
+ final View hoveredChild = mHoveredChild;
final boolean detach = mAttachInfo != null;
View clearChildFocus = null;
@@ -3389,6 +3468,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
mTransition.removeChild(this, view);
}
+ if (view == hoveredChild) {
+ mHoveredChild = null;
+ }
+
if (view == focused) {
view.clearFocusForRemoval();
clearChildFocus = view;
@@ -3610,13 +3693,13 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
if (drawAnimation) {
if (view != null) {
view.mPrivateFlags |= DRAW_ANIMATION;
- } else if (parent instanceof ViewRoot) {
- ((ViewRoot) parent).mIsAnimating = true;
+ } else if (parent instanceof ViewAncestor) {
+ ((ViewAncestor) parent).mIsAnimating = true;
}
}
- if (parent instanceof ViewRoot) {
- ((ViewRoot) parent).invalidate();
+ if (parent instanceof ViewAncestor) {
+ ((ViewAncestor) parent).invalidate();
parent = null;
} else if (view != null) {
if ((view.mPrivateFlags & DRAWN) == DRAWN ||
@@ -3671,8 +3754,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
if (drawAnimation) {
if (view != null) {
view.mPrivateFlags |= DRAW_ANIMATION;
- } else if (parent instanceof ViewRoot) {
- ((ViewRoot) parent).mIsAnimating = true;
+ } else if (parent instanceof ViewAncestor) {
+ ((ViewAncestor) parent).mIsAnimating = true;
}
}
@@ -4195,7 +4278,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
// If this group is dirty, check that the parent is dirty as well
if ((mPrivateFlags & DIRTY_MASK) != 0) {
final ViewParent parent = getParent();
- if (parent != null && !(parent instanceof ViewRoot)) {
+ if (parent != null && !(parent instanceof ViewAncestor)) {
if ((((View) parent).mPrivateFlags & DIRTY_MASK) == 0) {
result = false;
android.util.Log.d(ViewDebug.CONSISTENCY_LOG_TAG,
@@ -4755,6 +4838,19 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
}
/**
+ * Return true if the pressed state should be delayed for children or descendants of this
+ * ViewGroup. Generally, this should be done for containers that can scroll, such as a List.
+ * This prevents the pressed state from appearing when the user is actually trying to scroll
+ * the content.
+ *
+ * The default implementation returns true for compatibility reasons. Subclasses that do
+ * not scroll should generally override this method and return false.
+ */
+ public boolean shouldDelayChildPressedState() {
+ return true;
+ }
+
+ /**
* LayoutParams are used by views to tell their parents how they want to be
* laid out. See
* {@link android.R.styleable#ViewGroup_Layout ViewGroup Layout Attributes}
diff --git a/core/java/android/view/ViewParent.java b/core/java/android/view/ViewParent.java
index d7d4c3f..655df39 100644
--- a/core/java/android/view/ViewParent.java
+++ b/core/java/android/view/ViewParent.java
@@ -17,6 +17,7 @@
package android.view;
import android.graphics.Rect;
+import android.view.accessibility.AccessibilityEvent;
/**
* Defines the responsibilities for a class that will be a parent of a View.
@@ -222,4 +223,22 @@ public interface ViewParent {
*/
public boolean requestChildRectangleOnScreen(View child, Rect rectangle,
boolean immediate);
+
+ /**
+ * Called by a child to request from its parent to send an {@link AccessibilityEvent}.
+ * The child has already populated a record for itself in the event and is delegating
+ * to its parent to send the event. The parent can optionally add a record for itself.
+ * <p>
+ * Note: An accessibility event is fired by an individual view which populates the
+ * event with a record for its state and requests from its parent to perform
+ * the sending. The parent can optionally add a record for itself before
+ * dispatching the request to its parent. A parent can also choose not to
+ * respect the request for sending the event. The accessibility event is sent
+ * by the topmost view in the view tree.
+ *
+ * @param child The child which requests sending the event.
+ * @param event The event to be sent.
+ * @return True if the event was sent.
+ */
+ public boolean requestSendAccessibilityEvent(View child, AccessibilityEvent event);
}
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 8a18aaf..9395d5c 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -388,6 +388,12 @@ public interface WindowManager extends ViewManager {
public static final int TYPE_POINTER = FIRST_SYSTEM_WINDOW+18;
/**
+ * Window type: Navigation bar (when distinct from status bar)
+ * @hide
+ */
+ public static final int TYPE_NAVIGATION_BAR = FIRST_SYSTEM_WINDOW+19;
+
+ /**
* End of types of system windows.
*/
public static final int LAST_SYSTEM_WINDOW = 2999;
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index a4c4544..d7a3096 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -19,7 +19,6 @@ package android.view;
import android.graphics.PixelFormat;
import android.os.IBinder;
import android.util.AndroidRuntimeException;
-import android.util.Config;
import android.util.Log;
import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
@@ -102,7 +101,7 @@ public class WindowManagerImpl implements WindowManager {
private void addView(View view, ViewGroup.LayoutParams params, boolean nest)
{
- if (Config.LOGV) Log.v("WindowManager", "addView view=" + view);
+ if (false) Log.v("WindowManager", "addView view=" + view);
if (!(params instanceof WindowManager.LayoutParams)) {
throw new IllegalArgumentException(
@@ -112,7 +111,7 @@ public class WindowManagerImpl implements WindowManager {
final WindowManager.LayoutParams wparams
= (WindowManager.LayoutParams)params;
- ViewRoot root;
+ ViewAncestor root;
View panelParentView = null;
synchronized (this) {
@@ -149,7 +148,7 @@ public class WindowManagerImpl implements WindowManager {
}
}
- root = new ViewRoot(view.getContext());
+ root = new ViewAncestor(view.getContext());
root.mAddNesting = 1;
view.setLayoutParams(wparams);
@@ -157,7 +156,7 @@ public class WindowManagerImpl implements WindowManager {
if (mViews == null) {
index = 1;
mViews = new View[1];
- mRoots = new ViewRoot[1];
+ mRoots = new ViewAncestor[1];
mParams = new WindowManager.LayoutParams[1];
} else {
index = mViews.length + 1;
@@ -165,7 +164,7 @@ public class WindowManagerImpl implements WindowManager {
mViews = new View[index];
System.arraycopy(old, 0, mViews, 0, index-1);
old = mRoots;
- mRoots = new ViewRoot[index];
+ mRoots = new ViewAncestor[index];
System.arraycopy(old, 0, mRoots, 0, index-1);
old = mParams;
mParams = new WindowManager.LayoutParams[index];
@@ -193,7 +192,7 @@ public class WindowManagerImpl implements WindowManager {
synchronized (this) {
int index = findViewLocked(view, true);
- ViewRoot root = mRoots[index];
+ ViewAncestor root = mRoots[index];
mParams[index] = wparams;
root.setLayoutParams(wparams, false);
}
@@ -208,14 +207,14 @@ public class WindowManagerImpl implements WindowManager {
}
throw new IllegalStateException("Calling with view " + view
- + " but the ViewRoot is attached to " + curView);
+ + " but the ViewAncestor is attached to " + curView);
}
}
public void removeViewImmediate(View view) {
synchronized (this) {
int index = findViewLocked(view, true);
- ViewRoot root = mRoots[index];
+ ViewAncestor root = mRoots[index];
View curView = root.getView();
root.mAddNesting = 0;
@@ -226,12 +225,12 @@ public class WindowManagerImpl implements WindowManager {
}
throw new IllegalStateException("Calling with view " + view
- + " but the ViewRoot is attached to " + curView);
+ + " but the ViewAncestor is attached to " + curView);
}
}
View removeViewLocked(int index) {
- ViewRoot root = mRoots[index];
+ ViewAncestor root = mRoots[index];
View view = root.getView();
// Don't really remove until we have matched all calls to add().
@@ -257,7 +256,7 @@ public class WindowManagerImpl implements WindowManager {
removeItem(tmpViews, mViews, index);
mViews = tmpViews;
- ViewRoot[] tmpRoots = new ViewRoot[count-1];
+ ViewAncestor[] tmpRoots = new ViewAncestor[count-1];
removeItem(tmpRoots, mRoots, index);
mRoots = tmpRoots;
@@ -282,7 +281,7 @@ public class WindowManagerImpl implements WindowManager {
//Log.i("foo", "@ " + i + " token " + mParams[i].token
// + " view " + mRoots[i].getView());
if (token == null || mParams[i].token == token) {
- ViewRoot root = mRoots[i];
+ ViewAncestor root = mRoots[i];
root.mAddNesting = 1;
//Log.i("foo", "Force closing " + root);
@@ -309,7 +308,7 @@ public class WindowManagerImpl implements WindowManager {
int count = mViews.length;
for (int i=0; i<count; i++) {
if (token == null || mParams[i].token == token) {
- ViewRoot root = mRoots[i];
+ ViewAncestor root = mRoots[i];
root.setStopped(stopped);
}
}
@@ -318,13 +317,13 @@ public class WindowManagerImpl implements WindowManager {
public WindowManager.LayoutParams getRootViewLayoutParameter(View view) {
ViewParent vp = view.getParent();
- while (vp != null && !(vp instanceof ViewRoot)) {
+ while (vp != null && !(vp instanceof ViewAncestor)) {
vp = vp.getParent();
}
if (vp == null) return null;
- ViewRoot vr = (ViewRoot)vp;
+ ViewAncestor vr = (ViewAncestor)vp;
int N = mRoots.length;
for (int i = 0; i < N; ++i) {
@@ -345,7 +344,7 @@ public class WindowManagerImpl implements WindowManager {
}
private View[] mViews;
- private ViewRoot[] mRoots;
+ private ViewAncestor[] mRoots;
private WindowManager.LayoutParams[] mParams;
private static void removeItem(Object[] dst, Object[] src, int index)
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 086ed5a..4e52e40 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -81,6 +81,8 @@ public interface WindowManagerPolicy {
public final static int FLAG_INJECTED = 0x01000000;
public final static int FLAG_TRUSTED = 0x02000000;
+ public final static int FLAG_FILTERED = 0x04000000;
+ public final static int FLAG_DISABLE_KEY_REPEAT = 0x08000000;
public final static int FLAG_WOKE_HERE = 0x10000000;
public final static int FLAG_BRIGHT_HERE = 0x20000000;
diff --git a/core/java/android/view/WindowOrientationListener.java b/core/java/android/view/WindowOrientationListener.java
index 62d3e6a..d128b57 100755
--- a/core/java/android/view/WindowOrientationListener.java
+++ b/core/java/android/view/WindowOrientationListener.java
@@ -21,7 +21,6 @@ import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
-import android.util.Config;
import android.util.Log;
import android.util.Slog;
@@ -47,7 +46,7 @@ import android.util.Slog;
public abstract class WindowOrientationListener {
private static final String TAG = "WindowOrientationListener";
private static final boolean DEBUG = false;
- private static final boolean localLOGV = DEBUG || Config.DEBUG;
+ private static final boolean localLOGV = DEBUG || false;
private SensorManager mSensorManager;
private boolean mEnabled;
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index fc61700..5e18f55 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -21,13 +21,26 @@ 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.
* <p>
+ * An accessibility event is fired by an individual view which populates the event with
+ * a record for its state and requests from its parent to send the event to interested
+ * parties. The parent can optionally add a record for itself before dispatching a similar
+ * request to its parent. A parent can also choose not to respect the request for sending
+ * an event. The accessibility event is sent by the topmost view in the view tree.
+ * Therefore, an {@link android.accessibilityservice.AccessibilityService} can explore
+ * all records in an accessibility event to obtain more information about the context
+ * in which the event was fired.
+ * <p>
+ * A client can add, remove, and modify records. The getters and setters for individual
+ * properties operate on the current record which can be explicitly set by the client. By
+ * default current is the first record. Thus, querying a record would require setting
+ * it as the current one and interacting with the property getters and setters.
+ * <p>
* 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
@@ -145,7 +158,7 @@ import java.util.List;
* @see android.view.accessibility.AccessibilityManager
* @see android.accessibilityservice.AccessibilityService
*/
-public final class AccessibilityEvent implements Parcelable {
+public final class AccessibilityEvent extends AccessibilityRecord implements Parcelable {
/**
* Invalid selection/focus position.
@@ -159,7 +172,12 @@ public final class AccessibilityEvent implements Parcelable {
*
* @see #getBeforeText()
* @see #getText()
+ * </br>
+ * Note: This constant is no longer needed since there
+ * is no limit on the length of text that is contained
+ * in an accessibility event anymore.
*/
+ @Deprecated
public static final int MAX_TEXT_LENGTH = 500;
/**
@@ -202,6 +220,26 @@ public final class AccessibilityEvent implements Parcelable {
public static final int TYPE_NOTIFICATION_STATE_CHANGED = 0x00000040;
/**
+ * Represents the event of a hover enter over a {@link android.view.View}.
+ */
+ public static final int TYPE_VIEW_HOVER_ENTER = 0x00000080;
+
+ /**
+ * Represents the event of a hover exit over a {@link android.view.View}.
+ */
+ public static final int TYPE_VIEW_HOVER_EXIT = 0x00000100;
+
+ /**
+ * Represents the event of starting a touch exploration gesture.
+ */
+ public static final int TYPE_TOUCH_EXPLORATION_GESTURE_START = 0x00000200;
+
+ /**
+ * Represents the event of ending a touch exploration gesture.
+ */
+ public static final int TYPE_TOUCH_EXPLORATION_GESTURE_END = 0x00000400;
+
+ /**
* Mask for {@link AccessibilityEvent} all types.
*
* @see #TYPE_VIEW_CLICKED
@@ -214,116 +252,53 @@ public final class AccessibilityEvent implements Parcelable {
*/
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 final int MAX_POOL_SIZE = 10;
+ private static final Object sPoolLock = 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 boolean mIsInPool;
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<CharSequence> mText = new ArrayList<CharSequence>();
+ private long mEventTime;
- private boolean mIsInPool;
+ private final ArrayList<AccessibilityRecord> mRecords = new ArrayList<AccessibilityRecord>();
/*
* 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.
+ * Gets the number of records contained in the event.
*
- * @param isPassword True if the view is a password field, false otherwise.
+ * @return The number of records.
*/
- public void setPassword(boolean isPassword) {
- setBooleanProperty(PASSWORD, isPassword);
+ public int getRecordCount() {
+ return mRecords.size();
}
/**
- * Sets if the source is taking the entire screen.
+ * Appends an {@link AccessibilityRecord} to the end of event records.
*
- * @param isFullScreen True if the source is full screen, false otherwise.
+ * @param record The record to append.
*/
- public void setFullScreen(boolean isFullScreen) {
- setBooleanProperty(FULL_SCREEN, isFullScreen);
+ public void appendRecord(AccessibilityRecord record) {
+ mRecords.add(record);
}
/**
- * Gets if the source is taking the entire screen.
+ * Gets the records at a given index.
*
- * @return True if the source is full screen, false otherwise.
+ * @param index The index.
+ * @return The records at the specified index.
*/
- public boolean isFullScreen() {
- return getBooleanProperty(FULL_SCREEN);
+ public AccessibilityRecord getRecord(int index) {
+ return mRecords.get(index);
}
/**
@@ -345,96 +320,6 @@ public final class AccessibilityEvent implements Parcelable {
}
/**
- * 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.
@@ -453,24 +338,6 @@ public final class AccessibilityEvent implements Parcelable {
}
/**
- * 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.
@@ -489,70 +356,6 @@ public final class AccessibilityEvent implements Parcelable {
}
/**
- * 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<CharSequence> 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.
*
@@ -572,7 +375,7 @@ public final class AccessibilityEvent implements Parcelable {
* @return An instance.
*/
public static AccessibilityEvent obtain() {
- synchronized (mPoolLock) {
+ synchronized (sPoolLock) {
if (sPool != null) {
AccessibilityEvent event = sPool;
sPool = sPool.mNext;
@@ -589,14 +392,16 @@ public final class AccessibilityEvent implements Parcelable {
* Return an instance back to be reused.
* <p>
* <b>Note: You must not touch the object after calling this function.</b>
+ *
+ * @throws IllegalStateException If the event is already recycled.
*/
+ @Override
public void recycle() {
if (mIsInPool) {
- return;
+ throw new IllegalStateException("Event already recycled!");
}
-
clear();
- synchronized (mPoolLock) {
+ synchronized (sPoolLock) {
if (sPoolSize <= MAX_POOL_SIZE) {
mNext = sPool;
sPool = this;
@@ -609,44 +414,15 @@ public final class AccessibilityEvent implements Parcelable {
/**
* Clears the state of this instance.
*/
- private void clear() {
+ @Override
+ protected void clear() {
+ super.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;
- mParcelableData = 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;
+ mEventTime = 0;
+ while (!mRecords.isEmpty()) {
+ AccessibilityRecord record = mRecords.remove(0);
+ record.recycle();
}
}
@@ -657,38 +433,82 @@ public final class AccessibilityEvent implements Parcelable {
*/
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);
+ mEventTime = parcel.readLong();
+ readAccessibilityRecordFromParcel(this, parcel);
+
+ // Read the records.
+ final int recordCount = parcel.readInt();
+ for (int i = 0; i < recordCount; i++) {
+ AccessibilityRecord record = AccessibilityRecord.obtain();
+ readAccessibilityRecordFromParcel(record, parcel);
+ mRecords.add(record);
+ }
}
+ /**
+ * Reads an {@link AccessibilityRecord} from a parcel.
+ *
+ * @param record The record to initialize.
+ * @param parcel The parcel to read from.
+ */
+ private void readAccessibilityRecordFromParcel(AccessibilityRecord record,
+ Parcel parcel) {
+ record.mBooleanProperties = parcel.readInt();
+ record.mCurrentItemIndex = parcel.readInt();
+ record.mItemCount = parcel.readInt();
+ record.mFromIndex = parcel.readInt();
+ record.mAddedCount = parcel.readInt();
+ record.mRemovedCount = parcel.readInt();
+ record.mClassName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
+ record.mContentDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
+ record.mBeforeText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
+ record.mParcelableData = parcel.readParcelable(null);
+ parcel.readList(record.mText, null);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
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);
+ parcel.writeLong(mEventTime);
+ writeAccessibilityRecordToParcel(this, parcel, flags);
+
+ // Write the records.
+ final int recordCount = getRecordCount();
+ parcel.writeInt(recordCount);
+ for (int i = 0; i < recordCount; i++) {
+ AccessibilityRecord record = mRecords.get(i);
+ writeAccessibilityRecordToParcel(record, parcel, flags);
+ }
+ }
+
+ /**
+ * Writes an {@link AccessibilityRecord} to a parcel.
+ *
+ * @param record The record to write.
+ * @param parcel The parcel to which to write.
+ */
+ private void writeAccessibilityRecordToParcel(AccessibilityRecord record, Parcel parcel,
+ int flags) {
+ parcel.writeInt(record.mBooleanProperties);
+ parcel.writeInt(record.mCurrentItemIndex);
+ parcel.writeInt(record.mItemCount);
+ parcel.writeInt(record.mFromIndex);
+ parcel.writeInt(record.mAddedCount);
+ parcel.writeInt(record.mRemovedCount);
+ TextUtils.writeToParcel(record.mClassName, parcel, flags);
+ TextUtils.writeToParcel(record.mContentDescription, parcel, flags);
+ TextUtils.writeToParcel(record.mBeforeText, parcel, flags);
+ parcel.writeParcelable(record.mParcelableData, flags);
+ parcel.writeList(record.mText);
}
+ /**
+ * {@inheritDoc}
+ */
public int describeContents() {
return 0;
}
@@ -696,24 +516,21 @@ public final class AccessibilityEvent implements Parcelable {
@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);
+ builder.append(" \n{\n");
+ builder.append(super.toString());
+ builder.append("\n");
+ for (int i = 0; i < mRecords.size(); i++) {
+ AccessibilityRecord record = mRecords.get(i);
+ builder.append(" Record ");
+ builder.append(i);
+ builder.append(":");
+ builder.append(record.toString());
+ builder.append("\n");
+ }
+ builder.append("}\n");
return builder.toString();
}
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index f406da9..dd77193 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -16,8 +16,8 @@
package android.view.accessibility;
-import static android.util.Config.LOGV;
-
+import android.accessibilityservice.AccessibilityService;
+import android.accessibilityservice.AccessibilityServiceInfo;
import android.content.Context;
import android.content.pm.ServiceInfo;
import android.os.Binder;
@@ -46,6 +46,8 @@ import java.util.List;
* @see android.content.Context#getSystemService
*/
public final class AccessibilityManager {
+ private static final boolean DEBUG = false;
+
private static final String LOG_TAG = "AccessibilityManager";
static final Object sInstanceSync = new Object();
@@ -166,7 +168,7 @@ public final class AccessibilityManager {
long identityToken = Binder.clearCallingIdentity();
doRecycle = mService.sendAccessibilityEvent(event);
Binder.restoreCallingIdentity(identityToken);
- if (LOGV) {
+ if (DEBUG) {
Log.i(LOG_TAG, event + " sent");
}
} catch (RemoteException re) {
@@ -187,7 +189,7 @@ public final class AccessibilityManager {
}
try {
mService.interrupt();
- if (LOGV) {
+ if (DEBUG) {
Log.i(LOG_TAG, "Requested interrupt from all services");
}
} catch (RemoteException re) {
@@ -204,7 +206,33 @@ public final class AccessibilityManager {
List<ServiceInfo> services = null;
try {
services = mService.getAccessibilityServiceList();
- if (LOGV) {
+ if (DEBUG) {
+ 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);
+ }
+
+ /**
+ * Returns the {@link ServiceInfo}s of the enabled accessibility services
+ * for a given feedback type.
+ *
+ * @param feedbackType The type of feedback.
+ * @return An unmodifiable list with {@link ServiceInfo}s.
+ *
+ * @see AccessibilityServiceInfo#FEEDBACK_AUDIBLE
+ * @see AccessibilityServiceInfo#FEEDBACK_HAPTIC
+ * @see AccessibilityServiceInfo#FEEDBACK_SPOKEN
+ * @see AccessibilityServiceInfo#FEEDBACK_VISUAL
+ * @see AccessibilityServiceInfo#FEEDBACK_GENERIC
+ */
+ public List<ServiceInfo> getEnabledAccessibilityServiceList(int feedbackType) {
+ List<ServiceInfo> services = null;
+ try {
+ services = mService.getEnabledAccessibilityServiceList(feedbackType);
+ if (DEBUG) {
Log.i(LOG_TAG, "Installed AccessibilityServices " + services);
}
} catch (RemoteException re) {
diff --git a/core/java/android/view/accessibility/AccessibilityRecord.java b/core/java/android/view/accessibility/AccessibilityRecord.java
new file mode 100644
index 0000000..7819b17
--- /dev/null
+++ b/core/java/android/view/accessibility/AccessibilityRecord.java
@@ -0,0 +1,417 @@
+/*
+ * Copyright (C) 2011 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.Parcelable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Represents a record in an accessibility event. This class encapsulates
+ * the information for a {@link android.view.View}. Note that not all properties
+ * are applicable to all view types. For detailed information please refer to
+ * {@link AccessibilityEvent}.
+ *
+ * @see AccessibilityEvent
+ */
+public class AccessibilityRecord {
+
+ private static final int INVALID_POSITION = -1;
+
+ private static final int PROPERTY_CHECKED = 0x00000001;
+ private static final int PROPERTY_ENABLED = 0x00000002;
+ private static final int PROPERTY_PASSWORD = 0x00000004;
+ private static final int PROPERTY_FULL_SCREEN = 0x00000080;
+
+ private static final int MAX_POOL_SIZE = 10;
+ private static final Object sPoolLock = new Object();
+ private static AccessibilityRecord sPool;
+ private static int sPoolSize;
+
+ private AccessibilityRecord mNext;
+ private boolean mIsInPool;
+
+ protected int mBooleanProperties;
+ protected int mCurrentItemIndex;
+ protected int mItemCount;
+ protected int mFromIndex;
+ protected int mAddedCount;
+ protected int mRemovedCount;
+
+ protected CharSequence mClassName;
+ protected CharSequence mContentDescription;
+ protected CharSequence mBeforeText;
+ protected Parcelable mParcelableData;
+
+ protected final List<CharSequence> mText = new ArrayList<CharSequence>();
+
+ /*
+ * Hide constructor.
+ */
+ protected AccessibilityRecord() {
+
+ }
+
+ /**
+ * Gets if the source is checked.
+ *
+ * @return True if the view is checked, false otherwise.
+ */
+ public boolean isChecked() {
+ return getBooleanProperty(PROPERTY_CHECKED);
+ }
+
+ /**
+ * Sets if the source is checked.
+ *
+ * @param isChecked True if the view is checked, false otherwise.
+ */
+ public void setChecked(boolean isChecked) {
+ setBooleanProperty(PROPERTY_CHECKED, isChecked);
+ }
+
+ /**
+ * Gets if the source is enabled.
+ *
+ * @return True if the view is enabled, false otherwise.
+ */
+ public boolean isEnabled() {
+ return getBooleanProperty(PROPERTY_ENABLED);
+ }
+
+ /**
+ * Sets if the source is enabled.
+ *
+ * @param isEnabled True if the view is enabled, false otherwise.
+ */
+ public void setEnabled(boolean isEnabled) {
+ setBooleanProperty(PROPERTY_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(PROPERTY_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(PROPERTY_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(PROPERTY_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(PROPERTY_FULL_SCREEN);
+ }
+
+ /**
+ * 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 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 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<CharSequence> 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;
+ }
+
+ /**
+ * Gets the value of a boolean property.
+ *
+ * @param property The property.
+ * @return The value.
+ */
+ public 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;
+ }
+ }
+
+ /**
+ * Returns a cached instance if such is available or a new one is
+ * instantiated.
+ *
+ * @return An instance.
+ */
+ public static AccessibilityRecord obtain() {
+ synchronized (sPoolLock) {
+ if (sPool != null) {
+ AccessibilityRecord record = sPool;
+ sPool = sPool.mNext;
+ sPoolSize--;
+ record.mNext = null;
+ record.mIsInPool = false;
+ return record;
+ }
+ return new AccessibilityRecord();
+ }
+ }
+
+ /**
+ * Return an instance back to be reused.
+ * <p>
+ * <b>Note: You must not touch the object after calling this function.</b>
+ *
+ * @throws IllegalStateException If the record is already recycled.
+ */
+ public void recycle() {
+ if (mIsInPool) {
+ throw new IllegalStateException("Record already recycled!");
+ }
+ clear();
+ synchronized (sPoolLock) {
+ if (sPoolSize <= MAX_POOL_SIZE) {
+ mNext = sPool;
+ sPool = this;
+ mIsInPool = true;
+ sPoolSize++;
+ }
+ }
+ }
+
+ /**
+ * Clears the state of this instance.
+ */
+ protected void clear() {
+ mBooleanProperties = 0;
+ mCurrentItemIndex = INVALID_POSITION;
+ mItemCount = 0;
+ mFromIndex = 0;
+ mAddedCount = 0;
+ mRemovedCount = 0;
+ mClassName = null;
+ mContentDescription = null;
+ mBeforeText = null;
+ mParcelableData = null;
+ mText.clear();
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append(" [ ClassName: " + mClassName);
+ builder.append("; Text: " + mText);
+ builder.append("; ContentDescription: " + mContentDescription);
+ builder.append("; ItemCount: " + mItemCount);
+ builder.append("; CurrentItemIndex: " + mCurrentItemIndex);
+ builder.append("; IsEnabled: " + getBooleanProperty(PROPERTY_ENABLED));
+ builder.append("; IsPassword: " + getBooleanProperty(PROPERTY_PASSWORD));
+ builder.append("; IsChecked: " + getBooleanProperty(PROPERTY_CHECKED));
+ builder.append("; IsFullScreen: " + getBooleanProperty(PROPERTY_FULL_SCREEN));
+ builder.append("; BeforeText: " + mBeforeText);
+ builder.append("; FromIndex: " + mFromIndex);
+ builder.append("; AddedCount: " + mAddedCount);
+ builder.append("; RemovedCount: " + mRemovedCount);
+ builder.append("; ParcelableData: " + mParcelableData);
+ builder.append(" ]");
+ return builder.toString();
+ }
+}
diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl
index 7633569..aaaae32 100644
--- a/core/java/android/view/accessibility/IAccessibilityManager.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl
@@ -35,5 +35,7 @@ interface IAccessibilityManager {
List<ServiceInfo> getAccessibilityServiceList();
+ List<ServiceInfo> getEnabledAccessibilityServiceList(int feedbackType);
+
void interrupt();
}
diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java
index e644045..b4303f4 100644
--- a/core/java/android/view/inputmethod/BaseInputConnection.java
+++ b/core/java/android/view/inputmethod/BaseInputConnection.java
@@ -34,7 +34,7 @@ import android.util.LogPrinter;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.View;
-import android.view.ViewRoot;
+import android.view.ViewAncestor;
class ComposingText implements NoCopySpan {
}
@@ -501,7 +501,7 @@ public class BaseInputConnection implements InputConnection {
}
}
if (h != null) {
- h.sendMessage(h.obtainMessage(ViewRoot.DISPATCH_KEY_FROM_IME,
+ h.sendMessage(h.obtainMessage(ViewAncestor.DISPATCH_KEY_FROM_IME,
event));
}
}
@@ -644,7 +644,7 @@ public class BaseInputConnection implements InputConnection {
lp.println("Composing text:");
TextUtils.dumpSpans(text, lp, " ");
}
-
+
// Position the cursor appropriately, so that after replacing the
// desired range of text it will be located in the correct spot.
// This allows us to deal with filters performing edits on the text
diff --git a/core/java/android/view/inputmethod/InputConnectionWrapper.java b/core/java/android/view/inputmethod/InputConnectionWrapper.java
index 4d9d51e..690ea85 100644
--- a/core/java/android/view/inputmethod/InputConnectionWrapper.java
+++ b/core/java/android/view/inputmethod/InputConnectionWrapper.java
@@ -58,8 +58,7 @@ public class InputConnectionWrapper implements InputConnection {
return mTarget.getCursorCapsMode(reqModes);
}
- public ExtractedText getExtractedText(ExtractedTextRequest request,
- int flags) {
+ public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) {
return mTarget.getExtractedText(request, flags);
}
diff --git a/core/java/android/view/inputmethod/InputMethodInfo.java b/core/java/android/view/inputmethod/InputMethodInfo.java
index 32eec9f..1f7441d 100644
--- a/core/java/android/view/inputmethod/InputMethodInfo.java
+++ b/core/java/android/view/inputmethod/InputMethodInfo.java
@@ -322,7 +322,12 @@ public final class InputMethodInfo implements Parcelable {
InputMethodInfo obj = (InputMethodInfo) o;
return mId.equals(obj.mId);
}
-
+
+ @Override
+ public int hashCode() {
+ return mId.hashCode();
+ }
+
/**
* Used to package this object into a {@link Parcel}.
*
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index a39c7c7..27cbaf7 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -41,7 +41,7 @@ import android.util.Printer;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
-import android.view.ViewRoot;
+import android.view.ViewAncestor;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -505,6 +505,13 @@ public final class InputMethodManager {
}
}
+ /**
+ * Returns a list of enabled input method subtypes for the specified input method info.
+ * @param imi An input method info whose subtypes list will be returned.
+ * @param allowsImplicitlySelectedSubtypes A boolean flag to allow to return the implicitly
+ * selected subtypes. If an input method info doesn't have enabled subtypes, the framework
+ * will implicitly enable subtypes according to the current system language.
+ */
public List<InputMethodSubtype> getEnabledInputMethodSubtypeList(InputMethodInfo imi,
boolean allowsImplicitlySelectedSubtypes) {
try {
@@ -629,7 +636,7 @@ public final class InputMethodManager {
if (vh != null) {
// This will result in a call to reportFinishInputConnection()
// below.
- vh.sendMessage(vh.obtainMessage(ViewRoot.FINISH_INPUT_CONNECTION,
+ vh.sendMessage(vh.obtainMessage(ViewAncestor.FINISH_INPUT_CONNECTION,
mServedInputConnection));
}
}
@@ -1086,9 +1093,9 @@ public final class InputMethodManager {
void scheduleCheckFocusLocked(View view) {
Handler vh = view.getHandler();
- if (vh != null && !vh.hasMessages(ViewRoot.CHECK_FOCUS)) {
+ if (vh != null && !vh.hasMessages(ViewAncestor.CHECK_FOCUS)) {
// This will result in a call to checkFocus() below.
- vh.sendMessage(vh.obtainMessage(ViewRoot.CHECK_FOCUS));
+ vh.sendMessage(vh.obtainMessage(ViewAncestor.CHECK_FOCUS));
}
}
@@ -1143,7 +1150,7 @@ public final class InputMethodManager {
}
/**
- * Called by ViewRoot when its window gets input focus.
+ * Called by ViewAncestor when its window gets input focus.
* @hide
*/
public void onWindowFocus(View rootView, View focusedView, int softInputMode,
@@ -1429,16 +1436,26 @@ public final class InputMethodManager {
}
}
- public void showInputMethodAndSubtypeEnabler(String topId) {
+ /**
+ * Show the settings for enabling subtypes of the specified input method.
+ * @param imiId An input method, whose subtypes settings will be shown. If imiId is null,
+ * subtypes of all input methods will be shown.
+ */
+ public void showInputMethodAndSubtypeEnabler(String imiId) {
synchronized (mH) {
try {
- mService.showInputMethodAndSubtypeEnablerFromClient(mClient, topId);
+ mService.showInputMethodAndSubtypeEnablerFromClient(mClient, imiId);
} catch (RemoteException e) {
Log.w(TAG, "IME died: " + mCurId, e);
}
}
}
+ /**
+ * Returns the current input method subtype. This subtype is one of the subtypes in
+ * the current input method. This method returns null when the current input method doesn't
+ * have any input method subtype.
+ */
public InputMethodSubtype getCurrentInputMethodSubtype() {
synchronized (mH) {
try {
@@ -1450,6 +1467,12 @@ public final class InputMethodManager {
}
}
+ /**
+ * Switch to a new input method subtype of the current input method.
+ * @param subtype A new input method subtype to switch.
+ * @return true if the current subtype was successfully switched. When the specified subtype is
+ * null, this method returns false.
+ */
public boolean setCurrentInputMethodSubtype(InputMethodSubtype subtype) {
synchronized (mH) {
try {
@@ -1461,6 +1484,9 @@ public final class InputMethodManager {
}
}
+ /**
+ * Returns a map of all shortcut input method info and their subtypes.
+ */
public Map<InputMethodInfo, List<InputMethodSubtype>> getShortcutInputMethodsAndSubtypes() {
synchronized (mH) {
HashMap<InputMethodInfo, List<InputMethodSubtype>> ret =
@@ -1493,6 +1519,15 @@ public final class InputMethodManager {
}
}
+ /**
+ * Force switch to the last used input method and subtype. If the last input method didn't have
+ * any subtypes, the framework will simply switch to the last input method with no subtype
+ * specified.
+ * @param imeToken Supplies the identifying token given to an input method when it was started,
+ * which allows it to perform this operation on itself.
+ * @return true if the current input method and subtype was successfully switched to the last
+ * used input method and subtype.
+ */
public boolean switchToLastInputMethod(IBinder imeToken) {
synchronized (mH) {
try {
@@ -1504,6 +1539,17 @@ public final class InputMethodManager {
}
}
+ public InputMethodSubtype getLastInputMethodSubtype() {
+ synchronized (mH) {
+ try {
+ return mService.getLastInputMethodSubtype();
+ } catch (RemoteException e) {
+ Log.w(TAG, "IME died: " + mCurId, e);
+ return null;
+ }
+ }
+ }
+
void doDump(FileDescriptor fd, PrintWriter fout, String[] args) {
final Printer p = new PrintWriterPrinter(fout);
p.println("Input method client state for " + this + ":");
diff --git a/core/java/android/view/inputmethod/InputMethodSubtype.java b/core/java/android/view/inputmethod/InputMethodSubtype.java
index 807f6ce..de38fbe 100644
--- a/core/java/android/view/inputmethod/InputMethodSubtype.java
+++ b/core/java/android/view/inputmethod/InputMethodSubtype.java
@@ -38,12 +38,13 @@ public final class InputMethodSubtype implements Parcelable {
private static final String EXTRA_VALUE_PAIR_SEPARATOR = ",";
private static final String EXTRA_VALUE_KEY_VALUE_SEPARATOR = "=";
- private final int mSubtypeNameResId;
+ private final boolean mIsAuxiliary;
+ private final int mSubtypeHashCode;
private final int mSubtypeIconResId;
+ private final int mSubtypeNameResId;
private final String mSubtypeLocale;
private final String mSubtypeMode;
private final String mSubtypeExtraValue;
- private final int mSubtypeHashCode;
private HashMap<String, String> mExtraValueHashMapCache;
/**
@@ -55,12 +56,27 @@ public final class InputMethodSubtype implements Parcelable {
* @param extraValue The extra value of the subtype
*/
InputMethodSubtype(int nameId, int iconId, String locale, String mode, String extraValue) {
+ this(nameId, iconId, locale, mode, extraValue, false);
+ }
+
+ /**
+ * Constructor
+ * @param nameId The name of the subtype
+ * @param iconId The icon of the subtype
+ * @param locale The locale supported by the subtype
+ * @param modeId The mode supported by the subtype
+ * @param extraValue The extra value of the subtype
+ * @param isAuxiliary true when this subtype is one shot subtype.
+ */
+ InputMethodSubtype(int nameId, int iconId, String locale, String mode, String extraValue,
+ boolean isAuxiliary) {
mSubtypeNameResId = nameId;
mSubtypeIconResId = iconId;
mSubtypeLocale = locale != null ? locale : "";
mSubtypeMode = mode != null ? mode : "";
mSubtypeExtraValue = extraValue != null ? extraValue : "";
mSubtypeHashCode = hashCodeInternal(mSubtypeLocale, mSubtypeMode, mSubtypeExtraValue);
+ mIsAuxiliary = isAuxiliary;
}
InputMethodSubtype(Parcel source) {
@@ -74,6 +90,7 @@ public final class InputMethodSubtype implements Parcelable {
s = source.readString();
mSubtypeExtraValue = s != null ? s : "";
mSubtypeHashCode = hashCodeInternal(mSubtypeLocale, mSubtypeMode, mSubtypeExtraValue);
+ mIsAuxiliary = (source.readInt() == 1);
}
/**
@@ -111,6 +128,15 @@ public final class InputMethodSubtype implements Parcelable {
return mSubtypeExtraValue;
}
+ /**
+ * @return true if this subtype is one shot subtype. One shot subtype will not be shown in the
+ * ime switch list when this subtype is implicitly enabled. The framework will never
+ * switch the current ime to this subtype by switchToLastInputMethod in InputMethodManager.
+ */
+ public boolean isAuxiliary() {
+ return mIsAuxiliary;
+ }
+
private HashMap<String, String> getExtraValueHashMap() {
if (mExtraValueHashMapCache == null) {
mExtraValueHashMapCache = new HashMap<String, String>();
@@ -170,24 +196,29 @@ public final class InputMethodSubtype implements Parcelable {
return false;
}
+ @Override
public int describeContents() {
return 0;
}
+ @Override
public void writeToParcel(Parcel dest, int parcelableFlags) {
dest.writeInt(mSubtypeNameResId);
dest.writeInt(mSubtypeIconResId);
dest.writeString(mSubtypeLocale);
dest.writeString(mSubtypeMode);
dest.writeString(mSubtypeExtraValue);
+ dest.writeInt(mIsAuxiliary ? 1 : 0);
}
public static final Parcelable.Creator<InputMethodSubtype> CREATOR
= new Parcelable.Creator<InputMethodSubtype>() {
+ @Override
public InputMethodSubtype createFromParcel(Parcel source) {
return new InputMethodSubtype(source);
}
+ @Override
public InputMethodSubtype[] newArray(int size) {
return new InputMethodSubtype[size];
}
diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java
index c7a7374..9f2fd12 100644
--- a/core/java/android/webkit/BrowserFrame.java
+++ b/core/java/android/webkit/BrowserFrame.java
@@ -35,7 +35,7 @@ import android.os.Message;
import android.util.Log;
import android.util.TypedValue;
import android.view.Surface;
-import android.view.ViewRoot;
+import android.view.ViewAncestor;
import android.view.WindowManager;
import junit.framework.Assert;
@@ -222,7 +222,7 @@ class BrowserFrame extends Handler {
sConfigCallback = new ConfigCallback(
(WindowManager) appContext.getSystemService(
Context.WINDOW_SERVICE));
- ViewRoot.addConfigCallback(sConfigCallback);
+ ViewAncestor.addConfigCallback(sConfigCallback);
}
sConfigCallback.addHandler(this);
@@ -1043,13 +1043,16 @@ class BrowserFrame extends Handler {
// These ids need to be in sync with enum rawResId in PlatformBridge.h
private static final int NODOMAIN = 1;
private static final int LOADERROR = 2;
- private static final int DRAWABLEDIR = 3;
+ /* package */ static final int DRAWABLEDIR = 3;
private static final int FILE_UPLOAD_LABEL = 4;
private static final int RESET_LABEL = 5;
private static final int SUBMIT_LABEL = 6;
private static final int FILE_UPLOAD_NO_FILE_CHOSEN = 7;
- String getRawResFilename(int id) {
+ private String getRawResFilename(int id) {
+ return getRawResFilename(id, mContext);
+ }
+ /* package */ static String getRawResFilename(int id, Context context) {
int resid;
switch (id) {
case NODOMAIN:
@@ -1066,19 +1069,19 @@ class BrowserFrame extends Handler {
break;
case FILE_UPLOAD_LABEL:
- return mContext.getResources().getString(
+ return context.getResources().getString(
com.android.internal.R.string.upload_file);
case RESET_LABEL:
- return mContext.getResources().getString(
+ return context.getResources().getString(
com.android.internal.R.string.reset);
case SUBMIT_LABEL:
- return mContext.getResources().getString(
+ return context.getResources().getString(
com.android.internal.R.string.submit);
case FILE_UPLOAD_NO_FILE_CHOSEN:
- return mContext.getResources().getString(
+ return context.getResources().getString(
com.android.internal.R.string.no_file_chosen);
default:
@@ -1086,7 +1089,7 @@ class BrowserFrame extends Handler {
return "";
}
TypedValue value = new TypedValue();
- mContext.getResources().getValue(resid, value, true);
+ context.getResources().getValue(resid, value, true);
if (id == DRAWABLEDIR) {
String path = value.string.toString();
int index = path.lastIndexOf('/');
diff --git a/core/java/android/webkit/HTML5VideoFullScreen.java b/core/java/android/webkit/HTML5VideoFullScreen.java
index 0918683..1004b5f 100644
--- a/core/java/android/webkit/HTML5VideoFullScreen.java
+++ b/core/java/android/webkit/HTML5VideoFullScreen.java
@@ -208,7 +208,7 @@ public class HTML5VideoFullScreen extends HTML5VideoView
// view. This happens in the WebChromeClient before this method
// is invoked.
pauseAndDispatch(mProxy);
-
+ mProxy.dispatchOnStopFullScreen();
mLayout.removeView(getSurfaceView());
if (mProgressView != null) {
@@ -253,7 +253,8 @@ public class HTML5VideoFullScreen extends HTML5VideoView
client.onShowCustomView(mLayout, mCallback);
// Plugins like Flash will draw over the video so hide
// them while we're playing.
- mProxy.getWebView().getViewManager().hideAll();
+ if (webView.getViewManager() != null)
+ webView.getViewManager().hideAll();
mProgressView = client.getVideoLoadingProgressView();
if (mProgressView != null) {
diff --git a/core/java/android/webkit/HTML5VideoViewProxy.java b/core/java/android/webkit/HTML5VideoViewProxy.java
index d1b8cfc..7d8669b 100644
--- a/core/java/android/webkit/HTML5VideoViewProxy.java
+++ b/core/java/android/webkit/HTML5VideoViewProxy.java
@@ -65,6 +65,7 @@ class HTML5VideoViewProxy extends Handler
private static final int ENDED = 201;
private static final int POSTER_FETCHED = 202;
private static final int PAUSED = 203;
+ private static final int STOPFULLSCREEN = 204;
// Timer thread -> UI thread
private static final int TIMEUPDATE = 300;
@@ -287,8 +288,13 @@ class HTML5VideoViewProxy extends Handler
}
public void dispatchOnPaused() {
- Message msg = Message.obtain(mWebCoreHandler, PAUSED);
- mWebCoreHandler.sendMessage(msg);
+ Message msg = Message.obtain(mWebCoreHandler, PAUSED);
+ mWebCoreHandler.sendMessage(msg);
+ }
+
+ public void dispatchOnStopFullScreen() {
+ Message msg = Message.obtain(mWebCoreHandler, STOPFULLSCREEN);
+ mWebCoreHandler.sendMessage(msg);
}
public void onTimeupdate() {
@@ -560,6 +566,9 @@ class HTML5VideoViewProxy extends Handler
case TIMEUPDATE:
nativeOnTimeupdate(msg.arg1, mNativePointer);
break;
+ case STOPFULLSCREEN:
+ nativeOnStopFullscreen(mNativePointer);
+ break;
}
}
};
@@ -686,6 +695,7 @@ class HTML5VideoViewProxy extends Handler
private native void nativeOnPaused(int nativePointer);
private native void nativeOnPosterFetched(Bitmap poster, int nativePointer);
private native void nativeOnTimeupdate(int position, int nativePointer);
+ private native void nativeOnStopFullscreen(int nativePointer);
private native static boolean nativeSendSurfaceTexture(SurfaceTexture texture,
int baseLayer, int videoLayerId, int textureName,
int playerState);
diff --git a/core/java/android/webkit/JniUtil.java b/core/java/android/webkit/JniUtil.java
index 62b415c..b5d4933 100644
--- a/core/java/android/webkit/JniUtil.java
+++ b/core/java/android/webkit/JniUtil.java
@@ -48,6 +48,12 @@ class JniUtil {
initialized = true;
}
+ protected static synchronized Context getContext() {
+ if (!initialized)
+ return null;
+ return sContext;
+ }
+
/**
* Called by JNI. Gets the application's database directory, excluding the trailing slash.
* @return String The application's database directory
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index 2b507fd..8ffbda2 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -19,7 +19,6 @@ package android.webkit;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
-import android.content.res.Resources;
import android.os.Build;
import android.os.Handler;
import android.os.Message;
@@ -139,6 +138,9 @@ public class WebSettings {
OFF
}
+ // TODO: Keep this up to date
+ private static final String PREVIOUS_VERSION = "3.1";
+
// WebView associated with this WebSettings.
private WebView mWebView;
// BrowserFrame used to access the native frame pointer.
@@ -221,6 +223,7 @@ public class WebSettings {
private boolean mAllowContentAccess = true;
private boolean mLoadWithOverviewMode = false;
private boolean mEnableSmoothTransition = false;
+ private boolean mForceUserScalable = false;
// AutoFill Profile data
/**
@@ -471,7 +474,14 @@ public class WebSettings {
// Add version
final String version = Build.VERSION.RELEASE;
if (version.length() > 0) {
- buffer.append(version);
+ if (Character.isDigit(version.charAt(0))) {
+ // Release is a version, eg "3.1"
+ buffer.append(version);
+ } else {
+ // Release is a codename, eg "Honeycomb"
+ // In this case, use the previous release's version
+ buffer.append(PREVIOUS_VERSION);
+ }
} else {
// default to "1.0"
buffer.append("1.0");
@@ -1672,6 +1682,23 @@ public class WebSettings {
}
}
+ /**
+ * Returns whether the viewport metatag can disable zooming
+ * @hide
+ */
+ public boolean forceUserScalable() {
+ return mForceUserScalable;
+ }
+
+ /**
+ * Sets whether viewport metatag can disable zooming.
+ * @param flag Whether or not to forceably enable user scalable.
+ * @hide
+ */
+ public synchronized void setForceUserScalable(boolean flag) {
+ mForceUserScalable = flag;
+ }
+
synchronized void setSyntheticLinksEnabled(boolean flag) {
if (mSyntheticLinksEnabled != flag) {
mSyntheticLinksEnabled = flag;
diff --git a/core/java/android/webkit/WebTextView.java b/core/java/android/webkit/WebTextView.java
index 0f24edc..7f4f103 100644
--- a/core/java/android/webkit/WebTextView.java
+++ b/core/java/android/webkit/WebTextView.java
@@ -51,8 +51,8 @@ import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputMethodManager;
import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InputMethodManager;
import android.widget.AbsoluteLayout.LayoutParams;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
@@ -515,7 +515,6 @@ import junit.framework.Assert;
int candEnd = EditableInputConnection.getComposingSpanEnd(sp);
imm.updateSelection(this, selStart, selEnd, candStart, candEnd);
}
- updateCursorControllerPositions();
}
@Override
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index cf83456..61a69ca 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -27,6 +27,7 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.content.res.AssetManager;
import android.content.res.Configuration;
import android.database.DataSetObserver;
import android.graphics.Bitmap;
@@ -53,7 +54,9 @@ import android.net.http.SslCertificate;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
+import android.os.Looper;
import android.os.Message;
+import android.os.StrictMode;
import android.provider.Settings;
import android.speech.tts.TextToSpeech;
import android.text.Selection;
@@ -821,6 +824,8 @@ public class WebView extends AbsoluteLayout
private WebViewCore.AutoFillData mAutoFillData;
+ private static boolean sNotificationsEnabled = true;
+
/**
* URI scheme for telephone number
*/
@@ -874,8 +879,9 @@ public class WebView extends AbsoluteLayout
*/
public static final int UNKNOWN_TYPE = 0;
/**
- * HitTestResult for hitting a HTML::a tag
+ * @deprecated This type is no longer used.
*/
+ @Deprecated
public static final int ANCHOR_TYPE = 1;
/**
* HitTestResult for hitting a phone number
@@ -894,8 +900,9 @@ public class WebView extends AbsoluteLayout
*/
public static final int IMAGE_TYPE = 5;
/**
- * HitTestResult for hitting a HTML::a tag which contains HTML::img
+ * @deprecated This type is no longer used.
*/
+ @Deprecated
public static final int IMAGE_ANCHOR_TYPE = 6;
/**
* HitTestResult for hitting a HTML::a tag with src=http
@@ -987,6 +994,7 @@ public class WebView extends AbsoluteLayout
protected WebView(Context context, AttributeSet attrs, int defStyle,
Map<String, Object> javaScriptInterfaces, boolean privateBrowsing) {
super(context, attrs, defStyle);
+ checkThread();
// Used by the chrome stack to find application paths
JniUtil.setContext(context);
@@ -1024,24 +1032,38 @@ public class WebView extends AbsoluteLayout
}
/*
- * A variable to track if there is a receiver added for PROXY_CHANGE_ACTION
+ * Receiver for PROXY_CHANGE_ACTION, will be null when it is not added handling broadcasts.
*/
- private static boolean sProxyReceiverAdded;
+ private static ProxyReceiver sProxyReceiver;
+ /*
+ * @param context This method expects this to be a valid context
+ */
private static synchronized void setupProxyListener(Context context) {
- if (sProxyReceiverAdded) {
+ if (sProxyReceiver != null || sNotificationsEnabled == false) {
return;
}
IntentFilter filter = new IntentFilter();
filter.addAction(Proxy.PROXY_CHANGE_ACTION);
+ sProxyReceiver = new ProxyReceiver();
Intent currentProxy = context.getApplicationContext().registerReceiver(
- new ProxyReceiver(), filter);
- sProxyReceiverAdded = true;
+ sProxyReceiver, filter);
if (currentProxy != null) {
handleProxyBroadcast(currentProxy);
}
}
+ /*
+ * @param context This method expects this to be a valid context
+ */
+ private static synchronized void disableProxyListener(Context context) {
+ if (sProxyReceiver == null)
+ return;
+
+ context.getApplicationContext().unregisterReceiver(sProxyReceiver);
+ sProxyReceiver = null;
+ }
+
private static void handleProxyBroadcast(Intent intent) {
ProxyProperties proxyProperties = (ProxyProperties)intent.getExtra(Proxy.EXTRA_PROXY_INFO);
if (proxyProperties == null || proxyProperties.getHost() == null) {
@@ -1139,7 +1161,7 @@ public class WebView extends AbsoluteLayout
PackageInfo pInfo = pm.getPackageInfo(name,
PackageManager.GET_ACTIVITIES | PackageManager.GET_SERVICES);
installedPackages.add(name);
- } catch(PackageManager.NameNotFoundException e) {
+ } catch (PackageManager.NameNotFoundException e) {
// package not found
}
}
@@ -1194,6 +1216,11 @@ public class WebView extends AbsoluteLayout
mHTML5VideoViewProxy = null ;
}
+ @Override
+ public boolean shouldDelayChildPressedState() {
+ return true;
+ }
+
/**
* Adds accessibility APIs to JavaScript.
*
@@ -1310,6 +1337,7 @@ public class WebView extends AbsoluteLayout
* @param overlay TRUE if horizontal scrollbar should have overlay style.
*/
public void setHorizontalScrollbarOverlay(boolean overlay) {
+ checkThread();
mOverlayHorizontalScrollbar = overlay;
}
@@ -1318,6 +1346,7 @@ public class WebView extends AbsoluteLayout
* @param overlay TRUE if vertical scrollbar should have overlay style.
*/
public void setVerticalScrollbarOverlay(boolean overlay) {
+ checkThread();
mOverlayVerticalScrollbar = overlay;
}
@@ -1326,6 +1355,7 @@ public class WebView extends AbsoluteLayout
* @return TRUE if horizontal scrollbar has overlay style.
*/
public boolean overlayHorizontalScrollbar() {
+ checkThread();
return mOverlayHorizontalScrollbar;
}
@@ -1334,6 +1364,7 @@ public class WebView extends AbsoluteLayout
* @return TRUE if vertical scrollbar has overlay style.
*/
public boolean overlayVerticalScrollbar() {
+ checkThread();
return mOverlayVerticalScrollbar;
}
@@ -1365,6 +1396,7 @@ public class WebView extends AbsoluteLayout
* @deprecated This method is now obsolete.
*/
public int getVisibleTitleHeight() {
+ checkThread();
// need to restrict mScrollY due to over scroll
return Math.max(getTitleHeight() - Math.max(0, mScrollY), 0);
}
@@ -1391,6 +1423,7 @@ public class WebView extends AbsoluteLayout
* there is no certificate (the site is not secure).
*/
public SslCertificate getCertificate() {
+ checkThread();
return mCertificate;
}
@@ -1398,6 +1431,7 @@ public class WebView extends AbsoluteLayout
* Sets the SSL certificate for the main top-level page.
*/
public void setCertificate(SslCertificate certificate) {
+ checkThread();
if (DebugFlags.WEB_VIEW) {
Log.v(LOGTAG, "setCertificate=" + certificate);
}
@@ -1417,6 +1451,7 @@ public class WebView extends AbsoluteLayout
* @param password The password for the given host.
*/
public void savePassword(String host, String username, String password) {
+ checkThread();
mDatabase.setUsernamePassword(host, username, password);
}
@@ -1431,6 +1466,7 @@ public class WebView extends AbsoluteLayout
*/
public void setHttpAuthUsernamePassword(String host, String realm,
String username, String password) {
+ checkThread();
mDatabase.setHttpAuthUsernamePassword(host, realm, username, password);
}
@@ -1444,6 +1480,7 @@ public class WebView extends AbsoluteLayout
* String[1] is password. Return null if it can't find anything.
*/
public String[] getHttpAuthUsernamePassword(String host, String realm) {
+ checkThread();
return mDatabase.getHttpAuthUsernamePassword(host, realm);
}
@@ -1476,6 +1513,11 @@ public class WebView extends AbsoluteLayout
* methods may be called on a WebView after destroy.
*/
public void destroy() {
+ checkThread();
+ destroyImpl();
+ }
+
+ private void destroyImpl() {
clearHelpers();
if (mListBoxDialog != null) {
mListBoxDialog.dismiss();
@@ -1510,21 +1552,38 @@ public class WebView extends AbsoluteLayout
/**
* Enables platform notifications of data state and proxy changes.
- * @deprecated Obsolete - platform notifications are always enabled.
+ * Notifications are enabled by default.
+ *
+ * @deprecated This method is now obsolete.
*/
@Deprecated
public static void enablePlatformNotifications() {
- Network.enablePlatformNotifications();
+ checkThread();
+ synchronized (WebView.class) {
+ Network.enablePlatformNotifications();
+ sNotificationsEnabled = true;
+ Context context = JniUtil.getContext();
+ if (context != null)
+ setupProxyListener(context);
+ }
}
/**
- * If platform notifications are enabled, this should be called
- * from the Activity's onPause() or onStop().
- * @deprecated Obsolete - platform notifications are always enabled.
+ * Disables platform notifications of data state and proxy changes.
+ * Notifications are enabled by default.
+ *
+ * @deprecated This method is now obsolete.
*/
@Deprecated
public static void disablePlatformNotifications() {
- Network.disablePlatformNotifications();
+ checkThread();
+ synchronized (WebView.class) {
+ Network.disablePlatformNotifications();
+ sNotificationsEnabled = false;
+ Context context = JniUtil.getContext();
+ if (context != null)
+ disableProxyListener(context);
+ }
}
/**
@@ -1535,6 +1594,7 @@ public class WebView extends AbsoluteLayout
* @hide pending API solidification
*/
public void setJsFlags(String flags) {
+ checkThread();
mWebViewCore.sendMessage(EventHub.SET_JS_FLAGS, flags);
}
@@ -1545,6 +1605,7 @@ public class WebView extends AbsoluteLayout
* @param networkUp boolean indicating if network is available
*/
public void setNetworkAvailable(boolean networkUp) {
+ checkThread();
mWebViewCore.sendMessage(EventHub.SET_NETWORK_STATE,
networkUp ? 1 : 0, 0);
}
@@ -1554,6 +1615,7 @@ public class WebView extends AbsoluteLayout
* {@hide}
*/
public void setNetworkType(String type, String subtype) {
+ checkThread();
Map<String, String> map = new HashMap<String, String>();
map.put("type", type);
map.put("subtype", subtype);
@@ -1573,6 +1635,7 @@ public class WebView extends AbsoluteLayout
* @see #restorePicture
*/
public WebBackForwardList saveState(Bundle outState) {
+ checkThread();
if (outState == null) {
return null;
}
@@ -1629,6 +1692,7 @@ public class WebView extends AbsoluteLayout
*/
@Deprecated
public boolean savePicture(Bundle b, final File dest) {
+ checkThread();
if (dest == null || b == null) {
return false;
}
@@ -1693,6 +1757,7 @@ public class WebView extends AbsoluteLayout
*/
@Deprecated
public boolean restorePicture(Bundle b, File src) {
+ checkThread();
if (src == null || b == null) {
return false;
}
@@ -1745,6 +1810,7 @@ public class WebView extends AbsoluteLayout
* @see #restorePicture
*/
public WebBackForwardList restoreState(Bundle inState) {
+ checkThread();
WebBackForwardList returnList = null;
if (inState == null) {
return returnList;
@@ -1804,6 +1870,11 @@ public class WebView extends AbsoluteLayout
* will be replaced by the intrinsic value of the WebView.
*/
public void loadUrl(String url, Map<String, String> extraHeaders) {
+ checkThread();
+ loadUrlImpl(url, extraHeaders);
+ }
+
+ private void loadUrlImpl(String url, Map<String, String> extraHeaders) {
switchOutDrawHistory();
WebViewCore.GetUrlData arg = new WebViewCore.GetUrlData();
arg.mUrl = url;
@@ -1817,10 +1888,15 @@ public class WebView extends AbsoluteLayout
* @param url The url of the resource to load.
*/
public void loadUrl(String url) {
+ checkThread();
+ loadUrlImpl(url);
+ }
+
+ private void loadUrlImpl(String url) {
if (url == null) {
return;
}
- loadUrl(url, null);
+ loadUrlImpl(url, null);
}
/**
@@ -1832,6 +1908,7 @@ public class WebView extends AbsoluteLayout
* @param postData The data will be passed to "POST" request.
*/
public void postUrl(String url, byte[] postData) {
+ checkThread();
if (URLUtil.isNetworkUrl(url)) {
switchOutDrawHistory();
WebViewCore.PostUrlData arg = new WebViewCore.PostUrlData();
@@ -1840,7 +1917,7 @@ public class WebView extends AbsoluteLayout
mWebViewCore.sendMessage(EventHub.POST_URL, arg);
clearHelpers();
} else {
- loadUrl(url);
+ loadUrlImpl(url);
}
}
@@ -1855,7 +1932,12 @@ public class WebView extends AbsoluteLayout
* @param encoding The encoding of the data. i.e. utf-8, base64
*/
public void loadData(String data, String mimeType, String encoding) {
- loadUrl("data:" + mimeType + ";" + encoding + "," + data);
+ checkThread();
+ loadDataImpl(data, mimeType, encoding);
+ }
+
+ private void loadDataImpl(String data, String mimeType, String encoding) {
+ loadUrlImpl("data:" + mimeType + ";" + encoding + "," + data);
}
/**
@@ -1881,9 +1963,10 @@ public class WebView extends AbsoluteLayout
*/
public void loadDataWithBaseURL(String baseUrl, String data,
String mimeType, String encoding, String historyUrl) {
+ checkThread();
if (baseUrl != null && baseUrl.toLowerCase().startsWith("data:")) {
- loadData(data, mimeType, encoding);
+ loadDataImpl(data, mimeType, encoding);
return;
}
switchOutDrawHistory();
@@ -1903,7 +1986,8 @@ public class WebView extends AbsoluteLayout
* @param filename The filename where the archive should be placed.
*/
public void saveWebArchive(String filename) {
- saveWebArchive(filename, false, null);
+ checkThread();
+ saveWebArchiveImpl(filename, false, null);
}
/* package */ static class SaveWebArchiveMessage {
@@ -1932,6 +2016,12 @@ public class WebView extends AbsoluteLayout
* file failed.
*/
public void saveWebArchive(String basename, boolean autoname, ValueCallback<String> callback) {
+ checkThread();
+ saveWebArchiveImpl(basename, autoname, callback);
+ }
+
+ private void saveWebArchiveImpl(String basename, boolean autoname,
+ ValueCallback<String> callback) {
mWebViewCore.sendMessage(EventHub.SAVE_WEBARCHIVE,
new SaveWebArchiveMessage(basename, autoname, callback));
}
@@ -1940,6 +2030,7 @@ public class WebView extends AbsoluteLayout
* Stop the current load.
*/
public void stopLoading() {
+ checkThread();
// TODO: should we clear all the messages in the queue before sending
// STOP_LOADING?
switchOutDrawHistory();
@@ -1950,6 +2041,7 @@ public class WebView extends AbsoluteLayout
* Reload the current url.
*/
public void reload() {
+ checkThread();
clearHelpers();
switchOutDrawHistory();
mWebViewCore.sendMessage(EventHub.RELOAD);
@@ -1960,6 +2052,7 @@ public class WebView extends AbsoluteLayout
* @return True iff this WebView has a back history item.
*/
public boolean canGoBack() {
+ checkThread();
WebBackForwardList l = mCallbackProxy.getBackForwardList();
synchronized (l) {
if (l.getClearPending()) {
@@ -1974,7 +2067,8 @@ public class WebView extends AbsoluteLayout
* Go back in the history of this WebView.
*/
public void goBack() {
- goBackOrForward(-1);
+ checkThread();
+ goBackOrForwardImpl(-1);
}
/**
@@ -1982,6 +2076,7 @@ public class WebView extends AbsoluteLayout
* @return True iff this Webview has a forward history item.
*/
public boolean canGoForward() {
+ checkThread();
WebBackForwardList l = mCallbackProxy.getBackForwardList();
synchronized (l) {
if (l.getClearPending()) {
@@ -1996,7 +2091,8 @@ public class WebView extends AbsoluteLayout
* Go forward in the history of this WebView.
*/
public void goForward() {
- goBackOrForward(1);
+ checkThread();
+ goBackOrForwardImpl(1);
}
/**
@@ -2006,6 +2102,7 @@ public class WebView extends AbsoluteLayout
* history.
*/
public boolean canGoBackOrForward(int steps) {
+ checkThread();
WebBackForwardList l = mCallbackProxy.getBackForwardList();
synchronized (l) {
if (l.getClearPending()) {
@@ -2025,6 +2122,11 @@ public class WebView extends AbsoluteLayout
* forward list.
*/
public void goBackOrForward(int steps) {
+ checkThread();
+ goBackOrForwardImpl(steps);
+ }
+
+ private void goBackOrForwardImpl(int steps) {
goBackOrForward(steps, false);
}
@@ -2040,6 +2142,7 @@ public class WebView extends AbsoluteLayout
* Returns true if private browsing is enabled in this WebView.
*/
public boolean isPrivateBrowsingEnabled() {
+ checkThread();
return getSettings().isPrivateBrowsingEnabled();
}
@@ -2062,6 +2165,7 @@ public class WebView extends AbsoluteLayout
* @return true if the page was scrolled
*/
public boolean pageUp(boolean top) {
+ checkThread();
if (mNativeClass == 0) {
return false;
}
@@ -2088,6 +2192,7 @@ public class WebView extends AbsoluteLayout
* @return true if the page was scrolled
*/
public boolean pageDown(boolean bottom) {
+ checkThread();
if (mNativeClass == 0) {
return false;
}
@@ -2112,6 +2217,7 @@ public class WebView extends AbsoluteLayout
* and onMeasure() will return 0 if MeasureSpec is not MeasureSpec.EXACTLY
*/
public void clearView() {
+ checkThread();
mContentWidth = 0;
mContentHeight = 0;
setBaseLayer(0, null, false, false);
@@ -2128,6 +2234,7 @@ public class WebView extends AbsoluteLayout
* bounds of the view.
*/
public Picture capturePicture() {
+ checkThread();
if (mNativeClass == 0) return null;
Picture result = new Picture();
nativeCopyBaseContentToPicture(result);
@@ -2158,6 +2265,7 @@ public class WebView extends AbsoluteLayout
* @return The current scale.
*/
public float getScale() {
+ checkThread();
return mZoomManager.getScale();
}
@@ -2170,6 +2278,7 @@ public class WebView extends AbsoluteLayout
* @param scaleInPercent The initial scale in percent.
*/
public void setInitialScale(int scaleInPercent) {
+ checkThread();
mZoomManager.setInitialScaleInPercent(scaleInPercent);
}
@@ -2179,6 +2288,7 @@ public class WebView extends AbsoluteLayout
* level of this WebView.
*/
public void invokeZoomPicker() {
+ checkThread();
if (!getSettings().supportZoom()) {
Log.w(LOGTAG, "This WebView doesn't support zoom.");
return;
@@ -2206,6 +2316,7 @@ public class WebView extends AbsoluteLayout
* HitTestResult type is set to UNKNOWN_TYPE.
*/
public HitTestResult getHitTestResult() {
+ checkThread();
return hitTestResult(mInitialHitTestResult);
}
@@ -2287,6 +2398,7 @@ public class WebView extends AbsoluteLayout
* - "src" returns the image's src attribute.
*/
public void requestFocusNodeHref(Message hrefMsg) {
+ checkThread();
if (hrefMsg == null) {
return;
}
@@ -2315,6 +2427,7 @@ public class WebView extends AbsoluteLayout
* as the data member with "url" as key. The result can be null.
*/
public void requestImageRef(Message msg) {
+ checkThread();
if (0 == mNativeClass) return; // client isn't initialized
int contentX = viewToContentX(mLastTouchX + mScrollX);
int contentY = viewToContentY(mLastTouchY + mScrollY);
@@ -2393,6 +2506,8 @@ public class WebView extends AbsoluteLayout
*/
public void setTitleBarGravity(int gravity) {
mTitleGravity = gravity;
+ // force refresh
+ invalidate();
}
/**
@@ -2809,6 +2924,7 @@ public class WebView extends AbsoluteLayout
* @return The url for the current page.
*/
public String getUrl() {
+ checkThread();
WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
return h != null ? h.getUrl() : null;
}
@@ -2822,6 +2938,7 @@ public class WebView extends AbsoluteLayout
* @return The url that was originally requested for the current page.
*/
public String getOriginalUrl() {
+ checkThread();
WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
return h != null ? h.getOriginalUrl() : null;
}
@@ -2832,6 +2949,7 @@ public class WebView extends AbsoluteLayout
* @return The title for the current page.
*/
public String getTitle() {
+ checkThread();
WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
return h != null ? h.getTitle() : null;
}
@@ -2842,6 +2960,7 @@ public class WebView extends AbsoluteLayout
* @return The favicon for the current page.
*/
public Bitmap getFavicon() {
+ checkThread();
WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
return h != null ? h.getFavicon() : null;
}
@@ -2862,6 +2981,7 @@ public class WebView extends AbsoluteLayout
* @return The progress for the current page between 0 and 100.
*/
public int getProgress() {
+ checkThread();
return mCallbackProxy.getProgress();
}
@@ -2869,6 +2989,7 @@ public class WebView extends AbsoluteLayout
* @return the height of the HTML content.
*/
public int getContentHeight() {
+ checkThread();
return mContentHeight;
}
@@ -2886,6 +3007,7 @@ public class WebView extends AbsoluteLayout
* useful if the application has been paused.
*/
public void pauseTimers() {
+ checkThread();
mWebViewCore.sendMessage(EventHub.PAUSE_TIMERS);
}
@@ -2894,6 +3016,7 @@ public class WebView extends AbsoluteLayout
* This will resume dispatching all timers.
*/
public void resumeTimers() {
+ checkThread();
mWebViewCore.sendMessage(EventHub.RESUME_TIMERS);
}
@@ -2906,6 +3029,7 @@ public class WebView extends AbsoluteLayout
* Note that this differs from pauseTimers(), which affects all WebViews.
*/
public void onPause() {
+ checkThread();
if (!mIsPaused) {
mIsPaused = true;
mWebViewCore.sendMessage(EventHub.ON_PAUSE);
@@ -2921,6 +3045,7 @@ public class WebView extends AbsoluteLayout
* Call this to resume a WebView after a previous call to onPause().
*/
public void onResume() {
+ checkThread();
if (mIsPaused) {
mIsPaused = false;
mWebViewCore.sendMessage(EventHub.ON_RESUME);
@@ -2941,6 +3066,7 @@ public class WebView extends AbsoluteLayout
* free any available memory.
*/
public void freeMemory() {
+ checkThread();
mWebViewCore.sendMessage(EventHub.FREE_MEMORY);
}
@@ -2951,6 +3077,7 @@ public class WebView extends AbsoluteLayout
* @param includeDiskFiles If false, only the RAM cache is cleared.
*/
public void clearCache(boolean includeDiskFiles) {
+ checkThread();
// Note: this really needs to be a static method as it clears cache for all
// WebView. But we need mWebViewCore to send message to WebCore thread, so
// we can't make this static.
@@ -2963,6 +3090,7 @@ public class WebView extends AbsoluteLayout
* currently focused textfield if there is one.
*/
public void clearFormData() {
+ checkThread();
if (inEditingMode()) {
AutoCompleteAdapter adapter = null;
mWebTextView.setAdapterCustom(adapter);
@@ -2973,6 +3101,7 @@ public class WebView extends AbsoluteLayout
* Tell the WebView to clear its internal back/forward list.
*/
public void clearHistory() {
+ checkThread();
mCallbackProxy.getBackForwardList().setClearPending();
mWebViewCore.sendMessage(EventHub.CLEAR_HISTORY);
}
@@ -2982,6 +3111,7 @@ public class WebView extends AbsoluteLayout
* certificate errors.
*/
public void clearSslPreferences() {
+ checkThread();
mWebViewCore.sendMessage(EventHub.CLEAR_SSL_PREF_TABLE);
}
@@ -2994,6 +3124,7 @@ public class WebView extends AbsoluteLayout
* updated to reflect any new state.
*/
public WebBackForwardList copyBackForwardList() {
+ checkThread();
return mCallbackProxy.getBackForwardList().clone();
}
@@ -3005,6 +3136,7 @@ public class WebView extends AbsoluteLayout
* @param forward Direction to search.
*/
public void findNext(boolean forward) {
+ checkThread();
if (0 == mNativeClass) return; // client isn't initialized
nativeFindNext(forward);
}
@@ -3016,6 +3148,7 @@ public class WebView extends AbsoluteLayout
* that were found.
*/
public int findAll(String find) {
+ checkThread();
if (0 == mNativeClass) return 0; // client isn't initialized
int result = find != null ? nativeFindAll(find.toLowerCase(),
find.toUpperCase(), find.equalsIgnoreCase(mLastFind)) : 0;
@@ -3035,6 +3168,7 @@ public class WebView extends AbsoluteLayout
* @return boolean True if the find dialog is shown, false otherwise.
*/
public boolean showFindDialog(String text, boolean showIme) {
+ checkThread();
FindActionModeCallback callback = new FindActionModeCallback(mContext);
if (getParent() == null || startActionMode(callback) == null) {
// Could not start the action mode, so end Find on page
@@ -3111,6 +3245,7 @@ public class WebView extends AbsoluteLayout
* @return the address, or if no address is found, return null.
*/
public static String findAddress(String addr) {
+ checkThread();
return findAddress(addr, false);
}
@@ -3144,6 +3279,7 @@ public class WebView extends AbsoluteLayout
* Clear the highlighting surrounding text matches created by findAll.
*/
public void clearMatches() {
+ checkThread();
if (mNativeClass == 0)
return;
nativeSetFindIsEmpty();
@@ -3173,6 +3309,7 @@ public class WebView extends AbsoluteLayout
* @param response The message that will be dispatched with the result.
*/
public void documentHasImages(Message response) {
+ checkThread();
if (response == null) {
return;
}
@@ -3568,6 +3705,7 @@ public class WebView extends AbsoluteLayout
* @param client An implementation of WebViewClient.
*/
public void setWebViewClient(WebViewClient client) {
+ checkThread();
mCallbackProxy.setWebViewClient(client);
}
@@ -3588,6 +3726,7 @@ public class WebView extends AbsoluteLayout
* @param listener An implementation of DownloadListener.
*/
public void setDownloadListener(DownloadListener listener) {
+ checkThread();
mCallbackProxy.setDownloadListener(listener);
}
@@ -3598,6 +3737,7 @@ public class WebView extends AbsoluteLayout
* @param client An implementation of WebChromeClient.
*/
public void setWebChromeClient(WebChromeClient client) {
+ checkThread();
mCallbackProxy.setWebChromeClient(client);
}
@@ -3638,6 +3778,7 @@ public class WebView extends AbsoluteLayout
*/
@Deprecated
public void setPictureListener(PictureListener listener) {
+ checkThread();
mPictureListener = listener;
}
@@ -3679,6 +3820,7 @@ public class WebView extends AbsoluteLayout
* JavaScript.
*/
public void addJavascriptInterface(Object obj, String interfaceName) {
+ checkThread();
if (obj == null) {
return;
}
@@ -3693,6 +3835,7 @@ public class WebView extends AbsoluteLayout
* @param interfaceName The name of the interface to remove.
*/
public void removeJavascriptInterface(String interfaceName) {
+ checkThread();
if (mWebViewCore != null) {
WebViewCore.JSInterfaceData arg = new WebViewCore.JSInterfaceData();
arg.mInterfaceName = interfaceName;
@@ -3707,6 +3850,7 @@ public class WebView extends AbsoluteLayout
* settings.
*/
public WebSettings getSettings() {
+ checkThread();
return (mWebViewCore != null) ? mWebViewCore.getSettings() : null;
}
@@ -3719,6 +3863,7 @@ public class WebView extends AbsoluteLayout
*/
@Deprecated
public static synchronized PluginList getPluginList() {
+ checkThread();
return new PluginList();
}
@@ -3727,7 +3872,9 @@ public class WebView extends AbsoluteLayout
* @deprecated This was used for Gears, which has been deprecated.
*/
@Deprecated
- public void refreshPlugins(boolean reloadOpenPages) { }
+ public void refreshPlugins(boolean reloadOpenPages) {
+ checkThread();
+ }
//-------------------------------------------------------------------------
// Override View methods
@@ -3736,7 +3883,7 @@ public class WebView extends AbsoluteLayout
@Override
protected void finalize() throws Throwable {
try {
- destroy();
+ destroyImpl();
} finally {
super.finalize();
}
@@ -4235,15 +4382,20 @@ public class WebView extends AbsoluteLayout
}
WebViewCore.CursorData cursorData() {
- WebViewCore.CursorData result = new WebViewCore.CursorData();
- result.mMoveGeneration = nativeMoveGeneration();
- result.mFrame = nativeCursorFramePointer();
+ WebViewCore.CursorData result = cursorDataNoPosition();
Point position = nativeCursorPosition();
result.mX = position.x;
result.mY = position.y;
return result;
}
+ WebViewCore.CursorData cursorDataNoPosition() {
+ WebViewCore.CursorData result = new WebViewCore.CursorData();
+ result.mMoveGeneration = nativeMoveGeneration();
+ result.mFrame = nativeCursorFramePointer();
+ return result;
+ }
+
/**
* Delete text from start to end in the focused textfield. If there is no
* focus, or if start == end, silently fail. If start and end are out of
@@ -4999,6 +5151,7 @@ public class WebView extends AbsoluteLayout
*/
@Deprecated
public void emulateShiftHeld() {
+ checkThread();
setUpSelect(false, 0, 0);
}
@@ -5442,6 +5595,18 @@ public class WebView extends AbsoluteLayout
private static final int DRAG_LAYER_FINGER_DISTANCE = 20000;
@Override
+ public boolean onHoverEvent(MotionEvent event) {
+ if (mNativeClass == 0) {
+ return false;
+ }
+ WebViewCore.CursorData data = cursorDataNoPosition();
+ data.mX = viewToContentX((int) event.getX());
+ data.mY = viewToContentY((int) event.getY());
+ mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE, data);
+ return true;
+ }
+
+ @Override
public boolean onTouchEvent(MotionEvent ev) {
if (mNativeClass == 0 || (!isClickable() && !isLongClickable())) {
return false;
@@ -6268,6 +6433,7 @@ public class WebView extends AbsoluteLayout
private boolean mMapTrackballToArrowKeys = true;
public void setMapTrackballToArrowKeys(boolean setMap) {
+ checkThread();
mMapTrackballToArrowKeys = setMap;
}
@@ -6560,6 +6726,7 @@ public class WebView extends AbsoluteLayout
}
public void flingScroll(int vx, int vy) {
+ checkThread();
mScroller.fling(mScrollX, mScrollY, vx, vy, 0, computeMaxScrollX(), 0,
computeMaxScrollY(), mOverflingDistance, mOverflingDistance);
invalidate();
@@ -6695,6 +6862,7 @@ public class WebView extends AbsoluteLayout
*/
@Deprecated
public View getZoomControls() {
+ checkThread();
if (!getSettings().supportZoom()) {
Log.w(LOGTAG, "This WebView doesn't support zoom.");
return null;
@@ -6714,6 +6882,7 @@ public class WebView extends AbsoluteLayout
* @return TRUE if the WebView can be zoomed in.
*/
public boolean canZoomIn() {
+ checkThread();
return mZoomManager.canZoomIn();
}
@@ -6721,6 +6890,7 @@ public class WebView extends AbsoluteLayout
* @return TRUE if the WebView can be zoomed out.
*/
public boolean canZoomOut() {
+ checkThread();
return mZoomManager.canZoomOut();
}
@@ -6729,6 +6899,7 @@ public class WebView extends AbsoluteLayout
* @return TRUE if zoom in succeeds. FALSE if no zoom changes.
*/
public boolean zoomIn() {
+ checkThread();
return mZoomManager.zoomIn();
}
@@ -6737,6 +6908,7 @@ public class WebView extends AbsoluteLayout
* @return TRUE if zoom out succeeds. FALSE if no zoom changes.
*/
public boolean zoomOut() {
+ checkThread();
return mZoomManager.zoomOut();
}
@@ -7154,7 +7326,10 @@ public class WebView extends AbsoluteLayout
cursorData(), 1000);
}
- /* package */ synchronized WebViewCore getWebViewCore() {
+ /**
+ * @hide
+ */
+ public synchronized WebViewCore getWebViewCore() {
return mWebViewCore;
}
@@ -7865,7 +8040,10 @@ public class WebView extends AbsoluteLayout
}
case WEBCORE_INITIALIZED_MSG_ID:
// nativeCreate sets mNativeClass to a non-zero value
- nativeCreate(msg.arg1);
+ String drawableDir = BrowserFrame.getRawResFilename(
+ BrowserFrame.DRAWABLEDIR, mContext);
+ AssetManager am = mContext.getAssets();
+ nativeCreate(msg.arg1, drawableDir, am);
break;
case UPDATE_TEXTFIELD_TEXT_MSG_ID:
// Make sure that the textfield is currently focused
@@ -8694,6 +8872,7 @@ public class WebView extends AbsoluteLayout
*/
@Deprecated
public void debugDump() {
+ checkThread();
nativeDebugDump();
mWebViewCore.sendMessage(EventHub.DUMP_NAVTREE);
}
@@ -8755,12 +8934,24 @@ public class WebView extends AbsoluteLayout
return mViewManager;
}
+ private static void checkThread() {
+ if (Looper.myLooper() != Looper.getMainLooper()) {
+ RuntimeException exception = new RuntimeException(
+ "A WebView method was called on thread '" +
+ Thread.currentThread().getName() + "'. " +
+ "All WebView methods must be called on the UI thread. " +
+ "Future versions of WebView may not support use on other threads.");
+ Log.e(LOGTAG, Log.getStackTraceString(exception));
+ StrictMode.onWebViewMethodCalledOnWrongThread(exception);
+ }
+ }
+
private native int nativeCacheHitFramePointer();
private native boolean nativeCacheHitIsPlugin();
private native Rect nativeCacheHitNodeBounds();
private native int nativeCacheHitNodePointer();
/* package */ native void nativeClearCursor();
- private native void nativeCreate(int ptr);
+ private native void nativeCreate(int ptr, String drawableDir, AssetManager am);
private native int nativeCursorFramePointer();
private native Rect nativeCursorNodeBounds();
private native int nativeCursorNodePointer();
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index 0271695..e8083eb 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -48,7 +48,10 @@ import java.util.Set;
import junit.framework.Assert;
-final class WebViewCore {
+/**
+ * @hide
+ */
+public final class WebViewCore {
private static final String LOGTAG = "webcore";
@@ -905,7 +908,10 @@ final class WebViewCore {
"REMOVE_JS_INTERFACE", // = 149;
};
- class EventHub {
+ /**
+ * @hide
+ */
+ public class EventHub {
// Message Ids
static final int REVEAL_SELECTION = 96;
static final int REQUEST_LABEL = 97;
@@ -936,7 +942,7 @@ final class WebViewCore {
static final int DELETE_SELECTION = 122;
static final int LISTBOX_CHOICES = 123;
static final int SINGLE_LISTBOX_CHOICE = 124;
- static final int MESSAGE_RELAY = 125;
+ public static final int MESSAGE_RELAY = 125;
static final int SET_BACKGROUND_COLOR = 126;
static final int SET_MOVE_FOCUS = 127;
static final int SAVE_DOCUMENT_STATE = 128;
@@ -1702,7 +1708,10 @@ final class WebViewCore {
// If it needs WebCore, it has to send message.
//-------------------------------------------------------------------------
- void sendMessage(Message msg) {
+ /**
+ * @hide
+ */
+ public void sendMessage(Message msg) {
mEventHub.sendMessage(msg);
}
@@ -2253,6 +2262,27 @@ final class WebViewCore {
// set the viewport settings from WebKit
setViewportSettingsFromNative();
+ if (mSettings.forceUserScalable()) {
+ mViewportUserScalable = true;
+ if (mViewportInitialScale > 0) {
+ if (mViewportMinimumScale > 0) {
+ mViewportMinimumScale = Math.min(mViewportMinimumScale,
+ mViewportInitialScale / 2);
+ }
+ if (mViewportMaximumScale > 0) {
+ mViewportMaximumScale = Math.max(mViewportMaximumScale,
+ mViewportInitialScale * 2);
+ }
+ } else {
+ if (mViewportMinimumScale > 0) {
+ mViewportMinimumScale = Math.min(mViewportMinimumScale, 50);
+ }
+ if (mViewportMaximumScale > 0) {
+ mViewportMaximumScale = Math.max(mViewportMaximumScale, 200);
+ }
+ }
+ }
+
// adjust the default scale to match the densityDpi
float adjust = 1.0f;
if (mViewportDensityDpi == -1) {
@@ -2589,11 +2619,11 @@ final class WebViewCore {
// called by JNI
private Class<?> getPluginClass(String libName, String clsName) {
-
+
if (mWebView == null) {
return null;
}
-
+
PluginManager pluginManager = PluginManager.getInstance(null);
String pkgName = pluginManager.getPluginsAPKName(libName);
@@ -2601,7 +2631,7 @@ final class WebViewCore {
Log.w(LOGTAG, "Unable to resolve " + libName + " to a plugin APK");
return null;
}
-
+
try {
return pluginManager.getPluginClass(pkgName, clsName);
} catch (NameNotFoundException e) {
@@ -2656,7 +2686,7 @@ final class WebViewCore {
view.mView = pluginView;
return view;
}
-
+
// called by JNI. PluginWidget functions for creating an embedded View for
// the surface drawing model.
private ViewManager.ChildView addSurface(View pluginView, int x, int y,
diff --git a/core/java/android/webkit/webdriver/By.java b/core/java/android/webkit/webdriver/By.java
new file mode 100644
index 0000000..fa4fe74
--- /dev/null
+++ b/core/java/android/webkit/webdriver/By.java
@@ -0,0 +1,252 @@
+/*
+ * Copyright (C) 2011 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.webkit.webdriver;
+
+import java.util.List;
+
+/**
+ * Mechanism to locate elements within the DOM of the page.
+ * @hide
+ */
+public abstract class By {
+ public abstract WebElement findElement(WebElement element);
+ public abstract List<WebElement> findElements(WebElement element);
+
+ /**
+ * Locates an element by its HTML id attribute.
+ *
+ * @param id The HTML id attribute to look for.
+ * @return A By instance that locates elements by their HTML id attributes.
+ */
+ public static By id(final String id) {
+ throwIfNull(id);
+ return new By() {
+ @Override
+ public WebElement findElement(WebElement element) {
+ return element.findElementById(id);
+ }
+
+ @Override
+ public List<WebElement> findElements(WebElement element) {
+ return element.findElementsById(id); // Yes, it happens a lot.
+ }
+
+ @Override
+ public String toString() {
+ return "By.id: " + id;
+ }
+ };
+ }
+
+ /**
+ * Locates an element by the matching the exact text on the HTML link.
+ *
+ * @param linkText The exact text to match against.
+ * @return A By instance that locates elements by the text displayed by
+ * the link.
+ */
+ public static By linkText(final String linkText) {
+ throwIfNull(linkText);
+ return new By() {
+ @Override
+ public WebElement findElement(WebElement element) {
+ return element.findElementByLinkText(linkText);
+ }
+
+ @Override
+ public List<WebElement> findElements(WebElement element) {
+ return element.findElementsByLinkText(linkText);
+ }
+
+ @Override
+ public String toString() {
+ return "By.linkText: " + linkText;
+ }
+ };
+ }
+
+ /**
+ * Locates an element by matching partial part of the text displayed by an
+ * HTML link.
+ *
+ * @param linkText The text that should be contained by the text displayed
+ * on the link.
+ * @return A By instance that locates elements that contain the given link
+ * text.
+ */
+ public static By partialLinkText(final String linkText) {
+ throwIfNull(linkText);
+ return new By() {
+ @Override
+ public WebElement findElement(WebElement element) {
+ return element.findElementByPartialLinkText(linkText);
+ }
+
+ @Override
+ public List<WebElement> findElements(WebElement element) {
+ return element.findElementsByPartialLinkText(linkText);
+ }
+
+ @Override
+ public String toString() {
+ return "By.partialLinkText: " + linkText;
+ }
+ };
+ }
+
+ /**
+ * Locates an element by matching its HTML name attribute.
+ *
+ * @param name The value of the HTML name attribute.
+ * @return A By instance that locates elements by the HTML name attribute.
+ */
+ public static By name(final String name) {
+ throwIfNull(name);
+ return new By() {
+ @Override
+ public WebElement findElement(WebElement element) {
+ return element.findElementByName(name);
+ }
+
+ @Override
+ public List<WebElement> findElements(WebElement element) {
+ return element.findElementsByName(name);
+ }
+
+ @Override
+ public String toString() {
+ return "By.name: " + name;
+ }
+ };
+ }
+
+ /**
+ * Locates an element by matching its class name.
+ * @param className The class name
+ * @return A By instance that locates elements by their class name attribute.
+ */
+ public static By className(final String className) {
+ throwIfNull(className);
+ return new By() {
+ @Override
+ public WebElement findElement(WebElement element) {
+ return element.findElementByClassName(className);
+ }
+
+ @Override
+ public List<WebElement> findElements(WebElement element) {
+ return element.findElementsByClassName(className);
+ }
+
+ @Override
+ public String toString() {
+ return "By.className: " + className;
+ }
+ };
+ }
+
+ /**
+ * Locates an element by matching its css property.
+ *
+ * @param css The css property.
+ * @return A By instance that locates elements by their css property.
+ */
+ public static By css(final String css) {
+ throwIfNull(css);
+ return new By() {
+ @Override
+ public WebElement findElement(WebElement element) {
+ return element.findElementByCss(css);
+ }
+
+ @Override
+ public List<WebElement> findElements(WebElement element) {
+ return element.findElementsByCss(css);
+ }
+
+ @Override
+ public String toString() {
+ return "By.css: " + css;
+ }
+ };
+ }
+
+ /**
+ * Locates an element by matching its HTML tag name.
+ *
+ * @param tagName The HTML tag name to look for.
+ * @return A By instance that locates elements using the name of the
+ * HTML tag.
+ */
+ public static By tagName(final String tagName) {
+ throwIfNull(tagName);
+ return new By() {
+ @Override
+ public WebElement findElement(WebElement element) {
+ return element.findElementByTagName(tagName);
+ }
+
+ @Override
+ public List<WebElement> findElements(WebElement element) {
+ return element.findElementsByTagName(tagName);
+ }
+
+ @Override
+ public String toString() {
+ return "By.tagName: " + tagName;
+ }
+ };
+ }
+
+ /**
+ * Locates an element using an XPath expression.
+ *
+ * <p>When using XPath, be aware that this follows standard conventions: a
+ * search prefixed with "//" will search the entire document, not just the
+ * children of the current node. Use ".//" to limit your search to the
+ * children of this {@link android.webkit.webdriver.WebElement}.
+ *
+ * @param xpath The XPath expression to use.
+ * @return A By instance that locates elements using the given XPath.
+ */
+ public static By xpath(final String xpath) {
+ throwIfNull(xpath);
+ return new By() {
+ @Override
+ public WebElement findElement(WebElement element) {
+ return element.findElementByXPath(xpath);
+ }
+
+ @Override
+ public List<WebElement> findElements(WebElement element) {
+ return element.findElementsByXPath(xpath);
+ }
+
+ @Override
+ public String toString() {
+ return "By.xpath: " + xpath;
+ }
+ };
+ }
+
+ private static void throwIfNull(String argument) {
+ if (argument == null) {
+ throw new IllegalArgumentException(
+ "Cannot find elements with null locator.");
+ }
+ }
+}
diff --git a/core/java/android/webkit/webdriver/WebDriver.java b/core/java/android/webkit/webdriver/WebDriver.java
new file mode 100644
index 0000000..79e6523
--- /dev/null
+++ b/core/java/android/webkit/webdriver/WebDriver.java
@@ -0,0 +1,843 @@
+/*
+ * Copyright (C) 2011 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.webkit.webdriver;
+
+import android.graphics.Point;
+import android.os.Handler;
+import android.os.Message;
+import android.os.SystemClock;
+import android.view.InputDevice;
+import android.view.KeyCharacterMap;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.webkit.WebView;
+import android.webkit.WebViewCore;
+
+import com.google.android.collect.Lists;
+import com.google.android.collect.Maps;
+
+import com.android.internal.R;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Drives a web application by controlling the WebView. This class
+ * provides a DOM-like API allowing to get information about the page,
+ * navigate, and interact with the web application. This is particularly useful
+ * for testing a web application.
+ *
+ * <p/>{@link android.webkit.webdriver.WebDriver} should be created in the main
+ * thread, and invoked from another thread. Here is a sample usage:
+ *
+ * public class WebDriverStubActivity extends Activity {
+ * private WebDriver mDriver;
+ *
+ * public void onCreate(Bundle savedInstanceState) {
+ * super.onCreate(savedInstanceState);
+ * WebView view = new WebView(this);
+ * mDriver = new WebDriver(view);
+ * setContentView(view);
+ * }
+ *
+ *
+ * public WebDriver getDriver() {
+ * return mDriver;
+ * }
+ *}
+ *
+ * public class WebDriverTest extends
+ * ActivityInstrumentationTestCase2<WebDriverStubActivity>{
+ * private WebDriver mDriver;
+ *
+ * public WebDriverTest() {
+ * super(WebDriverStubActivity.class);
+ * }
+ *
+ * protected void setUp() throws Exception {
+ * super.setUp();
+ * mDriver = getActivity().getDriver();
+ * }
+ *
+ * public void testGoogle() {
+ * mDriver.get("http://google.com");
+ * WebElement searchBox = mDriver.findElement(By.name("q"));
+ * q.sendKeys("Cheese!");
+ * q.submit();
+ * assertTrue(mDriver.findElements(By.partialLinkText("Cheese")).size() > 0);
+ * }
+ *}
+ *
+ * @hide
+ */
+public class WebDriver {
+ // Timeout for page load in milliseconds.
+ private static final int LOADING_TIMEOUT = 30000;
+ // Timeout for executing JavaScript in the WebView in milliseconds.
+ private static final int JS_EXECUTION_TIMEOUT = 10000;
+ // Timeout for the MotionEvent to be completely handled
+ private static final int MOTION_EVENT_TIMEOUT = 1000;
+ // Timeout for detecting a new page load
+ private static final int PAGE_STARTED_LOADING = 500;
+ // Timeout for handling KeyEvents
+ private static final int KEY_EVENT_TIMEOUT = 2000;
+
+ // Commands posted to the handler
+ private static final int CMD_GET_URL = 1;
+ private static final int CMD_EXECUTE_SCRIPT = 2;
+ private static final int CMD_SEND_TOUCH = 3;
+ private static final int CMD_SEND_KEYS = 4;
+ private static final int CMD_NAV_REFRESH = 5;
+ private static final int CMD_NAV_BACK = 6;
+ private static final int CMD_NAV_FORWARD = 7;
+ private static final int CMD_SEND_KEYCODE = 8;
+ private static final int CMD_MOVE_CURSOR_RIGHTMOST_POS = 9;
+ private static final int CMD_MESSAGE_RELAY_ECHO = 10;
+
+ private static final String ELEMENT_KEY = "ELEMENT";
+ private static final String STATUS = "status";
+ private static final String VALUE = "value";
+
+ private static final long MAIN_THREAD = Thread.currentThread().getId();
+
+ // This is updated by a callabck from JavaScript when the result is ready.
+ private String mJsResult;
+
+ // Used for synchronization
+ private final Object mSyncObject;
+ private final Object mSyncPageLoad;
+
+ // Updated when the command is done executing in the main thread.
+ private volatile boolean mCommandDone;
+ // Used by WebViewClientWrapper.onPageStarted() to notify that
+ // a page started loading.
+ private volatile boolean mPageStartedLoading;
+ // Used by WebChromeClientWrapper.onProgressChanged to notify when
+ // a page finished loading.
+ private volatile boolean mPageFinishedLoading;
+ private WebView mWebView;
+ private Navigation mNavigation;
+ // This WebElement represents the object document.documentElement
+ private WebElement mDocumentElement;
+
+
+ // This Handler runs in the main UI thread.
+ private final Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case CMD_GET_URL:
+ final String url = (String) msg.obj;
+ mWebView.loadUrl(url);
+ break;
+ case CMD_EXECUTE_SCRIPT:
+ mWebView.loadUrl("javascript:" + (String) msg.obj);
+ break;
+ case CMD_MESSAGE_RELAY_ECHO:
+ notifyCommandDone();
+ break;
+ case CMD_SEND_TOUCH:
+ touchScreen((Point) msg.obj);
+ notifyCommandDone();
+ break;
+ case CMD_SEND_KEYS:
+ dispatchKeys((CharSequence[]) msg.obj);
+ notifyCommandDone();
+ break;
+ case CMD_NAV_REFRESH:
+ mWebView.reload();
+ break;
+ case CMD_NAV_BACK:
+ mWebView.goBack();
+ break;
+ case CMD_NAV_FORWARD:
+ mWebView.goForward();
+ break;
+ case CMD_SEND_KEYCODE:
+ dispatchKeyCodes((int[]) msg.obj);
+ notifyCommandDone();
+ break;
+ case CMD_MOVE_CURSOR_RIGHTMOST_POS:
+ moveCursorToLeftMostPos((String) msg.obj);
+ notifyCommandDone();
+ break;
+ }
+ }
+ };
+
+ /**
+ * Error codes from the WebDriver wire protocol
+ * http://code.google.com/p/selenium/wiki/JsonWireProtocol#Response_Status_Codes
+ */
+ private enum ErrorCode {
+ SUCCESS(0),
+ NO_SUCH_ELEMENT(7),
+ NO_SUCH_FRAME(8),
+ UNKNOWN_COMMAND(9),
+ UNSUPPORTED_OPERATION(9), // Alias
+ STALE_ELEMENT_REFERENCE(10),
+ ELEMENT_NOT_VISISBLE(11),
+ INVALID_ELEMENT_STATE(12),
+ UNKNOWN_ERROR(13),
+ ELEMENT_NOT_SELECTABLE(15),
+ XPATH_LOOKUP_ERROR(19),
+ NO_SUCH_WINDOW(23),
+ INVALID_COOKIE_DOMAIN(24),
+ UNABLE_TO_SET_COOKIE(25),
+ MODAL_DIALOG_OPENED(26),
+ MODAL_DIALOG_OPEN(27),
+ SCRIPT_TIMEOUT(28);
+
+ private final int mCode;
+ private static ErrorCode[] values = ErrorCode.values();
+
+ ErrorCode(int code) {
+ this.mCode = code;
+ }
+
+ public int getCode() {
+ return mCode;
+ }
+
+ public static ErrorCode get(final int intValue) {
+ for (int i = 0; i < values.length; i++) {
+ if (values[i].getCode() == intValue) {
+ return values[i];
+ }
+ }
+ return UNKNOWN_ERROR;
+ }
+ }
+
+ public WebDriver(WebView webview) {
+ this.mWebView = webview;
+ mWebView.requestFocus();
+ if (mWebView == null) {
+ throw new IllegalArgumentException("WebView cannot be null");
+ }
+ if (!mWebView.getSettings().getJavaScriptEnabled()) {
+ throw new RuntimeException("Javascript is disabled in the WebView. "
+ + "Enable it to use WebDriver");
+ }
+ shouldRunInMainThread(true);
+
+ mSyncObject = new Object();
+ mSyncPageLoad = new Object();
+ this.mWebView = webview;
+ WebChromeClientWrapper chromeWrapper = new WebChromeClientWrapper(
+ webview.getWebChromeClient(), this);
+ mWebView.setWebChromeClient(chromeWrapper);
+ WebViewClientWrapper viewWrapper = new WebViewClientWrapper(
+ webview.getWebViewClient(), this);
+ mWebView.setWebViewClient(viewWrapper);
+ mWebView.addJavascriptInterface(new JavascriptResultReady(),
+ "webdriver");
+ mDocumentElement = new WebElement(this, "");
+ mNavigation = new Navigation();
+ }
+
+ /**
+ * @return The title of the current page, null if not set.
+ */
+ public String getTitle() {
+ return mWebView.getTitle();
+ }
+
+ /**
+ * Loads a URL in the WebView. This function is blocking and will return
+ * when the page has finished loading.
+ *
+ * @param url The URL to load.
+ */
+ public void get(String url) {
+ mNavigation.to(url);
+ }
+
+ /**
+ * @return The source page of the currently loaded page in WebView.
+ */
+ public String getPageSource() {
+ return (String) executeScript("return new XMLSerializer()."
+ + "serializeToString(document);");
+ }
+
+ /**
+ * Find the first {@link android.webkit.webdriver.WebElement} using the
+ * given method.
+ *
+ * @param by The locating mechanism to use.
+ * @return The first matching element on the current context.
+ * @throws {@link android.webkit.webdriver.WebElementNotFoundException} if
+ * no matching element was found.
+ */
+ public WebElement findElement(By by) {
+ checkNotNull(mDocumentElement, "Load a page using WebDriver.get() "
+ + "before looking for elements.");
+ return by.findElement(mDocumentElement);
+ }
+
+ /**
+ * Finds all {@link android.webkit.webdriver.WebElement} within the page
+ * using the given method.
+ *
+ * @param by The locating mechanism to use.
+ * @return A list of all {@link android.webkit.webdriver.WebElement} found,
+ * or an empty list if nothing matches.
+ */
+ public List<WebElement> findElements(By by) {
+ checkNotNull(mDocumentElement, "Load a page using WebDriver.get() "
+ + "before looking for elements.");
+ return by.findElements(mDocumentElement);
+ }
+
+ /**
+ * Clears the WebView's state and closes associated views.
+ */
+ public void quit() {
+ mWebView.clearCache(true);
+ mWebView.clearFormData();
+ mWebView.clearHistory();
+ mWebView.clearSslPreferences();
+ mWebView.clearView();
+ mWebView.removeAllViewsInLayout();
+ }
+
+ /**
+ * Executes javascript in the context of the main frame.
+ *
+ * If the script has a return value the following happens:
+ * <ul>
+ * <li>For an HTML element, this method returns a WebElement</li>
+ * <li>For a decimal, a Double is returned</li>
+ * <li>For non-decimal number, a Long is returned</li>
+ * <li>For a boolean, a Boolean is returned</li>
+ * <li>For all other cases, a String is returned</li>
+ * <li>For an array, this returns a List<Object> with each object
+ * following the rules above.</li>
+ * <li>For an object literal this returns a Map<String, Object>. Note that
+ * Object literals keys can only be Strings. Non Strings keys will
+ * be filtered out.</li>
+ * </ul>
+ *
+ * <p> Arguments must be a number, a boolean, a string a WebElement or
+ * a list of any combination of the above. The arguments will be made
+ * available to the javascript via the "arguments" magic variable,
+ * as if the function was called via "Function.apply".
+ *
+ * @param script The JavaScript to execute.
+ * @param args The arguments to the script. Can be any of a number, boolean,
+ * string, WebElement or a List of those.
+ * @return A Boolean, Long, Double, String, WebElement, List or null.
+ */
+ public Object executeScript(final String script, final Object... args) {
+ String scriptArgs = "[" + convertToJsArgs(args) + "]";
+ String injectScriptJs = getResourceAsString(R.raw.execute_script_android);
+ return executeRawJavascript("(" + injectScriptJs +
+ ")(" + escapeAndQuote(script) + ", " + scriptArgs + ", true)");
+ }
+
+ public Navigation navigate() {
+ return mNavigation;
+ }
+
+
+ /**
+ * @hide
+ */
+ public class Navigation {
+ /* package */ Navigation () {}
+
+ public void back() {
+ navigate(CMD_NAV_BACK, null);
+ }
+
+ public void forward() {
+ navigate(CMD_NAV_FORWARD, null);
+ }
+
+ public void to(String url) {
+ navigate(CMD_GET_URL, url);
+ }
+
+ public void refresh() {
+ navigate(CMD_NAV_REFRESH, null);
+ }
+
+ private void navigate(int command, String url) {
+ synchronized (mSyncPageLoad) {
+ mPageFinishedLoading = false;
+ Message msg = mHandler.obtainMessage(command);
+ msg.obj = url;
+ mHandler.sendMessage(msg);
+ waitForPageLoad();
+ }
+ }
+ }
+
+ /**
+ * Converts the arguments passed to a JavaScript friendly format.
+ *
+ * @param args The arguments to convert.
+ * @return Comma separated Strings containing the arguments.
+ */
+ /* package */ String convertToJsArgs(final Object... args) {
+ StringBuilder toReturn = new StringBuilder();
+ int length = args.length;
+ for (int i = 0; i < length; i++) {
+ toReturn.append((i > 0) ? "," : "");
+ if (args[i] instanceof List<?>) {
+ toReturn.append("[");
+ List<Object> aList = (List<Object>) args[i];
+ for (int j = 0 ; j < aList.size(); j++) {
+ String comma = ((j == 0) ? "" : ",");
+ toReturn.append(comma + convertToJsArgs(aList.get(j)));
+ }
+ toReturn.append("]");
+ } else if (args[i] instanceof Map<?, ?>) {
+ Map<Object, Object> aMap = (Map<Object, Object>) args[i];
+ String toAdd = "{";
+ for (Object key: aMap.keySet()) {
+ toAdd += key + ":"
+ + convertToJsArgs(aMap.get(key)) + ",";
+ }
+ toReturn.append(toAdd.substring(0, toAdd.length() -1) + "}");
+ } else if (args[i] instanceof WebElement) {
+ // WebElement are represented in JavaScript by Objects as
+ // follow: {ELEMENT:"id"} where "id" refers to the id
+ // of the HTML element in the javascript cache that can
+ // be accessed throught bot.inject.cache.getCache_()
+ toReturn.append("{\"" + ELEMENT_KEY + "\":\""
+ + ((WebElement) args[i]).getId() + "\"}");
+ } else if (args[i] instanceof Number || args[i] instanceof Boolean) {
+ toReturn.append(String.valueOf(args[i]));
+ } else if (args[i] instanceof String) {
+ toReturn.append(escapeAndQuote((String) args[i]));
+ } else {
+ throw new IllegalArgumentException(
+ "Javascript arguments can be "
+ + "a Number, a Boolean, a String, a WebElement, "
+ + "or a List or a Map of those. Got: "
+ + ((args[i] == null) ? "null" : args[i].getClass()
+ + ", value: " + args[i].toString()));
+ }
+ }
+ return toReturn.toString();
+ }
+
+ /* package */ Object executeRawJavascript(final String script) {
+ if (mWebView.getUrl() == null) {
+ throw new WebDriverException("Cannot operate on a blank page. "
+ + "Load a page using WebDriver.get().");
+ }
+ String result = executeCommand(CMD_EXECUTE_SCRIPT,
+ "if (!window.webdriver || !window.webdriver.resultReady) {" +
+ " return;" +
+ "}" +
+ "window.webdriver.resultReady(" + script + ")",
+ JS_EXECUTION_TIMEOUT);
+ if (result == null || "undefined".equals(result)) {
+ return null;
+ }
+ try {
+ JSONObject json = new JSONObject(result);
+ throwIfError(json);
+ Object value = json.get(VALUE);
+ return convertJsonToJavaObject(value);
+ } catch (JSONException e) {
+ throw new RuntimeException("Failed to parse JavaScript result: "
+ + result.toString(), e);
+ }
+ }
+
+ /* package */ String getResourceAsString(final int resourceId) {
+ InputStream is = mWebView.getResources().openRawResource(resourceId);
+ BufferedReader br = new BufferedReader(new InputStreamReader(is));
+ StringBuilder sb = new StringBuilder();
+ String line = null;
+ try {
+ while ((line = br.readLine()) != null) {
+ sb.append(line);
+ }
+ br.close();
+ is.close();
+ } catch (IOException e) {
+ throw new RuntimeException("Failed to open JavaScript resource.", e);
+ }
+ return sb.toString();
+ }
+
+ /* package */ void sendTouchScreen(Point coords) {
+ // Reset state
+ resetPageLoadState();
+ executeCommand(CMD_SEND_TOUCH, coords,LOADING_TIMEOUT);
+ // Wait for the events to be fully handled
+ waitForMessageRelay(MOTION_EVENT_TIMEOUT);
+
+ // If a page started loading, block until page finishes loading
+ waitForPageLoadIfNeeded();
+ }
+
+ /* package */ void resetPageLoadState() {
+ synchronized (mSyncPageLoad) {
+ mPageStartedLoading = false;
+ mPageFinishedLoading = false;
+ }
+ }
+
+ /* package */ void waitForPageLoadIfNeeded() {
+ synchronized (mSyncPageLoad) {
+ Long end = System.currentTimeMillis() + PAGE_STARTED_LOADING;
+ // Wait PAGE_STARTED_LOADING milliseconds to see if we detect a
+ // page load.
+ while (!mPageStartedLoading && (System.currentTimeMillis() <= end)) {
+ try {
+ // This is notified by WebChromeClientWrapper#onProgressChanged
+ // when the page finished loading.
+ mSyncPageLoad.wait(PAGE_STARTED_LOADING);
+ } catch (InterruptedException e) {
+ new RuntimeException(e);
+ }
+ }
+ if (mPageStartedLoading) {
+ waitForPageLoad();
+ }
+ }
+ }
+
+ private void touchScreen(Point coords) {
+ // Convert to screen coords
+ // screen = JS x zoom - offset
+ float zoom = mWebView.getScale();
+ float xOffset = mWebView.getX();
+ float yOffset = mWebView.getY();
+ Point screenCoords = new Point( (int)(coords.x*zoom - xOffset),
+ (int)(coords.y*zoom - yOffset));
+
+ long downTime = SystemClock.uptimeMillis();
+ MotionEvent down = MotionEvent.obtain(downTime,
+ SystemClock.uptimeMillis(), MotionEvent.ACTION_DOWN, screenCoords.x,
+ screenCoords.y, 0);
+ down.setSource(InputDevice.SOURCE_TOUCHSCREEN);
+ MotionEvent up = MotionEvent.obtain(downTime,
+ SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, screenCoords.x,
+ screenCoords.y, 0);
+ up.setSource(InputDevice.SOURCE_TOUCHSCREEN);
+ // Dispatch the events to WebView
+ mWebView.dispatchTouchEvent(down);
+ mWebView.dispatchTouchEvent(up);
+ }
+
+ /* package */ void notifyPageStartedLoading() {
+ synchronized (mSyncPageLoad) {
+ mPageStartedLoading = true;
+ mSyncPageLoad.notify();
+ }
+ }
+
+ /* package */ void notifyPageFinishedLoading() {
+ synchronized (mSyncPageLoad) {
+ mPageFinishedLoading = true;
+ mSyncPageLoad.notify();
+ }
+ }
+
+ /**
+ *
+ * @param keys The first element of the CharSequence should be the
+ * existing value in the text input, or the empty string if none.
+ */
+ /* package */ void sendKeys(CharSequence[] keys) {
+ executeCommand(CMD_SEND_KEYS, keys, KEY_EVENT_TIMEOUT);
+ // Wait for all KeyEvents to be handled
+ waitForMessageRelay(KEY_EVENT_TIMEOUT);
+ }
+
+ /* package */ void sendKeyCodes(int[] keycodes) {
+ executeCommand(CMD_SEND_KEYCODE, keycodes, KEY_EVENT_TIMEOUT);
+ // Wait for all KeyEvents to be handled
+ waitForMessageRelay(KEY_EVENT_TIMEOUT);
+ }
+
+ /* package */ void moveCursorToRightMostPosition(String value) {
+ executeCommand(CMD_MOVE_CURSOR_RIGHTMOST_POS, value, KEY_EVENT_TIMEOUT);
+ waitForMessageRelay(KEY_EVENT_TIMEOUT);
+ }
+
+ private void moveCursorToLeftMostPos(String value) {
+ // If there is text, move the cursor to the rightmost position
+ if (value != null && !value.equals("")) {
+ long downTime = SystemClock.uptimeMillis();
+ KeyEvent down = new KeyEvent(downTime, SystemClock.uptimeMillis(),
+ KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_RIGHT, 0);
+ KeyEvent up = new KeyEvent(downTime, SystemClock.uptimeMillis(),
+ KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_RIGHT,
+ value.length());
+ mWebView.dispatchKeyEvent(down);
+ mWebView.dispatchKeyEvent(up);
+ }
+ }
+
+ private void dispatchKeyCodes(int[] keycodes) {
+ for (int i = 0; i < keycodes.length; i++) {
+ KeyEvent down = new KeyEvent(KeyEvent.ACTION_DOWN, keycodes[i]);
+ KeyEvent up = new KeyEvent(KeyEvent.ACTION_UP, keycodes[i]);
+ mWebView.dispatchKeyEvent(down);
+ mWebView.dispatchKeyEvent(up);
+ }
+ }
+
+ private void dispatchKeys(CharSequence[] keys) {
+ KeyCharacterMap chararcterMap = KeyCharacterMap.load(
+ KeyCharacterMap.VIRTUAL_KEYBOARD);
+ for (int i = 0; i < keys.length; i++) {
+ CharSequence s = keys[i];
+ for (int j = 0; j < s.length(); j++) {
+ KeyEvent[] events =
+ chararcterMap.getEvents(new char[]{s.charAt(j)});
+ for (KeyEvent e : events) {
+ mWebView.dispatchKeyEvent(e);
+ }
+ }
+ }
+ }
+
+ private void waitForMessageRelay(long timeout) {
+ synchronized (mSyncObject) {
+ mCommandDone = false;
+ }
+ Message msg = Message.obtain();
+ msg.what = WebViewCore.EventHub.MESSAGE_RELAY;
+ Message echo = mHandler.obtainMessage(CMD_MESSAGE_RELAY_ECHO);
+ msg.obj = echo;
+
+ mWebView.getWebViewCore().sendMessage(msg);
+ synchronized (mSyncObject) {
+ long end = System.currentTimeMillis() + timeout;
+ while (!mCommandDone && (System.currentTimeMillis() <= end)) {
+ try {
+ // This is notifed by the mHandler when it receives the
+ // MESSAGE_RELAY back
+ mSyncObject.wait(timeout);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ }
+
+ private void waitForPageLoad() {
+ long endLoad = System.currentTimeMillis() + LOADING_TIMEOUT;
+ while (!mPageFinishedLoading
+ && (System.currentTimeMillis() <= endLoad)) {
+ try {
+ mSyncPageLoad.wait(LOADING_TIMEOUT);
+ } catch (InterruptedException e) {
+ throw new RuntimeException();
+ }
+ }
+ }
+
+ /**
+ * Wraps the given string into quotes and escape existing quotes
+ * and backslashes.
+ * "foo" -> "\"foo\""
+ * "foo\"" -> "\"foo\\\"\""
+ * "fo\o" -> "\"fo\\o\""
+ *
+ * @param toWrap The String to wrap in quotes
+ * @return a String wrapping the original String in quotes
+ */
+ private static String escapeAndQuote(final String toWrap) {
+ StringBuilder toReturn = new StringBuilder("\"");
+ for (int i = 0; i < toWrap.length(); i++) {
+ char c = toWrap.charAt(i);
+ if (c == '\"') {
+ toReturn.append("\\\"");
+ } else if (c == '\\') {
+ toReturn.append("\\\\");
+ } else {
+ toReturn.append(c);
+ }
+ }
+ toReturn.append("\"");
+ return toReturn.toString();
+ }
+
+ private Object convertJsonToJavaObject(final Object toConvert) {
+ try {
+ if (toConvert == null
+ || toConvert.equals(null)
+ || "undefined".equals(toConvert)
+ || "null".equals(toConvert)) {
+ return null;
+ } else if (toConvert instanceof Boolean) {
+ return toConvert;
+ } else if (toConvert instanceof Double
+ || toConvert instanceof Float) {
+ return Double.valueOf(String.valueOf(toConvert));
+ } else if (toConvert instanceof Integer
+ || toConvert instanceof Long) {
+ return Long.valueOf(String.valueOf(toConvert));
+ } else if (toConvert instanceof JSONArray) { // List
+ return convertJsonArrayToList((JSONArray) toConvert);
+ } else if (toConvert instanceof JSONObject) { // Map or WebElment
+ JSONObject map = (JSONObject) toConvert;
+ if (map.opt(ELEMENT_KEY) != null) { // WebElement
+ return new WebElement(this, (String) map.get(ELEMENT_KEY));
+ } else { // Map
+ return convertJsonObjectToMap(map);
+ }
+ } else {
+ return toConvert.toString();
+ }
+ } catch (JSONException e) {
+ throw new RuntimeException("Failed to parse JavaScript result: "
+ + toConvert.toString(), e);
+ }
+ }
+
+ private List<Object> convertJsonArrayToList(final JSONArray json) {
+ List<Object> toReturn = Lists.newArrayList();
+ for (int i = 0; i < json.length(); i++) {
+ try {
+ toReturn.add(convertJsonToJavaObject(json.get(i)));
+ } catch (JSONException e) {
+ throw new RuntimeException("Failed to parse JSON: "
+ + json.toString(), e);
+ }
+ }
+ return toReturn;
+ }
+
+ private Map<Object, Object> convertJsonObjectToMap(final JSONObject json) {
+ Map<Object, Object> toReturn = Maps.newHashMap();
+ for (Iterator it = json.keys(); it.hasNext();) {
+ String key = (String) it.next();
+ try {
+ Object value = json.get(key);
+ toReturn.put(convertJsonToJavaObject(key),
+ convertJsonToJavaObject(value));
+ } catch (JSONException e) {
+ throw new RuntimeException("Failed to parse JSON:"
+ + json.toString(), e);
+ }
+ }
+ return toReturn;
+ }
+
+ private void throwIfError(final JSONObject jsonObject) {
+ ErrorCode status;
+ String errorMsg;
+ try {
+ status = ErrorCode.get((Integer) jsonObject.get(STATUS));
+ errorMsg = String.valueOf(jsonObject.get(VALUE));
+ } catch (JSONException e) {
+ throw new RuntimeException("Failed to parse JSON Object: "
+ + jsonObject, e);
+ }
+ switch (status) {
+ case SUCCESS:
+ return;
+ case NO_SUCH_ELEMENT:
+ throw new WebElementNotFoundException("Could not find "
+ + "WebElement.");
+ case STALE_ELEMENT_REFERENCE:
+ throw new WebElementStaleException("WebElement is stale.");
+ default:
+ throw new WebDriverException("Error: " + errorMsg);
+ }
+ }
+
+ private void shouldRunInMainThread(boolean value) {
+ assert (value == (MAIN_THREAD == Thread.currentThread().getId()));
+ }
+
+ /**
+ * Interface called from JavaScript when the result is ready.
+ */
+ private class JavascriptResultReady {
+
+ /**
+ * A callback from JavaScript to Java that passes the result as a
+ * parameter. This method is available from the WebView's
+ * JavaScript DOM as window.webdriver.resultReady().
+ *
+ * @param result The result that should be sent to Java from Javascript.
+ */
+ public void resultReady(final String result) {
+ synchronized (mSyncObject) {
+ mJsResult = result;
+ mCommandDone = true;
+ mSyncObject.notify();
+ }
+ }
+ }
+
+ /* package */ void notifyCommandDone() {
+ synchronized (mSyncObject) {
+ mCommandDone = true;
+ mSyncObject.notify();
+ }
+ }
+
+ /**
+ * Executes the given command by posting a message to mHandler. This thread
+ * will block until the command which runs in the main thread is done.
+ *
+ * @param command The command to run.
+ * @param arg The argument for that command.
+ * @param timeout A timeout in milliseconds.
+ */
+ private String executeCommand(int command, final Object arg, long timeout) {
+ shouldRunInMainThread(false);
+ synchronized (mSyncObject) {
+ mCommandDone = false;
+ Message msg = mHandler.obtainMessage(command);
+ msg.obj = arg;
+ mHandler.sendMessage(msg);
+
+ long end = System.currentTimeMillis() + timeout;
+ while (!mCommandDone) {
+ if (System.currentTimeMillis() >= end) {
+ throw new RuntimeException("Timeout executing command: "
+ + command);
+ }
+ try {
+ mSyncObject.wait(timeout);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ return mJsResult;
+ }
+
+ private void checkNotNull(Object obj, String errosMsg) {
+ if (obj == null) {
+ throw new NullPointerException(errosMsg);
+ }
+ }
+}
diff --git a/core/java/android/webkit/webdriver/WebDriverException.java b/core/java/android/webkit/webdriver/WebDriverException.java
new file mode 100644
index 0000000..1a579c2
--- /dev/null
+++ b/core/java/android/webkit/webdriver/WebDriverException.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2011 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.webkit.webdriver;
+
+/**
+ * @hide
+ */
+public class WebDriverException extends RuntimeException {
+ public WebDriverException() {
+ super();
+ }
+
+ public WebDriverException(String reason) {
+ super(reason);
+ }
+
+ public WebDriverException(String reason, Throwable cause) {
+ super(reason, cause);
+ }
+
+ public WebDriverException(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/core/java/android/webkit/webdriver/WebElement.java b/core/java/android/webkit/webdriver/WebElement.java
new file mode 100644
index 0000000..02c1595
--- /dev/null
+++ b/core/java/android/webkit/webdriver/WebElement.java
@@ -0,0 +1,388 @@
+/*
+ * Copyright (C) 2011 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.webkit.webdriver;
+
+import android.graphics.Point;
+import android.view.KeyEvent;
+
+import com.android.internal.R;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Represents an HTML element. Typically most interactions with a web page
+ * will be performed through this class.
+ *
+ * @hide
+ */
+public class WebElement {
+ private final String mId;
+ private final WebDriver mDriver;
+
+ private static final String LOCATOR_ID = "id";
+ private static final String LOCATOR_LINK_TEXT = "linkText";
+ private static final String LOCATOR_PARTIAL_LINK_TEXT = "partialLinkText";
+ private static final String LOCATOR_NAME = "name";
+ private static final String LOCATOR_CLASS_NAME = "className";
+ private static final String LOCATOR_CSS = "css";
+ private static final String LOCATOR_TAG_NAME = "tagName";
+ private static final String LOCATOR_XPATH = "xpath";
+
+ /**
+ * Package constructor to prevent clients from creating a new WebElement
+ * instance.
+ *
+ * <p> A WebElement represents an HTML element on the page.
+ * The corresponding HTML element is stored in a JS cache in the page
+ * that can be accessed through JavaScript using "bot.inject.cache".
+ *
+ * @param driver The WebDriver instance to use.
+ * @param id The index of the HTML element in the JavaSctipt cache.
+ * document.documentElement object.
+ */
+ /* package */ WebElement(final WebDriver driver, final String id) {
+ this.mId = id;
+ this.mDriver = driver;
+ }
+
+ /**
+ * Finds the first {@link android.webkit.webdriver.WebElement} using the
+ * given method.
+ *
+ * @param by The locating mechanism to use.
+ * @return The first matching element on the current context.
+ */
+ public WebElement findElement(final By by) {
+ return by.findElement(this);
+ }
+
+ /**
+ * Finds all {@link android.webkit.webdriver.WebElement} within the page
+ * using the given method.
+ *
+ * @param by The locating mechanism to use.
+ * @return A list of all {@link android.webkit.webdriver.WebElement} found,
+ * or an empty list if nothing matches.
+ */
+ public List<WebElement> findElements(final By by) {
+ return by.findElements(this);
+ }
+
+ /**
+ * Gets the visisble (i.e. not hidden by CSS) innerText of this element,
+ * inlcuding sub-elements.
+ *
+ * @return the innerText of this element.
+ * @throws {@link android.webkit.webdriver.WebElementStaleException} if this
+ * element is stale, i.e. not on the current DOM.
+ */
+ public String getText() {
+ String getText = mDriver.getResourceAsString(R.raw.get_text_android);
+ return (String) executeAtom(getText, this);
+ }
+
+ /**
+ * Gets the value of an HTML attribute for this element or the value of the
+ * property with the same name if the attribute is not present. If neither
+ * is set, null is returned.
+ *
+ * @param attribute the HTML attribute.
+ * @return the value of that attribute or the value of the property with the
+ * same name if the attribute is not set, or null if neither are set. For
+ * boolean attribute values this will return the string "true" or "false".
+ */
+ public String getAttribute(String attribute) {
+ String getAttribute = mDriver.getResourceAsString(
+ R.raw.get_attribute_value_android);
+ return (String) executeAtom(getAttribute, this, attribute);
+ }
+
+ /**
+ * @return the tag name of this element.
+ */
+ public String getTagName() {
+ return (String) mDriver.executeScript("return arguments[0].tagName;",
+ this);
+ }
+
+ /**
+ * @return true if this element is enabled, false otherwise.
+ */
+ public boolean isEnabled() {
+ String isEnabled = mDriver.getResourceAsString(
+ R.raw.is_enabled_android);
+ return (Boolean) executeAtom(isEnabled, this);
+ }
+
+ /**
+ * Determines whether this element is selected or not. This applies to input
+ * elements such as checkboxes, options in a select, and radio buttons.
+ *
+ * @return True if this element is selected, false otherwise.
+ */
+ public boolean isSelected() {
+ String isSelected = mDriver.getResourceAsString(
+ R.raw.is_selected_android);
+ return (Boolean) executeAtom(isSelected, this);
+ }
+
+ /**
+ * Selects an element on the page. This works for selecting checkboxes,
+ * options in a select, and radio buttons.
+ */
+ public void setSelected() {
+ String setSelected = mDriver.getResourceAsString(
+ R.raw.set_selected_android);
+ executeAtom(setSelected, this);
+ }
+
+ /**
+ * This toggles the checkboxe state from selected to not selected, or
+ * from not selected to selected.
+ *
+ * @return True if the toggled element is selected, false otherwise.
+ */
+ public boolean toggle() {
+ String toggle = mDriver.getResourceAsString(R.raw.toggle_android);
+ return (Boolean) executeAtom(toggle, this);
+ }
+
+ /**
+ * Sends the KeyEvents for the given sequence of characters to the
+ * WebElement to simulate typing. The KeyEvents are generated using the
+ * device's {@link android.view.KeyCharacterMap.VIRTUAL_KEYBOARD}.
+ *
+ * @param keys The keys to send to this WebElement
+ */
+ public void sendKeys(CharSequence... keys) {
+ if (keys == null || keys.length == 0) {
+ return;
+ }
+ click();
+ mDriver.moveCursorToRightMostPosition(getAttribute("value"));
+ mDriver.sendKeys(keys);
+ }
+
+ /**
+ * Use this to send one of the key code constants defined in
+ * {@link android.view.KeyEvent}
+ *
+ * @param keys
+ */
+ public void sendKeyCodes(int... keys) {
+ if (keys == null || keys.length == 0) {
+ return;
+ }
+ click();
+ mDriver.moveCursorToRightMostPosition(getAttribute("value"));
+ mDriver.sendKeyCodes(keys);
+ }
+
+ /**
+ * Sends a touch event to the center coordinates of this WebElement.
+ */
+ public void click() {
+ Point topLeft = getLocation();
+ Point size = getSize();
+ int jsX = topLeft.x + size.x/2;
+ int jsY = topLeft.y + size.y/2;
+ Point center = new Point(jsX, jsY);
+ mDriver.sendTouchScreen(center);
+ }
+
+ /**
+ * Submits the form containing this WebElement.
+ */
+ public void submit() {
+ mDriver.resetPageLoadState();
+ String submit = mDriver.getResourceAsString(R.raw.submit_android);
+ executeAtom(submit, this);
+ mDriver.waitForPageLoadIfNeeded();
+ }
+
+ /**
+ * Clears the text value if this is a text entry element. Does nothing
+ * otherwise.
+ */
+ public void clear() {
+ String value = getAttribute("value");
+ if (value == null || value.equals("")) {
+ return;
+ }
+ int length = value.length();
+ int[] keys = new int[length];
+ for (int i = 0; i < length; i++) {
+ keys[i] = KeyEvent.KEYCODE_DEL;
+ }
+ sendKeyCodes(keys);
+ }
+
+ /**
+ * @return the value of the given CSS property if found, null otherwise.
+ */
+ public String getCssValue(String cssProperty) {
+ String getCssProp = mDriver.getResourceAsString(
+ R.raw.get_value_of_css_property_android);
+ return (String) executeAtom(getCssProp, this, cssProperty);
+ }
+
+ /**
+ * Gets the width and height of the rendered element.
+ *
+ * @return a {@link android.graphics.Point}, where Point.x represents the
+ * width, and Point.y represents the height of the element.
+ */
+ public Point getSize() {
+ String getSize = mDriver.getResourceAsString(R.raw.get_size_android);
+ Map<String, Long> map = (Map<String, Long>) executeAtom(getSize, this);
+ return new Point(map.get("width").intValue(),
+ map.get("height").intValue());
+ }
+
+ /**
+ * Gets the location of the top left corner of this element on the screen.
+ * If the element is not visisble, this will scroll to get the element into
+ * the visisble screen.
+ *
+ * @return a {@link android.graphics.Point} containing the x and y
+ * coordinates of the top left corner of this element.
+ */
+ public Point getLocation() {
+ String getLocation = mDriver.getResourceAsString(
+ R.raw.get_top_left_coordinates_android);
+ Map<String,Long> map = (Map<String, Long>) executeAtom(getLocation,
+ this);
+ return new Point(map.get("x").intValue(), map.get("y").intValue());
+ }
+
+ /**
+ * @return True if the WebElement is displayed on the screen,
+ * false otherwise.
+ */
+ public boolean isDisplayed() {
+ String isDisplayed = mDriver.getResourceAsString(
+ R.raw.is_displayed_android);
+ return (Boolean) executeAtom(isDisplayed, this);
+ }
+
+ /*package*/ String getId() {
+ return mId;
+ }
+
+ /* package */ WebElement findElementById(final String locator) {
+ return findElement(LOCATOR_ID, locator);
+ }
+
+ /* package */ WebElement findElementByLinkText(final String linkText) {
+ return findElement(LOCATOR_LINK_TEXT, linkText);
+ }
+
+ /* package */ WebElement findElementByPartialLinkText(
+ final String linkText) {
+ return findElement(LOCATOR_PARTIAL_LINK_TEXT, linkText);
+ }
+
+ /* package */ WebElement findElementByName(final String name) {
+ return findElement(LOCATOR_NAME, name);
+ }
+
+ /* package */ WebElement findElementByClassName(final String className) {
+ return findElement(LOCATOR_CLASS_NAME, className);
+ }
+
+ /* package */ WebElement findElementByCss(final String css) {
+ return findElement(LOCATOR_CSS, css);
+ }
+
+ /* package */ WebElement findElementByTagName(final String tagName) {
+ return findElement(LOCATOR_TAG_NAME, tagName);
+ }
+
+ /* package */ WebElement findElementByXPath(final String xpath) {
+ return findElement(LOCATOR_XPATH, xpath);
+ }
+
+ /* package */ List<WebElement> findElementsById(final String locator) {
+ return findElements(LOCATOR_ID, locator);
+ }
+
+ /* package */ List<WebElement> findElementsByLinkText(final String linkText) {
+ return findElements(LOCATOR_LINK_TEXT, linkText);
+ }
+
+ /* package */ List<WebElement> findElementsByPartialLinkText(
+ final String linkText) {
+ return findElements(LOCATOR_PARTIAL_LINK_TEXT, linkText);
+ }
+
+ /* package */ List<WebElement> findElementsByName(final String name) {
+ return findElements(LOCATOR_NAME, name);
+ }
+
+ /* package */ List<WebElement> findElementsByClassName(final String className) {
+ return findElements(LOCATOR_CLASS_NAME, className);
+ }
+
+ /* package */ List<WebElement> findElementsByCss(final String css) {
+ return findElements(LOCATOR_CSS, css);
+ }
+
+ /* package */ List<WebElement> findElementsByTagName(final String tagName) {
+ return findElements(LOCATOR_TAG_NAME, tagName);
+ }
+
+ /* package */ List<WebElement> findElementsByXPath(final String xpath) {
+ return findElements(LOCATOR_XPATH, xpath);
+ }
+
+ private Object executeAtom(final String atom, final Object... args) {
+ String scriptArgs = mDriver.convertToJsArgs(args);
+ return mDriver.executeRawJavascript("(" +
+ atom + ")(" + scriptArgs + ")");
+ }
+
+ private List<WebElement> findElements(String strategy, String locator) {
+ String findElements = mDriver.getResourceAsString(
+ R.raw.find_elements_android);
+ if (mId.equals("")) {
+ return (List<WebElement>) executeAtom(findElements,
+ strategy, locator);
+ } else {
+ return (List<WebElement>) executeAtom(findElements,
+ strategy, locator, this);
+ }
+ }
+
+ private WebElement findElement(String strategy, String locator) {
+ String findElement = mDriver.getResourceAsString(
+ R.raw.find_element_android);
+ WebElement el;
+ if (mId.equals("")) {
+ el = (WebElement) executeAtom(findElement,
+ strategy, locator);
+ } else {
+ el = (WebElement) executeAtom(findElement,
+ strategy, locator, this);
+ }
+ if (el == null) {
+ throw new WebElementNotFoundException("Could not find element "
+ + "with " + strategy + ": " + locator);
+ }
+ return el;
+ }
+}
diff --git a/core/java/android/webkit/webdriver/WebElementNotFoundException.java b/core/java/android/webkit/webdriver/WebElementNotFoundException.java
new file mode 100644
index 0000000..e66d279
--- /dev/null
+++ b/core/java/android/webkit/webdriver/WebElementNotFoundException.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2011 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.webkit.webdriver;
+
+/**
+ * Thrown when a {@link android.webkit.webdriver.WebElement} is not found in the
+ * DOM of the page.
+ * @hide
+ */
+public class WebElementNotFoundException extends RuntimeException {
+
+ public WebElementNotFoundException() {
+ super();
+ }
+
+ public WebElementNotFoundException(String reason) {
+ super(reason);
+ }
+
+ public WebElementNotFoundException(String reason, Throwable cause) {
+ super(reason, cause);
+ }
+
+ public WebElementNotFoundException(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/core/java/android/webkit/webdriver/WebElementStaleException.java b/core/java/android/webkit/webdriver/WebElementStaleException.java
new file mode 100644
index 0000000..c59e794
--- /dev/null
+++ b/core/java/android/webkit/webdriver/WebElementStaleException.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2011 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.webkit.webdriver;
+
+/**
+ * Thrown when trying to access a {@link android.webkit.webdriver.WebElement}
+ * that is stale. This mean that the {@link android.webkit.webdriver.WebElement}
+ * is no longer present on the DOM of the page.
+ * @hide
+ */
+public class WebElementStaleException extends RuntimeException {
+
+ public WebElementStaleException() {
+ super();
+ }
+
+ public WebElementStaleException(String reason) {
+ super(reason);
+ }
+
+ public WebElementStaleException(String reason, Throwable cause) {
+ super(reason, cause);
+ }
+
+ public WebElementStaleException(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/core/java/android/webkit/webdriver/WebViewClient.java b/core/java/android/webkit/webdriver/WebViewClient.java
new file mode 100644
index 0000000..c582b24
--- /dev/null
+++ b/core/java/android/webkit/webdriver/WebViewClient.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2011 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.webkit.webdriver;
+
+import android.graphics.Bitmap;
+import android.net.http.SslError;
+import android.os.Message;
+import android.view.KeyEvent;
+import android.webkit.HttpAuthHandler;
+import android.webkit.SslErrorHandler;
+import android.webkit.WebResourceResponse;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+
+/* package */ class WebViewClientWrapper extends WebViewClient {
+ private final WebViewClient mDelegate;
+ private final WebDriver mDriver;
+
+ public WebViewClientWrapper(WebViewClient delegate, WebDriver driver) {
+ if (delegate == null) {
+ mDelegate = new WebViewClient();
+ } else {
+ mDelegate = delegate;
+ }
+ this.mDriver = driver;
+ }
+
+ @Override
+ public boolean shouldOverrideUrlLoading(WebView view, String url) {
+ return mDelegate.shouldOverrideUrlLoading(view, url);
+ }
+
+ @Override
+ public void onPageStarted(WebView view, String url, Bitmap favicon) {
+ mDriver.notifyPageStartedLoading();
+ mDelegate.onPageStarted(view, url, favicon);
+ }
+
+ @Override
+ public void onPageFinished(WebView view, String url) {
+ mDelegate.onPageFinished(view, url);
+ }
+
+ @Override
+ public void onLoadResource(WebView view, String url) {
+ mDelegate.onLoadResource(view, url);
+ }
+
+ @Override
+ public WebResourceResponse shouldInterceptRequest(WebView view,
+ String url) {
+ return mDelegate.shouldInterceptRequest(view, url);
+ }
+
+ @Override
+ public void onTooManyRedirects(WebView view, Message cancelMsg,
+ Message continueMsg) {
+ mDelegate.onTooManyRedirects(view, cancelMsg, continueMsg);
+ }
+
+ @Override
+ public void onReceivedError(WebView view, int errorCode, String description,
+ String failingUrl) {
+ mDelegate.onReceivedError(view, errorCode, description, failingUrl);
+ }
+
+ @Override
+ public void onFormResubmission(WebView view, Message dontResend,
+ Message resend) {
+ mDelegate.onFormResubmission(view, dontResend, resend);
+ }
+
+ @Override
+ public void doUpdateVisitedHistory(WebView view, String url,
+ boolean isReload) {
+ mDelegate.doUpdateVisitedHistory(view, url, isReload);
+ }
+
+ @Override
+ public void onReceivedSslError(WebView view, SslErrorHandler handler,
+ SslError error) {
+ mDelegate.onReceivedSslError(view, handler, error);
+ }
+
+ @Override
+ public void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler,
+ String host, String realm) {
+ mDelegate.onReceivedHttpAuthRequest(view, handler, host, realm);
+ }
+
+ @Override
+ public boolean shouldOverrideKeyEvent(WebView view, KeyEvent event) {
+ return mDelegate.shouldOverrideKeyEvent(view, event);
+ }
+
+ @Override
+ public void onUnhandledKeyEvent(WebView view, KeyEvent event) {
+ mDelegate.onUnhandledKeyEvent(view, event);
+ }
+
+ @Override
+ public void onScaleChanged(WebView view, float oldScale, float newScale) {
+ mDelegate.onScaleChanged(view, oldScale, newScale);
+ }
+
+ @Override
+ public void onReceivedLoginRequest(WebView view, String realm,
+ String account, String args) {
+ mDelegate.onReceivedLoginRequest(view, realm, account, args);
+ }
+}
diff --git a/core/java/android/webkit/webdriver/WebchromeClientWrapper.java b/core/java/android/webkit/webdriver/WebchromeClientWrapper.java
new file mode 100644
index 0000000..a9e5d19
--- /dev/null
+++ b/core/java/android/webkit/webdriver/WebchromeClientWrapper.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2011 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.webkit.webdriver;
+
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.os.Message;
+import android.view.View;
+import android.webkit.ConsoleMessage;
+import android.webkit.GeolocationPermissions;
+import android.webkit.JsPromptResult;
+import android.webkit.JsResult;
+import android.webkit.ValueCallback;
+import android.webkit.WebChromeClient;
+import android.webkit.WebStorage;
+import android.webkit.WebView;
+
+/* package */ class WebChromeClientWrapper extends WebChromeClient {
+
+ private final WebChromeClient mDelegate;
+ private final WebDriver mDriver;
+
+ public WebChromeClientWrapper(WebChromeClient delegate, WebDriver driver) {
+ if (delegate == null) {
+ this.mDelegate = new WebChromeClient();
+ } else {
+ this.mDelegate = delegate;
+ }
+ this.mDriver = driver;
+ }
+
+ @Override
+ public void onProgressChanged(WebView view, int newProgress) {
+ if (newProgress == 100) {
+ mDriver.notifyPageFinishedLoading();
+ }
+ mDelegate.onProgressChanged(view, newProgress);
+ }
+
+ @Override
+ public void onReceivedTitle(WebView view, String title) {
+ mDelegate.onReceivedTitle(view, title);
+ }
+
+ @Override
+ public void onReceivedIcon(WebView view, Bitmap icon) {
+ mDelegate.onReceivedIcon(view, icon);
+ }
+
+ @Override
+ public void onReceivedTouchIconUrl(WebView view, String url,
+ boolean precomposed) {
+ mDelegate.onReceivedTouchIconUrl(view, url, precomposed);
+ }
+
+ @Override
+ public void onShowCustomView(View view,
+ CustomViewCallback callback) {
+ mDelegate.onShowCustomView(view, callback);
+ }
+
+ @Override
+ public void onHideCustomView() {
+ mDelegate.onHideCustomView();
+ }
+
+ @Override
+ public boolean onCreateWindow(WebView view, boolean dialog,
+ boolean userGesture, Message resultMsg) {
+ return mDelegate.onCreateWindow(view, dialog, userGesture, resultMsg);
+ }
+
+ @Override
+ public void onRequestFocus(WebView view) {
+ mDelegate.onRequestFocus(view);
+ }
+
+ @Override
+ public void onCloseWindow(WebView window) {
+ mDelegate.onCloseWindow(window);
+ }
+
+ @Override
+ public boolean onJsAlert(WebView view, String url, String message,
+ JsResult result) {
+ return mDelegate.onJsAlert(view, url, message, result);
+ }
+
+ @Override
+ public boolean onJsConfirm(WebView view, String url, String message,
+ JsResult result) {
+ return mDelegate.onJsConfirm(view, url, message, result);
+ }
+
+ @Override
+ public boolean onJsPrompt(WebView view, String url, String message,
+ String defaultValue, JsPromptResult result) {
+ return mDelegate.onJsPrompt(view, url, message, defaultValue, result);
+ }
+
+ @Override
+ public boolean onJsBeforeUnload(WebView view, String url, String message,
+ JsResult result) {
+ return mDelegate.onJsBeforeUnload(view, url, message, result);
+ }
+
+ @Override
+ public void onExceededDatabaseQuota(String url, String databaseIdentifier,
+ long currentQuota, long estimatedSize, long totalUsedQuota,
+ WebStorage.QuotaUpdater quotaUpdater) {
+ mDelegate.onExceededDatabaseQuota(url, databaseIdentifier, currentQuota,
+ estimatedSize, totalUsedQuota, quotaUpdater);
+ }
+
+ @Override
+ public void onReachedMaxAppCacheSize(long spaceNeeded, long totalUsedQuota,
+ WebStorage.QuotaUpdater quotaUpdater) {
+ mDelegate.onReachedMaxAppCacheSize(spaceNeeded, totalUsedQuota,
+ quotaUpdater);
+ }
+
+ @Override
+ public void onGeolocationPermissionsShowPrompt(String origin,
+ GeolocationPermissions.Callback callback) {
+ mDelegate.onGeolocationPermissionsShowPrompt(origin, callback);
+ }
+
+ @Override
+ public void onGeolocationPermissionsHidePrompt() {
+ mDelegate.onGeolocationPermissionsHidePrompt();
+ }
+
+ @Override
+ public boolean onJsTimeout() {
+ return mDelegate.onJsTimeout();
+ }
+
+ @Override
+ public void onConsoleMessage(String message, int lineNumber,
+ String sourceID) {
+ mDelegate.onConsoleMessage(message, lineNumber, sourceID);
+ }
+
+ @Override
+ public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
+ return mDelegate.onConsoleMessage(consoleMessage);
+ }
+
+ @Override
+ public Bitmap getDefaultVideoPoster() {
+ return mDelegate.getDefaultVideoPoster();
+ }
+
+ @Override
+ public View getVideoLoadingProgressView() {
+ return mDelegate.getVideoLoadingProgressView();
+ }
+
+ @Override
+ public void getVisitedHistory(ValueCallback<String[]> callback) {
+ mDelegate.getVisitedHistory(callback);
+ }
+
+ @Override
+ public void openFileChooser(ValueCallback<Uri> uploadFile,
+ String acceptType) {
+ mDelegate.openFileChooser(uploadFile, acceptType);
+ }
+
+ @Override
+ public void setInstallableWebApp() {
+ mDelegate.setInstallableWebApp();
+ }
+
+ @Override
+ public void setupAutoFill(Message msg) {
+ mDelegate.setupAutoFill(msg);
+ }
+}
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index d39271e..f4300a5 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -55,6 +55,7 @@ import android.view.ViewConfiguration;
import android.view.ViewDebug;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
+import android.view.accessibility.AccessibilityEvent;
import android.view.inputmethod.BaseInputConnection;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
@@ -2532,6 +2533,21 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
return mContextMenuInfo;
}
+ /** @hide */
+ @Override
+ public boolean showContextMenu(float x, float y, int metaState) {
+ final int position = pointToPosition((int)x, (int)y);
+ if (position != INVALID_POSITION) {
+ final long id = mAdapter.getItemId(position);
+ View child = getChildAt(position - mFirstPosition);
+ if (child != null) {
+ mContextMenuInfo = createContextMenuInfo(child, position, id);
+ return super.showContextMenuForChild(AbsListView.this);
+ }
+ }
+ return super.showContextMenu(x, y, metaState);
+ }
+
@Override
public boolean showContextMenuForChild(View originalView) {
final int longPressPosition = getPositionForView(originalView);
@@ -2556,6 +2572,17 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
}
@Override
+ public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) {
+ // Add a record for ourselves as well.
+ AccessibilityEvent record = AccessibilityEvent.obtain();
+ // Set the class since it is not populated in #dispatchPopulateAccessibilityEvent
+ record.setClassName(getClass().getName());
+ child.dispatchPopulateAccessibilityEvent(record);
+ event.appendRecord(record);
+ return true;
+ }
+
+ @Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
return false;
}
@@ -2806,7 +2833,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
if (ev.getEdgeFlags() != 0 && motionPosition < 0) {
// If we couldn't find a view to click on, but the down event
// was touching the edge, we will bail out and try again.
- // This allows the edge correcting code in ViewRoot to try to
+ // This allows the edge correcting code in ViewAncestor to try to
// find a nearby view to select
return false;
}
@@ -2834,6 +2861,12 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
break;
}
}
+
+ if (performButtonActionOnTouchDown(ev)) {
+ if (mTouchMode == TOUCH_MODE_DOWN) {
+ removeCallbacks(mPendingCheckForTap);
+ }
+ }
break;
}
@@ -4529,8 +4562,9 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
* Otherwise resurrects the selection and returns true if resurrected.
*/
boolean resurrectSelectionIfNeeded() {
- if (mSelectedPosition < 0) {
- return resurrectSelection();
+ if (mSelectedPosition < 0 && resurrectSelection()) {
+ updateSelectorState();
+ return true;
}
return false;
}
@@ -5008,7 +5042,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
public boolean sendKeyEvent(KeyEvent event) {
// Use our own input connection, since the filter
// text view may not be shown in a window so has
- // no ViewRoot to dispatch events with.
+ // no ViewAncestor to dispatch events with.
return mDefInputConnection.sendKeyEvent(event);
}
};
diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java
index 0da73a4..2621e64 100644
--- a/core/java/android/widget/AbsSeekBar.java
+++ b/core/java/android/widget/AbsSeekBar.java
@@ -201,7 +201,8 @@ public abstract class AbsSeekBar extends ProgressBar {
}
@Override
- void onProgressRefresh(float scale, boolean fromUser) {
+ void onProgressRefresh(float scale, boolean fromUser) {
+ super.onProgressRefresh(scale, fromUser);
Drawable thumb = mThumb;
if (thumb != null) {
setThumbPos(getWidth(), thumb, scale, Integer.MIN_VALUE);
diff --git a/core/java/android/widget/AbsoluteLayout.java b/core/java/android/widget/AbsoluteLayout.java
index ac82af7..7df6aab 100644
--- a/core/java/android/widget/AbsoluteLayout.java
+++ b/core/java/android/widget/AbsoluteLayout.java
@@ -141,6 +141,11 @@ public class AbsoluteLayout extends ViewGroup {
return new LayoutParams(p);
}
+ @Override
+ public boolean shouldDelayChildPressedState() {
+ return false;
+ }
+
/**
* Per-child layout information associated with AbsoluteLayout.
* See
diff --git a/core/java/android/widget/AdapterView.java b/core/java/android/widget/AdapterView.java
index f16efbd..14ea853 100644
--- a/core/java/android/widget/AdapterView.java
+++ b/core/java/android/widget/AdapterView.java
@@ -876,7 +876,6 @@ public abstract class AdapterView<T extends Adapter> 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
@@ -885,22 +884,29 @@ public abstract class AdapterView<T extends Adapter> extends ViewGroup {
event.setEventType(AccessibilityEvent.TYPE_VIEW_SELECTED);
}
- // we send selection events only from AdapterView to avoid
- // generation of such event for each child
+ // We first get a chance to populate the event.
+ onPopulateAccessibilityEvent(event);
+
+ // 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);
+ return selectedView.dispatchPopulateAccessibilityEvent(event);
}
- if (!populated) {
- if (selectedView != null) {
- event.setEnabled(selectedView.isEnabled());
- }
- event.setItemCount(getCount());
- event.setCurrentItemIndex(getSelectedItemPosition());
- }
+ return false;
+ }
- return populated;
+ @Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+
+ View selectedView = getSelectedView();
+ if (selectedView != null) {
+ event.setEnabled(selectedView.isEnabled());
+ }
+ event.setItemCount(getCount());
+ event.setCurrentItemIndex(getSelectedItemPosition());
}
@Override
diff --git a/core/java/android/widget/AdapterViewAnimator.java b/core/java/android/widget/AdapterViewAnimator.java
index 072992e..c773527 100644
--- a/core/java/android/widget/AdapterViewAnimator.java
+++ b/core/java/android/widget/AdapterViewAnimator.java
@@ -79,7 +79,7 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter>
/**
* Map of the children of the {@link AdapterViewAnimator}.
*/
- HashMap<Integer, ViewAndIndex> mViewsMap = new HashMap<Integer, ViewAndIndex>();
+ HashMap<Integer, ViewAndMetaData> mViewsMap = new HashMap<Integer, ViewAndMetaData>();
/**
* List of views pending removal from the {@link AdapterViewAnimator}
@@ -103,11 +103,6 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter>
int mCurrentWindowStartUnbounded = 0;
/**
- * Handler to post events to the main thread
- */
- Handler mMainQueue;
-
- /**
* Listens for data changes from the adapter
*/
AdapterDataSetObserver mDataSetObserver;
@@ -163,15 +158,18 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter>
private static final int DEFAULT_ANIMATION_DURATION = 200;
public AdapterViewAnimator(Context context) {
- super(context);
- initViewAnimator();
+ this(context, null);
}
public AdapterViewAnimator(Context context, AttributeSet attrs) {
- super(context, attrs);
+ this(context, attrs, 0);
+ }
+
+ public AdapterViewAnimator(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
TypedArray a = context.obtainStyledAttributes(attrs,
- com.android.internal.R.styleable.AdapterViewAnimator);
+ com.android.internal.R.styleable.AdapterViewAnimator, defStyleAttr, 0);
int resource = a.getResourceId(
com.android.internal.R.styleable.AdapterViewAnimator_inAnimation, 0);
if (resource > 0) {
@@ -203,17 +201,21 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter>
* Initialize this {@link AdapterViewAnimator}
*/
private void initViewAnimator() {
- mMainQueue = new Handler(Looper.myLooper());
mPreviousViews = new ArrayList<Integer>();
}
- class ViewAndIndex {
- ViewAndIndex(View v, int i) {
- view = v;
- index = i;
- }
+ class ViewAndMetaData {
View view;
- int index;
+ int relativeIndex;
+ int adapterPosition;
+ long itemId;
+
+ ViewAndMetaData(View view, int relativeIndex, int adapterPosition, long itemId) {
+ this.view = view;
+ this.relativeIndex = relativeIndex;
+ this.adapterPosition = adapterPosition;
+ this.itemId = itemId;
+ }
}
/**
@@ -379,6 +381,15 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter>
}
}
+ private ViewAndMetaData getMetaDataForChild(View child) {
+ for (ViewAndMetaData vm: mViewsMap.values()) {
+ if (vm.view == child) {
+ return vm;
+ }
+ }
+ return null;
+ }
+
LayoutParams createOrReuseLayoutParams(View v) {
final ViewGroup.LayoutParams currentLp = v.getLayoutParams();
if (currentLp instanceof ViewGroup.LayoutParams) {
@@ -481,7 +492,7 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter>
if (remove) {
View previousView = mViewsMap.get(index).view;
- int oldRelativeIndex = mViewsMap.get(index).index;
+ int oldRelativeIndex = mViewsMap.get(index).relativeIndex;
mPreviousViews.add(index);
transformViewForTransition(oldRelativeIndex, -1, previousView, animate);
@@ -497,7 +508,7 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter>
int index = modulo(i, getWindowSize());
int oldRelativeIndex;
if (mViewsMap.containsKey(index)) {
- oldRelativeIndex = mViewsMap.get(index).index;
+ oldRelativeIndex = mViewsMap.get(index).relativeIndex;
} else {
oldRelativeIndex = -1;
}
@@ -510,14 +521,16 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter>
if (inOldRange) {
View view = mViewsMap.get(index).view;
- mViewsMap.get(index).index = newRelativeIndex;
+ mViewsMap.get(index).relativeIndex = newRelativeIndex;
applyTransformForChildAtIndex(view, newRelativeIndex);
transformViewForTransition(oldRelativeIndex, newRelativeIndex, view, animate);
// Otherwise this view is new to the window
} else {
// Get the new view from the adapter, add it and apply any transform / animation
- View newView = mAdapter.getView(modulo(i, adapterCount), null, this);
+ final int adapterPosition = modulo(i, adapterCount);
+ View newView = mAdapter.getView(adapterPosition, null, this);
+ long itemId = mAdapter.getItemId(adapterPosition);
// We wrap the new view in a FrameLayout so as to respect the contract
// with the adapter, that is, that we don't modify this view directly
@@ -527,7 +540,8 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter>
if (newView != null) {
fl.addView(newView);
}
- mViewsMap.put(index, new ViewAndIndex(fl, newRelativeIndex));
+ mViewsMap.put(index, new ViewAndMetaData(fl, newRelativeIndex,
+ adapterPosition, itemId));
addChild(fl);
applyTransformForChildAtIndex(fl, newRelativeIndex);
transformViewForTransition(-1, newRelativeIndex, fl, animate);
@@ -604,6 +618,7 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter>
case MotionEvent.ACTION_UP: {
if (mTouchMode == TOUCH_MODE_DOWN_IN_CURRENT_VIEW) {
final View v = getCurrentView();
+ final ViewAndMetaData viewData = getMetaDataForChild(v);
if (v != null) {
if (isTransformedTouchPointInView(ev.getX(), ev.getY(), v, null)) {
final Handler handler = getHandler();
@@ -616,7 +631,12 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter>
hideTapFeedback(v);
post(new Runnable() {
public void run() {
- performItemClick(v, 0, 0);
+ if (viewData != null) {
+ performItemClick(v, viewData.adapterPosition,
+ viewData.itemId);
+ } else {
+ performItemClick(v, 0, 0);
+ }
}
});
}
diff --git a/core/java/android/widget/CheckedTextView.java b/core/java/android/widget/CheckedTextView.java
index bf63607..8d4aaea 100644
--- a/core/java/android/widget/CheckedTextView.java
+++ b/core/java/android/widget/CheckedTextView.java
@@ -199,11 +199,8 @@ public class CheckedTextView extends TextView implements Checkable {
}
@Override
- public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
- boolean populated = super.dispatchPopulateAccessibilityEvent(event);
- if (!populated) {
- event.setChecked(mChecked);
- }
- return populated;
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+ event.setChecked(mChecked);
}
}
diff --git a/core/java/android/widget/CompoundButton.java b/core/java/android/widget/CompoundButton.java
index 0df45cc..a730018 100644
--- a/core/java/android/widget/CompoundButton.java
+++ b/core/java/android/widget/CompoundButton.java
@@ -208,22 +208,9 @@ public abstract class CompoundButton extends Button implements Checkable {
}
@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;
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+ event.setChecked(mChecked);
}
@Override
diff --git a/core/java/android/widget/CursorAdapter.java b/core/java/android/widget/CursorAdapter.java
index 516162a..6c4c39d 100644
--- a/core/java/android/widget/CursorAdapter.java
+++ b/core/java/android/widget/CursorAdapter.java
@@ -21,7 +21,6 @@ import android.database.ContentObserver;
import android.database.Cursor;
import android.database.DataSetObserver;
import android.os.Handler;
-import android.util.Config;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
@@ -440,7 +439,7 @@ public abstract class CursorAdapter extends BaseAdapter implements Filterable,
*/
protected void onContentChanged() {
if (mAutoRequery && mCursor != null && !mCursor.isClosed()) {
- if (Config.LOGV) Log.v("Cursor", "Auto requerying " + mCursor + " due to update");
+ if (false) Log.v("Cursor", "Auto requerying " + mCursor + " due to update");
mDataValid = mCursor.requery();
}
}
diff --git a/core/java/android/widget/CursorTreeAdapter.java b/core/java/android/widget/CursorTreeAdapter.java
index 3fadf4c..44d1656 100644
--- a/core/java/android/widget/CursorTreeAdapter.java
+++ b/core/java/android/widget/CursorTreeAdapter.java
@@ -22,7 +22,6 @@ import android.database.ContentObserver;
import android.database.Cursor;
import android.database.DataSetObserver;
import android.os.Handler;
-import android.util.Config;
import android.util.Log;
import android.util.SparseArray;
import android.view.View;
@@ -499,7 +498,7 @@ public abstract class CursorTreeAdapter extends BaseExpandableListAdapter implem
@Override
public void onChange(boolean selfChange) {
if (mAutoRequery && mCursor != null) {
- if (Config.LOGV) Log.v("Cursor", "Auto requerying " + mCursor +
+ if (false) Log.v("Cursor", "Auto requerying " + mCursor +
" due to update");
mDataValid = mCursor.requery();
}
diff --git a/core/java/android/widget/DatePicker.java b/core/java/android/widget/DatePicker.java
index 1d442db..30fb927 100644
--- a/core/java/android/widget/DatePicker.java
+++ b/core/java/android/widget/DatePicker.java
@@ -353,13 +353,14 @@ public class DatePicker extends FrameLayout {
}
@Override
- public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
- int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_WEEKDAY
+ public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
+ super.onPopulateAccessibilityEvent(event);
+
+ final int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_WEEKDAY
| DateUtils.FORMAT_SHOW_YEAR;
String selectedDateUtterance = DateUtils.formatDateTime(mContext,
mCurrentDate.getTimeInMillis(), flags);
event.getText().add(selectedDateUtterance);
- return true;
}
/**
@@ -410,74 +411,28 @@ public class DatePicker extends FrameLayout {
}
/**
- * Reorders the spinners according to the date format in the current
- * {@link Locale}.
+ * Reorders the spinners according to the date format that is
+ * explicitly set by the user and if no such is set fall back
+ * to the current locale's default format.
*/
private void reorderSpinners() {
- java.text.DateFormat format;
- String order;
-
- /*
- * If the user is in a locale where the medium date format is still
- * numeric (Japanese and Czech, for example), respect the date format
- * order setting. Otherwise, use the order that the locale says is
- * appropriate for a spelled-out date.
- */
-
- if (getShortMonths()[0].startsWith("1")) {
- format = DateFormat.getDateFormat(getContext());
- } else {
- format = DateFormat.getMediumDateFormat(getContext());
- }
-
- if (format instanceof SimpleDateFormat) {
- order = ((SimpleDateFormat) format).toPattern();
- } else {
- // Shouldn't happen, but just in case.
- order = new String(DateFormat.getDateFormatOrder(getContext()));
- }
-
- /*
- * Remove the 3 spinners from their parent and then add them back in the
- * required order.
- */
- LinearLayout parent = mSpinners;
- parent.removeAllViews();
-
- boolean quoted = false;
- boolean didDay = false, didMonth = false, didYear = false;
-
- for (int i = 0; i < order.length(); i++) {
- char c = order.charAt(i);
-
- if (c == '\'') {
- quoted = !quoted;
- }
-
- if (!quoted) {
- if (c == DateFormat.DATE && !didDay) {
- parent.addView(mDaySpinner);
- didDay = true;
- } else if ((c == DateFormat.MONTH || c == 'L') && !didMonth) {
- parent.addView(mMonthSpinner);
- didMonth = true;
- } else if (c == DateFormat.YEAR && !didYear) {
- parent.addView(mYearSpinner);
- didYear = true;
- }
+ mSpinners.removeAllViews();
+ char[] order = DateFormat.getDateFormatOrder(getContext());
+ for (int i = 0; i < order.length; i++) {
+ switch (order[i]) {
+ case DateFormat.DATE:
+ mSpinners.addView(mDaySpinner);
+ break;
+ case DateFormat.MONTH:
+ mSpinners.addView(mMonthSpinner);
+ break;
+ case DateFormat.YEAR:
+ mSpinners.addView(mYearSpinner);
+ break;
+ default:
+ throw new IllegalArgumentException();
}
}
-
- // Shouldn't happen, but just in case.
- if (!didMonth) {
- parent.addView(mMonthSpinner);
- }
- if (!didDay) {
- parent.addView(mDaySpinner);
- }
- if (!didYear) {
- parent.addView(mYearSpinner);
- }
}
/**
diff --git a/core/java/android/widget/ExpandableListView.java b/core/java/android/widget/ExpandableListView.java
index f862368..ead9b4f 100644
--- a/core/java/android/widget/ExpandableListView.java
+++ b/core/java/android/widget/ExpandableListView.java
@@ -599,12 +599,35 @@ public class ExpandableListView extends ListView {
* was already expanded, this will return false)
*/
public boolean expandGroup(int groupPos) {
- boolean retValue = mConnector.expandGroup(groupPos);
+ return expandGroup(groupPos, false);
+ }
+
+ /**
+ * Expand a group in the grouped list view
+ *
+ * @param groupPos the group to be expanded
+ * @param animate true if the expanding group should be animated in
+ * @return True if the group was expanded, false otherwise (if the group
+ * was already expanded, this will return false)
+ */
+ public boolean expandGroup(int groupPos, boolean animate) {
+ PositionMetadata pm = mConnector.getFlattenedPos(ExpandableListPosition.obtain(
+ ExpandableListPosition.GROUP, groupPos, -1, -1));
+ boolean retValue = mConnector.expandGroup(pm);
if (mOnGroupExpandListener != null) {
mOnGroupExpandListener.onGroupExpand(groupPos);
}
-
+
+ if (animate) {
+ final int groupFlatPos = pm.position.flatListPos;
+
+ final int shiftedGroupPosition = groupFlatPos + getHeaderViewsCount();
+ smoothScrollToPosition(shiftedGroupPosition + mAdapter.getChildrenCount(groupPos),
+ shiftedGroupPosition);
+ }
+ pm.recycle();
+
return retValue;
}
diff --git a/core/java/android/widget/FrameLayout.java b/core/java/android/widget/FrameLayout.java
index f659ead..0659063 100644
--- a/core/java/android/widget/FrameLayout.java
+++ b/core/java/android/widget/FrameLayout.java
@@ -39,7 +39,7 @@ import java.util.ArrayList;
* Children are drawn in a stack, with the most recently added child on top.
* The size of the frame layout is the size of its largest child (plus padding), visible
* or not (if the FrameLayout's parent permits). Views that are GONE are used for sizing
- * only if {@link #setMeasureAllChildren(boolean) setConsiderGoneChildrenWhenMeasuring()}
+ * only if {@link #setMeasureAllChildren(boolean) setMeasureAllChildren()}
* is set to true.
*
* @attr ref android.R.styleable#FrameLayout_foreground
@@ -485,6 +485,11 @@ public class FrameLayout extends ViewGroup {
return new FrameLayout.LayoutParams(getContext(), attrs);
}
+ @Override
+ public boolean shouldDelayChildPressedState() {
+ return false;
+ }
+
/**
* {@inheritDoc}
*/
@@ -566,4 +571,3 @@ public class FrameLayout extends ViewGroup {
}
}
}
-
diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java
index 1fe6f4b..d8068f9 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -218,15 +218,16 @@ public class ImageView extends View {
/**
* An optional argument to supply a maximum width for this view. Only valid if
- * {@link #setAdjustViewBounds} has been set to true. To set an image to be a maximum of 100 x
- * 100 while preserving the original aspect ratio, do the following: 1) set adjustViewBounds to
- * true 2) set maxWidth and maxHeight to 100 3) set the height and width layout params to
- * WRAP_CONTENT.
+ * {@link #setAdjustViewBounds(boolean)} has been set to true. To set an image to be a maximum
+ * of 100 x 100 while preserving the original aspect ratio, do the following: 1) set
+ * adjustViewBounds to true 2) set maxWidth and maxHeight to 100 3) set the height and width
+ * layout params to WRAP_CONTENT.
*
* <p>
* Note that this view could be still smaller than 100 x 100 using this approach if the original
* image is small. To set an image to a fixed size, specify that size in the layout params and
- * then use {@link #setScaleType} to determine how to fit the image within the bounds.
+ * then use {@link #setScaleType(android.widget.ImageView.ScaleType)} to determine how to fit
+ * the image within the bounds.
* </p>
*
* @param maxWidth maximum width for this view
@@ -240,15 +241,16 @@ public class ImageView extends View {
/**
* An optional argument to supply a maximum height for this view. Only valid if
- * {@link #setAdjustViewBounds} has been set to true. To set an image to be a maximum of 100 x
- * 100 while preserving the original aspect ratio, do the following: 1) set adjustViewBounds to
- * true 2) set maxWidth and maxHeight to 100 3) set the height and width layout params to
- * WRAP_CONTENT.
+ * {@link #setAdjustViewBounds(boolean)} has been set to true. To set an image to be a
+ * maximum of 100 x 100 while preserving the original aspect ratio, do the following: 1) set
+ * adjustViewBounds to true 2) set maxWidth and maxHeight to 100 3) set the height and width
+ * layout params to WRAP_CONTENT.
*
* <p>
* Note that this view could be still smaller than 100 x 100 using this approach if the original
* image is small. To set an image to a fixed size, specify that size in the layout params and
- * then use {@link #setScaleType} to determine how to fit the image within the bounds.
+ * then use {@link #setScaleType(android.widget.ImageView.ScaleType)} to determine how to fit
+ * the image within the bounds.
* </p>
*
* @param maxHeight maximum height for this view
@@ -272,8 +274,8 @@ public class ImageView extends View {
*
* <p class="note">This does Bitmap reading and decoding on the UI
* thread, which can cause a latency hiccup. If that's a concern,
- * consider using {@link #setImageDrawable} or
- * {@link #setImageBitmap} and
+ * consider using {@link #setImageDrawable(android.graphics.drawable.Drawable)} or
+ * {@link #setImageBitmap(android.graphics.Bitmap)} and
* {@link android.graphics.BitmapFactory} instead.</p>
*
* @param resId the resource identifier of the the drawable
@@ -297,8 +299,8 @@ public class ImageView extends View {
*
* <p class="note">This does Bitmap reading and decoding on the UI
* thread, which can cause a latency hiccup. If that's a concern,
- * consider using {@link #setImageDrawable} or
- * {@link #setImageBitmap} and
+ * consider using {@link #setImageDrawable(android.graphics.drawable.Drawable)} or
+ * {@link #setImageBitmap(android.graphics.Bitmap)} and
* {@link android.graphics.BitmapFactory} instead.</p>
*
* @param uri The Uri of an image
@@ -902,12 +904,12 @@ public class ImageView extends View {
/**
* <p>Set the offset of the widget's text baseline from the widget's top
- * boundary. This value is overridden by the {@link #setBaselineAlignBottom}
+ * boundary. This value is overridden by the {@link #setBaselineAlignBottom(boolean)}
* property.</p>
*
* @param baseline The baseline to use, or -1 if none is to be provided.
*
- * @see #setBaseline
+ * @see #setBaseline(int)
* @attr ref android.R.styleable#ImageView_baseline
*/
public void setBaseline(int baseline) {
diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java
index fd0e53d..bac849e 100644
--- a/core/java/android/widget/LinearLayout.java
+++ b/core/java/android/widget/LinearLayout.java
@@ -201,6 +201,11 @@ public class LinearLayout extends ViewGroup {
mShowDividers = showDividers;
}
+ @Override
+ public boolean shouldDelayChildPressedState() {
+ return false;
+ }
+
/**
* @return A flag set indicating how dividers should be shown around items.
* @see #setShowDividers(int)
@@ -230,6 +235,39 @@ public class LinearLayout extends ViewGroup {
requestLayout();
}
+ /**
+ * Set padding displayed on both ends of dividers.
+ *
+ * @param padding Padding value in pixels that will be applied to each end
+ *
+ * @see #setShowDividers(int)
+ * @see #setDividerDrawable(Drawable)
+ * @see #getDividerPadding()
+ */
+ public void setDividerPadding(int padding) {
+ mDividerPadding = padding;
+ }
+
+ /**
+ * Get the padding size used to inset dividers in pixels
+ *
+ * @see #setShowDividers(int)
+ * @see #setDividerDrawable(Drawable)
+ * @see #setDividerPadding(int)
+ */
+ public int getDividerPadding() {
+ return mDividerPadding;
+ }
+
+ /**
+ * Get the width of the current divider drawable.
+ *
+ * @hide Used internally by framework.
+ */
+ public int getDividerWidth() {
+ return mDividerWidth;
+ }
+
@Override
protected void onDraw(Canvas canvas) {
if (mDivider == null) {
@@ -244,29 +282,15 @@ public class LinearLayout extends ViewGroup {
}
void drawDividersVertical(Canvas canvas) {
- final boolean showDividerBeginning =
- (mShowDividers & SHOW_DIVIDER_BEGINNING) == SHOW_DIVIDER_BEGINNING;
- final boolean showDividerMiddle =
- (mShowDividers & SHOW_DIVIDER_MIDDLE) == SHOW_DIVIDER_MIDDLE;
- final boolean showDividerEnd =
- (mShowDividers & SHOW_DIVIDER_END) == SHOW_DIVIDER_END;
-
final int count = getVirtualChildCount();
int top = getPaddingTop();
- boolean firstVisible = true;
for (int i = 0; i < count; i++) {
final View child = getVirtualChildAt(i);
if (child == null) {
top += measureNullChild(i);
} else if (child.getVisibility() != GONE) {
- if (firstVisible) {
- firstVisible = false;
- if (showDividerBeginning) {
- drawHorizontalDivider(canvas, top);
- top += mDividerHeight;
- }
- } else if (showDividerMiddle) {
+ if (hasDividerBeforeChildAt(i)) {
drawHorizontalDivider(canvas, top);
top += mDividerHeight;
}
@@ -276,35 +300,21 @@ public class LinearLayout extends ViewGroup {
}
}
- if (showDividerEnd) {
+ if (hasDividerBeforeChildAt(count)) {
drawHorizontalDivider(canvas, top);
}
}
void drawDividersHorizontal(Canvas canvas) {
- final boolean showDividerBeginning =
- (mShowDividers & SHOW_DIVIDER_BEGINNING) == SHOW_DIVIDER_BEGINNING;
- final boolean showDividerMiddle =
- (mShowDividers & SHOW_DIVIDER_MIDDLE) == SHOW_DIVIDER_MIDDLE;
- final boolean showDividerEnd =
- (mShowDividers & SHOW_DIVIDER_END) == SHOW_DIVIDER_END;
-
final int count = getVirtualChildCount();
int left = getPaddingLeft();
- boolean firstVisible = true;
for (int i = 0; i < count; i++) {
final View child = getVirtualChildAt(i);
if (child == null) {
left += measureNullChild(i);
} else if (child.getVisibility() != GONE) {
- if (firstVisible) {
- firstVisible = false;
- if (showDividerBeginning) {
- drawVerticalDivider(canvas, left);
- left += mDividerWidth;
- }
- } else if (showDividerMiddle) {
+ if (hasDividerBeforeChildAt(i)) {
drawVerticalDivider(canvas, left);
left += mDividerWidth;
}
@@ -314,7 +324,7 @@ public class LinearLayout extends ViewGroup {
}
}
- if (showDividerEnd) {
+ if (hasDividerBeforeChildAt(count)) {
drawVerticalDivider(canvas, left);
}
}
@@ -523,6 +533,23 @@ public class LinearLayout extends ViewGroup {
}
/**
+ * Determines where to position dividers between children.
+ *
+ * @param childIndex Index of child to check for preceding divider
+ * @return true if there should be a divider before the child at childIndex
+ * @hide Pending API consideration. Currently only used internally by the system.
+ */
+ protected boolean hasDividerBeforeChildAt(int childIndex) {
+ if (childIndex == 0) {
+ return (mShowDividers & SHOW_DIVIDER_BEGINNING) != 0;
+ } else if (childIndex == getChildCount()) {
+ return (mShowDividers & SHOW_DIVIDER_END) != 0;
+ } else {
+ return (mShowDividers & SHOW_DIVIDER_MIDDLE) != 0;
+ }
+ }
+
+ /**
* Measures the children when the orientation of this LinearLayout is set
* to {@link #VERTICAL}.
*
@@ -554,14 +581,7 @@ public class LinearLayout extends ViewGroup {
int largestChildHeight = Integer.MIN_VALUE;
- // A divider at the end will change how much space views can consume.
- final boolean showDividerBeginning =
- (mShowDividers & SHOW_DIVIDER_BEGINNING) == SHOW_DIVIDER_BEGINNING;
- final boolean showDividerMiddle =
- (mShowDividers & SHOW_DIVIDER_MIDDLE) == SHOW_DIVIDER_MIDDLE;
-
// See how tall everyone is. Also remember max width.
- boolean firstVisible = true;
for (int i = 0; i < count; ++i) {
final View child = getVirtualChildAt(i);
@@ -575,12 +595,7 @@ public class LinearLayout extends ViewGroup {
continue;
}
- if (firstVisible) {
- firstVisible = false;
- if (showDividerBeginning) {
- mTotalLength += mDividerHeight;
- }
- } else if (showDividerMiddle) {
+ if (hasDividerBeforeChildAt(i)) {
mTotalLength += mDividerHeight;
}
@@ -677,7 +692,7 @@ public class LinearLayout extends ViewGroup {
i += getChildrenSkipCount(child, i);
}
- if (mTotalLength > 0 && (mShowDividers & SHOW_DIVIDER_END) == SHOW_DIVIDER_END) {
+ if (mTotalLength > 0 && hasDividerBeforeChildAt(count)) {
mTotalLength += mDividerHeight;
}
@@ -881,14 +896,7 @@ public class LinearLayout extends ViewGroup {
int largestChildWidth = Integer.MIN_VALUE;
- // A divider at the end will change how much space views can consume.
- final boolean showDividerBeginning =
- (mShowDividers & SHOW_DIVIDER_BEGINNING) == SHOW_DIVIDER_BEGINNING;
- final boolean showDividerMiddle =
- (mShowDividers & SHOW_DIVIDER_MIDDLE) == SHOW_DIVIDER_MIDDLE;
-
// See how wide everyone is. Also remember max height.
- boolean firstVisible = true;
for (int i = 0; i < count; ++i) {
final View child = getVirtualChildAt(i);
@@ -902,12 +910,7 @@ public class LinearLayout extends ViewGroup {
continue;
}
- if (firstVisible) {
- firstVisible = false;
- if (showDividerBeginning) {
- mTotalLength += mDividerWidth;
- }
- } else if (showDividerMiddle) {
+ if (hasDividerBeforeChildAt(i)) {
mTotalLength += mDividerWidth;
}
@@ -1022,7 +1025,7 @@ public class LinearLayout extends ViewGroup {
i += getChildrenSkipCount(child, i);
}
- if (mTotalLength > 0 && (mShowDividers & SHOW_DIVIDER_END) == SHOW_DIVIDER_END) {
+ if (mTotalLength > 0 && hasDividerBeforeChildAt(count)) {
mTotalLength += mDividerWidth;
}
@@ -1328,7 +1331,7 @@ public class LinearLayout extends ViewGroup {
void layoutVertical() {
final int paddingLeft = mPaddingLeft;
- int childTop = mPaddingTop;
+ int childTop;
int childLeft;
// Where right end of child should go
@@ -1343,26 +1346,21 @@ public class LinearLayout extends ViewGroup {
final int majorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
final int minorGravity = mGravity & Gravity.HORIZONTAL_GRAVITY_MASK;
- if (majorGravity != Gravity.TOP) {
- switch (majorGravity) {
- case Gravity.BOTTOM:
- // mTotalLength contains the padding already, we add the top
- // padding to compensate
- childTop = mBottom - mTop + mPaddingTop - mTotalLength;
- break;
-
- case Gravity.CENTER_VERTICAL:
- childTop += ((mBottom - mTop) - mTotalLength) / 2;
- break;
- }
-
- }
-
- final boolean showDividerMiddle =
- (mShowDividers & SHOW_DIVIDER_MIDDLE) == SHOW_DIVIDER_MIDDLE;
-
- if ((mShowDividers & SHOW_DIVIDER_BEGINNING) == SHOW_DIVIDER_BEGINNING) {
- childTop += mDividerHeight;
+ switch (majorGravity) {
+ case Gravity.BOTTOM:
+ // mTotalLength contains the padding already
+ childTop = mPaddingTop + mBottom - mTop - mTotalLength;
+ break;
+
+ // mTotalLength contains the padding already
+ case Gravity.CENTER_VERTICAL:
+ childTop = mPaddingTop + (mBottom - mTop - mTotalLength) / 2;
+ break;
+
+ case Gravity.TOP:
+ default:
+ childTop = mPaddingTop;
+ break;
}
for (int i = 0; i < count; i++) {
@@ -1380,12 +1378,8 @@ public class LinearLayout extends ViewGroup {
if (gravity < 0) {
gravity = minorGravity;
}
-
- switch (gravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
- case Gravity.LEFT:
- childLeft = paddingLeft + lp.leftMargin;
- break;
+ switch (gravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
case Gravity.CENTER_HORIZONTAL:
childLeft = paddingLeft + ((childSpace - childWidth) / 2)
+ lp.leftMargin - lp.rightMargin;
@@ -1394,20 +1388,22 @@ public class LinearLayout extends ViewGroup {
case Gravity.RIGHT:
childLeft = childRight - childWidth - lp.rightMargin;
break;
+
+ case Gravity.LEFT:
default:
- childLeft = paddingLeft;
+ childLeft = paddingLeft + lp.leftMargin;
break;
}
-
+
+ if (hasDividerBeforeChildAt(i)) {
+ childTop += mDividerHeight;
+ }
+
childTop += lp.topMargin;
setChildFrame(child, childLeft, childTop + getLocationOffset(child),
childWidth, childHeight);
childTop += childHeight + lp.bottomMargin + getNextLocationOffset(child);
- if (showDividerMiddle) {
- childTop += mDividerHeight;
- }
-
i += getChildrenSkipCount(child, i);
}
}
@@ -1422,10 +1418,11 @@ public class LinearLayout extends ViewGroup {
* @see #onLayout(boolean, int, int, int, int)
*/
void layoutHorizontal() {
+ final boolean isLayoutRtl = isLayoutRtl();
final int paddingTop = mPaddingTop;
int childTop;
- int childLeft = mPaddingLeft;
+ int childLeft;
// Where bottom of child should go
final int height = mBottom - mTop;
@@ -1444,32 +1441,37 @@ public class LinearLayout extends ViewGroup {
final int[] maxAscent = mMaxAscent;
final int[] maxDescent = mMaxDescent;
- if (majorGravity != Gravity.LEFT) {
- switch (majorGravity) {
- case Gravity.RIGHT:
- // mTotalLength contains the padding already, we add the left
- // padding to compensate
- childLeft = mRight - mLeft + mPaddingLeft - mTotalLength;
- break;
-
- case Gravity.CENTER_HORIZONTAL:
- childLeft += ((mRight - mLeft) - mTotalLength) / 2;
- break;
- }
+ switch (majorGravity) {
+ case Gravity.RIGHT:
+ // mTotalLength contains the padding already
+ childLeft = mPaddingLeft + mRight - mLeft - mTotalLength;
+ break;
+
+ case Gravity.CENTER_HORIZONTAL:
+ // mTotalLength contains the padding already
+ childLeft = mPaddingLeft + (mRight - mLeft - mTotalLength) / 2;
+ break;
+
+ case Gravity.LEFT:
+ default:
+ childLeft = mPaddingLeft;
+ break;
}
- final boolean showDividerMiddle =
- (mShowDividers & SHOW_DIVIDER_MIDDLE) == SHOW_DIVIDER_MIDDLE;
-
- if ((mShowDividers & SHOW_DIVIDER_BEGINNING) == SHOW_DIVIDER_BEGINNING) {
- childLeft += mDividerWidth;
+ int start = 0;
+ int dir = 1;
+ //In case of RTL, start drawing from the last child.
+ if (isLayoutRtl) {
+ start = count - 1;
+ dir = -1;
}
for (int i = 0; i < count; i++) {
- final View child = getVirtualChildAt(i);
+ int childIndex = start + dir * i;
+ final View child = getVirtualChildAt(childIndex);
if (child == null) {
- childLeft += measureNullChild(i);
+ childLeft += measureNullChild(childIndex);
} else if (child.getVisibility() != GONE) {
final int childWidth = child.getMeasuredWidth();
final int childHeight = child.getMeasuredHeight();
@@ -1523,17 +1525,17 @@ public class LinearLayout extends ViewGroup {
break;
}
+ if (hasDividerBeforeChildAt(childIndex)) {
+ childLeft += mDividerWidth;
+ }
+
childLeft += lp.leftMargin;
setChildFrame(child, childLeft + getLocationOffset(child), childTop,
childWidth, childHeight);
childLeft += childWidth + lp.rightMargin +
getNextLocationOffset(child);
- if (showDividerMiddle) {
- childLeft += mDividerWidth;
- }
-
- i += getChildrenSkipCount(child, i);
+ i += getChildrenSkipCount(child, childIndex);
}
}
}
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index af954c9..e7a9e41 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -251,7 +251,7 @@ public class ListView extends AbsListView {
*/
public void addHeaderView(View v, Object data, boolean isSelectable) {
- if (mAdapter != null) {
+ if (mAdapter != null && ! (mAdapter instanceof HeaderViewListAdapter)) {
throw new IllegalStateException(
"Cannot add header view to list -- setAdapter has already been called.");
}
@@ -261,6 +261,12 @@ public class ListView extends AbsListView {
info.data = data;
info.isSelectable = isSelectable;
mHeaderViewInfos.add(info);
+
+ // in the case of re-adding a header view, or adding one later on,
+ // we need to notify the observer
+ if (mDataSetObserver != null) {
+ mDataSetObserver.onChanged();
+ }
}
/**
@@ -294,7 +300,9 @@ public class ListView extends AbsListView {
if (mHeaderViewInfos.size() > 0) {
boolean result = false;
if (((HeaderViewListAdapter) mAdapter).removeHeader(v)) {
- mDataSetObserver.onChanged();
+ if (mDataSetObserver != null) {
+ mDataSetObserver.onChanged();
+ }
result = true;
}
removeFixedViewInfo(v, mHeaderViewInfos);
@@ -328,6 +336,12 @@ public class ListView extends AbsListView {
* @param isSelectable true if the footer view can be selected
*/
public void addFooterView(View v, Object data, boolean isSelectable) {
+
+ // NOTE: do not enforce the adapter being null here, since unlike in
+ // addHeaderView, it was never enforced here, and so existing apps are
+ // relying on being able to add a footer and then calling setAdapter to
+ // force creation of the HeaderViewListAdapter wrapper
+
FixedViewInfo info = new FixedViewInfo();
info.view = v;
info.data = data;
@@ -371,7 +385,9 @@ public class ListView extends AbsListView {
if (mFooterViewInfos.size() > 0) {
boolean result = false;
if (((HeaderViewListAdapter) mAdapter).removeFooter(v)) {
- mDataSetObserver.onChanged();
+ if (mDataSetObserver != null) {
+ mDataSetObserver.onChanged();
+ }
result = true;
}
removeFixedViewInfo(v, mFooterViewInfos);
@@ -1552,7 +1568,7 @@ public class ListView extends AbsListView {
// take focus back to us temporarily to avoid the eventual
// call to clear focus when removing the focused child below
- // from messing things up when ViewRoot assigns focus back
+ // from messing things up when ViewAncestor assigns focus back
// to someone else
final View focusedChild = getFocusedChild();
if (focusedChild != null) {
@@ -1982,36 +1998,28 @@ public class ListView extends AbsListView {
}
@Override
- public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
- boolean populated = super.dispatchPopulateAccessibilityEvent(event);
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(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;
+ int itemCount = 0;
+ int currentItemIndex = getSelectedItemPosition();
+
+ ListAdapter adapter = getAdapter();
+ if (adapter != null) {
+ final int count = adapter.getCount();
+ for (int i = 0; i < count; i++) {
+ if (adapter.isEnabled(i)) {
+ itemCount++;
+ } else if (i <= currentItemIndex) {
+ currentItemIndex--;
}
}
-
- event.setItemCount(itemCount);
- event.setCurrentItemIndex(currentItemIndex);
}
- return populated;
+ event.setItemCount(itemCount);
+ event.setCurrentItemIndex(currentItemIndex);
}
/**
diff --git a/core/java/android/widget/MultiAutoCompleteTextView.java b/core/java/android/widget/MultiAutoCompleteTextView.java
index 02c1ec7..134e4c4 100644
--- a/core/java/android/widget/MultiAutoCompleteTextView.java
+++ b/core/java/android/widget/MultiAutoCompleteTextView.java
@@ -30,7 +30,7 @@ import android.widget.MultiAutoCompleteTextView.Tokenizer;
* can show completion suggestions for the substring of the text where
* the user is typing instead of necessarily for the entire thing.
* <p>
- * You must must provide a {@link Tokenizer} to distinguish the
+ * You must provide a {@link Tokenizer} to distinguish the
* various substrings.
*
* <p>The following code snippet shows how to create a text view which suggests
@@ -41,7 +41,7 @@ import android.widget.MultiAutoCompleteTextView.Tokenizer;
* protected void onCreate(Bundle savedInstanceState) {
* super.onCreate(savedInstanceState);
* setContentView(R.layout.autocomplete_7);
- *
+ *
* ArrayAdapter&lt;String&gt; adapter = new ArrayAdapter&lt;String&gt;(this,
* android.R.layout.simple_dropdown_item_1line, COUNTRIES);
* MultiAutoCompleteTextView textView = (MultiAutoCompleteTextView) findViewById(R.id.edit);
@@ -132,7 +132,7 @@ public class MultiAutoCompleteTextView extends AutoCompleteTextView {
* Instead of validating the entire text, this subclass method validates
* each token of the text individually. Empty tokens are removed.
*/
- @Override
+ @Override
public void performValidation() {
Validator v = getValidator();
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index a5b7281..563fc26 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -392,11 +392,11 @@ public class PopupWindow {
mContentView = contentView;
- if (mContext == null) {
+ if (mContext == null && mContentView != null) {
mContext = mContentView.getContext();
}
- if (mWindowManager == null) {
+ if (mWindowManager == null && mContentView != null) {
mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
}
}
@@ -939,7 +939,9 @@ public class PopupWindow {
* @param p the layout parameters of the popup's content view
*/
private void invokePopup(WindowManager.LayoutParams p) {
- p.packageName = mContext.getPackageName();
+ if (mContext != null) {
+ p.packageName = mContext.getPackageName();
+ }
mWindowManager.addView(mPopupView, p);
}
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index 6b676b4..30374af 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -16,6 +16,8 @@
package android.widget;
+import com.android.internal.R;
+
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
@@ -41,6 +43,8 @@ import android.view.Gravity;
import android.view.RemotableViewMethod;
import android.view.View;
import android.view.ViewDebug;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
@@ -49,8 +53,6 @@ import android.view.animation.LinearInterpolator;
import android.view.animation.Transformation;
import android.widget.RemoteViews.RemoteView;
-import com.android.internal.R;
-
/**
* <p>
@@ -187,6 +189,7 @@ import com.android.internal.R;
public class ProgressBar extends View {
private static final int MAX_LEVEL = 10000;
private static final int ANIMATION_RESOLUTION = 200;
+ private static final int TIMEOUT_SEND_ACCESSIBILITY_EVENT = 200;
int mMinWidth;
int mMaxWidth;
@@ -218,6 +221,8 @@ public class ProgressBar extends View {
private int mAnimationResolution;
+ private AccessibilityEventSender mAccessibilityEventSender;
+
/**
* Create a new progress bar with range 0...100 and initial progress of 0.
* @param context the application environment
@@ -604,8 +609,11 @@ public class ProgressBar extends View {
onProgressRefresh(scale, fromUser);
}
}
-
- void onProgressRefresh(float scale, boolean fromUser) {
+
+ void onProgressRefresh(float scale, boolean fromUser) {
+ if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+ scheduleAccessibilityEventSender();
+ }
}
private synchronized void refreshProgress(int id, int progress, boolean fromUser) {
@@ -1069,8 +1077,46 @@ public class ProgressBar extends View {
if (mIndeterminate) {
stopAnimation();
}
+ if(mRefreshProgressRunnable != null) {
+ removeCallbacks(mRefreshProgressRunnable);
+ }
+ if (mAccessibilityEventSender != null) {
+ removeCallbacks(mAccessibilityEventSender);
+ }
// This should come after stopAnimation(), otherwise an invalidate message remains in the
// queue, which can prevent the entire view hierarchy from being GC'ed during a rotation
super.onDetachedFromWindow();
}
+
+ @Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+ event.setItemCount(mMax);
+ event.setCurrentItemIndex(mProgress);
+ }
+
+ /**
+ * Schedule a command for sending an accessibility event.
+ * </br>
+ * Note: A command is used to ensure that accessibility events
+ * are sent at most one in a given time frame to save
+ * system resources while the progress changes quickly.
+ */
+ private void scheduleAccessibilityEventSender() {
+ if (mAccessibilityEventSender == null) {
+ mAccessibilityEventSender = new AccessibilityEventSender();
+ } else {
+ removeCallbacks(mAccessibilityEventSender);
+ }
+ postDelayed(mAccessibilityEventSender, TIMEOUT_SEND_ACCESSIBILITY_EVENT);
+ }
+
+ /**
+ * Command for sending an accessibility event.
+ */
+ private class AccessibilityEventSender implements Runnable {
+ public void run() {
+ sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
+ }
+ }
}
diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java
index a47359f..9069283 100644
--- a/core/java/android/widget/RelativeLayout.java
+++ b/core/java/android/widget/RelativeLayout.java
@@ -186,6 +186,11 @@ public class RelativeLayout extends ViewGroup {
a.recycle();
}
+ @Override
+ public boolean shouldDelayChildPressedState() {
+ return false;
+ }
+
/**
* Defines which View is ignored when the gravity is applied. This setting has no
* effect if the gravity is <code>Gravity.LEFT | Gravity.TOP</code>.
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index c854fac..9cf2718 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -125,7 +125,7 @@ public class RemoteViews implements Parcelable, Filter {
* SUBCLASSES MUST BE IMMUTABLE SO CLONE WORKS!!!!!
*/
private abstract static class Action implements Parcelable {
- public abstract void apply(View root) throws ActionException;
+ public abstract void apply(View root, ViewGroup rootParent) throws ActionException;
public int describeContents() {
return 0;
@@ -183,7 +183,7 @@ public class RemoteViews implements Parcelable, Filter {
}
@Override
- public void apply(View root) {
+ public void apply(View root, ViewGroup rootParent) {
final View view = root.findViewById(viewId);
if (!(view instanceof AdapterView<?>)) return;
@@ -214,7 +214,7 @@ public class RemoteViews implements Parcelable, Filter {
}
@Override
- public void apply(View root) {
+ public void apply(View root, ViewGroup rootParent) {
final View target = root.findViewById(viewId);
if (target == null) return;
@@ -295,7 +295,7 @@ public class RemoteViews implements Parcelable, Filter {
}
@Override
- public void apply(View root) {
+ public void apply(View root, ViewGroup rootParent) {
final View target = root.findViewById(viewId);
if (target == null) return;
@@ -360,6 +360,60 @@ public class RemoteViews implements Parcelable, Filter {
public final static int TAG = 8;
}
+ private class SetRemoteViewsAdapterIntent extends Action {
+ public SetRemoteViewsAdapterIntent(int id, Intent intent) {
+ this.viewId = id;
+ this.intent = intent;
+ }
+
+ public SetRemoteViewsAdapterIntent(Parcel parcel) {
+ viewId = parcel.readInt();
+ intent = Intent.CREATOR.createFromParcel(parcel);
+ }
+
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(TAG);
+ dest.writeInt(viewId);
+ intent.writeToParcel(dest, flags);
+ }
+
+ @Override
+ public void apply(View root, ViewGroup rootParent) {
+ final View target = root.findViewById(viewId);
+ if (target == null) return;
+
+ // Ensure that we are applying to an AppWidget root
+ if (!(rootParent instanceof AppWidgetHostView)) {
+ Log.e("RemoteViews", "SetRemoteViewsAdapterIntent action can only be used for " +
+ "AppWidgets (root id: " + viewId + ")");
+ return;
+ }
+ // Ensure that we are calling setRemoteAdapter on an AdapterView that supports it
+ if (!(target instanceof AbsListView) && !(target instanceof AdapterViewAnimator)) {
+ Log.e("RemoteViews", "Cannot setRemoteViewsAdapter on a view which is not " +
+ "an AbsListView or AdapterViewAnimator (id: " + viewId + ")");
+ return;
+ }
+
+ // Embed the AppWidget Id for use in RemoteViewsAdapter when connecting to the intent
+ // RemoteViewsService
+ AppWidgetHostView host = (AppWidgetHostView) rootParent;
+ intent.putExtra(EXTRA_REMOTEADAPTER_APPWIDGET_ID, host.getAppWidgetId());
+ if (target instanceof AbsListView) {
+ AbsListView v = (AbsListView) target;
+ v.setRemoteViewsAdapter(intent);
+ } else if (target instanceof AdapterViewAnimator) {
+ AdapterViewAnimator v = (AdapterViewAnimator) target;
+ v.setRemoteViewsAdapter(intent);
+ }
+ }
+
+ int viewId;
+ Intent intent;
+
+ public final static int TAG = 10;
+ }
+
/**
* Equivalent to calling
* {@link android.view.View#setOnClickListener(android.view.View.OnClickListener)}
@@ -383,7 +437,7 @@ public class RemoteViews implements Parcelable, Filter {
}
@Override
- public void apply(View root) {
+ public void apply(View root, ViewGroup rootParent) {
final View target = root.findViewById(viewId);
if (target == null) return;
@@ -479,7 +533,7 @@ public class RemoteViews implements Parcelable, Filter {
}
@Override
- public void apply(View root) {
+ public void apply(View root, ViewGroup rootParent) {
final View target = root.findViewById(viewId);
if (target == null) return;
@@ -539,7 +593,7 @@ public class RemoteViews implements Parcelable, Filter {
}
@Override
- public void apply(View root) {
+ public void apply(View root, ViewGroup rootParent) {
final View view = root.findViewById(viewId);
if (view == null) return;
@@ -755,7 +809,7 @@ public class RemoteViews implements Parcelable, Filter {
}
@Override
- public void apply(View root) {
+ public void apply(View root, ViewGroup rootParent) {
final View view = root.findViewById(viewId);
if (view == null) return;
@@ -850,7 +904,7 @@ public class RemoteViews implements Parcelable, Filter {
}
@Override
- public void apply(View root) {
+ public void apply(View root, ViewGroup rootParent) {
final Context context = root.getContext();
final ViewGroup target = (ViewGroup) root.findViewById(viewId);
if (target == null) return;
@@ -952,6 +1006,9 @@ public class RemoteViews implements Parcelable, Filter {
case SetOnClickFillInIntent.TAG:
mActions.add(new SetOnClickFillInIntent(parcel));
break;
+ case SetRemoteViewsAdapterIntent.TAG:
+ mActions.add(new SetRemoteViewsAdapterIntent(parcel));
+ break;
default:
throw new ActionException("Tag " + tag + " not found");
}
@@ -1287,16 +1344,29 @@ public class RemoteViews implements Parcelable, Filter {
/**
* Equivalent to calling {@link android.widget.AbsListView#setRemoteViewsAdapter(Intent)}.
*
- * @param appWidgetId The id of the app widget which contains the specified view
+ * @param appWidgetId The id of the app widget which contains the specified view. (This
+ * parameter is ignored in this deprecated method)
* @param viewId The id of the view whose text should change
* @param intent The intent of the service which will be
* providing data to the RemoteViewsAdapter
+ * @deprecated This method has been deprecated. See
+ * {@link android.widget.RemoteViews#setRemoteAdapter(int, Intent)}
*/
+ @Deprecated
public void setRemoteAdapter(int appWidgetId, int viewId, Intent intent) {
- // Embed the AppWidget Id for use in RemoteViewsAdapter when connecting to the intent
- // RemoteViewsService
- intent.putExtra(EXTRA_REMOTEADAPTER_APPWIDGET_ID, appWidgetId);
- setIntent(viewId, "setRemoteViewsAdapter", intent);
+ setRemoteAdapter(viewId, intent);
+ }
+
+ /**
+ * Equivalent to calling {@link android.widget.AbsListView#setRemoteViewsAdapter(Intent)}.
+ * Can only be used for App Widgets.
+ *
+ * @param viewId The id of the view whose text should change
+ * @param intent The intent of the service which will be
+ * providing data to the RemoteViewsAdapter
+ */
+ public void setRemoteAdapter(int viewId, Intent intent) {
+ addAction(new SetRemoteViewsAdapterIntent(viewId, intent));
}
/**
@@ -1499,7 +1569,7 @@ public class RemoteViews implements Parcelable, Filter {
result = inflater.inflate(mLayoutId, parent, false);
- performApply(result);
+ performApply(result, parent);
return result;
}
@@ -1514,15 +1584,15 @@ public class RemoteViews implements Parcelable, Filter {
*/
public void reapply(Context context, View v) {
prepareContext(context);
- performApply(v);
+ performApply(v, (ViewGroup) v.getParent());
}
- private void performApply(View v) {
+ private void performApply(View v, ViewGroup parent) {
if (mActions != null) {
final int count = mActions.size();
for (int i = 0; i < count; i++) {
Action a = mActions.get(i);
- a.apply(v);
+ a.apply(v, parent);
}
}
}
diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java
index 1c0a2bb..40b0a9c 100644
--- a/core/java/android/widget/RemoteViewsAdapter.java
+++ b/core/java/android/widget/RemoteViewsAdapter.java
@@ -29,6 +29,7 @@ import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
+import android.os.RemoteException;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
@@ -156,13 +157,16 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback
// create in response to this bind
factory.onDataSetChanged();
}
- } catch (Exception e) {
+ } catch (RemoteException e) {
Log.e(TAG, "Error notifying factory of data set changed in " +
"onServiceConnected(): " + e.getMessage());
// Return early to prevent anything further from being notified
// (effectively nothing has changed)
return;
+ } catch (RuntimeException e) {
+ Log.e(TAG, "Error notifying factory of data set changed in " +
+ "onServiceConnected(): " + e.getMessage());
}
// Request meta data so that we have up to date data when calling back to
@@ -777,7 +781,9 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback
tmpMetaData.count = count;
tmpMetaData.setLoadingViewTemplates(loadingView, firstView);
}
- } catch (Exception e) {
+ } catch(RemoteException e) {
+ processException("updateMetaData", e);
+ } catch(RuntimeException e) {
processException("updateMetaData", e);
}
}
@@ -792,12 +798,15 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback
try {
remoteViews = factory.getViewAt(position);
itemId = factory.getItemId(position);
- } catch (Exception e) {
+ } catch (RemoteException e) {
Log.e(TAG, "Error in updateRemoteViews(" + position + "): " + e.getMessage());
// Return early to prevent additional work in re-centering the view cache, and
// swapping from the loading view
return;
+ } catch (RuntimeException e) {
+ Log.e(TAG, "Error in updateRemoteViews(" + position + "): " + e.getMessage());
+ return;
}
if (remoteViews == null) {
@@ -971,18 +980,20 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback
return getCount() <= 0;
}
-
private void onNotifyDataSetChanged() {
// Complete the actual notifyDataSetChanged() call initiated earlier
IRemoteViewsFactory factory = mServiceConnection.getRemoteViewsFactory();
try {
factory.onDataSetChanged();
- } catch (Exception e) {
+ } catch (RemoteException e) {
Log.e(TAG, "Error in updateNotifyDataSetChanged(): " + e.getMessage());
// Return early to prevent from further being notified (since nothing has
// changed)
return;
+ } catch (RuntimeException e) {
+ Log.e(TAG, "Error in updateNotifyDataSetChanged(): " + e.getMessage());
+ return;
}
// Flush the cache so that we can reload new items from the service
diff --git a/core/java/android/widget/RemoteViewsService.java b/core/java/android/widget/RemoteViewsService.java
index e0b08d4..7ba4777 100644
--- a/core/java/android/widget/RemoteViewsService.java
+++ b/core/java/android/widget/RemoteViewsService.java
@@ -138,34 +138,87 @@ public abstract class RemoteViewsService extends Service {
return mIsCreated;
}
public synchronized void onDataSetChanged() {
- mFactory.onDataSetChanged();
+ try {
+ mFactory.onDataSetChanged();
+ } catch (Exception ex) {
+ Thread t = Thread.currentThread();
+ Thread.getDefaultUncaughtExceptionHandler().uncaughtException(t, ex);
+ }
}
public synchronized int getCount() {
- return mFactory.getCount();
+ int count = 0;
+ try {
+ count = mFactory.getCount();
+ } catch (Exception ex) {
+ Thread t = Thread.currentThread();
+ Thread.getDefaultUncaughtExceptionHandler().uncaughtException(t, ex);
+ }
+ return count;
}
public synchronized RemoteViews getViewAt(int position) {
- RemoteViews rv = mFactory.getViewAt(position);
- rv.setIsWidgetCollectionChild(true);
+ RemoteViews rv = null;
+ try {
+ rv = mFactory.getViewAt(position);
+ if (rv != null) {
+ rv.setIsWidgetCollectionChild(true);
+ }
+ } catch (Exception ex) {
+ Thread t = Thread.currentThread();
+ Thread.getDefaultUncaughtExceptionHandler().uncaughtException(t, ex);
+ }
return rv;
}
public synchronized RemoteViews getLoadingView() {
- return mFactory.getLoadingView();
+ RemoteViews rv = null;
+ try {
+ rv = mFactory.getLoadingView();
+ } catch (Exception ex) {
+ Thread t = Thread.currentThread();
+ Thread.getDefaultUncaughtExceptionHandler().uncaughtException(t, ex);
+ }
+ return rv;
}
public synchronized int getViewTypeCount() {
- return mFactory.getViewTypeCount();
+ int count = 0;
+ try {
+ count = mFactory.getViewTypeCount();
+ } catch (Exception ex) {
+ Thread t = Thread.currentThread();
+ Thread.getDefaultUncaughtExceptionHandler().uncaughtException(t, ex);
+ }
+ return count;
}
public synchronized long getItemId(int position) {
- return mFactory.getItemId(position);
+ long id = 0;
+ try {
+ id = mFactory.getItemId(position);
+ } catch (Exception ex) {
+ Thread t = Thread.currentThread();
+ Thread.getDefaultUncaughtExceptionHandler().uncaughtException(t, ex);
+ }
+ return id;
}
public synchronized boolean hasStableIds() {
- return mFactory.hasStableIds();
+ boolean hasStableIds = false;
+ try {
+ hasStableIds = mFactory.hasStableIds();
+ } catch (Exception ex) {
+ Thread t = Thread.currentThread();
+ Thread.getDefaultUncaughtExceptionHandler().uncaughtException(t, ex);
+ }
+ return hasStableIds;
}
public void onDestroy(Intent intent) {
synchronized (sLock) {
Intent.FilterComparison fc = new Intent.FilterComparison(intent);
if (RemoteViewsService.sRemoteViewFactories.containsKey(fc)) {
RemoteViewsFactory factory = RemoteViewsService.sRemoteViewFactories.get(fc);
- factory.onDestroy();
+ try {
+ factory.onDestroy();
+ } catch (Exception ex) {
+ Thread t = Thread.currentThread();
+ Thread.getDefaultUncaughtExceptionHandler().uncaughtException(t, ex);
+ }
RemoteViewsService.sRemoteViewFactories.remove(fc);
}
}
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index ade3a0a..27edb88 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -162,6 +162,11 @@ public class ScrollView extends FrameLayout {
}
@Override
+ public boolean shouldDelayChildPressedState() {
+ return true;
+ }
+
+ @Override
protected float getTopFadingEdgeStrength() {
if (getChildCount() == 0) {
return 0.0f;
diff --git a/core/java/android/widget/SimpleCursorAdapter.java b/core/java/android/widget/SimpleCursorAdapter.java
index 3d2a252..c5c6c69 100644
--- a/core/java/android/widget/SimpleCursorAdapter.java
+++ b/core/java/android/widget/SimpleCursorAdapter.java
@@ -338,6 +338,12 @@ public class SimpleCursorAdapter extends ResourceCursorAdapter {
@Override
public Cursor swapCursor(Cursor c) {
+ // super.swapCursor() will notify observers before we have
+ // a valid mapping, make sure we have a mapping before this
+ // happens
+ if (mFrom == null) {
+ findColumns(mOriginalFrom);
+ }
Cursor res = super.swapCursor(c);
// rescan columns in case cursor layout is different
findColumns(mOriginalFrom);
@@ -358,7 +364,13 @@ public class SimpleCursorAdapter extends ResourceCursorAdapter {
public void changeCursorAndColumns(Cursor c, String[] from, int[] to) {
mOriginalFrom = from;
mTo = to;
- super.changeCursor(c);
+ // super.changeCursor() will notify observers before we have
+ // a valid mapping, make sure we have a mapping before this
+ // happens
+ if (mFrom == null) {
+ findColumns(mOriginalFrom);
+ }
+ super.changeCursor(c);
findColumns(mOriginalFrom);
}
diff --git a/core/java/android/widget/StackView.java b/core/java/android/widget/StackView.java
index 21c61bd..71c91e1 100644
--- a/core/java/android/widget/StackView.java
+++ b/core/java/android/widget/StackView.java
@@ -20,6 +20,7 @@ import java.lang.ref.WeakReference;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.content.Context;
+import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BlurMaskFilter;
import android.graphics.Canvas;
@@ -132,6 +133,8 @@ public class StackView extends AdapterViewAnimator {
private int mMaximumVelocity;
private VelocityTracker mVelocityTracker;
private boolean mTransitionIsSetup = false;
+ private int mResOutColor;
+ private int mClickColor;
private static HolographicHelper sHolographicHelper;
private ImageView mHighlight;
@@ -146,12 +149,24 @@ public class StackView extends AdapterViewAnimator {
private final Rect stackInvalidateRect = new Rect();
public StackView(Context context) {
- super(context);
- initStackView();
+ this(context, null);
}
public StackView(Context context, AttributeSet attrs) {
- super(context, attrs);
+ this(context, attrs, com.android.internal.R.attr.stackViewStyle);
+ }
+
+ public StackView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ TypedArray a = context.obtainStyledAttributes(attrs,
+ com.android.internal.R.styleable.StackView, defStyleAttr, 0);
+
+ mResOutColor = a.getColor(
+ com.android.internal.R.styleable.StackView_resOutColor, 0);
+ mClickColor = a.getColor(
+ com.android.internal.R.styleable.StackView_clickColor, 0);
+
+ a.recycle();
initStackView();
}
@@ -357,7 +372,7 @@ public class StackView extends AdapterViewAnimator {
private void setupStackSlider(View v, int mode) {
mStackSlider.setMode(mode);
if (v != null) {
- mHighlight.setImageBitmap(sHolographicHelper.createOutline(v));
+ mHighlight.setImageBitmap(sHolographicHelper.createResOutline(v, mResOutColor));
mHighlight.setRotation(v.getRotation());
mHighlight.setTranslationY(v.getTranslationY());
mHighlight.setTranslationX(v.getTranslationX());
@@ -412,8 +427,8 @@ public class StackView extends AdapterViewAnimator {
// Here we need to make sure that the z-order of the children is correct
for (int i = mCurrentWindowEnd; i >= mCurrentWindowStart; i--) {
int index = modulo(i, getWindowSize());
- ViewAndIndex vi = mViewsMap.get(index);
- if (vi != null) {
+ ViewAndMetaData vm = mViewsMap.get(index);
+ if (vm != null) {
View v = mViewsMap.get(index).view;
if (v != null) v.bringToFront();
}
@@ -429,8 +444,8 @@ public class StackView extends AdapterViewAnimator {
if (!mClickFeedbackIsValid) {
View v = getViewAtRelativeIndex(1);
if (v != null) {
- mClickFeedback.setImageBitmap(sHolographicHelper.createOutline(v,
- HolographicHelper.CLICK_FEEDBACK));
+ mClickFeedback.setImageBitmap(
+ sHolographicHelper.createClickOutline(v, mClickColor));
mClickFeedback.setTranslationX(v.getTranslationX());
mClickFeedback.setTranslationY(v.getTranslationY());
}
@@ -1355,16 +1370,19 @@ public class StackView extends AdapterViewAnimator {
mLargeBlurMaskFilter = new BlurMaskFilter(4 * mDensity, BlurMaskFilter.Blur.NORMAL);
}
- Bitmap createOutline(View v) {
- return createOutline(v, RES_OUT);
+ Bitmap createClickOutline(View v, int color) {
+ return createOutline(v, CLICK_FEEDBACK, color);
+ }
+
+ Bitmap createResOutline(View v, int color) {
+ return createOutline(v, RES_OUT, color);
}
- Bitmap createOutline(View v, int type) {
+ Bitmap createOutline(View v, int type, int color) {
+ mHolographicPaint.setColor(color);
if (type == RES_OUT) {
- mHolographicPaint.setColor(0xff6699ff);
mBlurPaint.setMaskFilter(mSmallBlurMaskFilter);
} else if (type == CLICK_FEEDBACK) {
- mHolographicPaint.setColor(0x886699ff);
mBlurPaint.setMaskFilter(mLargeBlurMaskFilter);
}
diff --git a/core/java/android/widget/TabWidget.java b/core/java/android/widget/TabWidget.java
index 6f76dd0..1fe1f79 100644
--- a/core/java/android/widget/TabWidget.java
+++ b/core/java/android/widget/TabWidget.java
@@ -427,12 +427,19 @@ public class TabWidget extends LinearLayout implements OnFocusChangeListener {
@Override
public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
- event.setItemCount(getTabCount());
- event.setCurrentItemIndex(mSelectedTab);
+ onPopulateAccessibilityEvent(event);
+ // Dispatch only to the selected tab.
if (mSelectedTab != -1) {
- getChildTabViewAt(mSelectedTab).dispatchPopulateAccessibilityEvent(event);
+ return getChildTabViewAt(mSelectedTab).dispatchPopulateAccessibilityEvent(event);
}
- return true;
+ return false;
+ }
+
+ @Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+ event.setItemCount(getTabCount());
+ event.setCurrentItemIndex(mSelectedTab);
}
/**
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 13b9285f..d58c72b 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -60,6 +60,7 @@ import android.text.Selection;
import android.text.SpanWatcher;
import android.text.Spannable;
import android.text.SpannableString;
+import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.SpannedString;
import android.text.StaticLayout;
@@ -80,12 +81,17 @@ import android.text.method.SingleLineTransformationMethod;
import android.text.method.TextKeyListener;
import android.text.method.TimeKeyListener;
import android.text.method.TransformationMethod;
+import android.text.method.WordIterator;
import android.text.style.ClickableSpan;
import android.text.style.ParagraphStyle;
+import android.text.style.SuggestionSpan;
+import android.text.style.TextAppearanceSpan;
import android.text.style.URLSpan;
+import android.text.style.UnderlineSpan;
import android.text.style.UpdateAppearance;
import android.text.util.Linkify;
import android.util.AttributeSet;
+import android.util.DisplayMetrics;
import android.util.FloatMath;
import android.util.Log;
import android.util.TypedValue;
@@ -102,12 +108,12 @@ import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
+import android.view.ViewAncestor;
import android.view.ViewConfiguration;
import android.view.ViewDebug;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.ViewParent;
-import android.view.ViewRoot;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent;
@@ -125,6 +131,7 @@ import android.widget.RemoteViews.RemoteView;
import java.io.IOException;
import java.lang.ref.WeakReference;
+import java.text.BreakIterator;
import java.util.ArrayList;
/**
@@ -309,6 +316,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
private int mTextEditPasteWindowLayout, mTextEditSidePasteWindowLayout;
private int mTextEditNoPasteWindowLayout, mTextEditSideNoPasteWindowLayout;
+ private int mTextEditSuggestionsBottomWindowLayout, mTextEditSuggestionsTopWindowLayout;
+ private int mTextEditSuggestionItemLayout;
+ private SuggestionsPopupWindow mSuggestionsPopupWindow;
+ private SuggestionRangeSpan mSuggestionRangeSpan;
+
private int mCursorDrawableRes;
private final Drawable[] mCursorDrawable = new Drawable[2];
private int mCursorCount; // Actual current number of used mCursorDrawable: 0, 1 or 2
@@ -324,6 +336,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
// Set when this TextView gained focus with some text selected. Will start selection mode.
private boolean mCreatedWithASelection = false;
+ private WordIterator mWordIterator;
+
/*
* Kick-start the font cache for the zygote process (to pay the cost of
* initializing freetype for our default font only once).
@@ -777,6 +791,18 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
mTextEditSideNoPasteWindowLayout = a.getResourceId(attr, 0);
break;
+ case com.android.internal.R.styleable.TextView_textEditSuggestionsBottomWindowLayout:
+ mTextEditSuggestionsBottomWindowLayout = a.getResourceId(attr, 0);
+ break;
+
+ case com.android.internal.R.styleable.TextView_textEditSuggestionsTopWindowLayout:
+ mTextEditSuggestionsTopWindowLayout = a.getResourceId(attr, 0);
+ break;
+
+ case com.android.internal.R.styleable.TextView_textEditSuggestionItemLayout:
+ mTextEditSuggestionItemLayout = a.getResourceId(attr, 0);
+ break;
+
case com.android.internal.R.styleable.TextView_textIsSelectable:
mTextIsSelectable = a.getBoolean(attr, false);
break;
@@ -2949,6 +2975,16 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
advancesIndex);
}
+ public float getTextRunAdvances(int start, int end, int contextStart,
+ int contextEnd, int flags, float[] advances, int advancesIndex,
+ Paint p, int reserved) {
+ int count = end - start;
+ int contextCount = contextEnd - contextStart;
+ return p.getTextRunAdvances(mChars, start + mStart, count,
+ contextStart + mStart, contextCount, flags, advances,
+ advancesIndex, reserved);
+ }
+
public int getTextRunCursor(int contextStart, int contextEnd, int flags,
int offset, int cursorOpt, Paint p) {
int contextCount = contextEnd - contextStart;
@@ -3337,13 +3373,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
Handler h = getHandler();
if (h != null) {
long eventTime = SystemClock.uptimeMillis();
- h.sendMessage(h.obtainMessage(ViewRoot.DISPATCH_KEY_FROM_IME,
+ h.sendMessage(h.obtainMessage(ViewAncestor.DISPATCH_KEY_FROM_IME,
new KeyEvent(eventTime, eventTime,
KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER, 0, 0,
KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE
| KeyEvent.FLAG_EDITOR_ACTION)));
- h.sendMessage(h.obtainMessage(ViewRoot.DISPATCH_KEY_FROM_IME,
+ h.sendMessage(h.obtainMessage(ViewAncestor.DISPATCH_KEY_FROM_IME,
new KeyEvent(SystemClock.uptimeMillis(), eventTime,
KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER, 0, 0,
KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
@@ -3977,13 +4013,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
observer.removeOnPreDrawListener(this);
mPreDrawState = PREDRAW_NOT_REGISTERED;
}
- // No need to create the controller, as getXXController would.
- if (mInsertionPointCursorController != null) {
- observer.removeOnTouchModeChangeListener(mInsertionPointCursorController);
- }
- if (mSelectionModifierCursorController != null) {
- observer.removeOnTouchModeChangeListener(mSelectionModifierCursorController);
- }
if (mError != null) {
hideError();
@@ -4210,6 +4239,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
@Override
protected void onDraw(Canvas canvas) {
+ if (mPreDrawState == PREDRAW_DONE) {
+ final ViewTreeObserver observer = getViewTreeObserver();
+ observer.removeOnPreDrawListener(this);
+ mPreDrawState = PREDRAW_NOT_REGISTERED;
+ }
+
if (mCurrentAlpha <= ViewConfiguration.ALPHA_THRESHOLD_INT) return;
restartMarqueeIfNeeded();
@@ -4281,12 +4316,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
}
- if (mPreDrawState == PREDRAW_DONE) {
- final ViewTreeObserver observer = getViewTreeObserver();
- observer.removeOnPreDrawListener(this);
- mPreDrawState = PREDRAW_NOT_REGISTERED;
- }
-
int color = mCurTextColor;
if (mLayout == null) {
@@ -4549,15 +4578,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
if (translate) canvas.translate(0, -cursorOffsetVertical);
}
- /**
- * Update the positions of the CursorControllers. Needed by WebTextView,
- * which does not draw.
- * @hide
- */
- protected void updateCursorControllerPositions() {
- // TODO remove
- }
-
@Override
public void getFocusedRect(Rect r) {
if (mLayout == null) {
@@ -5236,6 +5256,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
* @param text The auto complete text the user has selected.
*/
public void onCommitCompletion(CompletionInfo text) {
+ // intentionally empty
}
/**
@@ -5422,6 +5443,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
* of edit operations through a call to link {@link #beginBatchEdit()}.
*/
public void onBeginBatchEdit() {
+ // intentionally empty
}
/**
@@ -5429,6 +5451,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
* of edit operations through a call to link {@link #endBatchEdit}.
*/
public void onEndBatchEdit() {
+ // intentionally empty
}
/**
@@ -6275,15 +6298,15 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
if (isFocused()) {
- // This offsets because getInterestingRect() is in terms of
- // viewport coordinates, but requestRectangleOnScreen()
- // is in terms of content coordinates.
+ // This offsets because getInterestingRect() is in terms of viewport coordinates, but
+ // requestRectangleOnScreen() is in terms of content coordinates.
- Rect r = new Rect(x, top, x + 1, bottom);
- getInterestingRect(r, line);
- r.offset(mScrollX, mScrollY);
+ if (mTempRect == null) mTempRect = new Rect();
+ mTempRect.set(x, top, x + 1, bottom);
+ getInterestingRect(mTempRect, line);
+ mTempRect.offset(mScrollX, mScrollY);
- if (requestRectangleOnScreen(r)) {
+ if (requestRectangleOnScreen(mTempRect)) {
changed = true;
}
}
@@ -6756,25 +6779,22 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
/**
- * This method is called when the text is changed, in case any
- * subclasses would like to know.
+ * This method is called when the text is changed, in case any subclasses
+ * would like to know.
*
- * @param text The text the TextView is displaying.
- * @param start The offset of the start of the range of the text
- * that was modified.
- * @param before The offset of the former end of the range of the
- * text that was modified. If text was simply inserted,
- * this will be the same as <code>start</code>.
- * If text was replaced with new text or deleted, the
- * length of the old text was <code>before-start</code>.
- * @param after The offset of the end of the range of the text
- * that was modified. If text was simply deleted,
- * this will be the same as <code>start</code>.
- * If text was replaced with new text or inserted,
- * the length of the new text is <code>after-start</code>.
+ * Within <code>text</code>, the <code>lengthAfter</code> characters
+ * beginning at <code>start</code> have just replaced old text that had
+ * length <code>lengthBefore</code>. It is an error to attempt to make
+ * changes to <code>text</code> from this callback.
+ *
+ * @param text The text the TextView is displaying
+ * @param start The offset of the start of the range of the text that was
+ * modified
+ * @param lengthBefore The length of the former text that has been replaced
+ * @param lengthAfter The length of the replacement modified text
*/
- protected void onTextChanged(CharSequence text,
- int start, int before, int after) {
+ protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
+ // intentionally empty
}
/**
@@ -6785,6 +6805,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
* @param selEnd The new selection end location.
*/
protected void onSelectionChanged(int selStart, int selEnd) {
+ // intentionally empty
}
/**
@@ -7131,7 +7152,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
// The DecorView does not have focus when the 'Done' ExtractEditText button is
- // pressed. Since it is the ViewRoot's mView, it requests focus before
+ // pressed. Since it is the ViewAncestor's mView, it requests focus before
// ExtractEditText clears focus, which gives focus to the ExtractEditText.
// This special case ensure that we keep current selection in that case.
// It would be better to know why the DecorView does not have focus at that time.
@@ -7200,14 +7221,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
super.onFocusChanged(focused, direction, previouslyFocusedRect);
-
- // Performed after super.onFocusChanged so that this TextView is registered and can ask for
- // the IME. Showing the IME while focus is moved using the D-Pad is a bad idea, however this
- // does not happen in that case (using the arrows on a bluetooth keyboard).
- if (focused && isTextEditable()) {
- final InputMethodManager imm = InputMethodManager.peekInstance();
- if (imm != null) imm.showSoftInput(this, 0);
- }
}
private int getLastTapPosition() {
@@ -7247,11 +7260,23 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
mInputContentType.enterDown = false;
}
hideControllers();
+ removeAllSuggestionSpans();
}
startStopMarquee(hasWindowFocus);
}
+ private void removeAllSuggestionSpans() {
+ if (mText instanceof Editable) {
+ Editable editable = ((Editable) mText);
+ SuggestionSpan[] spans = editable.getSpans(0, mText.length(), SuggestionSpan.class);
+ final int length = spans.length;
+ for (int i = 0; i < length; i++) {
+ editable.removeSpan(spans[i]);
+ }
+ }
+ }
+
@Override
protected void onVisibilityChanged(View changedView, int visibility) {
super.onVisibilityChanged(changedView, visibility);
@@ -7326,9 +7351,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
&& mText instanceof Spannable && mLayout != null) {
boolean handled = false;
- final int oldScrollX = mScrollX;
- final int oldScrollY = mScrollY;
-
if (mMovement != null) {
handled |= mMovement.onTouchEvent(this, (Spannable) mText, event);
}
@@ -7345,27 +7367,21 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
}
- if (isTextEditable() || mTextIsSelectable) {
- if (mScrollX != oldScrollX || mScrollY != oldScrollY) { // TODO remove
- // Hide insertion anchor while scrolling. Leave selection.
- hideInsertionPointCursorController(); // TODO any motion should hide it
+ if ((isTextEditable() || mTextIsSelectable) && touchIsFinished) {
+ // Show the IME, except when selecting in read-only text.
+ if (!mTextIsSelectable) {
+ final InputMethodManager imm = InputMethodManager.peekInstance();
+ handled |= imm != null && imm.showSoftInput(this, 0);
}
- if (touchIsFinished) {
- // Show the IME, except when selecting in read-only text.
- if (!mTextIsSelectable) {
- final InputMethodManager imm = InputMethodManager.peekInstance();
- handled |= imm != null && imm.showSoftInput(this, 0);
- }
-
- boolean selectAllGotFocus = mSelectAllOnFocus && didTouchFocusSelect();
- if (!selectAllGotFocus && hasSelection()) {
- startSelectionActionMode();
- } else {
- stopSelectionActionMode();
- if (hasInsertionController() && !selectAllGotFocus && mText.length() > 0) {
- getInsertionController().show();
- }
+ boolean selectAllGotFocus = mSelectAllOnFocus && didTouchFocusSelect();
+ if (!selectAllGotFocus && hasSelection()) {
+ startSelectionActionMode();
+ } else {
+ stopSelectionActionMode();
+ hideSuggestions();
+ if (hasInsertionController() && !selectAllGotFocus && mText.length() > 0) {
+ getInsertionController().show();
}
}
}
@@ -7752,78 +7768,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
type == Character.DECIMAL_DIGIT_NUMBER);
}
- /**
- * Returns the offsets delimiting the 'word' located at position offset.
- *
- * @param offset An offset in the text.
- * @return The offsets for the start and end of the word located at <code>offset</code>.
- * The two ints offsets are packed in a long using {@link #packRangeInLong(int, int)}.
- * Returns -1 if no valid word was found.
- */
- private long getWordLimitsAt(int offset) {
- int klass = mInputType & InputType.TYPE_MASK_CLASS;
- int variation = mInputType & InputType.TYPE_MASK_VARIATION;
-
- // Text selection is not permitted in password fields
- if (hasPasswordTransformationMethod()) {
- return -1;
- }
-
- final int len = mText.length();
-
- // Specific text fields: always select the entire text
- if (klass == InputType.TYPE_CLASS_NUMBER ||
- klass == InputType.TYPE_CLASS_PHONE ||
- klass == InputType.TYPE_CLASS_DATETIME ||
- variation == InputType.TYPE_TEXT_VARIATION_URI ||
- variation == InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS ||
- variation == InputType.TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS ||
- variation == InputType.TYPE_TEXT_VARIATION_FILTER) {
- return len > 0 ? packRangeInLong(0, len) : -1;
- }
-
- int end = Math.min(offset, len);
- if (end < 0) {
- return -1;
- }
-
- final int MAX_LENGTH = 48;
- int start = end;
-
- for (; start > 0; start--) {
- final char c = mTransformed.charAt(start - 1);
- final int type = Character.getType(c);
- if (start == end && type == Character.OTHER_PUNCTUATION) {
- // Cases where the text ends with a '.' and we select from the end of the line
- // (right after the dot), or when we select from the space character in "aaa, bbb".
- continue;
- }
- if (type == Character.SURROGATE) { // Two Character codepoint
- end = start - 1; // Recheck as a pair when scanning forward
- continue;
- }
- if (!isWordCharacter(c, type)) break;
- if ((end - start) > MAX_LENGTH) return -1;
- }
-
- for (; end < len; end++) {
- final int c = Character.codePointAt(mTransformed, end);
- final int type = Character.getType(c);
- if (!isWordCharacter(c, type)) break;
- if ((end - start) > MAX_LENGTH) return -1;
- if (c > 0xFFFF) { // Two Character codepoint
- end++;
- }
- }
-
- if (start == end) {
- return -1;
- }
-
- // Two ints packed in a long
- return packRangeInLong(start, end);
- }
-
private static long packRangeInLong(int start, int end) {
return (((long) start) << 32) | end;
}
@@ -7836,21 +7780,40 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
return (int) (range & 0x00000000FFFFFFFFL);
}
- private void selectAll() {
- Selection.setSelection((Spannable) mText, 0, mText.length());
+ private boolean selectAll() {
+ final int length = mText.length();
+ Selection.setSelection((Spannable) mText, 0, length);
+ return length > 0;
}
- private void selectCurrentWord() {
+ /**
+ * Adjusts selection to the word under last touch offset.
+ * Return true if the operation was successfully performed.
+ */
+ private boolean selectCurrentWord() {
if (!canSelectText()) {
- return;
+ return false;
}
if (hasPasswordTransformationMethod()) {
// Always select all on a password field.
// Cut/copy menu entries are not available for passwords, but being able to select all
// is however useful to delete or paste to replace the entire content.
- selectAll();
- return;
+ return selectAll();
+ }
+
+ int klass = mInputType & InputType.TYPE_MASK_CLASS;
+ int variation = mInputType & InputType.TYPE_MASK_VARIATION;
+
+ // Specific text field types: select the entire text for these
+ if (klass == InputType.TYPE_CLASS_NUMBER ||
+ klass == InputType.TYPE_CLASS_PHONE ||
+ klass == InputType.TYPE_CLASS_DATETIME ||
+ variation == InputType.TYPE_TEXT_VARIATION_URI ||
+ variation == InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS ||
+ variation == InputType.TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS ||
+ variation == InputType.TYPE_TEXT_VARIATION_FILTER) {
+ return selectAll();
}
long lastTouchOffsets = getLastTouchOffsets();
@@ -7866,22 +7829,21 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
selectionStart = ((Spanned) mText).getSpanStart(url);
selectionEnd = ((Spanned) mText).getSpanEnd(url);
} else {
- long wordLimits = getWordLimitsAt(minOffset);
- if (wordLimits >= 0) {
- selectionStart = extractRangeStartFromLong(wordLimits);
- } else {
- selectionStart = Math.max(minOffset - 5, 0);
+ if (mWordIterator == null) {
+ mWordIterator = new WordIterator();
}
+ // WordIerator handles text changes, this is a no-op if text in unchanged.
+ mWordIterator.setCharSequence(mText);
- wordLimits = getWordLimitsAt(maxOffset);
- if (wordLimits >= 0) {
- selectionEnd = extractRangeEndFromLong(wordLimits);
- } else {
- selectionEnd = Math.min(maxOffset + 5, mText.length());
- }
+ selectionStart = mWordIterator.getBeginning(minOffset);
+ if (selectionStart == BreakIterator.DONE) return false;
+
+ selectionEnd = mWordIterator.getEnd(maxOffset);
+ if (selectionEnd == BreakIterator.DONE) return false;
}
Selection.setSelection((Spannable) mText, selectionStart, selectionEnd);
+ return true;
}
private long getLastTouchOffsets() {
@@ -7900,28 +7862,27 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
@Override
- public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
- if (!isShown()) {
- return false;
- }
+ public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
+ super.onPopulateAccessibilityEvent(event);
final boolean isPassword = hasPasswordTransformationMethod();
-
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;
+ }
+
+ @Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+
+ final boolean isPassword = hasPasswordTransformationMethod();
+ event.setPassword(isPassword);
}
void sendAccessibilityEventTypeViewTextChanged(CharSequence beforeText,
@@ -8009,6 +7970,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
* this will be {@link android.R.id#copyUrl}, {@link android.R.id#selectTextMode},
* {@link android.R.id#selectAll}, {@link android.R.id#paste}, {@link android.R.id#cut}
* or {@link android.R.id#copy}.
+ *
+ * @return true if the context menu item action was performed.
*/
public boolean onTextContextMenuItem(int id) {
int min = 0;
@@ -8045,7 +8008,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
case ID_SELECTION_MODE:
if (mSelectionActionMode != null) {
// Selection mode is already started, simply change selected part.
- updateSelectedRegion();
+ selectCurrentWord();
} else {
startSelectionActionMode();
}
@@ -8053,7 +8016,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
case ID_SELECT_ALL:
// This does not enter text selection mode. Text is highlighted, so that it can be
- // bulk edited, like selectAllOnFocus does.
+ // bulk edited, like selectAllOnFocus does. Returns true even if text is empty.
selectAll();
return true;
@@ -8179,8 +8142,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
mInsertionControllerEnabled) {
final int offset = getOffset(mLastDownPositionX, mLastDownPositionY);
stopSelectionActionMode();
- Selection.setSelection((Spannable)mText, offset);
- getInsertionController().show(0);
+ Selection.setSelection((Spannable) mText, offset);
+ getInsertionController().showWithPaste();
handled = true;
}
@@ -8195,8 +8158,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
startDrag(data, getTextThumbnailBuilder(selectedText), localState, 0);
stopSelectionActionMode();
} else {
- // New selection at touch position
- updateSelectedRegion();
+ selectCurrentWord();
}
handled = true;
}
@@ -8212,17 +8174,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
return handled;
}
- /**
- * When selection mode is already started, this method simply updates the selected part of text
- * to the text under the finger.
- */
- private void updateSelectedRegion() {
- // Start a new selection at current position, keep selectionAction mode on
- selectCurrentWord();
- // Updates handles' positions
- getSelectionController().show();
- }
-
private boolean touchPositionIsInSelection() {
int selectionStart = getSelectionStart();
int selectionEnd = getSelectionEnd();
@@ -8245,6 +8196,424 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
return ((minOffset >= selectionStart) && (maxOffset < selectionEnd));
}
+ private static class SuggestionRangeSpan extends UnderlineSpan {
+ // TODO themable, would be nice to make it a child class of TextAppearanceSpan, but
+ // there is no way to have underline and TextAppearanceSpan.
+ }
+
+ private class SuggestionsPopupWindow implements OnClickListener {
+ private static final int MAX_NUMBER_SUGGESTIONS = 5;
+ private static final int NO_SUGGESTIONS = -1;
+ private final PopupWindow mContainer;
+ private final ViewGroup[] mSuggestionViews = new ViewGroup[2];
+ private final int[] mSuggestionViewLayouts = new int[] {
+ mTextEditSuggestionsBottomWindowLayout, mTextEditSuggestionsTopWindowLayout};
+ private WordIterator mWordIterator;
+ private TextAppearanceSpan[] mHighlightSpans = new TextAppearanceSpan[0];
+
+ public SuggestionsPopupWindow() {
+ mContainer = new PopupWindow(TextView.this.mContext, null,
+ com.android.internal.R.attr.textSuggestionsWindowStyle);
+ mContainer.setSplitTouchEnabled(true);
+ mContainer.setClippingEnabled(false);
+ mContainer.setWindowLayoutType(WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL);
+
+ mContainer.setWidth(ViewGroup.LayoutParams.WRAP_CONTENT);
+ mContainer.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
+ }
+
+ private class SuggestionInfo {
+ int suggestionStart, suggestionEnd; // range of suggestion item with replacement text
+ int spanStart, spanEnd; // range in TextView where text should be inserted
+ SuggestionSpan suggestionSpan; // the SuggestionSpan that this TextView represents
+ int suggestionIndex; // the index of the suggestion inside suggestionSpan
+ }
+
+ private ViewGroup getViewGroup(boolean under) {
+ final int viewIndex = under ? 0 : 1;
+ ViewGroup viewGroup = mSuggestionViews[viewIndex];
+
+ if (viewGroup == null) {
+ final int layout = mSuggestionViewLayouts[viewIndex];
+ LayoutInflater inflater = (LayoutInflater) TextView.this.mContext.
+ getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+
+ if (inflater == null) {
+ throw new IllegalArgumentException(
+ "Unable to create TextEdit suggestion window inflater");
+ }
+
+ View view = inflater.inflate(layout, null);
+
+ if (! (view instanceof ViewGroup)) {
+ throw new IllegalArgumentException(
+ "Inflated TextEdit suggestion window is not a ViewGroup: " + view);
+ }
+
+ viewGroup = (ViewGroup) view;
+
+ // Inflate the suggestion items once and for all.
+ for (int i = 0; i < MAX_NUMBER_SUGGESTIONS; i++) {
+ View childView = inflater.inflate(mTextEditSuggestionItemLayout, viewGroup,
+ false);
+
+ if (! (childView instanceof TextView)) {
+ throw new IllegalArgumentException(
+ "Inflated TextEdit suggestion item is not a TextView: " + childView);
+ }
+
+ childView.setTag(new SuggestionInfo());
+ viewGroup.addView(childView);
+ childView.setOnClickListener(this);
+ }
+
+ mSuggestionViews[viewIndex] = viewGroup;
+ }
+
+ return viewGroup;
+ }
+
+ public void show() {
+ if (!(mText instanceof Editable)) return;
+
+ final int pos = TextView.this.getSelectionStart();
+ Spannable spannable = (Spannable)TextView.this.mText;
+ SuggestionSpan[] suggestionSpans = spannable.getSpans(pos, pos, SuggestionSpan.class);
+ final int nbSpans = suggestionSpans.length;
+
+ ViewGroup viewGroup = getViewGroup(true);
+ mContainer.setContentView(viewGroup);
+
+ int totalNbSuggestions = 0;
+ int spanUnionStart = mText.length();
+ int spanUnionEnd = 0;
+
+ for (int spanIndex = 0; spanIndex < nbSpans; spanIndex++) {
+ SuggestionSpan suggestionSpan = suggestionSpans[spanIndex];
+ final int spanStart = spannable.getSpanStart(suggestionSpan);
+ final int spanEnd = spannable.getSpanEnd(suggestionSpan);
+ spanUnionStart = Math.min(spanStart, spanUnionStart);
+ spanUnionEnd = Math.max(spanEnd, spanUnionEnd);
+
+ String[] suggestions = suggestionSpan.getSuggestions();
+ int nbSuggestions = suggestions.length;
+ for (int suggestionIndex = 0; suggestionIndex < nbSuggestions; suggestionIndex++) {
+ TextView textView = (TextView) viewGroup.getChildAt(totalNbSuggestions);
+ textView.setText(suggestions[suggestionIndex]);
+ SuggestionInfo suggestionInfo = (SuggestionInfo) textView.getTag();
+ suggestionInfo.spanStart = spanStart;
+ suggestionInfo.spanEnd = spanEnd;
+ suggestionInfo.suggestionSpan = suggestionSpan;
+ suggestionInfo.suggestionIndex = suggestionIndex;
+
+ totalNbSuggestions++;
+ if (totalNbSuggestions == MAX_NUMBER_SUGGESTIONS) {
+ // Also end outer for loop
+ spanIndex = nbSpans;
+ break;
+ }
+ }
+ }
+
+ if (totalNbSuggestions == 0) {
+ // TODO Replace by final text, use a dedicated layout, add a fade out timer...
+ TextView textView = (TextView) viewGroup.getChildAt(0);
+ textView.setText("No suggestions available");
+ SuggestionInfo suggestionInfo = (SuggestionInfo) textView.getTag();
+ suggestionInfo.spanStart = NO_SUGGESTIONS;
+ totalNbSuggestions++;
+ } else {
+ if (mSuggestionRangeSpan == null) mSuggestionRangeSpan = new SuggestionRangeSpan();
+ ((Editable) mText).setSpan(mSuggestionRangeSpan, spanUnionStart, spanUnionEnd,
+ Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+
+ for (int i = 0; i < totalNbSuggestions; i++) {
+ final TextView textView = (TextView) viewGroup.getChildAt(i);
+ highlightTextDifferences(textView, spanUnionStart, spanUnionEnd);
+ }
+ }
+
+ for (int i = 0; i < MAX_NUMBER_SUGGESTIONS; i++) {
+ viewGroup.getChildAt(i).setVisibility(i < totalNbSuggestions ? VISIBLE : GONE);
+ }
+
+ final int size = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
+ viewGroup.measure(size, size);
+
+ positionAtCursor();
+ }
+
+ private long[] getWordLimits(CharSequence text) {
+ if (mWordIterator == null) mWordIterator = new WordIterator(); // TODO locale
+ mWordIterator.setCharSequence(text);
+
+ // First pass will simply count the number of words to be able to create an array
+ // Not too expensive since previous break positions are cached by the BreakIterator
+ int nbWords = 0;
+ int position = mWordIterator.following(0);
+ while (position != BreakIterator.DONE) {
+ nbWords++;
+ position = mWordIterator.following(position);
+ }
+
+ int index = 0;
+ long[] result = new long[nbWords];
+
+ position = mWordIterator.following(0);
+ while (position != BreakIterator.DONE) {
+ int wordStart = mWordIterator.getBeginning(position);
+ result[index++] = packRangeInLong(wordStart, position);
+ position = mWordIterator.following(position);
+ }
+
+ return result;
+ }
+
+ private TextAppearanceSpan highlightSpan(int index) {
+ final int length = mHighlightSpans.length;
+ if (index < length) {
+ return mHighlightSpans[index];
+ }
+
+ // Assumes indexes are requested in sequence: simply append one more item
+ TextAppearanceSpan[] newArray = new TextAppearanceSpan[length + 1];
+ System.arraycopy(mHighlightSpans, 0, newArray, 0, length);
+ TextAppearanceSpan highlightSpan = new TextAppearanceSpan(mContext,
+ android.R.style.TextAppearance_SuggestionHighlight);
+ newArray[length] = highlightSpan;
+ mHighlightSpans = newArray;
+ return highlightSpan;
+ }
+
+ private void highlightTextDifferences(TextView textView, int unionStart, int unionEnd) {
+ SuggestionInfo suggestionInfo = (SuggestionInfo) textView.getTag();
+ final int spanStart = suggestionInfo.spanStart;
+ final int spanEnd = suggestionInfo.spanEnd;
+
+ // Remove all text formating by converting to Strings
+ final String text = textView.getText().toString();
+ final String sourceText = mText.subSequence(spanStart, spanEnd).toString();
+
+ long[] sourceWordLimits = getWordLimits(sourceText);
+ long[] wordLimits = getWordLimits(text);
+
+ SpannableStringBuilder ssb = new SpannableStringBuilder();
+ // span [spanStart, spanEnd] is included in union [spanUnionStart, int spanUnionEnd]
+ // The final result is made of 3 parts: the text before, between and after the span
+ // This is the text before, provided for context
+ ssb.append(mText.subSequence(unionStart, spanStart).toString());
+
+ // shift is used to offset spans positions wrt span's beginning
+ final int shift = spanStart - unionStart;
+ suggestionInfo.suggestionStart = shift;
+ suggestionInfo.suggestionEnd = shift + text.length();
+
+ // This is the actual suggestion text, which will be highlighted by the following code
+ ssb.append(text);
+
+ String[] words = new String[wordLimits.length];
+ for (int i = 0; i < wordLimits.length; i++) {
+ int wordStart = extractRangeStartFromLong(wordLimits[i]);
+ int wordEnd = extractRangeEndFromLong(wordLimits[i]);
+ words[i] = text.substring(wordStart, wordEnd);
+ }
+
+ // Highlighted word algorithm is based on word matching between source and text
+ // Matching words are found from left to right. TODO: change for RTL languages
+ // Characters between matching words are highlighted
+ int previousCommonWordIndex = -1;
+ int nbHighlightSpans = 0;
+ for (int i = 0; i < sourceWordLimits.length; i++) {
+ int wordStart = extractRangeStartFromLong(sourceWordLimits[i]);
+ int wordEnd = extractRangeEndFromLong(sourceWordLimits[i]);
+ String sourceWord = sourceText.substring(wordStart, wordEnd);
+
+ for (int j = previousCommonWordIndex + 1; j < words.length; j++) {
+ if (sourceWord.equals(words[j])) {
+ if (j != previousCommonWordIndex + 1) {
+ int firstDifferentPosition = previousCommonWordIndex < 0 ? 0 :
+ extractRangeEndFromLong(wordLimits[previousCommonWordIndex]);
+ int lastDifferentPosition = extractRangeStartFromLong(wordLimits[j]);
+ ssb.setSpan(highlightSpan(nbHighlightSpans++),
+ shift + firstDifferentPosition, shift + lastDifferentPosition,
+ Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ } else {
+ // Compare characters between words
+ int previousSourceWordEnd = i == 0 ? 0 :
+ extractRangeEndFromLong(sourceWordLimits[i - 1]);
+ int sourceWordStart = extractRangeStartFromLong(sourceWordLimits[i]);
+ String sourceSpaces = sourceText.substring(previousSourceWordEnd,
+ sourceWordStart);
+
+ int previousWordEnd = j == 0 ? 0 :
+ extractRangeEndFromLong(wordLimits[j - 1]);
+ int currentWordStart = extractRangeStartFromLong(wordLimits[j]);
+ String textSpaces = text.substring(previousWordEnd, currentWordStart);
+
+ if (!sourceSpaces.equals(textSpaces)) {
+ ssb.setSpan(highlightSpan(nbHighlightSpans++),
+ shift + previousWordEnd, shift + currentWordStart,
+ Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ }
+ previousCommonWordIndex = j;
+ break;
+ }
+ }
+ }
+
+ // Finally, compare ends of Strings
+ if (previousCommonWordIndex < words.length - 1) {
+ int firstDifferentPosition = previousCommonWordIndex < 0 ? 0 :
+ extractRangeEndFromLong(wordLimits[previousCommonWordIndex]);
+ int lastDifferentPosition = textView.length();
+ ssb.setSpan(highlightSpan(nbHighlightSpans++),
+ shift + firstDifferentPosition, shift + lastDifferentPosition,
+ Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ } else {
+ int lastSourceWordEnd = sourceWordLimits.length == 0 ? 0 :
+ extractRangeEndFromLong(sourceWordLimits[sourceWordLimits.length - 1]);
+ String sourceSpaces = sourceText.substring(lastSourceWordEnd, sourceText.length());
+
+ int lastCommonTextWordEnd = previousCommonWordIndex < 0 ? 0 :
+ extractRangeEndFromLong(wordLimits[previousCommonWordIndex]);
+ String textSpaces = text.substring(lastCommonTextWordEnd, textView.length());
+
+ if (!sourceSpaces.equals(textSpaces) && textSpaces.length() > 0) {
+ ssb.setSpan(highlightSpan(nbHighlightSpans++),
+ shift + lastCommonTextWordEnd, shift + textView.length(),
+ Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ }
+
+ // Final part, text after the current suggestion range.
+ ssb.append(mText.subSequence(spanEnd, unionEnd).toString());
+ textView.setText(ssb);
+ }
+
+ public void hide() {
+ if ((mText instanceof Editable) && mSuggestionRangeSpan != null) {
+ ((Editable) mText).removeSpan(mSuggestionRangeSpan);
+ }
+ mContainer.dismiss();
+ }
+
+ @Override
+ public void onClick(View view) {
+ if (view instanceof TextView) {
+ TextView textView = (TextView) view;
+ SuggestionInfo suggestionInfo = (SuggestionInfo) textView.getTag();
+ final int spanStart = suggestionInfo.spanStart;
+ final int spanEnd = suggestionInfo.spanEnd;
+ if (spanStart != NO_SUGGESTIONS) {
+ // SuggestionSpans are removed by replace: save them before
+ Editable editable = ((Editable) mText);
+ SuggestionSpan[] suggestionSpans = editable.getSpans(spanStart, spanEnd,
+ SuggestionSpan.class);
+ final int length = suggestionSpans.length;
+ int[] suggestionSpansStarts = new int[length];
+ int[] suggestionSpansEnds = new int[length];
+ int[] suggestionSpansFlags = new int[length];
+ for (int i = 0; i < length; i++) {
+ final SuggestionSpan suggestionSpan = suggestionSpans[i];
+ suggestionSpansStarts[i] = editable.getSpanStart(suggestionSpan);
+ suggestionSpansEnds[i] = editable.getSpanEnd(suggestionSpan);
+ suggestionSpansFlags[i] = editable.getSpanFlags(suggestionSpan);
+ }
+
+ final int suggestionStart = suggestionInfo.suggestionStart;
+ final int suggestionEnd = suggestionInfo.suggestionEnd;
+ final String suggestion = textView.getText().subSequence(
+ suggestionStart, suggestionEnd).toString();
+ final String originalText = mText.subSequence(spanStart, spanEnd).toString();
+ ((Editable) mText).replace(spanStart, spanEnd, suggestion);
+
+ // Swap text content between actual text and Suggestion span
+ String[] suggestions = suggestionInfo.suggestionSpan.getSuggestions();
+ suggestions[suggestionInfo.suggestionIndex] = originalText;
+
+ // Restore previous SuggestionSpans
+ final int lengthDifference = suggestion.length() - (spanEnd - spanStart);
+ for (int i = 0; i < length; i++) {
+ // Only spans that include the modified region make sense after replacement
+ // Spans partially included in the replaced region are removed, there is no
+ // way to assign them a valid range after replacement
+ if (suggestionSpansStarts[i] <= spanStart &&
+ suggestionSpansEnds[i] >= spanEnd) {
+ editable.setSpan(suggestionSpans[i], suggestionSpansStarts[i],
+ suggestionSpansEnds[i] + lengthDifference,
+ suggestionSpansFlags[i]);
+ }
+ }
+ }
+ }
+ hide();
+ }
+
+ void positionAtCursor() {
+ View contentView = mContainer.getContentView();
+ int width = contentView.getMeasuredWidth();
+ int height = contentView.getMeasuredHeight();
+ final int offset = TextView.this.getSelectionStart();
+ final int line = mLayout.getLineForOffset(offset);
+ final int lineBottom = mLayout.getLineBottom(line);
+ float primaryHorizontal = mLayout.getPrimaryHorizontal(offset);
+
+ final Rect bounds = sCursorControllerTempRect;
+ bounds.left = (int) (primaryHorizontal - width / 2.0f);
+ bounds.top = lineBottom;
+
+ bounds.right = bounds.left + width;
+ bounds.bottom = bounds.top + height;
+
+ convertFromViewportToContentCoordinates(bounds);
+
+ final int[] coords = mTempCoords;
+ TextView.this.getLocationInWindow(coords);
+ coords[0] += bounds.left;
+ coords[1] += bounds.top;
+
+ final DisplayMetrics displayMetrics = mContext.getResources().getDisplayMetrics();
+ final int screenHeight = displayMetrics.heightPixels;
+
+ // Vertical clipping
+ if (coords[1] + height > screenHeight) {
+ // Try to position above current line instead
+ // TODO use top layout instead, reverse suggestion order,
+ // try full screen vertical down if it still does not fit. TBD with designers.
+
+ // Update dimensions from new view
+ contentView = mContainer.getContentView();
+ width = contentView.getMeasuredWidth();
+ height = contentView.getMeasuredHeight();
+
+ final int lineTop = mLayout.getLineTop(line);
+ final int lineHeight = lineBottom - lineTop;
+ coords[1] -= height + lineHeight;
+ }
+
+ // Horizontal clipping
+ coords[0] = Math.max(0, coords[0]);
+ coords[0] = Math.min(displayMetrics.widthPixels - width, coords[0]);
+
+ mContainer.showAtLocation(TextView.this, Gravity.NO_GRAVITY, coords[0], coords[1]);
+ }
+ }
+
+ void showSuggestions() {
+ if (mSuggestionsPopupWindow == null) {
+ mSuggestionsPopupWindow = new SuggestionsPopupWindow();
+ }
+ hideControllers();
+ mSuggestionsPopupWindow.show();
+ }
+
+ void hideSuggestions() {
+ if (mSuggestionsPopupWindow != null) {
+ mSuggestionsPopupWindow.hide();
+ }
+ }
+
/**
* If provided, this ActionMode.Callback will be used to create the ActionMode when text
* selection is initiated in this View.
@@ -8296,9 +8665,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
return false;
}
- if (!hasSelection()) {
- // If selection mode is started after a device rotation, there is already a selection.
- selectCurrentWord();
+ boolean currentWordSelected = selectCurrentWord();
+ if (!currentWordSelected) {
+ // No word found under cursor or text selection not permitted.
+ return false;
}
ActionMode.Callback actionModeCallback = new SelectionActionModeCallback();
@@ -8329,16 +8699,17 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
(ClipboardManager) getContext().getSystemService(Context.CLIPBOARD_SERVICE);
ClipData clip = clipboard.getPrimaryClip();
if (clip != null) {
- boolean didfirst = false;
+ boolean didFirst = false;
for (int i=0; i<clip.getItemCount(); i++) {
CharSequence paste = clip.getItemAt(i).coerceToText(getContext());
if (paste != null) {
- if (!didfirst) {
+ if (!didFirst) {
long minMax = prepareSpacesAroundPaste(min, max, paste);
min = extractRangeStartFromLong(minMax);
max = extractRangeEndFromLong(minMax);
Selection.setSelection((Spannable) mText, max);
((Editable) mText).replace(min, max, paste);
+ didFirst = true;
} else {
((Editable) mText).insert(getSelectionEnd(), "\n");
((Editable) mText).insert(getSelectionEnd(), paste);
@@ -8453,65 +8824,14 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
}
- /**
- * A CursorController instance can be used to control a cursor in the text.
- * It is not used outside of {@link TextView}.
- * @hide
- */
- private interface CursorController extends ViewTreeObserver.OnTouchModeChangeListener {
- /**
- * Makes the cursor controller visible on screen. Will be drawn by {@link #draw(Canvas)}.
- * See also {@link #hide()}.
- */
- public void show();
-
- /**
- * Hide the cursor controller from screen.
- * See also {@link #show()}.
- */
- public void hide();
-
- /**
- * @return true if the CursorController is currently visible
- */
- public boolean isShowing();
-
- /**
- * Update the controller's position.
- */
- public void updatePosition(HandleView handle, int x, int y);
-
- public void updateOffset(HandleView handle, int offset);
-
- public void updatePosition();
-
- public int getCurrentOffset(HandleView handle);
-
- /**
- * This method is called by {@link #onTouchEvent(MotionEvent)} and gives the controller
- * a chance to become active and/or visible.
- * @param event The touch event
- */
- public boolean onTouchEvent(MotionEvent event);
-
- /**
- * Called when the view is detached from window. Perform house keeping task, such as
- * stopping Runnable thread that would otherwise keep a reference on the context, thus
- * preventing the activity to be recycled.
- */
- public void onDetached();
- }
-
- private class PastePopupMenu implements OnClickListener {
+ private class PastePopupWindow implements OnClickListener {
private final PopupWindow mContainer;
- private int mPositionX;
- private int mPositionY;
private final View[] mPasteViews = new View[4];
private final int[] mPasteViewLayouts = new int[] {
mTextEditPasteWindowLayout, mTextEditNoPasteWindowLayout,
mTextEditSidePasteWindowLayout, mTextEditSideNoPasteWindowLayout };
- public PastePopupMenu() {
+ public PastePopupWindow() {
mContainer = new PopupWindow(TextView.this.mContext, null,
com.android.internal.R.attr.textSelectHandleWindowStyle);
mContainer.setSplitTouchEnabled(true);
@@ -8594,14 +8914,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
convertFromViewportToContentCoordinates(bounds);
- mPositionX = bounds.left;
- mPositionY = bounds.top;
-
-
final int[] coords = mTempCoords;
TextView.this.getLocationInWindow(coords);
- coords[0] += mPositionX;
- coords[1] += mPositionY;
+ coords[0] += bounds.left;
+ coords[1] += bounds.top;
final int screenWidth = mContext.getResources().getDisplayMetrics().widthPixels;
if (coords[1] < 0) {
@@ -8637,32 +8953,43 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
}
- private class HandleView extends View implements ViewTreeObserver.OnPreDrawListener {
- private Drawable mDrawable;
+ private abstract class HandleView extends View implements ViewTreeObserver.OnPreDrawListener {
+ protected Drawable mDrawable;
private final PopupWindow mContainer;
// Position with respect to the parent TextView
private int mPositionX, mPositionY;
- private final CursorController mController;
private boolean mIsDragging;
// Offset from touch position to mPosition
private float mTouchToWindowOffsetX, mTouchToWindowOffsetY;
- private float mHotspotX;
+ protected float mHotspotX;
// Offsets the hotspot point up, so that cursor is not hidden by the finger when moving up
private float mTouchOffsetY;
// Where the touch position should be on the handle to ensure a maximum cursor visibility
private float mIdealVerticalOffset;
- // Parent's (TextView) position in window
+ // Parent's (TextView) previous position in window
private int mLastParentX, mLastParentY;
- private float mDownPositionX, mDownPositionY;
// PopupWindow container absolute position with respect to the enclosing window
private int mContainerPositionX, mContainerPositionY;
// Visible or not (scrolled off screen), whether or not this handle should be visible
private boolean mIsActive = false;
- // The insertion handle can have an associated PastePopupMenu
- private boolean mIsInsertionHandle = false;
- // Used to detect taps on the insertion handle, which will affect the PastePopupMenu
- private long mTouchTimer;
- private PastePopupMenu mPastePopupWindow;
+
+ public HandleView() {
+ super(TextView.this.mContext);
+ mContainer = new PopupWindow(TextView.this.mContext, null,
+ com.android.internal.R.attr.textSelectHandleWindowStyle);
+ mContainer.setSplitTouchEnabled(true);
+ mContainer.setClippingEnabled(false);
+ mContainer.setWindowLayoutType(WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL);
+ mContainer.setContentView(this);
+
+ initDrawable();
+
+ final int handleHeight = mDrawable.getIntrinsicHeight();
+ mTouchOffsetY = -0.3f * handleHeight;
+ mIdealVerticalOffset = 0.7f * handleHeight;
+ }
+
+ protected abstract void initDrawable();
// Touch-up filter: number of previous positions remembered
private static final int HISTORY_SIZE = 5;
@@ -8703,85 +9030,19 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
if (i > 0 && i < iMax &&
(now - mPreviousOffsetsTimes[index]) > TOUCH_UP_FILTER_DELAY_BEFORE) {
- mController.updateOffset(this, mPreviousOffsets[index]);
+ updateOffset(mPreviousOffsets[index]);
}
}
- public static final int LEFT = 0;
- public static final int CENTER = 1;
- public static final int RIGHT = 2;
-
- public HandleView(CursorController controller, int pos) {
- super(TextView.this.mContext);
- mController = controller;
- mContainer = new PopupWindow(TextView.this.mContext, null,
- com.android.internal.R.attr.textSelectHandleWindowStyle);
- mContainer.setSplitTouchEnabled(true);
- mContainer.setClippingEnabled(false);
- mContainer.setWindowLayoutType(WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL);
- mContainer.setContentView(this);
-
- setPosition(pos);
- }
-
- private void setPosition(int pos) {
- int handleWidth;
- switch (pos) {
- case LEFT: {
- if (mSelectHandleLeft == null) {
- mSelectHandleLeft = mContext.getResources().getDrawable(
- mTextSelectHandleLeftRes);
- }
- mDrawable = mSelectHandleLeft;
- handleWidth = mDrawable.getIntrinsicWidth();
- mHotspotX = handleWidth * 3.0f / 4.0f;
- break;
- }
-
- case RIGHT: {
- if (mSelectHandleRight == null) {
- mSelectHandleRight = mContext.getResources().getDrawable(
- mTextSelectHandleRightRes);
- }
- mDrawable = mSelectHandleRight;
- handleWidth = mDrawable.getIntrinsicWidth();
- mHotspotX = handleWidth / 4.0f;
- break;
- }
-
- case CENTER:
- default: {
- if (mSelectHandleCenter == null) {
- mSelectHandleCenter = mContext.getResources().getDrawable(
- mTextSelectHandleRes);
- }
- mDrawable = mSelectHandleCenter;
- handleWidth = mDrawable.getIntrinsicWidth();
- mHotspotX = handleWidth / 2.0f;
- mIsInsertionHandle = true;
- break;
- }
- }
-
- final int handleHeight = mDrawable.getIntrinsicHeight();
- mTouchOffsetY = -0.3f * handleHeight;
- mIdealVerticalOffset = 0.7f * handleHeight;
-
- invalidate();
- }
-
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(mDrawable.getIntrinsicWidth(), mDrawable.getIntrinsicHeight());
}
public void show() {
- updateContainerPosition();
if (isShowing()) {
mContainer.update(mContainerPositionX, mContainerPositionY,
mRight - mLeft, mBottom - mTop);
-
- hidePastePopupWindow();
} else {
mContainer.showAtLocation(TextView.this, 0,
mContainerPositionX, mContainerPositionY);
@@ -8793,10 +9054,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
}
- private void dismiss() {
+ protected void dismiss() {
mIsDragging = false;
mContainer.dismiss();
- hidePastePopupWindow();
}
public void hide() {
@@ -8827,24 +9087,22 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
final int compoundPaddingLeft = getCompoundPaddingLeft();
final int compoundPaddingRight = getCompoundPaddingRight();
- final TextView hostView = TextView.this;
+ final TextView textView = TextView.this;
- if (mTempRect == null) {
- mTempRect = new Rect();
- }
+ if (mTempRect == null) mTempRect = new Rect();
final Rect clip = mTempRect;
clip.left = compoundPaddingLeft;
clip.top = extendedPaddingTop;
- clip.right = hostView.getWidth() - compoundPaddingRight;
- clip.bottom = hostView.getHeight() - extendedPaddingBottom;
+ clip.right = textView.getWidth() - compoundPaddingRight;
+ clip.bottom = textView.getHeight() - extendedPaddingBottom;
- final ViewParent parent = hostView.getParent();
- if (parent == null || !parent.getChildVisibleRect(hostView, clip, null)) {
+ final ViewParent parent = textView.getParent();
+ if (parent == null || !parent.getChildVisibleRect(textView, clip, null)) {
return false;
}
final int[] coords = mTempCoords;
- hostView.getLocationInWindow(coords);
+ textView.getLocationInWindow(coords);
final int posX = coords[0] + mPositionX + (int) mHotspotX;
final int posY = coords[1] + mPositionY;
@@ -8853,45 +9111,52 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
posY >= clip.top && posY <= clip.bottom;
}
- private void moveTo(int x, int y) {
- mPositionX = x - TextView.this.mScrollX;
- mPositionY = y - TextView.this.mScrollY;
+ public abstract int getCurrentCursorOffset();
- if (mIsDragging) {
- TextView.this.getLocationInWindow(mTempCoords);
- if (mTempCoords[0] != mLastParentX || mTempCoords[1] != mLastParentY) {
- mTouchToWindowOffsetX += mTempCoords[0] - mLastParentX;
- mTouchToWindowOffsetY += mTempCoords[1] - mLastParentY;
- mLastParentX = mTempCoords[0];
- mLastParentY = mTempCoords[1];
- }
- // Hide paste popup window as soon as the handle is dragged.
- hidePastePopupWindow();
- }
+ public abstract void updateOffset(int offset);
+
+ public abstract void updatePosition(int x, int y);
+
+ protected void positionAtCursorOffset(int offset) {
+ addPositionToTouchUpFilter(offset);
+ final int line = mLayout.getLineForOffset(offset);
+ final int lineBottom = mLayout.getLineBottom(line);
+
+ mPositionX = (int) (mLayout.getPrimaryHorizontal(offset) - 0.5f - mHotspotX);
+ mPositionY = lineBottom;
+
+ // Take TextView's padding into account.
+ mPositionX += viewportToContentHorizontalOffset();
+ mPositionY += viewportToContentVerticalOffset();
}
- /**
- * Updates the global container's position.
- * @return whether or not the position has actually changed
- */
- private boolean updateContainerPosition() {
- // TODO Prevent this using different HandleView subclasses
- mController.updateOffset(this, mController.getCurrentOffset(this));
+ protected boolean updateContainerPosition() {
+ positionAtCursorOffset(getCurrentCursorOffset());
+
+ final int previousContainerPositionX = mContainerPositionX;
+ final int previousContainerPositionY = mContainerPositionY;
+
TextView.this.getLocationInWindow(mTempCoords);
- final int containerPositionX = mTempCoords[0] + mPositionX;
- final int containerPositionY = mTempCoords[1] + mPositionY;
+ mContainerPositionX = mTempCoords[0] + mPositionX;
+ mContainerPositionY = mTempCoords[1] + mPositionY;
- if (containerPositionX != mContainerPositionX ||
- containerPositionY != mContainerPositionY) {
- mContainerPositionX = containerPositionX;
- mContainerPositionY = containerPositionY;
- return true;
- }
- return false;
+ return (previousContainerPositionX != mContainerPositionX ||
+ previousContainerPositionY != mContainerPositionY);
}
public boolean onPreDraw() {
if (updateContainerPosition()) {
+ if (mIsDragging) {
+ if (mTempCoords[0] != mLastParentX || mTempCoords[1] != mLastParentY) {
+ mTouchToWindowOffsetX += mTempCoords[0] - mLastParentX;
+ mTouchToWindowOffsetY += mTempCoords[1] - mLastParentY;
+ mLastParentX = mTempCoords[0];
+ mLastParentY = mTempCoords[1];
+ }
+ }
+
+ onHandleMoved();
+
if (isPositionVisible()) {
mContainer.update(mContainerPositionX, mContainerPositionY,
mRight - mLeft, mBottom - mTop);
@@ -8904,9 +9169,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
dismiss();
}
}
-
- // Hide paste popup as soon as the view is scrolled or moved
- hidePastePopupWindow();
}
return true;
}
@@ -8921,20 +9183,15 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getActionMasked()) {
case MotionEvent.ACTION_DOWN: {
- startTouchUpFilter(mController.getCurrentOffset(this));
- mDownPositionX = ev.getRawX();
- mDownPositionY = ev.getRawY();
- mTouchToWindowOffsetX = mDownPositionX - mPositionX;
- mTouchToWindowOffsetY = mDownPositionY - mPositionY;
+ startTouchUpFilter(getCurrentCursorOffset());
+ mTouchToWindowOffsetX = ev.getRawX() - mPositionX;
+ mTouchToWindowOffsetY = ev.getRawY() - mPositionY;
final int[] coords = mTempCoords;
TextView.this.getLocationInWindow(coords);
mLastParentX = coords[0];
mLastParentY = coords[1];
mIsDragging = true;
- if (mIsInsertionHandle) {
- mTouchTimer = SystemClock.uptimeMillis();
- }
break;
}
@@ -8958,27 +9215,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
final float newPosX = rawX - mTouchToWindowOffsetX + mHotspotX;
final float newPosY = rawY - mTouchToWindowOffsetY + mTouchOffsetY;
- mController.updatePosition(this, Math.round(newPosX), Math.round(newPosY));
+ updatePosition(Math.round(newPosX), Math.round(newPosY));
break;
}
case MotionEvent.ACTION_UP:
- if (mIsInsertionHandle) {
- long delay = SystemClock.uptimeMillis() - mTouchTimer;
- if (delay < ViewConfiguration.getTapTimeout()) {
- final float deltaX = mDownPositionX - ev.getRawX();
- final float deltaY = mDownPositionY - ev.getRawY();
- final float distanceSquared = deltaX * deltaX + deltaY * deltaY;
- if (distanceSquared < mSquaredTouchSlopDistance) {
- if (mPastePopupWindow != null && mPastePopupWindow.isShowing()) {
- // Tapping on the handle dismisses the displayed paste view,
- mPastePopupWindow.hide();
- } else {
- ((InsertionPointCursorController) mController).show(0);
- }
- }
- }
- }
filterOnTouchUp();
mIsDragging = false;
break;
@@ -8994,60 +9235,36 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
return mIsDragging;
}
- void positionAtCursor(int offset) {
- addPositionToTouchUpFilter(offset);
- final int width = mDrawable.getIntrinsicWidth();
- final int height = mDrawable.getIntrinsicHeight();
- final int line = mLayout.getLineForOffset(offset);
- final int lineBottom = mLayout.getLineBottom(line);
-
- final Rect bounds = sCursorControllerTempRect;
- bounds.left = (int) (mLayout.getPrimaryHorizontal(offset) - 0.5f - mHotspotX) +
- TextView.this.mScrollX;
- bounds.top = lineBottom + TextView.this.mScrollY;
-
- bounds.right = bounds.left + width;
- bounds.bottom = bounds.top + height;
-
- convertFromViewportToContentCoordinates(bounds);
- moveTo(bounds.left, bounds.top);
- }
-
- void showPastePopupWindow() {
- if (mIsInsertionHandle) {
- if (mPastePopupWindow == null) {
- // Lazy initialisation: create when actually shown only.
- mPastePopupWindow = new PastePopupMenu();
- }
- mPastePopupWindow.show();
- }
+ void onHandleMoved() {
+ // Does nothing by default
}
- void hidePastePopupWindow() {
- if (mPastePopupWindow != null) {
- mPastePopupWindow.hide();
- }
+ public void onDetached() {
+ // Should be overriden to clean possible Runnable
}
}
- private class InsertionPointCursorController implements CursorController {
+ private class InsertionHandleView extends HandleView {
private static final int DELAY_BEFORE_FADE_OUT = 4000;
- private static final int DELAY_BEFORE_PASTE = 2000;
- private static final int RECENT_CUT_COPY_DURATION = 15 * 1000;
+ private static final int RECENT_CUT_COPY_DURATION = 15 * 1000; // seconds
- // The cursor controller image. Lazily created.
- private HandleView mHandle;
+ // Used to detect taps on the insertion handle, which will affect the PastePopupWindow
+ private long mTouchTimer;
+ private float mDownPositionX, mDownPositionY;
+ private PastePopupWindow mPastePopupWindow;
private Runnable mHider;
private Runnable mPastePopupShower;
+ @Override
public void show() {
- show(DELAY_BEFORE_PASTE);
+ super.show();
+ hideDelayed();
+ hidePastePopupWindow();
}
public void show(int delayBeforePaste) {
- getHandle().show();
- hideDelayed();
- removePastePopupCallback();
+ show();
+
final long durationSinceCutOrCopy = SystemClock.uptimeMillis() - sLastCutOrCopyTime;
if (durationSinceCutOrCopy < RECENT_CUT_COPY_DURATION) {
delayBeforePaste = 0;
@@ -9056,81 +9273,256 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
if (mPastePopupShower == null) {
mPastePopupShower = new Runnable() {
public void run() {
- getHandle().showPastePopupWindow();
+ showPastePopupWindow();
}
};
}
- postDelayed(mPastePopupShower, delayBeforePaste);
+ TextView.this.postDelayed(mPastePopupShower, delayBeforePaste);
}
}
- private void removePastePopupCallback() {
- if (mPastePopupShower != null) {
- removeCallbacks(mPastePopupShower);
+ @Override
+ protected void dismiss() {
+ super.dismiss();
+ onDetached();
+ }
+
+ private void hideDelayed() {
+ removeHiderCallback();
+ if (mHider == null) {
+ mHider = new Runnable() {
+ public void run() {
+ hide();
+ }
+ };
}
+ TextView.this.postDelayed(mHider, DELAY_BEFORE_FADE_OUT);
}
private void removeHiderCallback() {
if (mHider != null) {
- removeCallbacks(mHider);
+ TextView.this.removeCallbacks(mHider);
}
}
- public void hide() {
- if (mHandle != null) {
- mHandle.hide();
+ @Override
+ protected void initDrawable() {
+ if (mSelectHandleCenter == null) {
+ mSelectHandleCenter = mContext.getResources().getDrawable(
+ mTextSelectHandleRes);
}
- removeHiderCallback();
- removePastePopupCallback();
+ mDrawable = mSelectHandleCenter;
+ mHotspotX = mDrawable.getIntrinsicWidth() / 2.0f;
}
- private void hideDelayed() {
- removeHiderCallback();
- if (mHider == null) {
- mHider = new Runnable() {
- public void run() {
- hide();
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ final boolean result = super.onTouchEvent(ev);
+
+ switch (ev.getActionMasked()) {
+ case MotionEvent.ACTION_DOWN:
+ mDownPositionX = ev.getRawX();
+ mDownPositionY = ev.getRawY();
+ mTouchTimer = SystemClock.uptimeMillis();
+ break;
+
+ case MotionEvent.ACTION_UP:
+ long delay = SystemClock.uptimeMillis() - mTouchTimer;
+ if (delay < ViewConfiguration.getTapTimeout()) {
+ final float deltaX = mDownPositionX - ev.getRawX();
+ final float deltaY = mDownPositionY - ev.getRawY();
+ final float distanceSquared = deltaX * deltaX + deltaY * deltaY;
+ if (distanceSquared < mSquaredTouchSlopDistance) {
+ if (mPastePopupWindow != null && mPastePopupWindow.isShowing()) {
+ // Tapping on the handle dismisses the displayed paste view,
+ mPastePopupWindow.hide();
+ } else {
+ show(0);
+ }
+ }
}
- };
+ hideDelayed();
+ break;
+
+ case MotionEvent.ACTION_CANCEL:
+ hideDelayed();
+ break;
+
+ default:
+ break;
}
- postDelayed(mHider, DELAY_BEFORE_FADE_OUT);
+
+ return result;
}
- public boolean isShowing() {
- return mHandle != null && mHandle.isShowing();
+ @Override
+ public int getCurrentCursorOffset() {
+ return TextView.this.getSelectionStart();
+ }
+
+ @Override
+ public void updateOffset(int offset) {
+ Selection.setSelection((Spannable) mText, offset);
}
- public void updatePosition(HandleView handle, int x, int y) {
- final int previousOffset = getSelectionStart();
- final int newOffset = getOffset(x, y);
+ @Override
+ public void updatePosition(int x, int y) {
+ updateOffset(getOffset(x, y));
+ }
- if (newOffset != previousOffset) {
- updateOffset(handle, newOffset);
- removePastePopupCallback();
+ void showPastePopupWindow() {
+ if (mPastePopupWindow == null) {
+ mPastePopupWindow = new PastePopupWindow();
}
- hideDelayed();
+ mPastePopupWindow.show();
}
- public void updateOffset(HandleView handle, int offset) {
- Selection.setSelection((Spannable) mText, offset);
- updatePosition();
+ @Override
+ void onHandleMoved() {
+ removeHiderCallback();
+ hidePastePopupWindow();
}
- public void updatePosition() {
- final int offset = getSelectionStart();
+ void hidePastePopupWindow() {
+ if (mPastePopupShower != null) {
+ TextView.this.removeCallbacks(mPastePopupShower);
+ }
+ if (mPastePopupWindow != null) {
+ mPastePopupWindow.hide();
+ }
+ }
- if (offset < 0) {
- // Should never happen, safety check.
- Log.w(LOG_TAG, "Update cursor controller position called with no cursor");
- hide();
- return;
+ @Override
+ public void onDetached() {
+ removeHiderCallback();
+ hidePastePopupWindow();
+ }
+ }
+
+ private class SelectionStartHandleView extends HandleView {
+ @Override
+ protected void initDrawable() {
+ if (mSelectHandleLeft == null) {
+ mSelectHandleLeft = mContext.getResources().getDrawable(
+ mTextSelectHandleLeftRes);
}
+ mDrawable = mSelectHandleLeft;
+ mHotspotX = mDrawable.getIntrinsicWidth() * 3.0f / 4.0f;
+ }
+
+ @Override
+ public int getCurrentCursorOffset() {
+ return TextView.this.getSelectionStart();
+ }
+
+ @Override
+ public void updateOffset(int offset) {
+ Selection.setSelection((Spannable) mText, offset, getSelectionEnd());
+ }
+
+ @Override
+ public void updatePosition(int x, int y) {
+ final int selectionStart = getSelectionStart();
+ final int selectionEnd = getSelectionEnd();
+
+ int offset = getOffset(x, y);
+
+ // No need to redraw when the offset is unchanged
+ if (offset == selectionStart) return;
+ // Handles can not cross and selection is at least one character
+ if (offset >= selectionEnd) offset = selectionEnd - 1;
+
+ Selection.setSelection((Spannable) mText, offset, selectionEnd);
+ }
+ }
+
+ private class SelectionEndHandleView extends HandleView {
+ @Override
+ protected void initDrawable() {
+ if (mSelectHandleRight == null) {
+ mSelectHandleRight = mContext.getResources().getDrawable(
+ mTextSelectHandleRightRes);
+ }
+ mDrawable = mSelectHandleRight;
+ mHotspotX = mDrawable.getIntrinsicWidth() / 4.0f;
+ }
+
+ @Override
+ public int getCurrentCursorOffset() {
+ return TextView.this.getSelectionEnd();
+ }
+
+ @Override
+ public void updateOffset(int offset) {
+ Selection.setSelection((Spannable) mText, getSelectionStart(), offset);
+ }
+
+ @Override
+ public void updatePosition(int x, int y) {
+ final int selectionStart = getSelectionStart();
+ final int selectionEnd = getSelectionEnd();
+
+ int offset = getOffset(x, y);
+
+ // No need to redraw when the offset is unchanged
+ if (offset == selectionEnd) return;
+ // Handles can not cross and selection is at least one character
+ if (offset <= selectionStart) offset = selectionStart + 1;
+
+ Selection.setSelection((Spannable) mText, selectionStart, offset);
+ }
+ }
+
+ /**
+ * A CursorController instance can be used to control a cursor in the text.
+ * It is not used outside of {@link TextView}.
+ * @hide
+ */
+ private interface CursorController extends ViewTreeObserver.OnTouchModeChangeListener {
+ /**
+ * Makes the cursor controller visible on screen. Will be drawn by {@link #draw(Canvas)}.
+ * See also {@link #hide()}.
+ */
+ public void show();
- getHandle().positionAtCursor(offset);
+ /**
+ * Hide the cursor controller from screen.
+ * See also {@link #show()}.
+ */
+ public void hide();
+
+ /**
+ * This method is called by {@link #onTouchEvent(MotionEvent)} and gives the controller
+ * a chance to become active and/or visible.
+ * @param event The touch event
+ */
+ public boolean onTouchEvent(MotionEvent event);
+
+ /**
+ * Called when the view is detached from window. Perform house keeping task, such as
+ * stopping Runnable thread that would otherwise keep a reference on the context, thus
+ * preventing the activity from being recycled.
+ */
+ public void onDetached();
+ }
+
+ private class InsertionPointCursorController implements CursorController {
+ private static final int DELAY_BEFORE_PASTE = 2000;
+
+ private InsertionHandleView mHandle;
+
+ public void show() {
+ ((InsertionHandleView) getHandle()).show(DELAY_BEFORE_PASTE);
}
- public int getCurrentOffset(HandleView handle) {
- return getSelectionStart();
+ public void showWithPaste() {
+ ((InsertionHandleView) getHandle()).show(0);
+ }
+
+ public void hide() {
+ if (mHandle != null) {
+ mHandle.hide();
+ }
}
public boolean onTouchEvent(MotionEvent ev) {
@@ -9145,30 +9537,30 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
private HandleView getHandle() {
if (mHandle == null) {
- mHandle = new HandleView(this, HandleView.CENTER);
+ mHandle = new InsertionHandleView();
}
return mHandle;
}
@Override
public void onDetached() {
- removeHiderCallback();
- removePastePopupCallback();
+ final ViewTreeObserver observer = getViewTreeObserver();
+ observer.removeOnTouchModeChangeListener(this);
+
+ if (mHandle != null) mHandle.onDetached();
}
}
private class SelectionModifierCursorController implements CursorController {
- // The cursor controller images, lazily created when shown.
- private HandleView mStartHandle, mEndHandle;
+ // The cursor controller handles, lazily created when shown.
+ private SelectionStartHandleView mStartHandle;
+ private SelectionEndHandleView mEndHandle;
// The offsets of that last touch down event. Remembered to start selection there.
private int mMinTouchOffset, mMaxTouchOffset;
- // Whether selection anchors are active
- private boolean mIsShowing;
// Double tap detection
private long mPreviousTapUpTime = 0;
- private int mPreviousTapPositionX;
- private int mPreviousTapPositionY;
+ private int mPreviousTapPositionX, mPreviousTapPositionY;
SelectionModifierCursorController() {
resetTouchOffsets();
@@ -9180,96 +9572,19 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
// Lazy object creation has to be done before updatePosition() is called.
- if (mStartHandle == null) mStartHandle = new HandleView(this, HandleView.LEFT);
- if (mEndHandle == null) mEndHandle = new HandleView(this, HandleView.RIGHT);
-
- mIsShowing = true;
+ if (mStartHandle == null) mStartHandle = new SelectionStartHandleView();
+ if (mEndHandle == null) mEndHandle = new SelectionEndHandleView();
mStartHandle.show();
mEndHandle.show();
hideInsertionPointCursorController();
+ hideSuggestions();
}
public void hide() {
if (mStartHandle != null) mStartHandle.hide();
if (mEndHandle != null) mEndHandle.hide();
- mIsShowing = false;
- }
-
- public boolean isShowing() {
- return mIsShowing;
- }
-
- public void updatePosition(HandleView handle, int x, int y) {
- int selectionStart = getSelectionStart();
- int selectionEnd = getSelectionEnd();
-
- int offset = getOffset(x, y);
-
- // Handle the case where start and end are swapped, making sure start <= end
- if (handle == mStartHandle) {
- if (selectionStart == offset || offset > selectionEnd) {
- return; // no change, no need to redraw;
- }
- // If the user "closes" the selection entirely they were probably trying to
- // select a single character. Help them out.
- if (offset == selectionEnd) {
- offset = selectionEnd - 1;
- }
- selectionStart = offset;
- } else {
- if (selectionEnd == offset || offset < selectionStart) {
- return; // no change, no need to redraw;
- }
- // If the user "closes" the selection entirely they were probably trying to
- // select a single character. Help them out.
- if (offset == selectionStart) {
- offset = selectionStart + 1;
- }
- selectionEnd = offset;
- }
-
- Selection.setSelection((Spannable) mText, selectionStart, selectionEnd);
- updatePosition();
- }
-
- public void updateOffset(HandleView handle, int offset) {
- int start = getSelectionStart();
- int end = getSelectionEnd();
-
- if (mStartHandle == handle) {
- start = offset;
- } else {
- end = offset;
- }
-
- Selection.setSelection((Spannable) mText, start, end);
- updatePosition();
- }
-
- public void updatePosition() {
- if (!isShowing()) {
- return;
- }
-
- final int selectionStart = getSelectionStart();
- final int selectionEnd = getSelectionEnd();
-
- if ((selectionStart < 0) || (selectionEnd < 0)) {
- // Should never happen, safety check.
- Log.w(LOG_TAG, "Update selection controller position called with no cursor");
- hide();
- return;
- }
-
- // The handles have been created since the controller isShowing().
- mStartHandle.positionAtCursor(selectionStart);
- mEndHandle.positionAtCursor(selectionEnd);
- }
-
- public int getCurrentOffset(HandleView handle) {
- return mStartHandle == handle ? getSelectionStart() : getSelectionEnd();
}
public boolean onTouchEvent(MotionEvent event) {
@@ -9292,7 +9607,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
final int deltaY = y - mPreviousTapPositionY;
final int distanceSquared = deltaX * deltaX + deltaY * deltaY;
if (distanceSquared < mSquaredTouchSlopDistance) {
- startSelectionActionMode();
+ showSuggestions();
mDiscardNextActionUp = true;
}
}
@@ -9360,7 +9675,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
@Override
- public void onDetached() {}
+ public void onDetached() {
+ final ViewTreeObserver observer = getViewTreeObserver();
+ observer.removeOnTouchModeChangeListener(this);
+
+ if (mStartHandle != null) mStartHandle.onDetached();
+ if (mEndHandle != null) mEndHandle.onDetached();
+ }
}
private void hideInsertionPointCursorController() {
@@ -9376,6 +9697,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
private void hideControllers() {
hideInsertionPointCursorController();
stopSelectionActionMode();
+ hideSuggestions();
}
/**
diff --git a/core/java/android/widget/TimePicker.java b/core/java/android/widget/TimePicker.java
index 029d690..423e735 100644
--- a/core/java/android/widget/TimePicker.java
+++ b/core/java/android/widget/TimePicker.java
@@ -409,7 +409,9 @@ public class TimePicker extends FrameLayout {
}
@Override
- public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
+ public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
+ super.onPopulateAccessibilityEvent(event);
+
int flags = DateUtils.FORMAT_SHOW_TIME;
if (mIs24HourView) {
flags |= DateUtils.FORMAT_24HOUR;
@@ -421,7 +423,6 @@ public class TimePicker extends FrameLayout {
String selectedDateUtterance = DateUtils.formatDateTime(mContext,
mTempCalendar.getTimeInMillis(), flags);
event.getText().add(selectedDateUtterance);
- return true;
}
private void updateHourControl() {
diff --git a/core/java/android/widget/ZoomButtonsController.java b/core/java/android/widget/ZoomButtonsController.java
index 450c966..9e37c7b 100644
--- a/core/java/android/widget/ZoomButtonsController.java
+++ b/core/java/android/widget/ZoomButtonsController.java
@@ -33,7 +33,7 @@ import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.ViewParent;
-import android.view.ViewRoot;
+import android.view.ViewAncestor;
import android.view.WindowManager;
import android.view.View.OnClickListener;
import android.view.WindowManager.LayoutParams;
@@ -501,7 +501,7 @@ public class ZoomButtonsController implements View.OnTouchListener {
} else {
- ViewRoot viewRoot = getOwnerViewRoot();
+ ViewAncestor viewRoot = getOwnerViewAncestor();
if (viewRoot != null) {
viewRoot.dispatchKey(event);
}
@@ -526,15 +526,15 @@ public class ZoomButtonsController implements View.OnTouchListener {
}
}
- private ViewRoot getOwnerViewRoot() {
+ private ViewAncestor getOwnerViewAncestor() {
View rootViewOfOwner = mOwnerView.getRootView();
if (rootViewOfOwner == null) {
return null;
}
ViewParent parentOfRootView = rootViewOfOwner.getParent();
- if (parentOfRootView instanceof ViewRoot) {
- return (ViewRoot) parentOfRootView;
+ if (parentOfRootView instanceof ViewAncestor) {
+ return (ViewAncestor) parentOfRootView;
} else {
return null;
}
diff --git a/core/java/com/android/internal/app/ActionBarImpl.java b/core/java/com/android/internal/app/ActionBarImpl.java
index 8f1354b..1e576ce 100644
--- a/core/java/com/android/internal/app/ActionBarImpl.java
+++ b/core/java/com/android/internal/app/ActionBarImpl.java
@@ -19,19 +19,20 @@ package com.android.internal.app;
import com.android.internal.view.menu.MenuBuilder;
import com.android.internal.view.menu.MenuPopupHelper;
import com.android.internal.view.menu.SubMenuBuilder;
+import com.android.internal.widget.AbsActionBarView;
import com.android.internal.widget.ActionBarContainer;
import com.android.internal.widget.ActionBarContextView;
import com.android.internal.widget.ActionBarView;
import android.animation.Animator;
import android.animation.Animator.AnimatorListener;
+import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.TimeInterpolator;
import android.app.ActionBar;
import android.app.Activity;
import android.app.Dialog;
-import android.app.Fragment;
import android.app.FragmentTransaction;
import android.content.Context;
import android.graphics.drawable.Drawable;
@@ -42,10 +43,10 @@ import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
+import android.view.ViewGroup;
import android.view.Window;
import android.view.animation.DecelerateInterpolator;
-import android.widget.FrameLayout;
-import android.widget.LinearLayout;
+import android.widget.HorizontalScrollView;
import android.widget.SpinnerAdapter;
import java.lang.ref.WeakReference;
@@ -59,8 +60,7 @@ import java.util.ArrayList;
* which is normally hidden.
*/
public class ActionBarImpl extends ActionBar {
- private static final int NORMAL_VIEW = 0;
- private static final int CONTEXT_VIEW = 1;
+ private static final String TAG = "ActionBarImpl";
private Context mContext;
private Activity mActivity;
@@ -68,9 +68,10 @@ public class ActionBarImpl extends ActionBar {
private ActionBarContainer mContainerView;
private ActionBarView mActionView;
- private ActionBarContextView mUpperContextView;
- private LinearLayout mLowerContextView;
+ private ActionBarContextView mContextView;
+ private ActionBarContainer mSplitView;
private View mContentView;
+ private ViewGroup mExternalTabView;
private ArrayList<TabImpl> mTabs = new ArrayList<TabImpl>();
@@ -92,60 +93,14 @@ public class ActionBarImpl extends ActionBar {
final Handler mHandler = new Handler();
- private Animator mCurrentAnim;
+ private Animator mCurrentShowAnim;
+ private Animator mCurrentModeAnim;
private boolean mShowHideAnimationEnabled;
+ boolean mWasHiddenBeforeMode;
private static final TimeInterpolator sFadeOutInterpolator = new DecelerateInterpolator();
- final AnimatorListener[] mAfterAnimation = new AnimatorListener[] {
- new AnimatorListener() { // NORMAL_VIEW
- @Override
- public void onAnimationStart(Animator animation) {
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- if (mLowerContextView != null) {
- mLowerContextView.removeAllViews();
- }
- mCurrentAnim = null;
- hideAllExcept(NORMAL_VIEW);
- }
-
- @Override
- public void onAnimationCancel(Animator animation) {
- }
-
- @Override
- public void onAnimationRepeat(Animator animation) {
- }
- },
- new AnimatorListener() { // CONTEXT_VIEW
- @Override
- public void onAnimationStart(Animator animation) {
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- mCurrentAnim = null;
- hideAllExcept(CONTEXT_VIEW);
- }
-
- @Override
- public void onAnimationCancel(Animator animation) {
- }
-
- @Override
- public void onAnimationRepeat(Animator animation) {
- }
- }
- };
-
- final AnimatorListener mHideListener = new AnimatorListener() {
- @Override
- public void onAnimationStart(Animator animation) {
- }
-
+ final AnimatorListener mHideListener = new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
if (mContentView != null) {
@@ -153,36 +108,16 @@ public class ActionBarImpl extends ActionBar {
}
mContainerView.setVisibility(View.GONE);
mContainerView.setTransitioning(false);
- mCurrentAnim = null;
- }
-
- @Override
- public void onAnimationCancel(Animator animation) {
- }
-
- @Override
- public void onAnimationRepeat(Animator animation) {
+ mCurrentShowAnim = null;
}
};
- final AnimatorListener mShowListener = new AnimatorListener() {
- @Override
- public void onAnimationStart(Animator animation) {
- }
-
+ final AnimatorListener mShowListener = new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
- mCurrentAnim = null;
+ mCurrentShowAnim = null;
mContainerView.requestLayout();
}
-
- @Override
- public void onAnimationCancel(Animator animation) {
- }
-
- @Override
- public void onAnimationRepeat(Animator animation) {
- }
};
public ActionBarImpl(Activity activity) {
@@ -203,21 +138,33 @@ public class ActionBarImpl extends ActionBar {
private void init(View decor) {
mContext = decor.getContext();
mActionView = (ActionBarView) decor.findViewById(com.android.internal.R.id.action_bar);
- mUpperContextView = (ActionBarContextView) decor.findViewById(
+ mContextView = (ActionBarContextView) decor.findViewById(
com.android.internal.R.id.action_context_bar);
- mLowerContextView = (LinearLayout) decor.findViewById(
- com.android.internal.R.id.lower_action_context_bar);
mContainerView = (ActionBarContainer) decor.findViewById(
com.android.internal.R.id.action_bar_container);
+ mSplitView = (ActionBarContainer) decor.findViewById(
+ com.android.internal.R.id.split_action_bar);
- if (mActionView == null || mUpperContextView == null || mContainerView == null) {
+ if (mActionView == null || mContextView == null || mContainerView == null) {
throw new IllegalStateException(getClass().getSimpleName() + " can only be used " +
"with a compatible window decor layout");
}
- mActionView.setContextView(mUpperContextView);
- mContextDisplayMode = mLowerContextView == null ?
- CONTEXT_DISPLAY_NORMAL : CONTEXT_DISPLAY_SPLIT;
+ mActionView.setContextView(mContextView);
+ mContextDisplayMode = mActionView.isSplitActionBar() ?
+ CONTEXT_DISPLAY_SPLIT : CONTEXT_DISPLAY_NORMAL;
+
+ if (!mActionView.hasEmbeddedTabs()) {
+ HorizontalScrollView tabScroller = new HorizontalScrollView(mContext);
+ ViewGroup tabContainer = mActionView.createTabContainer();
+ tabScroller.setHorizontalFadingEdgeEnabled(true);
+ tabScroller.addView(tabContainer);
+ tabScroller.setVisibility(getNavigationMode() == NAVIGATION_MODE_TABS ?
+ View.VISIBLE : View.GONE);
+ mActionView.setExternalTabLayout(tabContainer);
+ mContainerView.setTabContainer(tabScroller);
+ mExternalTabView = tabScroller;
+ }
}
/**
@@ -229,8 +176,8 @@ public class ActionBarImpl extends ActionBar {
*/
public void setShowHideAnimationEnabled(boolean enabled) {
mShowHideAnimationEnabled = enabled;
- if (!enabled && mCurrentAnim != null) {
- mCurrentAnim.end();
+ if (!enabled && mCurrentShowAnim != null) {
+ mCurrentShowAnim.end();
}
}
@@ -285,6 +232,11 @@ public class ActionBarImpl extends ActionBar {
}
@Override
+ public void setDisplayDisableHomeEnabled(boolean disableHome) {
+ setDisplayOptions(disableHome ? DISPLAY_DISABLE_HOME : 0, DISPLAY_DISABLE_HOME);
+ }
+
+ @Override
public void setTitle(int resId) {
setTitle(mContext.getString(resId));
}
@@ -367,18 +319,18 @@ public class ActionBarImpl extends ActionBar {
mActionMode.finish();
}
- mUpperContextView.killMode();
+ mContextView.killMode();
ActionMode mode = new ActionModeImpl(callback);
if (callback.onCreateActionMode(mode, mode.getMenu())) {
+ mWasHiddenBeforeMode = !isShowing();
mode.invalidate();
- mUpperContextView.initForMode(mode);
- animateTo(CONTEXT_VIEW);
- if (mLowerContextView != null) {
+ mContextView.initForMode(mode);
+ animateToMode(true);
+ if (mSplitView != null) {
// TODO animate this
- mLowerContextView.setVisibility(View.VISIBLE);
+ mSplitView.setVisibility(View.VISIBLE);
}
mActionMode = mode;
- show();
return mode;
}
return null;
@@ -444,7 +396,10 @@ public class ActionBarImpl extends ActionBar {
int selectedTabPosition = mSelectedTab != null
? mSelectedTab.getPosition() : mSavedTabPosition;
mActionView.removeTabAt(position);
- mTabs.remove(position);
+ TabImpl removedTab = mTabs.remove(position);
+ if (removedTab != null) {
+ removedTab.setPosition(-1);
+ }
final int newTabCount = mTabs.size();
for (int i = position; i < newTabCount; i++) {
@@ -498,10 +453,15 @@ public class ActionBarImpl extends ActionBar {
@Override
public void show() {
- if (mCurrentAnim != null) {
- mCurrentAnim.end();
+ show(true);
+ }
+
+ void show(boolean markHiddenBeforeMode) {
+ if (mCurrentShowAnim != null) {
+ mCurrentShowAnim.end();
}
if (mContainerView.getVisibility() == View.VISIBLE) {
+ if (markHiddenBeforeMode) mWasHiddenBeforeMode = false;
return;
}
mContainerView.setVisibility(View.VISIBLE);
@@ -516,18 +476,24 @@ public class ActionBarImpl extends ActionBar {
mContainerView.setTranslationY(-mContainerView.getHeight());
b.with(ObjectAnimator.ofFloat(mContainerView, "translationY", 0));
}
+ if (mSplitView != null) {
+ mSplitView.setAlpha(0);
+ b.with(ObjectAnimator.ofFloat(mSplitView, "alpha", 1));
+ }
anim.addListener(mShowListener);
- mCurrentAnim = anim;
+ mCurrentShowAnim = anim;
anim.start();
} else {
+ mContainerView.setAlpha(1);
+ mContainerView.setTranslationY(0);
mShowListener.onAnimationEnd(null);
}
}
@Override
public void hide() {
- if (mCurrentAnim != null) {
- mCurrentAnim.end();
+ if (mCurrentShowAnim != null) {
+ mCurrentShowAnim.end();
}
if (mContainerView.getVisibility() == View.GONE) {
return;
@@ -544,8 +510,12 @@ public class ActionBarImpl extends ActionBar {
b.with(ObjectAnimator.ofFloat(mContainerView, "translationY",
-mContainerView.getHeight()));
}
+ if (mSplitView != null) {
+ mSplitView.setAlpha(1);
+ b.with(ObjectAnimator.ofFloat(mSplitView, "alpha", 0));
+ }
anim.addListener(mHideListener);
- mCurrentAnim = anim;
+ mCurrentShowAnim = anim;
anim.start();
} else {
mHideListener.onAnimationEnd(null);
@@ -556,41 +526,14 @@ public class ActionBarImpl extends ActionBar {
return mContainerView.getVisibility() == View.VISIBLE;
}
- private long animateTo(int viewIndex) {
- show();
-
- AnimatorSet set = new AnimatorSet();
-
- final View targetChild = mContainerView.getChildAt(viewIndex);
- targetChild.setVisibility(View.VISIBLE);
- AnimatorSet.Builder b = set.play(ObjectAnimator.ofFloat(targetChild, "alpha", 1));
-
- final int count = mContainerView.getChildCount();
- for (int i = 0; i < count; i++) {
- final View child = mContainerView.getChildAt(i);
- if (i == viewIndex) {
- continue;
- }
-
- if (child.getVisibility() != View.GONE) {
- Animator a = ObjectAnimator.ofFloat(child, "alpha", 0);
- a.setInterpolator(sFadeOutInterpolator);
- b.with(a);
- }
+ void animateToMode(boolean toActionMode) {
+ show(false);
+ if (mCurrentModeAnim != null) {
+ mCurrentModeAnim.end();
}
- set.addListener(mAfterAnimation[viewIndex]);
-
- mCurrentAnim = set;
- set.start();
- return set.getDuration();
- }
-
- private void hideAllExcept(int viewIndex) {
- final int count = mContainerView.getChildCount();
- for (int i = 0; i < count; i++) {
- mContainerView.getChildAt(i).setVisibility(i == viewIndex ? View.VISIBLE : View.GONE);
- }
+ mActionView.animateToVisibility(toActionMode ? View.GONE : View.VISIBLE);
+ mContextView.animateToVisibility(toActionMode ? View.VISIBLE : View.GONE);
}
/**
@@ -627,15 +570,15 @@ public class ActionBarImpl extends ActionBar {
mCallback.onDestroyActionMode(this);
mCallback = null;
- animateTo(NORMAL_VIEW);
+ animateToMode(false);
// Clear out the context mode views after the animation finishes
- mUpperContextView.closeMode();
- if (mLowerContextView != null && mLowerContextView.getVisibility() != View.GONE) {
- // TODO Animate this
- mLowerContextView.setVisibility(View.GONE);
- }
+ mContextView.closeMode();
mActionMode = null;
+
+ if (mWasHiddenBeforeMode) {
+ hide();
+ }
}
@Override
@@ -647,18 +590,18 @@ public class ActionBarImpl extends ActionBar {
@Override
public void setCustomView(View view) {
- mUpperContextView.setCustomView(view);
+ mContextView.setCustomView(view);
mCustomView = new WeakReference<View>(view);
}
@Override
public void setSubtitle(CharSequence subtitle) {
- mUpperContextView.setSubtitle(subtitle);
+ mContextView.setSubtitle(subtitle);
}
@Override
public void setTitle(CharSequence title) {
- mUpperContextView.setTitle(title);
+ mContextView.setTitle(title);
}
@Override
@@ -673,12 +616,12 @@ public class ActionBarImpl extends ActionBar {
@Override
public CharSequence getTitle() {
- return mUpperContextView.getTitle();
+ return mContextView.getTitle();
}
@Override
public CharSequence getSubtitle() {
- return mUpperContextView.getSubtitle();
+ return mContextView.getSubtitle();
}
@Override
@@ -718,7 +661,7 @@ public class ActionBarImpl extends ActionBar {
return;
}
invalidate();
- mUpperContextView.openOverflowMenu();
+ mContextView.showOverflowMenu();
}
}
@@ -730,7 +673,7 @@ public class ActionBarImpl extends ActionBar {
private Object mTag;
private Drawable mIcon;
private CharSequence mText;
- private int mPosition;
+ private int mPosition = -1;
private View mCustomView;
@Override
@@ -762,6 +705,7 @@ public class ActionBarImpl extends ActionBar {
@Override
public Tab setCustomView(View view) {
mCustomView = view;
+ if (mPosition >= 0) mActionView.updateTab(mPosition);
return this;
}
@@ -792,6 +736,7 @@ public class ActionBarImpl extends ActionBar {
@Override
public Tab setIcon(Drawable icon) {
mIcon = icon;
+ if (mPosition >= 0) mActionView.updateTab(mPosition);
return this;
}
@@ -803,6 +748,7 @@ public class ActionBarImpl extends ActionBar {
@Override
public Tab setText(CharSequence text) {
mText = text;
+ if (mPosition >= 0) mActionView.updateTab(mPosition);
return this;
}
@@ -871,11 +817,17 @@ public class ActionBarImpl extends ActionBar {
case NAVIGATION_MODE_TABS:
mSavedTabPosition = getSelectedNavigationIndex();
selectTab(null);
+ if (!mActionView.hasEmbeddedTabs()) {
+ mExternalTabView.setVisibility(View.GONE);
+ }
break;
}
mActionView.setNavigationMode(mode);
switch (mode) {
case NAVIGATION_MODE_TABS:
+ if (!mActionView.hasEmbeddedTabs()) {
+ mExternalTabView.setVisibility(View.VISIBLE);
+ }
if (mSavedTabPosition != INVALID_POSITION) {
setSelectedNavigationItem(mSavedTabPosition);
mSavedTabPosition = INVALID_POSITION;
@@ -889,23 +841,24 @@ public class ActionBarImpl extends ActionBar {
return mTabs.get(index);
}
- /**
- * This fragment is added when we're keeping a back stack in a tab switch
- * transaction. We use it to change the selected tab in the action bar view
- * when we back out.
- */
- private class SwitchSelectedTabViewFragment extends Fragment {
- private int mSelectedTabIndex;
- public SwitchSelectedTabViewFragment(int oldSelectedTab) {
- mSelectedTabIndex = oldSelectedTab;
- }
+ @Override
+ public void setIcon(int resId) {
+ mActionView.setIcon(resId);
+ }
- @Override
- public void onDetach() {
- if (mSelectedTabIndex >= 0 && mSelectedTabIndex < getTabCount()) {
- mActionView.setTabSelected(mSelectedTabIndex);
- }
- }
+ @Override
+ public void setIcon(Drawable icon) {
+ mActionView.setIcon(icon);
+ }
+
+ @Override
+ public void setLogo(int resId) {
+ mActionView.setLogo(resId);
+ }
+
+ @Override
+ public void setLogo(Drawable logo) {
+ mActionView.setLogo(logo);
}
}
diff --git a/core/java/com/android/internal/app/IMediaContainerService.aidl b/core/java/com/android/internal/app/IMediaContainerService.aidl
index aee1626..dd22e25 100755
--- a/core/java/com/android/internal/app/IMediaContainerService.aidl
+++ b/core/java/com/android/internal/app/IMediaContainerService.aidl
@@ -27,8 +27,9 @@ interface IMediaContainerService {
String key, String resFileName);
boolean copyResource(in Uri packageURI,
in ParcelFileDescriptor outStream);
- PackageInfoLite getMinimalPackageInfo(in Uri fileUri, int flags);
- boolean checkFreeStorage(boolean external, in Uri fileUri);
+ PackageInfoLite getMinimalPackageInfo(in Uri fileUri, in int flags, in long threshold);
+ boolean checkInternalFreeStorage(in Uri fileUri, in long threshold);
+ boolean checkExternalFreeStorage(in Uri fileUri);
ObbInfo getObbInfo(in String filename);
long calculateDirectorySize(in String directory);
}
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 2e56996..ba2f5d4 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -30,7 +30,6 @@ import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.os.PatternMatcher;
-import android.util.Config;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
@@ -238,7 +237,7 @@ public class ResolverActivity extends AlertActivity implements
ResolveInfo r0 = rList.get(0);
for (int i=1; i<N; i++) {
ResolveInfo ri = rList.get(i);
- if (Config.LOGV) Log.v(
+ if (false) Log.v(
"ResolveListActivity",
r0.activityInfo.name + "=" +
r0.priority + "/" + r0.isDefault + " vs " +
diff --git a/core/java/com/android/internal/content/NativeLibraryHelper.java b/core/java/com/android/internal/content/NativeLibraryHelper.java
index 4ae55fc..9ae7def 100644
--- a/core/java/com/android/internal/content/NativeLibraryHelper.java
+++ b/core/java/com/android/internal/content/NativeLibraryHelper.java
@@ -4,7 +4,6 @@ import android.content.pm.PackageManager;
import android.os.Build;
import android.os.FileUtils;
import android.os.SystemProperties;
-import android.util.Config;
import android.util.Log;
import android.util.Pair;
import android.util.Slog;
@@ -176,7 +175,7 @@ public class NativeLibraryHelper {
continue;
}
- if (Config.LOGD) {
+ if (false) {
Log.d(TAG, "Found gdbserver: " + entry.getName());
}
diff --git a/core/java/com/android/internal/content/PackageHelper.java b/core/java/com/android/internal/content/PackageHelper.java
index d6c43f9..b57046c 100644
--- a/core/java/com/android/internal/content/PackageHelper.java
+++ b/core/java/com/android/internal/content/PackageHelper.java
@@ -56,18 +56,13 @@ public class PackageHelper {
return null;
}
- public static String createSdDir(long sizeBytes, String cid,
+ public static String createSdDir(int sizeMb, String cid,
String sdEncKey, int uid) {
// Create mount point via MountService
IMountService mountService = getMountService();
- int sizeMb = (int) (sizeBytes >> 20);
- if ((sizeBytes - (sizeMb * 1024 * 1024)) > 0) {
- sizeMb++;
- }
- // Add buffer size
- sizeMb++;
+
if (localLOGV)
- Log.i(TAG, "Size of container " + sizeMb + " MB " + sizeBytes + " bytes");
+ Log.i(TAG, "Size of container " + sizeMb + " MB");
try {
int rc = mountService.createSecureContainer(
diff --git a/core/java/com/android/internal/net/DomainNameValidator.java b/core/java/com/android/internal/net/DomainNameValidator.java
index 36973f1..3950655 100644
--- a/core/java/com/android/internal/net/DomainNameValidator.java
+++ b/core/java/com/android/internal/net/DomainNameValidator.java
@@ -16,7 +16,6 @@
package com.android.internal.net;
import android.net.NetworkUtils;
-import android.util.Config;
import android.util.Log;
import java.net.InetAddress;
@@ -35,7 +34,7 @@ public class DomainNameValidator {
private final static String TAG = "DomainNameValidator";
private static final boolean DEBUG = false;
- private static final boolean LOG_ENABLED = DEBUG ? Config.LOGD : Config.LOGV;
+ private static final boolean LOG_ENABLED = false;
private static final int ALT_DNS_NAME = 2;
private static final int ALT_IPA_NAME = 7;
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 2847cf3..12687a1 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -36,6 +36,7 @@ import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.telephony.TelephonyManager;
import android.util.Log;
+import android.util.LogWriter;
import android.util.PrintWriterPrinter;
import android.util.Printer;
import android.util.Slog;
@@ -70,7 +71,7 @@ public final class BatteryStatsImpl extends BatteryStats {
private static final int MAGIC = 0xBA757475; // 'BATSTATS'
// Current on-disk Parcel version
- private static final int VERSION = 54;
+ private static final int VERSION = 60;
// Maximum number of items we will record in the history.
private static final int MAX_HISTORY_ITEMS = 2000;
@@ -154,11 +155,27 @@ public final class BatteryStatsImpl extends BatteryStats {
boolean mHaveBatteryLevel = false;
boolean mRecordingHistory = true;
int mNumHistoryItems;
+
+ static final int MAX_HISTORY_BUFFER = 128*1024; // 128KB
+ static final int MAX_MAX_HISTORY_BUFFER = 144*1024; // 144KB
+ final Parcel mHistoryBuffer = Parcel.obtain();
+ final HistoryItem mHistoryLastWritten = new HistoryItem();
+ final HistoryItem mHistoryLastLastWritten = new HistoryItem();
+ final HistoryItem mHistoryReadTmp = new HistoryItem();
+ int mHistoryBufferLastPos = -1;
+ boolean mHistoryOverflow = false;
+ long mLastHistoryTime = 0;
+
+ final HistoryItem mHistoryCur = new HistoryItem();
+
HistoryItem mHistory;
HistoryItem mHistoryEnd;
HistoryItem mHistoryLastEnd;
HistoryItem mHistoryCache;
- final HistoryItem mHistoryCur = new HistoryItem();
+
+ private HistoryItem mHistoryIterator;
+ private boolean mReadOverflow;
+ private boolean mIteratingHistory;
int mStartCount;
@@ -1189,9 +1206,84 @@ public final class BatteryStatsImpl extends BatteryStats {
mBtHeadset = headset;
}
+ int mChangedBufferStates = 0;
+
+ void addHistoryBufferLocked(long curTime) {
+ if (!mHaveBatteryLevel || !mRecordingHistory) {
+ return;
+ }
+
+ final long timeDiff = (mHistoryBaseTime+curTime) - mHistoryLastWritten.time;
+ if (mHistoryBufferLastPos >= 0 && mHistoryLastWritten.cmd == HistoryItem.CMD_UPDATE
+ && timeDiff < 2000
+ && ((mHistoryLastWritten.states^mHistoryCur.states)&mChangedBufferStates) == 0) {
+ // If the current is the same as the one before, then we no
+ // longer need the entry.
+ mHistoryBuffer.setDataSize(mHistoryBufferLastPos);
+ mHistoryBuffer.setDataPosition(mHistoryBufferLastPos);
+ mHistoryBufferLastPos = -1;
+ if (mHistoryLastLastWritten.cmd == HistoryItem.CMD_UPDATE
+ && timeDiff < 500 && mHistoryLastLastWritten.same(mHistoryCur)) {
+ // If this results in us returning to the state written
+ // prior to the last one, then we can just delete the last
+ // written one and drop the new one. Nothing more to do.
+ mHistoryLastWritten.setTo(mHistoryLastLastWritten);
+ mHistoryLastLastWritten.cmd = HistoryItem.CMD_NULL;
+ return;
+ }
+ mChangedBufferStates |= mHistoryLastWritten.states^mHistoryCur.states;
+ curTime = mHistoryLastWritten.time - mHistoryBaseTime;
+ mHistoryLastWritten.setTo(mHistoryLastLastWritten);
+ } else {
+ mChangedBufferStates = 0;
+ }
+
+ final int dataSize = mHistoryBuffer.dataSize();
+ if (dataSize >= MAX_HISTORY_BUFFER) {
+ if (!mHistoryOverflow) {
+ mHistoryOverflow = true;
+ addHistoryBufferLocked(curTime, HistoryItem.CMD_OVERFLOW);
+ }
+
+ // Once we've reached the maximum number of items, we only
+ // record changes to the battery level and the most interesting states.
+ // Once we've reached the maximum maximum number of items, we only
+ // record changes to the battery level.
+ if (mHistoryLastWritten.batteryLevel == mHistoryCur.batteryLevel &&
+ (dataSize >= MAX_MAX_HISTORY_BUFFER
+ || ((mHistoryEnd.states^mHistoryCur.states)
+ & HistoryItem.MOST_INTERESTING_STATES) == 0)) {
+ return;
+ }
+ }
+
+ addHistoryBufferLocked(curTime, HistoryItem.CMD_UPDATE);
+ }
+
+ void addHistoryBufferLocked(long curTime, byte cmd) {
+ int origPos = 0;
+ if (mIteratingHistory) {
+ origPos = mHistoryBuffer.dataPosition();
+ mHistoryBuffer.setDataPosition(mHistoryBuffer.dataSize());
+ }
+ mHistoryBufferLastPos = mHistoryBuffer.dataPosition();
+ mHistoryLastLastWritten.setTo(mHistoryLastWritten);
+ mHistoryLastWritten.setTo(mHistoryBaseTime + curTime, cmd, mHistoryCur);
+ mHistoryLastWritten.writeDelta(mHistoryBuffer, mHistoryLastLastWritten);
+ mLastHistoryTime = curTime;
+ if (DEBUG_HISTORY) Slog.i(TAG, "Writing history buffer: was " + mHistoryBufferLastPos
+ + " now " + mHistoryBuffer.dataPosition()
+ + " size is now " + mHistoryBuffer.dataSize());
+ if (mIteratingHistory) {
+ mHistoryBuffer.setDataPosition(origPos);
+ }
+ }
+
int mChangedStates = 0;
void addHistoryRecordLocked(long curTime) {
+ addHistoryBufferLocked(curTime);
+
if (!mHaveBatteryLevel || !mRecordingHistory) {
return;
}
@@ -1206,6 +1298,7 @@ public final class BatteryStatsImpl extends BatteryStats {
// If the current is the same as the one before, then we no
// longer need the entry.
if (mHistoryLastEnd != null && mHistoryLastEnd.cmd == HistoryItem.CMD_UPDATE
+ && (mHistoryBaseTime+curTime) < (mHistoryEnd.time+500)
&& mHistoryLastEnd.same(mHistoryCur)) {
mHistoryLastEnd.next = null;
mHistoryEnd.next = mHistoryCache;
@@ -1268,6 +1361,7 @@ public final class BatteryStatsImpl extends BatteryStats {
}
void clearHistoryLocked() {
+ if (DEBUG_HISTORY) Slog.i(TAG, "********** CLEARING HISTORY!");
if (mHistory != null) {
mHistoryEnd.next = mHistoryCache;
mHistoryCache = mHistory;
@@ -1275,6 +1369,15 @@ public final class BatteryStatsImpl extends BatteryStats {
}
mNumHistoryItems = 0;
mHistoryBaseTime = 0;
+ mLastHistoryTime = 0;
+
+ mHistoryBuffer.setDataSize(0);
+ mHistoryBuffer.setDataPosition(0);
+ mHistoryBuffer.setDataCapacity(MAX_HISTORY_BUFFER/2);
+ mHistoryLastLastWritten.cmd = HistoryItem.CMD_NULL;
+ mHistoryLastWritten.cmd = HistoryItem.CMD_NULL;
+ mHistoryBufferLastPos = -1;
+ mHistoryOverflow = false;
}
public void doUnplugLocked(long batteryUptime, long batteryRealtime) {
@@ -3910,11 +4013,13 @@ public final class BatteryStatsImpl extends BatteryStats {
mDischargeUnplugLevel = 0;
mDischargeCurrentLevel = 0;
initDischarge();
+ clearHistoryLocked();
}
public BatteryStatsImpl(Parcel p) {
mFile = null;
mHandler = null;
+ clearHistoryLocked();
readFromParcel(p);
}
@@ -3932,25 +4037,84 @@ public final class BatteryStatsImpl extends BatteryStats {
}
}
- private HistoryItem mHistoryIterator;
-
- public boolean startIteratingHistoryLocked() {
+ @Override
+ public boolean startIteratingOldHistoryLocked() {
+ if (DEBUG_HISTORY) Slog.i(TAG, "ITERATING: buff size=" + mHistoryBuffer.dataSize()
+ + " pos=" + mHistoryBuffer.dataPosition());
+ mHistoryBuffer.setDataPosition(0);
+ mHistoryReadTmp.clear();
+ mReadOverflow = false;
+ mIteratingHistory = true;
return (mHistoryIterator = mHistory) != null;
}
- public boolean getNextHistoryLocked(HistoryItem out) {
+ @Override
+ public boolean getNextOldHistoryLocked(HistoryItem out) {
+ boolean end = mHistoryBuffer.dataPosition() >= mHistoryBuffer.dataSize();
+ if (!end) {
+ mHistoryReadTmp.readDelta(mHistoryBuffer);
+ mReadOverflow |= mHistoryReadTmp.cmd == HistoryItem.CMD_OVERFLOW;
+ }
HistoryItem cur = mHistoryIterator;
if (cur == null) {
+ if (!mReadOverflow && !end) {
+ Slog.w(TAG, "Old history ends before new history!");
+ }
return false;
}
out.setTo(cur);
mHistoryIterator = cur.next;
+ if (!mReadOverflow) {
+ if (end) {
+ Slog.w(TAG, "New history ends before old history!");
+ } else if (!out.same(mHistoryReadTmp)) {
+ long now = getHistoryBaseTime() + SystemClock.elapsedRealtime();
+ PrintWriter pw = new PrintWriter(new LogWriter(android.util.Log.WARN, TAG));
+ pw.println("Histories differ!");
+ pw.println("Old history:");
+ (new HistoryPrinter()).printNextItem(pw, out, now);
+ pw.println("New history:");
+ (new HistoryPrinter()).printNextItem(pw, mHistoryReadTmp, now);
+ }
+ }
return true;
}
@Override
- public HistoryItem getHistory() {
- return mHistory;
+ public void finishIteratingOldHistoryLocked() {
+ mIteratingHistory = false;
+ mHistoryBuffer.setDataPosition(mHistoryBuffer.dataSize());
+ }
+
+ @Override
+ public boolean startIteratingHistoryLocked() {
+ if (DEBUG_HISTORY) Slog.i(TAG, "ITERATING: buff size=" + mHistoryBuffer.dataSize()
+ + " pos=" + mHistoryBuffer.dataPosition());
+ mHistoryBuffer.setDataPosition(0);
+ mReadOverflow = false;
+ mIteratingHistory = true;
+ return mHistoryBuffer.dataSize() > 0;
+ }
+
+ @Override
+ public boolean getNextHistoryLocked(HistoryItem out) {
+ final int pos = mHistoryBuffer.dataPosition();
+ if (pos == 0) {
+ out.clear();
+ }
+ boolean end = pos >= mHistoryBuffer.dataSize();
+ if (end) {
+ return false;
+ }
+
+ out.readDelta(mHistoryBuffer);
+ return true;
+ }
+
+ @Override
+ public void finishIteratingHistoryLocked() {
+ mIteratingHistory = false;
+ mHistoryBuffer.setDataPosition(mHistoryBuffer.dataSize());
}
@Override
@@ -4697,7 +4861,9 @@ public final class BatteryStatsImpl extends BatteryStats {
Slog.e("BatteryStats", "Error reading battery statistics", e);
}
- addHistoryRecordLocked(SystemClock.elapsedRealtime(), HistoryItem.CMD_START);
+ long now = SystemClock.elapsedRealtime();
+ addHistoryRecordLocked(now, HistoryItem.CMD_START);
+ addHistoryBufferLocked(now, HistoryItem.CMD_START);
}
public int describeContents() {
@@ -4705,30 +4871,54 @@ public final class BatteryStatsImpl extends BatteryStats {
}
void readHistory(Parcel in) {
- mHistory = mHistoryEnd = mHistoryCache = null;
- mHistoryBaseTime = 0;
- long time;
- while (in.dataAvail() > 0 && (time=in.readLong()) >= 0) {
- HistoryItem rec = new HistoryItem(time, in);
- addHistoryRecordLocked(rec);
- if (rec.time > mHistoryBaseTime) {
- mHistoryBaseTime = rec.time;
- }
+ mHistoryBaseTime = in.readLong();
+
+ mHistoryBuffer.setDataSize(0);
+ mHistoryBuffer.setDataPosition(0);
+
+ int bufSize = in.readInt();
+ int curPos = in.dataPosition();
+ if (bufSize >= (MAX_MAX_HISTORY_BUFFER*3)) {
+ Slog.w(TAG, "File corrupt: history data buffer too large " + bufSize);
+ } else if ((bufSize&~3) != bufSize) {
+ Slog.w(TAG, "File corrupt: history data buffer not aligned " + bufSize);
+ } else {
+ if (DEBUG_HISTORY) Slog.i(TAG, "***************** READING NEW HISTORY: " + bufSize
+ + " bytes at " + curPos);
+ mHistoryBuffer.appendFrom(in, curPos, bufSize);
+ in.setDataPosition(curPos + bufSize);
}
- long oldnow = SystemClock.elapsedRealtime() - (5*60*100);
+ long oldnow = SystemClock.elapsedRealtime() - (5*60*1000);
if (oldnow > 0) {
// If the system process has restarted, but not the entire
// system, then the mHistoryBaseTime already accounts for
// much of the elapsed time. We thus want to adjust it back,
// to avoid large gaps in the data. We determine we are
// in this case by arbitrarily saying it is so if at this
- // point in boot the elapsed time is already more than 5 seconds.
+ // point in boot the elapsed time is already more than 5 minutes.
mHistoryBaseTime -= oldnow;
}
}
+ void readOldHistory(Parcel in) {
+ mHistory = mHistoryEnd = mHistoryCache = null;
+ long time;
+ while (in.dataAvail() > 0 && (time=in.readLong()) >= 0) {
+ HistoryItem rec = new HistoryItem(time, in);
+ addHistoryRecordLocked(rec);
+ }
+ }
+
void writeHistory(Parcel out) {
+ out.writeLong(mLastHistoryTime);
+ out.writeInt(mHistoryBuffer.dataSize());
+ if (DEBUG_HISTORY) Slog.i(TAG, "***************** WRITING HISTORY: "
+ + mHistoryBuffer.dataSize() + " bytes at " + out.dataPosition());
+ out.appendFrom(mHistoryBuffer, 0, mHistoryBuffer.dataSize());
+ }
+
+ void writeOldHistory(Parcel out) {
HistoryItem rec = mHistory;
while (rec != null) {
if (rec.time >= 0) rec.writeToParcel(out, 0);
@@ -4746,6 +4936,7 @@ public final class BatteryStatsImpl extends BatteryStats {
}
readHistory(in);
+ readOldHistory(in);
mStartCount = in.readInt();
mBatteryUptime = in.readLong();
@@ -4935,6 +5126,9 @@ public final class BatteryStatsImpl extends BatteryStats {
* @param out the Parcel to be written to.
*/
public void writeSummaryToParcel(Parcel out) {
+ // Need to update with current kernel wake lock counts.
+ updateKernelWakelocksLocked();
+
final long NOW_SYS = SystemClock.uptimeMillis() * 1000;
final long NOWREAL_SYS = SystemClock.elapsedRealtime() * 1000;
final long NOW = getBatteryUptimeLocked(NOW_SYS);
@@ -4943,6 +5137,7 @@ public final class BatteryStatsImpl extends BatteryStats {
out.writeInt(VERSION);
writeHistory(out);
+ writeOldHistory(out);
out.writeInt(mStartCount);
out.writeLong(computeBatteryUptime(NOW_SYS, STATS_SINCE_CHARGED));
@@ -5256,6 +5451,9 @@ public final class BatteryStatsImpl extends BatteryStats {
@SuppressWarnings("unused")
void writeToParcelLocked(Parcel out, boolean inclUids, int flags) {
+ // Need to update with current kernel wake lock counts.
+ updateKernelWakelocksLocked();
+
final long uSecUptime = SystemClock.uptimeMillis() * 1000;
final long uSecRealtime = SystemClock.elapsedRealtime() * 1000;
final long batteryUptime = getBatteryUptimeLocked(uSecUptime);
@@ -5358,6 +5556,11 @@ public final class BatteryStatsImpl extends BatteryStats {
}
};
+ public void prepareForDumpLocked() {
+ // Need to retrieve current kernel wake lock stats before printing.
+ updateKernelWakelocksLocked();
+ }
+
public void dumpLocked(PrintWriter pw) {
if (DEBUG) {
Printer pr = new PrintWriterPrinter(pw);
diff --git a/core/java/com/android/internal/os/BinderInternal.java b/core/java/com/android/internal/os/BinderInternal.java
index ba0bf0d..f54a3e9 100644
--- a/core/java/com/android/internal/os/BinderInternal.java
+++ b/core/java/com/android/internal/os/BinderInternal.java
@@ -19,7 +19,6 @@ package com.android.internal.os;
import android.os.Binder;
import android.os.IBinder;
import android.os.SystemClock;
-import android.util.Config;
import android.util.EventLog;
import android.util.Log;
diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java
index f58f261..0f086f6 100644
--- a/core/java/com/android/internal/os/RuntimeInit.java
+++ b/core/java/com/android/internal/os/RuntimeInit.java
@@ -23,7 +23,6 @@ import android.os.Debug;
import android.os.IBinder;
import android.os.Process;
import android.os.SystemProperties;
-import android.util.Config;
import android.util.Log;
import android.util.Slog;
@@ -90,14 +89,14 @@ public class RuntimeInit {
}
private static final void commonInit() {
- if (Config.LOGV) Slog.d(TAG, "Entered RuntimeInit!");
+ if (false) Slog.d(TAG, "Entered RuntimeInit!");
/* set default handler; this applies to all threads in the VM */
Thread.setDefaultUncaughtExceptionHandler(new UncaughtHandler());
int hasQwerty = getQwertyKeyboard();
- if (Config.LOGV) Slog.d(TAG, ">>>>> qwerty keyboard = " + hasQwerty);
+ if (false) Slog.d(TAG, ">>>>> qwerty keyboard = " + hasQwerty);
if (hasQwerty == 1) {
System.setProperty("qwerty", "1");
}
@@ -234,7 +233,7 @@ public class RuntimeInit {
*/
finishInit();
- if (Config.LOGV) Slog.d(TAG, "Leaving RuntimeInit!");
+ if (false) Slog.d(TAG, "Leaving RuntimeInit!");
}
public static final native void finishInit();
diff --git a/core/java/com/android/internal/os/SamplingProfilerIntegration.java b/core/java/com/android/internal/os/SamplingProfilerIntegration.java
index 8c256e0..df0fcd9 100644
--- a/core/java/com/android/internal/os/SamplingProfilerIntegration.java
+++ b/core/java/com/android/internal/os/SamplingProfilerIntegration.java
@@ -20,12 +20,15 @@ import android.content.pm.PackageInfo;
import android.os.Build;
import android.os.SystemProperties;
import android.util.Log;
-import dalvik.system.SamplingProfiler;
+import dalvik.system.profiler.BinaryHprofWriter;
+import dalvik.system.profiler.SamplingProfiler;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.OutputStream;
import java.io.PrintStream;
+import java.util.Date;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
@@ -80,7 +83,8 @@ public class SamplingProfilerIntegration {
}
}
- private static SamplingProfiler INSTANCE;
+ private static SamplingProfiler samplingProfiler;
+ private static long startMillis;
/**
* Is profiling enabled?
@@ -96,10 +100,16 @@ public class SamplingProfilerIntegration {
if (!enabled) {
return;
}
+ if (samplingProfiler != null) {
+ Log.e(TAG, "SamplingProfilerIntegration already started at " + new Date(startMillis));
+ return;
+ }
+
ThreadGroup group = Thread.currentThread().getThreadGroup();
SamplingProfiler.ThreadSet threadSet = SamplingProfiler.newThreadGroupTheadSet(group);
- INSTANCE = new SamplingProfiler(samplingProfilerDepth, threadSet);
- INSTANCE.start(samplingProfilerMilliseconds);
+ samplingProfiler = new SamplingProfiler(samplingProfilerDepth, threadSet);
+ samplingProfiler.start(samplingProfilerMilliseconds);
+ startMillis = System.currentTimeMillis();
}
/**
@@ -109,6 +119,10 @@ public class SamplingProfilerIntegration {
if (!enabled) {
return;
}
+ if (samplingProfiler == null) {
+ Log.e(TAG, "SamplingProfilerIntegration is not started");
+ return;
+ }
/*
* If we're already writing a snapshot, don't bother enqueueing another
@@ -137,8 +151,9 @@ public class SamplingProfilerIntegration {
return;
}
writeSnapshotFile("zygote", null);
- INSTANCE.shutdown();
- INSTANCE = null;
+ samplingProfiler.shutdown();
+ samplingProfiler = null;
+ startMillis = 0;
}
/**
@@ -148,40 +163,44 @@ public class SamplingProfilerIntegration {
if (!enabled) {
return;
}
- INSTANCE.stop();
+ samplingProfiler.stop();
/*
- * We use the current time as a unique ID. We can't use a counter
- * because processes restart. This could result in some overlap if
- * we capture two snapshots in rapid succession.
+ * We use the global start time combined with the process name
+ * as a unique ID. We can't use a counter because processes
+ * restart. This could result in some overlap if we capture
+ * two snapshots in rapid succession.
*/
- long start = System.currentTimeMillis();
String name = processName.replaceAll(":", ".");
- String path = SNAPSHOT_DIR + "/" + name + "-" +System.currentTimeMillis() + ".snapshot";
- PrintStream out = null;
+ String path = SNAPSHOT_DIR + "/" + name + "-" + startMillis + ".snapshot";
+ long start = System.currentTimeMillis();
+ OutputStream outputStream = null;
try {
- out = new PrintStream(new BufferedOutputStream(new FileOutputStream(path)));
+ outputStream = new BufferedOutputStream(new FileOutputStream(path));
+ PrintStream out = new PrintStream(outputStream);
generateSnapshotHeader(name, packageInfo, out);
- new SamplingProfiler.AsciiHprofWriter(INSTANCE.getHprofData(), out).write();
if (out.checkError()) {
throw new IOException();
}
+ BinaryHprofWriter.write(samplingProfiler.getHprofData(), outputStream);
} catch (IOException e) {
Log.e(TAG, "Error writing snapshot to " + path, e);
return;
} finally {
- IoUtils.closeQuietly(out);
+ IoUtils.closeQuietly(outputStream);
}
// set file readable to the world so that SamplingProfilerService
// can put it to dropbox
new File(path).setReadable(true, false);
long elapsed = System.currentTimeMillis() - start;
- Log.i(TAG, "Wrote snapshot for " + name + " in " + elapsed + "ms.");
+ Log.i(TAG, "Wrote snapshot " + path + " in " + elapsed + "ms.");
+ samplingProfiler.start(samplingProfilerMilliseconds);
}
/**
- * generate header for snapshots, with the following format (like http header):
+ * generate header for snapshots, with the following format
+ * (like an HTTP header but without the \r):
*
* Version: <version number of profiler>\n
* Process: <process name>\n
@@ -194,7 +213,7 @@ public class SamplingProfilerIntegration {
private static void generateSnapshotHeader(String processName, PackageInfo packageInfo,
PrintStream out) {
// profiler version
- out.println("Version: 2");
+ out.println("Version: 3");
out.println("Process: " + processName);
if (packageInfo != null) {
out.println("Package: " + packageInfo.packageName);
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index dea53bf..fbe66e5 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -25,13 +25,13 @@ import android.os.Debug;
import android.os.FileUtils;
import android.os.SystemClock;
import android.os.SystemProperties;
-import android.util.Config;
import android.util.EventLog;
import android.util.Log;
import dalvik.system.VMRuntime;
import dalvik.system.Zygote;
-import dalvik.system.SamplingProfiler;
+
+import libcore.io.IoUtils;
import java.io.BufferedReader;
import java.io.FileDescriptor;
@@ -99,25 +99,6 @@ public class ZygoteInit {
private static final boolean PRELOAD_RESOURCES = true;
/**
- * List of methods we "warm up" in the register map cache. These were
- * chosen because they appeared on the stack in GCs in multiple
- * applications.
- *
- * This is in a VM-ready format, to minimize string processing. If a
- * class is not already loaded, or a method is not found, the entry
- * will be skipped.
- *
- * This doesn't really merit a separately-generated input file at this
- * time. The list is fairly short, and the consequences of failure
- * are minor.
- */
- private static final String[] REGISTER_MAP_METHODS = {
- // (currently not doing any)
- //"Landroid/app/Activity;.setContentView:(I)V",
- };
-
-
- /**
* Invokes a static "main(argv[]) method on class "className".
* Converts various failing exceptions into RuntimeExceptions, with
* the assumption that they will then cause the VM instance to exit.
@@ -274,7 +255,7 @@ public class ZygoteInit {
runtime.setTargetHeapUtilization(0.8f);
// Start with a clean slate.
- runtime.gcSoftReferences();
+ System.gc();
runtime.runFinalizationSync();
Debug.startAllocCounting();
@@ -292,16 +273,16 @@ public class ZygoteInit {
}
try {
- if (Config.LOGV) {
+ if (false) {
Log.v(TAG, "Preloading " + line + "...");
}
Class.forName(line);
if (Debug.getGlobalAllocSize() > PRELOAD_GC_THRESHOLD) {
- if (Config.LOGV) {
+ if (false) {
Log.v(TAG,
" GC at " + Debug.getGlobalAllocSize());
}
- runtime.gcSoftReferences();
+ System.gc();
runtime.runFinalizationSync();
Debug.resetGlobalAllocSize();
}
@@ -325,6 +306,7 @@ public class ZygoteInit {
} catch (IOException e) {
Log.e(TAG, "Error reading " + PRELOADED_CLASSES + ".", e);
} finally {
+ IoUtils.closeQuietly(is);
// Restore default.
runtime.setTargetHeapUtilization(defaultUtilization);
@@ -338,45 +320,6 @@ public class ZygoteInit {
}
/**
- * Pre-caches register maps for methods that are commonly used.
- */
- private static void cacheRegisterMaps() {
- String failed = null;
- int failure;
- long startTime = System.nanoTime();
-
- failure = 0;
-
- for (int i = 0; i < REGISTER_MAP_METHODS.length; i++) {
- String str = REGISTER_MAP_METHODS[i];
-
- if (!Debug.cacheRegisterMap(str)) {
- if (failed == null)
- failed = str;
- failure++;
- }
- }
-
- long delta = System.nanoTime() - startTime;
-
- if (failure == REGISTER_MAP_METHODS.length) {
- if (REGISTER_MAP_METHODS.length > 0) {
- Log.i(TAG,
- "Register map caching failed (precise GC not enabled?)");
- }
- return;
- }
-
- Log.i(TAG, "Register map cache: found " +
- (REGISTER_MAP_METHODS.length - failure) + " of " +
- REGISTER_MAP_METHODS.length + " methods in " +
- (delta / 1000000L) + "ms");
- if (failure > 0) {
- Log.i(TAG, " First failure: " + failed);
- }
- }
-
- /**
* Load in commonly used resources, so they can be shared across
* processes.
*
@@ -388,7 +331,7 @@ public class ZygoteInit {
Debug.startAllocCounting();
try {
- runtime.gcSoftReferences();
+ System.gc();
runtime.runFinalizationSync();
mResources = Resources.getSystem();
mResources.startPreloading();
@@ -421,15 +364,15 @@ public class ZygoteInit {
int N = ar.length();
for (int i=0; i<N; i++) {
if (Debug.getGlobalAllocSize() > PRELOAD_GC_THRESHOLD) {
- if (Config.LOGV) {
+ if (false) {
Log.v(TAG, " GC at " + Debug.getGlobalAllocSize());
}
- runtime.gcSoftReferences();
+ System.gc();
runtime.runFinalizationSync();
Debug.resetGlobalAllocSize();
}
int id = ar.getResourceId(i, 0);
- if (Config.LOGV) {
+ if (false) {
Log.v(TAG, "Preloading resource #" + Integer.toHexString(id));
}
if (id != 0) {
@@ -444,15 +387,15 @@ public class ZygoteInit {
int N = ar.length();
for (int i=0; i<N; i++) {
if (Debug.getGlobalAllocSize() > PRELOAD_GC_THRESHOLD) {
- if (Config.LOGV) {
+ if (false) {
Log.v(TAG, " GC at " + Debug.getGlobalAllocSize());
}
- runtime.gcSoftReferences();
+ System.gc();
runtime.runFinalizationSync();
Debug.resetGlobalAllocSize();
}
int id = ar.getResourceId(i, 0);
- if (Config.LOGV) {
+ if (false) {
Log.v(TAG, "Preloading resource #" + Integer.toHexString(id));
}
if (id != 0) {
@@ -478,11 +421,11 @@ public class ZygoteInit {
/* runFinalizationSync() lets finalizers be called in Zygote,
* which doesn't have a HeapWorker thread.
*/
- runtime.gcSoftReferences();
+ System.gc();
runtime.runFinalizationSync();
- runtime.gcSoftReferences();
+ System.gc();
runtime.runFinalizationSync();
- runtime.gcSoftReferences();
+ System.gc();
runtime.runFinalizationSync();
}
@@ -564,7 +507,6 @@ public class ZygoteInit {
EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
SystemClock.uptimeMillis());
preloadClasses();
- //cacheRegisterMaps();
preloadResources();
EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
SystemClock.uptimeMillis());
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 7f23ed5..7d21489 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -34,5 +34,6 @@ oneway interface IStatusBar
void setMenuKeyVisible(boolean visible);
void setImeWindowStatus(in IBinder token, int vis, int backDisposition);
void setHardKeyboardStatus(boolean available, boolean enabled);
+ void userActivity();
}
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index d6ca426..bfc717b 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -46,4 +46,5 @@ interface IStatusBarService
void onNotificationClear(String pkg, String tag, int id);
void setSystemUiVisibility(int vis);
void setHardKeyboardEnabled(boolean enabled);
+ void userActivity();
}
diff --git a/core/java/com/android/internal/view/IInputConnectionWrapper.java b/core/java/com/android/internal/view/IInputConnectionWrapper.java
index b5df812..c792d78 100644
--- a/core/java/com/android/internal/view/IInputConnectionWrapper.java
+++ b/core/java/com/android/internal/view/IInputConnectionWrapper.java
@@ -174,7 +174,7 @@ public class IInputConnectionWrapper extends IInputContext.Stub {
public void performPrivateCommand(String action, Bundle data) {
dispatchMessage(obtainMessageOO(DO_PERFORM_PRIVATE_COMMAND, action, data));
}
-
+
void dispatchMessage(Message msg) {
// If we are calling this from the main thread, then we can call
// right through. Otherwise, we need to send the message to the
diff --git a/core/java/com/android/internal/view/IInputContext.aidl b/core/java/com/android/internal/view/IInputContext.aidl
index e00dd4e..719a24f 100644
--- a/core/java/com/android/internal/view/IInputContext.aidl
+++ b/core/java/com/android/internal/view/IInputContext.aidl
@@ -72,4 +72,5 @@ import com.android.internal.view.IInputContextCallback;
void setComposingRegion(int start, int end);
void getSelectedText(int flags, int seq, IInputContextCallback callback);
+
}
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 611d987..4ffa4e1 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -33,6 +33,7 @@ interface IInputMethodManager {
List<InputMethodInfo> getEnabledInputMethodList();
List<InputMethodSubtype> getEnabledInputMethodSubtypeList(in InputMethodInfo imi,
boolean allowsImplicitlySelectedSubtypes);
+ InputMethodSubtype getLastInputMethodSubtype();
// TODO: We should change the return type from List to List<Parcelable>
// Currently there is a bug that aidl doesn't accept List<Parcelable>
List getShortcutInputMethodsAndSubtypes();
diff --git a/core/java/com/android/internal/view/InputConnectionWrapper.java b/core/java/com/android/internal/view/InputConnectionWrapper.java
index b13118a..a235d9a 100644
--- a/core/java/com/android/internal/view/InputConnectionWrapper.java
+++ b/core/java/com/android/internal/view/InputConnectionWrapper.java
@@ -251,7 +251,7 @@ public class InputConnectionWrapper implements InputConnection {
}
return value;
}
-
+
public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) {
ExtractedText value = null;
try {
diff --git a/core/java/com/android/internal/view/StandaloneActionMode.java b/core/java/com/android/internal/view/StandaloneActionMode.java
index 2d067da..b54daba 100644
--- a/core/java/com/android/internal/view/StandaloneActionMode.java
+++ b/core/java/com/android/internal/view/StandaloneActionMode.java
@@ -135,6 +135,6 @@ public class StandaloneActionMode extends ActionMode implements MenuBuilder.Call
public void onMenuModeChange(MenuBuilder menu) {
invalidate();
- mContextView.openOverflowMenu();
+ mContextView.showOverflowMenu();
}
}
diff --git a/core/java/com/android/internal/view/menu/ActionMenuItemView.java b/core/java/com/android/internal/view/menu/ActionMenuItemView.java
index 3325df6..beacf75 100644
--- a/core/java/com/android/internal/view/menu/ActionMenuItemView.java
+++ b/core/java/com/android/internal/view/menu/ActionMenuItemView.java
@@ -28,7 +28,7 @@ import android.widget.LinearLayout;
* @hide
*/
public class ActionMenuItemView extends LinearLayout
- implements MenuView.ItemView, View.OnClickListener {
+ implements MenuView.ItemView, View.OnClickListener, ActionMenuView.ActionMenuChildView {
private static final String TAG = "ActionMenuItemView";
private MenuItemImpl mItemData;
@@ -56,6 +56,7 @@ public class ActionMenuItemView extends LinearLayout
mTextButton = (Button) findViewById(com.android.internal.R.id.textButton);
mImageButton.setOnClickListener(this);
mTextButton.setOnClickListener(this);
+ setOnClickListener(this);
}
public MenuItemImpl getItemData() {
@@ -136,4 +137,12 @@ public class ActionMenuItemView extends LinearLayout
public boolean showsIcon() {
return true;
}
+
+ public boolean needsDividerBefore() {
+ return hasText() && mItemData.getIcon() == null;
+ }
+
+ public boolean needsDividerAfter() {
+ return hasText();
+ }
}
diff --git a/core/java/com/android/internal/view/menu/ActionMenuPresenter.java b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java
new file mode 100644
index 0000000..0051ec3
--- /dev/null
+++ b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java
@@ -0,0 +1,459 @@
+/*
+ * Copyright (C) 2011 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.view.menu;
+
+import com.android.internal.view.menu.ActionMenuView.ActionMenuChildView;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.util.SparseBooleanArray;
+import android.view.MenuItem;
+import android.view.SoundEffectConstants;
+import android.view.View;
+import android.view.View.MeasureSpec;
+import android.view.ViewGroup;
+import android.widget.ImageButton;
+
+import java.util.ArrayList;
+
+/**
+ * MenuPresenter for building action menus as seen in the action bar and action modes.
+ */
+public class ActionMenuPresenter extends BaseMenuPresenter {
+ private static final String TAG = "ActionMenuPresenter";
+
+ private View mOverflowButton;
+ private boolean mReserveOverflow;
+ private int mWidthLimit;
+ private int mActionItemWidthLimit;
+ private int mMaxItems;
+ private boolean mStrictWidthLimit;
+
+ // Group IDs that have been added as actions - used temporarily, allocated here for reuse.
+ private final SparseBooleanArray mActionButtonGroups = new SparseBooleanArray();
+
+ private View mScrapActionButtonView;
+
+ private OverflowPopup mOverflowPopup;
+ private ActionButtonSubmenu mActionButtonPopup;
+
+ private OpenOverflowRunnable mPostedOpenRunnable;
+
+ public ActionMenuPresenter() {
+ super(com.android.internal.R.layout.action_menu_layout,
+ com.android.internal.R.layout.action_menu_item_layout);
+ }
+
+ @Override
+ public void initForMenu(Context context, MenuBuilder menu) {
+ super.initForMenu(context, menu);
+
+ final Resources res = context.getResources();
+ final int screen = res.getConfiguration().screenLayout;
+ // TODO Use the no-buttons specifier instead here
+ mReserveOverflow = (screen & Configuration.SCREENLAYOUT_SIZE_MASK) ==
+ Configuration.SCREENLAYOUT_SIZE_XLARGE;
+ mWidthLimit = res.getDisplayMetrics().widthPixels / 2;
+
+ // Measure for initial configuration
+ mMaxItems = res.getInteger(com.android.internal.R.integer.max_action_buttons);
+
+ int width = mWidthLimit;
+ if (mReserveOverflow) {
+ if (mOverflowButton == null) {
+ mOverflowButton = new OverflowMenuButton(mContext);
+ final int spec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+ mOverflowButton.measure(spec, spec);
+ }
+ width -= mOverflowButton.getMeasuredWidth();
+ } else {
+ mOverflowButton = null;
+ }
+
+ mActionItemWidthLimit = width;
+
+ // Drop a scrap view as it may no longer reflect the proper context/config.
+ mScrapActionButtonView = null;
+ }
+
+ public void setWidthLimit(int width, boolean strict) {
+ if (mReserveOverflow) {
+ width -= mOverflowButton.getMeasuredWidth();
+ }
+ mActionItemWidthLimit = width;
+ mStrictWidthLimit = strict;
+ }
+
+ public void setItemLimit(int itemCount) {
+ mMaxItems = itemCount;
+ }
+
+ @Override
+ public MenuView getMenuView(ViewGroup root) {
+ MenuView result = super.getMenuView(root);
+ ((ActionMenuView) result).setPresenter(this);
+ return result;
+ }
+
+ @Override
+ public View getItemView(MenuItemImpl item, View convertView, ViewGroup parent) {
+ final View actionView = item.getActionView();
+ return actionView != null ? actionView : super.getItemView(item, convertView, parent);
+ }
+
+ @Override
+ public void bindItemView(MenuItemImpl item, MenuView.ItemView itemView) {
+ itemView.initialize(item, 0);
+ ((ActionMenuItemView) itemView).setItemInvoker((ActionMenuView) mMenuView);
+ }
+
+ @Override
+ public boolean shouldIncludeItem(int childIndex, MenuItemImpl item) {
+ return item.isActionButton();
+ }
+
+ @Override
+ public void updateMenuView(boolean cleared) {
+ super.updateMenuView(cleared);
+
+ if (mReserveOverflow && mMenu.getNonActionItems().size() > 0) {
+ if (mOverflowButton == null) {
+ mOverflowButton = new OverflowMenuButton(mContext);
+ mOverflowButton.setLayoutParams(
+ ((ActionMenuView) mMenuView).generateOverflowButtonLayoutParams());
+ }
+ ViewGroup parent = (ViewGroup) mOverflowButton.getParent();
+ if (parent != mMenuView) {
+ if (parent != null) {
+ parent.removeView(mOverflowButton);
+ }
+ ((ViewGroup) mMenuView).addView(mOverflowButton);
+ }
+ } else if (mOverflowButton != null && mOverflowButton.getParent() == mMenuView) {
+ ((ViewGroup) mMenuView).removeView(mOverflowButton);
+ }
+ }
+
+ @Override
+ public boolean filterLeftoverView(ViewGroup parent, int childIndex) {
+ if (parent.getChildAt(childIndex) == mOverflowButton) return false;
+ return super.filterLeftoverView(parent, childIndex);
+ }
+
+ public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
+ if (!subMenu.hasVisibleItems()) return false;
+
+ SubMenuBuilder topSubMenu = subMenu;
+ while (topSubMenu.getParentMenu() != mMenu) {
+ topSubMenu = (SubMenuBuilder) topSubMenu.getParentMenu();
+ }
+ View anchor = findViewForItem(topSubMenu.getItem());
+ if (anchor == null) return false;
+
+ mActionButtonPopup = new ActionButtonSubmenu(mContext, subMenu);
+ mActionButtonPopup.setAnchorView(anchor);
+ mActionButtonPopup.show();
+ super.onSubMenuSelected(subMenu);
+ return true;
+ }
+
+ private View findViewForItem(MenuItem item) {
+ final ViewGroup parent = (ViewGroup) mMenuView;
+ if (parent == null) return null;
+
+ final int count = parent.getChildCount();
+ for (int i = 0; i < count; i++) {
+ final View child = parent.getChildAt(i);
+ if (child instanceof MenuView.ItemView &&
+ ((MenuView.ItemView) child).getItemData() == item) {
+ return child;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Display the overflow menu if one is present.
+ * @return true if the overflow menu was shown, false otherwise.
+ */
+ public boolean showOverflowMenu() {
+ if (mReserveOverflow && !isOverflowMenuShowing() && mMenuView != null &&
+ mPostedOpenRunnable == null) {
+ OverflowPopup popup = new OverflowPopup(mContext, mMenu, mOverflowButton, true);
+ mPostedOpenRunnable = new OpenOverflowRunnable(popup);
+ // Post this for later; we might still need a layout for the anchor to be right.
+ ((View) mMenuView).post(mPostedOpenRunnable);
+
+ // ActionMenuPresenter uses null as a callback argument here
+ // to indicate overflow is opening.
+ super.onSubMenuSelected(null);
+
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Hide the overflow menu if it is currently showing.
+ *
+ * @return true if the overflow menu was hidden, false otherwise.
+ */
+ public boolean hideOverflowMenu() {
+ if (mPostedOpenRunnable != null && mMenuView != null) {
+ ((View) mMenuView).removeCallbacks(mPostedOpenRunnable);
+ return true;
+ }
+
+ MenuPopupHelper popup = mOverflowPopup;
+ if (popup != null) {
+ popup.dismiss();
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Dismiss all popup menus - overflow and submenus.
+ * @return true if popups were dismissed, false otherwise. (This can be because none were open.)
+ */
+ public boolean dismissPopupMenus() {
+ boolean result = hideOverflowMenu();
+ result |= hideSubMenus();
+ return result;
+ }
+
+ /**
+ * Dismiss all submenu popups.
+ *
+ * @return true if popups were dismissed, false otherwise. (This can be because none were open.)
+ */
+ public boolean hideSubMenus() {
+ if (mActionButtonPopup != null) {
+ mActionButtonPopup.dismiss();
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * @return true if the overflow menu is currently showing
+ */
+ public boolean isOverflowMenuShowing() {
+ return mOverflowPopup != null && mOverflowPopup.isShowing();
+ }
+
+ /**
+ * @return true if space has been reserved in the action menu for an overflow item.
+ */
+ public boolean isOverflowReserved() {
+ return mReserveOverflow;
+ }
+
+ public boolean flagActionItems() {
+ final ArrayList<MenuItemImpl> visibleItems = mMenu.getVisibleItems();
+ final int itemsSize = visibleItems.size();
+ int maxActions = mMaxItems;
+ int widthLimit = mActionItemWidthLimit;
+ final int querySpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+ final ViewGroup parent = (ViewGroup) mMenuView;
+
+ int requiredItems = 0;
+ int requestedItems = 0;
+ int firstActionWidth = 0;
+ boolean hasOverflow = false;
+ for (int i = 0; i < itemsSize; i++) {
+ MenuItemImpl item = visibleItems.get(i);
+ if (item.requiresActionButton()) {
+ requiredItems++;
+ } else if (item.requestsActionButton()) {
+ requestedItems++;
+ } else {
+ hasOverflow = true;
+ }
+ }
+
+ // Reserve a spot for the overflow item if needed.
+ if (mReserveOverflow &&
+ (hasOverflow || requiredItems + requestedItems > maxActions)) {
+ maxActions--;
+ }
+ maxActions -= requiredItems;
+
+ final SparseBooleanArray seenGroups = mActionButtonGroups;
+ seenGroups.clear();
+
+ // Flag as many more requested items as will fit.
+ for (int i = 0; i < itemsSize; i++) {
+ MenuItemImpl item = visibleItems.get(i);
+
+ if (item.requiresActionButton()) {
+ View v = item.getActionView();
+ if (v == null) {
+ v = getItemView(item, mScrapActionButtonView, parent);
+ if (mScrapActionButtonView == null) {
+ mScrapActionButtonView = v;
+ }
+ }
+ v.measure(querySpec, querySpec);
+ final int measuredWidth = v.getMeasuredWidth();
+ widthLimit -= measuredWidth;
+ if (firstActionWidth == 0) {
+ firstActionWidth = measuredWidth;
+ }
+ final int groupId = item.getGroupId();
+ if (groupId != 0) {
+ seenGroups.put(groupId, true);
+ }
+ } else if (item.requestsActionButton()) {
+ // Items in a group with other items that already have an action slot
+ // can break the max actions rule, but not the width limit.
+ final int groupId = item.getGroupId();
+ final boolean inGroup = seenGroups.get(groupId);
+ boolean isAction = (maxActions > 0 || inGroup) && widthLimit > 0;
+ maxActions--;
+
+ if (isAction) {
+ View v = item.getActionView();
+ if (v == null) {
+ v = getItemView(item, mScrapActionButtonView, parent);
+ if (mScrapActionButtonView == null) {
+ mScrapActionButtonView = v;
+ }
+ }
+ v.measure(querySpec, querySpec);
+ final int measuredWidth = v.getMeasuredWidth();
+ widthLimit -= measuredWidth;
+ if (firstActionWidth == 0) {
+ firstActionWidth = measuredWidth;
+ }
+
+ if (mStrictWidthLimit) {
+ isAction = widthLimit >= 0;
+ } else {
+ // Did this push the entire first item past the limit?
+ isAction = widthLimit + firstActionWidth > 0;
+ }
+ }
+
+ if (isAction && groupId != 0) {
+ seenGroups.put(groupId, true);
+ } else if (inGroup) {
+ // We broke the width limit. Demote the whole group, they all overflow now.
+ seenGroups.put(groupId, false);
+ for (int j = 0; j < i; j++) {
+ MenuItemImpl areYouMyGroupie = visibleItems.get(j);
+ if (areYouMyGroupie.getGroupId() == groupId) {
+ areYouMyGroupie.setIsActionButton(false);
+ }
+ }
+ }
+
+ item.setIsActionButton(isAction);
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
+ dismissPopupMenus();
+ super.onCloseMenu(menu, allMenusAreClosing);
+ }
+
+ private class OverflowMenuButton extends ImageButton implements ActionMenuChildView {
+ public OverflowMenuButton(Context context) {
+ super(context, null, com.android.internal.R.attr.actionOverflowButtonStyle);
+
+ setClickable(true);
+ setFocusable(true);
+ setVisibility(VISIBLE);
+ setEnabled(true);
+ }
+
+ @Override
+ public boolean performClick() {
+ if (super.performClick()) {
+ return true;
+ }
+
+ playSoundEffect(SoundEffectConstants.CLICK);
+ showOverflowMenu();
+ return true;
+ }
+
+ public boolean needsDividerBefore() {
+ return true;
+ }
+
+ public boolean needsDividerAfter() {
+ return false;
+ }
+ }
+
+ private class OverflowPopup extends MenuPopupHelper {
+ public OverflowPopup(Context context, MenuBuilder menu, View anchorView,
+ boolean overflowOnly) {
+ super(context, menu, anchorView, overflowOnly);
+ }
+
+ @Override
+ public void onDismiss() {
+ super.onDismiss();
+ mMenu.close();
+ mOverflowPopup = null;
+ }
+ }
+
+ private class ActionButtonSubmenu extends MenuPopupHelper {
+ private SubMenuBuilder mSubMenu;
+
+ public ActionButtonSubmenu(Context context, SubMenuBuilder subMenu) {
+ super(context, subMenu);
+ mSubMenu = subMenu;
+
+ MenuItemImpl item = (MenuItemImpl) subMenu.getItem();
+ if (!item.isActionButton()) {
+ // Give a reasonable anchor to nested submenus.
+ setAnchorView(mOverflowButton == null ? (View) mMenuView : mOverflowButton);
+ }
+ }
+
+ @Override
+ public void onDismiss() {
+ super.onDismiss();
+ mSubMenu.close();
+ mActionButtonPopup = null;
+ }
+ }
+
+ private class OpenOverflowRunnable implements Runnable {
+ private OverflowPopup mPopup;
+
+ public OpenOverflowRunnable(OverflowPopup popup) {
+ mPopup = popup;
+ }
+
+ public void run() {
+ mMenu.changeMenuMode();
+ if (mPopup.tryShow()) {
+ mOverflowPopup = mPopup;
+ mPostedOpenRunnable = null;
+ }
+ }
+ }
+}
diff --git a/core/java/com/android/internal/view/menu/ActionMenuView.java b/core/java/com/android/internal/view/menu/ActionMenuView.java
index 5da5e44..7b4f216 100644
--- a/core/java/com/android/internal/view/menu/ActionMenuView.java
+++ b/core/java/com/android/internal/view/menu/ActionMenuView.java
@@ -17,63 +17,25 @@ package com.android.internal.view.menu;
import android.content.Context;
import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.Gravity;
-import android.view.SoundEffectConstants;
import android.view.View;
+import android.view.ViewDebug;
import android.view.ViewGroup;
-import android.widget.ImageButton;
-import android.widget.ImageView;
import android.widget.LinearLayout;
-import java.util.ArrayList;
-
/**
* @hide
*/
public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvoker, MenuView {
private static final String TAG = "ActionMenuView";
-
- // TODO Theme/style this.
- private static final int DIVIDER_PADDING = 12; // dips
private MenuBuilder mMenu;
- private int mMaxItems;
- private int mWidthLimit;
private boolean mReserveOverflow;
- private OverflowMenuButton mOverflowButton;
- private MenuPopupHelper mOverflowPopup;
-
- private float mDividerPadding;
-
- private Drawable mDivider;
-
- private final Runnable mShowOverflow = new Runnable() {
- public void run() {
- showOverflowMenu();
- }
- };
-
- private class OpenOverflowRunnable implements Runnable {
- private MenuPopupHelper mPopup;
-
- public OpenOverflowRunnable(MenuPopupHelper popup) {
- mPopup = popup;
- }
-
- public void run() {
- if (mPopup.tryShow()) {
- mOverflowPopup = mPopup;
- mPostedOpenRunnable = null;
- }
- }
- }
-
- private OpenOverflowRunnable mPostedOpenRunnable;
+ private ActionMenuPresenter mPresenter;
+ private boolean mUpdateContentsBeforeMeasure;
+ private boolean mFormatItems;
public ActionMenuView(Context context) {
this(context, null);
@@ -81,57 +43,112 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo
public ActionMenuView(Context context, AttributeSet attrs) {
super(context, attrs);
-
- final Resources res = getResources();
-
- // Measure for initial configuration
- mMaxItems = getMaxActionButtons();
-
- // TODO There has to be a better way to indicate that we don't have a hard menu key.
- final Configuration config = res.getConfiguration();
- mReserveOverflow = config.isLayoutSizeAtLeast(Configuration.SCREENLAYOUT_SIZE_LARGE);
- mWidthLimit = res.getDisplayMetrics().widthPixels / 2;
-
- TypedArray a = context.obtainStyledAttributes(com.android.internal.R.styleable.Theme);
- mDivider = a.getDrawable(com.android.internal.R.styleable.Theme_dividerVertical);
- a.recycle();
-
- mDividerPadding = DIVIDER_PADDING * res.getDisplayMetrics().density;
-
setBaselineAligned(false);
}
+ public void setPresenter(ActionMenuPresenter presenter) {
+ mPresenter = presenter;
+ }
+
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
- mReserveOverflow = newConfig.isLayoutSizeAtLeast(Configuration.SCREENLAYOUT_SIZE_LARGE);
- mMaxItems = getMaxActionButtons();
- mWidthLimit = getResources().getDisplayMetrics().widthPixels / 2;
- if (mMenu != null) {
- mMenu.setMaxActionItems(mMaxItems);
- updateChildren(false);
+ mPresenter.updateMenuView(false);
+
+ if (mPresenter != null && mPresenter.isOverflowMenuShowing()) {
+ mPresenter.hideOverflowMenu();
+ mPresenter.showOverflowMenu();
}
+ }
- if (mOverflowPopup != null && mOverflowPopup.isShowing()) {
- mOverflowPopup.dismiss();
- post(mShowOverflow);
+ @Override
+ public void requestLayout() {
+ // Layout can influence how many action items fit.
+ mUpdateContentsBeforeMeasure = true;
+ super.requestLayout();
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ if (mUpdateContentsBeforeMeasure && mMenu != null) {
+ mMenu.onItemsChanged(true);
+ mUpdateContentsBeforeMeasure = false;
}
+ // If we've been given an exact size to match, apply special formatting during layout.
+ mFormatItems = MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY;
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
- public void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- if (mOverflowPopup != null && mOverflowPopup.isShowing()) {
- mOverflowPopup.dismiss();
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ if (!mFormatItems) {
+ super.onLayout(changed, left, top, right, bottom);
+ return;
+ }
+
+ final int childCount = getChildCount();
+ final int midVertical = (top + bottom) / 2;
+ final int dividerWidth = getDividerWidth();
+ int overflowWidth = 0;
+ int nonOverflowWidth = 0;
+ int nonOverflowCount = 0;
+ int widthRemaining = right - left - getPaddingRight() - getPaddingLeft();
+ for (int i = 0; i < childCount; i++) {
+ final View v = getChildAt(i);
+ if (v.getVisibility() == GONE) {
+ continue;
+ }
+
+ LayoutParams p = (LayoutParams) v.getLayoutParams();
+ if (p.isOverflowButton) {
+ overflowWidth = v.getMeasuredWidth();
+ if (hasDividerBeforeChildAt(i)) {
+ overflowWidth += dividerWidth;
+ }
+
+ int height = v.getMeasuredHeight();
+ int r = getPaddingRight();
+ int l = r - overflowWidth;
+ int t = midVertical - (height / 2);
+ int b = t + height;
+ v.layout(l, t, r, b);
+
+ widthRemaining -= overflowWidth;
+ } else {
+ nonOverflowWidth += v.getMeasuredWidth() + p.leftMargin + p.rightMargin;
+ if (hasDividerBeforeChildAt(i)) {
+ nonOverflowWidth += dividerWidth;
+ }
+ nonOverflowCount++;
+ }
}
- removeCallbacks(mShowOverflow);
- if (mPostedOpenRunnable != null) {
- removeCallbacks(mPostedOpenRunnable);
+
+ // Fill action items from the left. Overflow will always pin to the right edge.
+ if (nonOverflowWidth <= widthRemaining - overflowWidth) {
+ widthRemaining -= overflowWidth;
+ }
+
+ int startLeft = getPaddingLeft();
+ for (int i = 0; i < childCount; i++) {
+ final View v = getChildAt(i);
+ final LayoutParams lp = (LayoutParams) v.getLayoutParams();
+ if (v.getVisibility() == GONE || lp.isOverflowButton) {
+ continue;
+ }
+
+ startLeft += lp.leftMargin;
+ int width = v.getMeasuredWidth();
+ int height = v.getMeasuredHeight();
+ int t = midVertical - (height / 2);
+ v.layout(startLeft, t, startLeft + width, t + height);
+ startLeft += width + lp.rightMargin;
}
}
- private int getMaxActionButtons() {
- return getResources().getInteger(com.android.internal.R.integer.max_action_buttons);
+ @Override
+ public void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ mPresenter.dismissPopupMenus();
}
public boolean isOverflowReserved() {
@@ -141,10 +158,6 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo
public void setOverflowReserved(boolean reserveOverflow) {
mReserveOverflow = reserveOverflow;
}
-
- public View getOverflowButton() {
- return mOverflowButton;
- }
@Override
protected LayoutParams generateDefaultLayoutParams() {
@@ -166,6 +179,17 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo
return generateDefaultLayoutParams();
}
+ @Override
+ protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
+ return p instanceof LayoutParams;
+ }
+
+ public LayoutParams generateOverflowButtonLayoutParams() {
+ LayoutParams result = generateDefaultLayoutParams();
+ result.isOverflowButton = true;
+ return result;
+ }
+
public boolean invokeItem(MenuItemImpl item) {
return mMenu.performItemAction(item, 0);
}
@@ -174,243 +198,50 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo
return 0;
}
- public void initialize(MenuBuilder menu, int menuType) {
- int width = mWidthLimit;
- if (mReserveOverflow) {
- if (mOverflowButton == null) {
- OverflowMenuButton button = new OverflowMenuButton(mContext);
- mOverflowButton = button;
- }
- final int spec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
- mOverflowButton.measure(spec, spec);
- width -= mOverflowButton.getMeasuredWidth();
- }
-
- menu.setActionWidthLimit(width);
-
- menu.setMaxActionItems(mMaxItems);
- final boolean cleared = mMenu != menu;
+ public void initialize(MenuBuilder menu) {
mMenu = menu;
- updateChildren(cleared);
}
- public void updateChildren(boolean cleared) {
- final ArrayList<MenuItemImpl> itemsToShow = mMenu.getActionItems(mReserveOverflow);
- final int itemCount = itemsToShow.size();
-
- boolean needsDivider = false;
- int childIndex = 0;
- for (int i = 0; i < itemCount; i++) {
- final MenuItemImpl itemData = itemsToShow.get(i);
- boolean hasDivider = false;
-
- if (needsDivider) {
- if (!isDivider(getChildAt(childIndex))) {
- addView(makeDividerView(), childIndex, makeDividerLayoutParams());
- }
- hasDivider = true;
- childIndex++;
- }
-
- View childToAdd = itemData.getActionView();
- boolean needsPreDivider = false;
- if (childToAdd != null) {
- childToAdd.setLayoutParams(makeActionViewLayoutParams(childToAdd));
- } else {
- ActionMenuItemView view = (ActionMenuItemView) itemData.getItemView(
- MenuBuilder.TYPE_ACTION_BUTTON, this);
- view.setItemInvoker(this);
- needsPreDivider = i > 0 && !hasDivider && view.hasText() &&
- itemData.getIcon() == null;
- needsDivider = view.hasText();
- childToAdd = view;
- }
-
- boolean addPreDivider = removeChildrenUntil(childIndex, childToAdd, needsPreDivider);
-
- if (addPreDivider) addView(makeDividerView(), childIndex, makeDividerLayoutParams());
- if (needsPreDivider) childIndex++;
-
- if (getChildAt(childIndex) != childToAdd) {
- addView(childToAdd, childIndex);
- }
- childIndex++;
- }
-
- final boolean hasOverflow = mOverflowButton != null && mOverflowButton.getParent() == this;
- final boolean needsOverflow = mReserveOverflow && mMenu.getNonActionItems(true).size() > 0;
-
- if (hasOverflow != needsOverflow) {
- if (needsOverflow) {
- if (mOverflowButton == null) {
- OverflowMenuButton button = new OverflowMenuButton(mContext);
- mOverflowButton = button;
- }
- boolean addDivider = removeChildrenUntil(childIndex, mOverflowButton, true);
- if (addDivider && itemCount > 0) {
- addView(makeDividerView(), childIndex, makeDividerLayoutParams());
- childIndex++;
- }
- addView(mOverflowButton, childIndex);
- childIndex++;
- } else {
- removeView(mOverflowButton);
- }
- } else {
- if (needsOverflow) {
- boolean overflowDivider = itemCount > 0;
- boolean addDivider = removeChildrenUntil(childIndex, mOverflowButton,
- overflowDivider);
- if (addDivider && itemCount > 0) {
- addView(makeDividerView(), childIndex, makeDividerLayoutParams());
- }
- if (overflowDivider) {
- childIndex += 2;
- } else {
- childIndex++;
- }
- }
- }
-
- while (getChildCount() > childIndex) {
- removeViewAt(childIndex);
- }
- }
-
- private boolean removeChildrenUntil(int start, View targetChild, boolean needsPreDivider) {
- final int childCount = getChildCount();
- boolean found = false;
- for (int i = start; i < childCount; i++) {
- final View child = getChildAt(i);
- if (child == targetChild) {
- found = true;
- break;
- }
- }
-
- if (!found) {
- return needsPreDivider;
- }
-
- for (int i = start; i < getChildCount(); ) {
- final View child = getChildAt(i);
- if (needsPreDivider && isDivider(child)) {
- needsPreDivider = false;
- i++;
- continue;
- }
- if (child == targetChild) break;
- removeViewAt(i);
- }
-
- return needsPreDivider;
- }
-
- private static boolean isDivider(View v) {
- return v != null && v.getId() == com.android.internal.R.id.action_menu_divider;
- }
-
- public boolean showOverflowMenu() {
- if (mOverflowButton != null && !isOverflowMenuShowing()) {
- mMenu.getCallback().onMenuModeChange(mMenu);
- return true;
- }
- return false;
- }
-
- public void openOverflowMenu() {
- OverflowPopup popup = new OverflowPopup(getContext(), mMenu, mOverflowButton, true);
- mPostedOpenRunnable = new OpenOverflowRunnable(popup);
- // Post this for later; we might still need a layout for the anchor to be right.
- post(mPostedOpenRunnable);
- }
-
- public boolean isOverflowMenuShowing() {
- return mOverflowPopup != null && mOverflowPopup.isShowing();
- }
-
- public boolean isOverflowMenuOpen() {
- return mOverflowPopup != null;
- }
-
- public boolean hideOverflowMenu() {
- if (mPostedOpenRunnable != null) {
- removeCallbacks(mPostedOpenRunnable);
- return true;
+ @Override
+ protected boolean hasDividerBeforeChildAt(int childIndex) {
+ final View childBefore = getChildAt(childIndex - 1);
+ final View child = getChildAt(childIndex);
+ boolean result = false;
+ if (childIndex < getChildCount() && childBefore instanceof ActionMenuChildView) {
+ result |= ((ActionMenuChildView) childBefore).needsDividerAfter();
}
-
- MenuPopupHelper popup = mOverflowPopup;
- if (popup != null) {
- popup.dismiss();
- return true;
+ if (childIndex > 0 && child instanceof ActionMenuChildView) {
+ result |= ((ActionMenuChildView) child).needsDividerBefore();
}
- return false;
- }
-
- private boolean addItemView(boolean needsDivider, ActionMenuItemView view) {
- view.setItemInvoker(this);
- boolean hasText = view.hasText();
-
- if (hasText && needsDivider) {
- addView(makeDividerView(), makeDividerLayoutParams());
- }
- addView(view);
- return hasText;
- }
-
- private ImageView makeDividerView() {
- ImageView result = new ImageView(mContext);
- result.setImageDrawable(mDivider);
- result.setScaleType(ImageView.ScaleType.FIT_XY);
- result.setId(com.android.internal.R.id.action_menu_divider);
return result;
}
- private LayoutParams makeDividerLayoutParams() {
- LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT,
- LayoutParams.MATCH_PARENT);
- params.topMargin = (int) mDividerPadding;
- params.bottomMargin = (int) mDividerPadding;
- return params;
- }
-
- private LayoutParams makeActionViewLayoutParams(View view) {
- return generateLayoutParams(view.getLayoutParams());
+ public interface ActionMenuChildView {
+ public boolean needsDividerBefore();
+ public boolean needsDividerAfter();
}
- private class OverflowMenuButton extends ImageButton {
- public OverflowMenuButton(Context context) {
- super(context, null, com.android.internal.R.attr.actionOverflowButtonStyle);
+ public static class LayoutParams extends LinearLayout.LayoutParams {
+ @ViewDebug.ExportedProperty(category = "layout")
+ public boolean isOverflowButton;
- setClickable(true);
- setFocusable(true);
- setVisibility(VISIBLE);
- setEnabled(true);
+ public LayoutParams(Context c, AttributeSet attrs) {
+ super(c, attrs);
}
- @Override
- public boolean performClick() {
- if (super.performClick()) {
- return true;
- }
-
- playSoundEffect(SoundEffectConstants.CLICK);
- showOverflowMenu();
- return true;
+ public LayoutParams(LayoutParams other) {
+ super((LinearLayout.LayoutParams) other);
+ isOverflowButton = other.isOverflowButton;
}
- }
- private class OverflowPopup extends MenuPopupHelper {
- public OverflowPopup(Context context, MenuBuilder menu, View anchorView,
- boolean overflowOnly) {
- super(context, menu, anchorView, overflowOnly);
+ public LayoutParams(int width, int height) {
+ super(width, height);
+ isOverflowButton = false;
}
- @Override
- public void onDismiss() {
- super.onDismiss();
- mMenu.getCallback().onCloseMenu(mMenu, true);
- mOverflowPopup = null;
+ public LayoutParams(int width, int height, boolean isOverflowButton) {
+ super(width, height);
+ this.isOverflowButton = isOverflowButton;
}
}
}
diff --git a/core/java/com/android/internal/view/menu/BaseMenuPresenter.java b/core/java/com/android/internal/view/menu/BaseMenuPresenter.java
new file mode 100644
index 0000000..16f51fd
--- /dev/null
+++ b/core/java/com/android/internal/view/menu/BaseMenuPresenter.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2011 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.view.menu;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.util.ArrayList;
+
+/**
+ * Base class for MenuPresenters that have a consistent container view and item
+ * views. Behaves similarly to an AdapterView in that existing item views will
+ * be reused if possible when items change.
+ */
+public abstract class BaseMenuPresenter implements MenuPresenter {
+ protected Context mContext;
+ protected MenuBuilder mMenu;
+ protected LayoutInflater mInflater;
+ private Callback mCallback;
+
+ private int mMenuLayoutRes;
+ private int mItemLayoutRes;
+
+ protected MenuView mMenuView;
+
+ /**
+ * Construct a new BaseMenuPresenter.
+ *
+ * @param menuLayoutRes Layout resource ID for the menu container view
+ * @param itemLayoutRes Layout resource ID for a single item view
+ */
+ public BaseMenuPresenter(int menuLayoutRes, int itemLayoutRes) {
+ mMenuLayoutRes = menuLayoutRes;
+ mItemLayoutRes = itemLayoutRes;
+ }
+
+ @Override
+ public void initForMenu(Context context, MenuBuilder menu) {
+ mContext = context;
+ mInflater = LayoutInflater.from(mContext);
+ mMenu = menu;
+ }
+
+ @Override
+ public MenuView getMenuView(ViewGroup root) {
+ if (mMenuView == null) {
+ mMenuView = (MenuView) mInflater.inflate(mMenuLayoutRes, root, false);
+ mMenuView.initialize(mMenu);
+ updateMenuView(true);
+ }
+
+ return mMenuView;
+ }
+
+ /**
+ * Reuses item views when it can
+ */
+ public void updateMenuView(boolean cleared) {
+ mMenu.flagActionItems();
+ ArrayList<MenuItemImpl> visibleItems = mMenu.getVisibleItems();
+ final int itemCount = visibleItems.size();
+ final ViewGroup parent = (ViewGroup) mMenuView;
+ int childIndex = 0;
+ for (int i = 0; i < itemCount; i++) {
+ MenuItemImpl item = visibleItems.get(i);
+ if (shouldIncludeItem(childIndex, item)) {
+ final View convertView = parent.getChildAt(childIndex);
+ final View itemView = getItemView(item, convertView, parent);
+ if (itemView != convertView) {
+ addItemView(itemView, childIndex);
+ }
+ childIndex++;
+ }
+ }
+
+ // Remove leftover views.
+ while (childIndex < parent.getChildCount()) {
+ if (!filterLeftoverView(parent, childIndex)) {
+ childIndex++;
+ }
+ }
+ }
+
+ /**
+ * Add an item view at the given index.
+ *
+ * @param itemView View to add
+ * @param childIndex Index within the parent to insert at
+ */
+ protected void addItemView(View itemView, int childIndex) {
+ final ViewGroup currentParent = (ViewGroup) itemView.getParent();
+ if (currentParent != null) {
+ currentParent.removeView(itemView);
+ }
+ ((ViewGroup) mMenuView).addView(itemView, childIndex);
+ }
+
+ /**
+ * Filter the child view at index and remove it if appropriate.
+ * @param parent Parent to filter from
+ * @param childIndex Index to filter
+ * @return true if the child view at index was removed
+ */
+ protected boolean filterLeftoverView(ViewGroup parent, int childIndex) {
+ parent.removeViewAt(childIndex);
+ return true;
+ }
+
+ public void setCallback(Callback cb) {
+ mCallback = cb;
+ }
+
+ /**
+ * Create a new item view that can be re-bound to other item data later.
+ *
+ * @return The new item view
+ */
+ public MenuView.ItemView createItemView(ViewGroup parent) {
+ return (MenuView.ItemView) mInflater.inflate(mItemLayoutRes, parent, false);
+ }
+
+ /**
+ * Prepare an item view for use. See AdapterView for the basic idea at work here.
+ * This may require creating a new item view, but well-behaved implementations will
+ * re-use the view passed as convertView if present. The returned view will be populated
+ * with data from the item parameter.
+ *
+ * @param item Item to present
+ * @param convertView Existing view to reuse
+ * @param parent Intended parent view - use for inflation.
+ * @return View that presents the requested menu item
+ */
+ public View getItemView(MenuItemImpl item, View convertView, ViewGroup parent) {
+ MenuView.ItemView itemView;
+ if (convertView instanceof MenuView.ItemView) {
+ itemView = (MenuView.ItemView) convertView;
+ } else {
+ itemView = createItemView(parent);
+ }
+ bindItemView(item, itemView);
+ return (View) itemView;
+ }
+
+ /**
+ * Bind item data to an existing item view.
+ *
+ * @param item Item to bind
+ * @param itemView View to populate with item data
+ */
+ public abstract void bindItemView(MenuItemImpl item, MenuView.ItemView itemView);
+
+ /**
+ * Filter item by child index and item data.
+ *
+ * @param childIndex Indended presentation index of this item
+ * @param item Item to present
+ * @return true if this item should be included in this menu presentation; false otherwise
+ */
+ public boolean shouldIncludeItem(int childIndex, MenuItemImpl item) {
+ return true;
+ }
+
+ public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
+ if (mCallback != null) {
+ mCallback.onCloseMenu(menu, allMenusAreClosing);
+ }
+ }
+
+ public boolean onSubMenuSelected(SubMenuBuilder menu) {
+ if (mCallback != null) {
+ return mCallback.onOpenSubMenu(menu);
+ }
+ return false;
+ }
+
+ public boolean flagActionItems() {
+ return false;
+ }
+}
diff --git a/core/java/com/android/internal/view/menu/ExpandedMenuView.java b/core/java/com/android/internal/view/menu/ExpandedMenuView.java
index 9e4b4ce..723ece4 100644
--- a/core/java/com/android/internal/view/menu/ExpandedMenuView.java
+++ b/core/java/com/android/internal/view/menu/ExpandedMenuView.java
@@ -17,17 +17,15 @@
package com.android.internal.view.menu;
+import com.android.internal.view.menu.MenuBuilder.ItemInvoker;
+
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.View;
import android.widget.AdapterView;
-import android.widget.BaseAdapter;
-import android.widget.ListAdapter;
-import android.widget.ListView;
import android.widget.AdapterView.OnItemClickListener;
-
-import com.android.internal.view.menu.MenuBuilder.ItemInvoker;
+import android.widget.ListView;
/**
* The expanded menu view is a list-like menu with all of the available menu items. It is opened
@@ -53,23 +51,8 @@ public final class ExpandedMenuView extends ListView implements ItemInvoker, Men
setOnItemClickListener(this);
}
- public void initialize(MenuBuilder menu, int menuType) {
+ public void initialize(MenuBuilder menu) {
mMenu = menu;
-
- setAdapter(menu.new MenuAdapter(menuType));
- }
-
- public void updateChildren(boolean cleared) {
- ListAdapter adapter = getAdapter();
- // Tell adapter of the change, it will notify the mListView
- if (adapter != null) {
- if (cleared) {
- ((BaseAdapter)adapter).notifyDataSetInvalidated();
- }
- else {
- ((BaseAdapter)adapter).notifyDataSetChanged();
- }
- }
}
@Override
diff --git a/core/java/com/android/internal/view/menu/IconMenuItemView.java b/core/java/com/android/internal/view/menu/IconMenuItemView.java
index 3c5b422..afa8a01 100644
--- a/core/java/com/android/internal/view/menu/IconMenuItemView.java
+++ b/core/java/com/android/internal/view/menu/IconMenuItemView.java
@@ -112,6 +112,10 @@ public final class IconMenuItemView extends TextView implements MenuView.ItemVie
setEnabled(itemData.isEnabled());
}
+ public void setItemData(MenuItemImpl data) {
+ mItemData = data;
+ }
+
@Override
public boolean performClick() {
// Let the view's click listener have top priority (the More button relies on this)
diff --git a/core/java/com/android/internal/view/menu/IconMenuPresenter.java b/core/java/com/android/internal/view/menu/IconMenuPresenter.java
new file mode 100644
index 0000000..f717904
--- /dev/null
+++ b/core/java/com/android/internal/view/menu/IconMenuPresenter.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2011 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.view.menu;
+
+import com.android.internal.view.menu.MenuView.ItemView;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.util.SparseArray;
+import android.view.ContextThemeWrapper;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.util.ArrayList;
+
+/**
+ * MenuPresenter for the classic "six-pack" icon menu.
+ */
+public class IconMenuPresenter extends BaseMenuPresenter {
+ private IconMenuItemView mMoreView;
+ private int mMaxItems = -1;
+
+ private static final String VIEWS_TAG = "android:menu:icon";
+
+ public IconMenuPresenter() {
+ super(com.android.internal.R.layout.icon_menu_layout,
+ com.android.internal.R.layout.icon_menu_item_layout);
+ }
+
+ @Override
+ public void initForMenu(Context context, MenuBuilder menu) {
+ mContext = new ContextThemeWrapper(context, com.android.internal.R.style.Theme_IconMenu);
+ mInflater = LayoutInflater.from(mContext);
+ mMenu = menu;
+ mMaxItems = -1;
+ }
+
+ @Override
+ public void bindItemView(MenuItemImpl item, ItemView itemView) {
+ final IconMenuItemView view = (IconMenuItemView) itemView;
+ view.setItemData(item);
+
+ view.initialize(item.getTitleForItemView(view), item.getIcon());
+
+ view.setVisibility(item.isVisible() ? View.VISIBLE : View.GONE);
+ view.setEnabled(view.isEnabled());
+ view.setLayoutParams(view.getTextAppropriateLayoutParams());
+ }
+
+ @Override
+ public boolean shouldIncludeItem(int childIndex, MenuItemImpl item) {
+ final ArrayList<MenuItemImpl> itemsToShow = mMenu.getNonActionItems();
+ boolean fits = (itemsToShow.size() == mMaxItems && childIndex < mMaxItems) ||
+ childIndex < mMaxItems - 1;
+ return fits && !item.isActionButton();
+ }
+
+ @Override
+ protected void addItemView(View itemView, int childIndex) {
+ final IconMenuItemView v = (IconMenuItemView) itemView;
+ final IconMenuView parent = (IconMenuView) mMenuView;
+
+ v.setIconMenuView(parent);
+ v.setItemInvoker(parent);
+ v.setBackgroundDrawable(parent.getItemBackgroundDrawable());
+ super.addItemView(itemView, childIndex);
+ }
+
+ @Override
+ public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
+ if (!subMenu.hasVisibleItems()) return false;
+
+ // The window manager will give us a token.
+ new MenuDialogHelper(subMenu).show(null);
+ super.onSubMenuSelected(subMenu);
+ return true;
+ }
+
+ @Override
+ public void updateMenuView(boolean cleared) {
+ final IconMenuView menuView = (IconMenuView) mMenuView;
+ if (mMaxItems < 0) mMaxItems = menuView.getMaxItems();
+ final ArrayList<MenuItemImpl> itemsToShow = mMenu.getNonActionItems();
+ final boolean needsMore = itemsToShow.size() > mMaxItems;
+ super.updateMenuView(cleared);
+
+ if (needsMore && (mMoreView == null || mMoreView.getParent() != menuView)) {
+ if (mMoreView == null) {
+ mMoreView = menuView.createMoreItemView();
+ mMoreView.setBackgroundDrawable(menuView.getItemBackgroundDrawable());
+ }
+ menuView.addView(mMoreView);
+ } else if (!needsMore && mMoreView != null) {
+ menuView.removeView(mMoreView);
+ }
+
+ menuView.setNumActualItemsShown(needsMore ? mMaxItems - 1 : itemsToShow.size());
+ }
+
+ @Override
+ protected boolean filterLeftoverView(ViewGroup parent, int childIndex) {
+ if (parent.getChildAt(childIndex) != mMoreView) {
+ return super.filterLeftoverView(parent, childIndex);
+ }
+ return false;
+ }
+
+ public int getNumActualItemsShown() {
+ return ((IconMenuView) mMenuView).getNumActualItemsShown();
+ }
+
+ public void saveHierarchyState(Bundle outState) {
+ SparseArray<Parcelable> viewStates = new SparseArray<Parcelable>();
+ if (mMenuView != null) {
+ ((View) mMenuView).saveHierarchyState(viewStates);
+ }
+ outState.putSparseParcelableArray(VIEWS_TAG, viewStates);
+ }
+
+ public void restoreHierarchyState(Bundle inState) {
+ SparseArray<Parcelable> viewStates = inState.getSparseParcelableArray(VIEWS_TAG);
+ if (viewStates != null) {
+ ((View) mMenuView).restoreHierarchyState(viewStates);
+ }
+ }
+}
diff --git a/core/java/com/android/internal/view/menu/IconMenuView.java b/core/java/com/android/internal/view/menu/IconMenuView.java
index d18c9727..dab43eb 100644
--- a/core/java/com/android/internal/view/menu/IconMenuView.java
+++ b/core/java/com/android/internal/view/menu/IconMenuView.java
@@ -80,10 +80,7 @@ public final class IconMenuView extends ViewGroup implements ItemInvoker, MenuVi
/** Icon for the 'More' button */
private Drawable mMoreIcon;
-
- /** Item view for the 'More' button */
- private IconMenuItemView mMoreItemView;
-
+
/** Background of each item (should contain the selected and focused states) */
private Drawable mItemBackground;
@@ -172,6 +169,10 @@ public final class IconMenuView extends ViewGroup implements ItemInvoker, MenuVi
setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
}
+ int getMaxItems() {
+ return mMaxItems;
+ }
+
/**
* Figures out the layout for the menu items.
*
@@ -277,23 +278,8 @@ public final class IconMenuView extends ViewGroup implements ItemInvoker, MenuVi
return true;
}
- /**
- * Adds an IconMenuItemView to this icon menu view.
- * @param itemView The item's view to add
- */
- private void addItemView(IconMenuItemView itemView) {
- // Set ourselves on the item view
- itemView.setIconMenuView(this);
-
- // Apply the background to the item view
- itemView.setBackgroundDrawable(
- mItemBackground.getConstantState().newDrawable(
- getContext().getResources()));
-
- // This class is the invoker for all its item views
- itemView.setItemInvoker(this);
-
- addView(itemView, itemView.getTextAppropriateLayoutParams());
+ Drawable getItemBackgroundDrawable() {
+ return mItemBackground.getConstantState().newDrawable(getContext().getResources());
}
/**
@@ -302,25 +288,23 @@ public final class IconMenuView extends ViewGroup implements ItemInvoker, MenuVi
* have a MenuItemData backing it.
* @return The IconMenuItemView for the 'More' button
*/
- private IconMenuItemView createMoreItemView() {
- LayoutInflater inflater = mMenu.getMenuType(MenuBuilder.TYPE_ICON).getInflater();
+ IconMenuItemView createMoreItemView() {
+ Context context = getContext();
+ LayoutInflater inflater = LayoutInflater.from(context);
final IconMenuItemView itemView = (IconMenuItemView) inflater.inflate(
com.android.internal.R.layout.icon_menu_item_layout, null);
- Resources r = getContext().getResources();
+ Resources r = context.getResources();
itemView.initialize(r.getText(com.android.internal.R.string.more_item_label), mMoreIcon);
// Set up a click listener on the view since there will be no invocation sequence
// due to the lack of a MenuItemData this view
itemView.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
- // Switches the menu to expanded mode
- MenuBuilder.Callback cb = mMenu.getCallback();
- if (cb != null) {
- // Call callback
- cb.onMenuModeChange(mMenu);
- }
+ // Switches the menu to expanded mode. Requires support from
+ // the menu's active callback.
+ mMenu.changeMenuMode();
}
});
@@ -328,51 +312,8 @@ public final class IconMenuView extends ViewGroup implements ItemInvoker, MenuVi
}
- public void initialize(MenuBuilder menu, int menuType) {
+ public void initialize(MenuBuilder menu) {
mMenu = menu;
- updateChildren(true);
- }
-
- public void updateChildren(boolean cleared) {
- // This method does a clear refresh of children
- removeAllViews();
-
- // IconMenuView never wants content sorted for an overflow action button, since
- // it is never used in the presence of an overflow button.
- final ArrayList<MenuItemImpl> itemsToShow = mMenu.getNonActionItems(false);
- final int numItems = itemsToShow.size();
- final int numItemsThatCanFit = mMaxItems;
- // Minimum of the num that can fit and the num that we have
- final int minFitMinus1AndNumItems = Math.min(numItemsThatCanFit - 1, numItems);
-
- MenuItemImpl itemData;
- // Traverse through all but the last item that can fit since that last item can either
- // be a 'More' button or a sixth item
- for (int i = 0; i < minFitMinus1AndNumItems; i++) {
- itemData = itemsToShow.get(i);
- addItemView((IconMenuItemView) itemData.getItemView(MenuBuilder.TYPE_ICON, this));
- }
-
- if (numItems > numItemsThatCanFit) {
- // If there are more items than we can fit, show the 'More' button to
- // switch to expanded mode
- if (mMoreItemView == null) {
- mMoreItemView = createMoreItemView();
- }
-
- addItemView(mMoreItemView);
-
- // The last view is the more button, so the actual number of items is one less than
- // the number that can fit
- mNumActualItemsShown = numItemsThatCanFit - 1;
- } else if (numItems == numItemsThatCanFit) {
- // There are exactly the number we can show, so show the last item
- final MenuItemImpl lastItemData = itemsToShow.get(numItemsThatCanFit - 1);
- addItemView((IconMenuItemView) lastItemData.getItemView(MenuBuilder.TYPE_ICON, this));
-
- // The items shown fit exactly
- mNumActualItemsShown = numItemsThatCanFit;
- }
}
/**
@@ -463,13 +404,6 @@ public final class IconMenuView extends ViewGroup implements ItemInvoker, MenuVi
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- if (mHasStaleChildren) {
- mHasStaleChildren = false;
-
- // If we have stale data, resync with the menu
- updateChildren(false);
- }
-
int measuredWidth = resolveSize(Integer.MAX_VALUE, widthMeasureSpec);
calculateItemFittingMetadata(measuredWidth);
layoutItems(measuredWidth);
@@ -564,6 +498,9 @@ public final class IconMenuView extends ViewGroup implements ItemInvoker, MenuVi
return mNumActualItemsShown;
}
+ void setNumActualItemsShown(int count) {
+ mNumActualItemsShown = count;
+ }
public int getWindowAnimations() {
return mAnimations;
diff --git a/core/java/com/android/internal/view/menu/ListMenuItemView.java b/core/java/com/android/internal/view/menu/ListMenuItemView.java
index 02584b6..0c3c605 100644
--- a/core/java/com/android/internal/view/menu/ListMenuItemView.java
+++ b/core/java/com/android/internal/view/menu/ListMenuItemView.java
@@ -48,6 +48,8 @@ public class ListMenuItemView extends LinearLayout implements MenuView.ItemView
private int mMenuType;
+ private LayoutInflater mInflater;
+
public ListMenuItemView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs);
@@ -187,7 +189,7 @@ public class ListMenuItemView extends LinearLayout implements MenuView.ItemView
}
public void setIcon(Drawable icon) {
- final boolean showIcon = mItemData.shouldShowIcon(mMenuType);
+ final boolean showIcon = mItemData.shouldShowIcon();
if (!showIcon && !mPreserveIconSpacing) {
return;
}
@@ -212,14 +214,14 @@ public class ListMenuItemView extends LinearLayout implements MenuView.ItemView
}
private void insertIconView() {
- LayoutInflater inflater = mItemData.getLayoutInflater(mMenuType);
+ LayoutInflater inflater = getInflater();
mIconView = (ImageView) inflater.inflate(com.android.internal.R.layout.list_menu_item_icon,
this, false);
addView(mIconView, 0);
}
private void insertRadioButton() {
- LayoutInflater inflater = mItemData.getLayoutInflater(mMenuType);
+ LayoutInflater inflater = getInflater();
mRadioButton =
(RadioButton) inflater.inflate(com.android.internal.R.layout.list_menu_item_radio,
this, false);
@@ -227,7 +229,7 @@ public class ListMenuItemView extends LinearLayout implements MenuView.ItemView
}
private void insertCheckBox() {
- LayoutInflater inflater = mItemData.getLayoutInflater(mMenuType);
+ LayoutInflater inflater = getInflater();
mCheckBox =
(CheckBox) inflater.inflate(com.android.internal.R.layout.list_menu_item_checkbox,
this, false);
@@ -242,4 +244,10 @@ public class ListMenuItemView extends LinearLayout implements MenuView.ItemView
return false;
}
+ private LayoutInflater getInflater() {
+ if (mInflater == null) {
+ mInflater = LayoutInflater.from(mContext);
+ }
+ return mInflater;
+ }
}
diff --git a/core/java/com/android/internal/view/menu/ListMenuPresenter.java b/core/java/com/android/internal/view/menu/ListMenuPresenter.java
new file mode 100644
index 0000000..2cb2a10
--- /dev/null
+++ b/core/java/com/android/internal/view/menu/ListMenuPresenter.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2011 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.view.menu;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.util.SparseArray;
+import android.view.ContextThemeWrapper;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.BaseAdapter;
+import android.widget.ListAdapter;
+
+import java.util.ArrayList;
+
+/**
+ * MenuPresenter for list-style menus.
+ */
+public class ListMenuPresenter implements MenuPresenter, AdapterView.OnItemClickListener {
+ Context mContext;
+ LayoutInflater mInflater;
+ MenuBuilder mMenu;
+
+ ExpandedMenuView mMenuView;
+
+ private int mItemIndexOffset;
+ int mThemeRes;
+ int mItemLayoutRes;
+
+ private Callback mCallback;
+ private MenuAdapter mAdapter;
+
+ public static final String VIEWS_TAG = "android:menu:list";
+
+ /**
+ * Construct a new ListMenuPresenter.
+ * @param context Context to use for theming. This will supersede the context provided
+ * to initForMenu when this presenter is added.
+ * @param itemLayoutRes Layout resource for individual item views.
+ */
+ public ListMenuPresenter(Context context, int itemLayoutRes) {
+ this(itemLayoutRes, 0);
+ mContext = context;
+ }
+
+ /**
+ * Construct a new ListMenuPresenter.
+ * @param itemLayoutRes Layout resource for individual item views.
+ * @param themeRes Resource ID of a theme to use for views.
+ */
+ public ListMenuPresenter(int itemLayoutRes, int themeRes) {
+ mItemLayoutRes = itemLayoutRes;
+ mThemeRes = themeRes;
+ }
+
+ @Override
+ public void initForMenu(Context context, MenuBuilder menu) {
+ if (mThemeRes != 0) {
+ mContext = new ContextThemeWrapper(context, mThemeRes);
+ } else if (mContext == null) {
+ mContext = context;
+ }
+ mInflater = LayoutInflater.from(mContext);
+ mMenu = menu;
+ }
+
+ @Override
+ public MenuView getMenuView(ViewGroup root) {
+ if (mMenuView == null) {
+ mMenuView = (ExpandedMenuView) mInflater.inflate(
+ com.android.internal.R.layout.expanded_menu_layout, root, false);
+ if (mAdapter == null) {
+ mAdapter = new MenuAdapter();
+ }
+ mMenuView.setAdapter(mAdapter);
+ mMenuView.setOnItemClickListener(this);
+ }
+ return mMenuView;
+ }
+
+ /**
+ * Call this instead of getMenuView if you want to manage your own ListView.
+ * For proper operation, the ListView hosting this adapter should add
+ * this presenter as an OnItemClickListener.
+ *
+ * @return A ListAdapter containing the items in the menu.
+ */
+ public ListAdapter getAdapter() {
+ if (mAdapter == null) {
+ mAdapter = new MenuAdapter();
+ }
+ return mAdapter;
+ }
+
+ @Override
+ public void updateMenuView(boolean cleared) {
+ if (mAdapter != null) mAdapter.notifyDataSetChanged();
+ }
+
+ @Override
+ public void setCallback(Callback cb) {
+ mCallback = cb;
+ }
+
+ @Override
+ public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
+ if (!subMenu.hasVisibleItems()) return false;
+
+ // The window manager will give us a token.
+ new MenuDialogHelper(subMenu).show(null);
+ if (mCallback != null) {
+ mCallback.onOpenSubMenu(subMenu);
+ }
+ return true;
+ }
+
+ @Override
+ public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
+ if (mCallback != null) {
+ mCallback.onCloseMenu(menu, allMenusAreClosing);
+ }
+ }
+
+ int getItemIndexOffset() {
+ return mItemIndexOffset;
+ }
+
+ public void setItemIndexOffset(int offset) {
+ mItemIndexOffset = offset;
+ if (mMenuView != null) {
+ updateMenuView(false);
+ }
+ }
+
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ mMenu.performItemAction(mAdapter.getItem(position), 0);
+ }
+
+ @Override
+ public boolean flagActionItems() {
+ return false;
+ }
+
+ public void saveHierarchyState(Bundle outState) {
+ SparseArray<Parcelable> viewStates = new SparseArray<Parcelable>();
+ if (mMenuView != null) {
+ ((View) mMenuView).saveHierarchyState(viewStates);
+ }
+ outState.putSparseParcelableArray(VIEWS_TAG, viewStates);
+ }
+
+ public void restoreHierarchyState(Bundle inState) {
+ SparseArray<Parcelable> viewStates = inState.getSparseParcelableArray(VIEWS_TAG);
+ ((View) mMenuView).restoreHierarchyState(viewStates);
+ }
+
+ private class MenuAdapter extends BaseAdapter {
+ public int getCount() {
+ ArrayList<MenuItemImpl> items = mMenu.getVisibleItems();
+ return items.size() - mItemIndexOffset;
+ }
+
+ public MenuItemImpl getItem(int position) {
+ ArrayList<MenuItemImpl> items = mMenu.getVisibleItems();
+ return items.get(position + mItemIndexOffset);
+ }
+
+ public long getItemId(int position) {
+ // Since a menu item's ID is optional, we'll use the position as an
+ // ID for the item in the AdapterView
+ return position;
+ }
+
+ public View getView(int position, View convertView, ViewGroup parent) {
+ if (convertView == null) {
+ convertView = mInflater.inflate(mItemLayoutRes, parent, false);
+ }
+
+ MenuView.ItemView itemView = (MenuView.ItemView) convertView;
+ itemView.initialize(getItem(position), 0);
+ return convertView;
+ }
+ }
+}
diff --git a/core/java/com/android/internal/view/menu/MenuBuilder.java b/core/java/com/android/internal/view/menu/MenuBuilder.java
index 14d0ac5..e9fcb23 100644
--- a/core/java/com/android/internal/view/menu/MenuBuilder.java
+++ b/core/java/com/android/internal/view/menu/MenuBuilder.java
@@ -25,29 +25,22 @@ import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
-import android.os.Bundle;
import android.os.Parcelable;
+import android.util.Log;
import android.util.SparseArray;
-import android.util.SparseBooleanArray;
-import android.util.TypedValue;
import android.view.ContextMenu.ContextMenuInfo;
-import android.view.ContextThemeWrapper;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
-import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.SubMenu;
import android.view.View;
-import android.view.View.MeasureSpec;
-import android.view.ViewGroup;
-import android.widget.AdapterView;
-import android.widget.BaseAdapter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
+import java.util.Iterator;
import java.util.List;
-import java.util.Vector;
+import java.util.concurrent.CopyOnWriteArrayList;
/**
* Implementation of the {@link android.view.Menu} interface for creating a
@@ -55,60 +48,6 @@ import java.util.Vector;
*/
public class MenuBuilder implements Menu {
private static final String LOGTAG = "MenuBuilder";
-
- /** The number of different menu types */
- public static final int NUM_TYPES = 5;
- /** The menu type that represents the icon menu view */
- public static final int TYPE_ICON = 0;
- /** The menu type that represents the expanded menu view */
- public static final int TYPE_EXPANDED = 1;
- /**
- * The menu type that represents a menu dialog. Examples are context and sub
- * menus. This menu type will not have a corresponding MenuView, but it will
- * have an ItemView.
- */
- public static final int TYPE_DIALOG = 2;
- /**
- * The menu type that represents a button in the application's action bar.
- */
- public static final int TYPE_ACTION_BUTTON = 3;
- /**
- * The menu type that represents a menu popup.
- */
- public static final int TYPE_POPUP = 4;
-
- private static final String VIEWS_TAG = "android:views";
-
- private static final int THEME_SYSTEM_DEFAULT = 0;
- private static final int THEME_APPLICATION = -1;
- private static final int THEME_ALERT_DIALOG = -2;
-
- // Order must be the same order as the TYPE_*
- static final int THEME_RES_FOR_TYPE[] = new int[] {
- com.android.internal.R.style.Theme_IconMenu,
- com.android.internal.R.style.Theme_ExpandedMenu,
- THEME_ALERT_DIALOG,
- THEME_APPLICATION,
- THEME_APPLICATION,
- };
-
- // Order must be the same order as the TYPE_*
- static final int LAYOUT_RES_FOR_TYPE[] = new int[] {
- com.android.internal.R.layout.icon_menu_layout,
- com.android.internal.R.layout.expanded_menu_layout,
- 0,
- com.android.internal.R.layout.action_menu_layout,
- 0,
- };
-
- // Order must be the same order as the TYPE_*
- static final int ITEM_LAYOUT_RES_FOR_TYPE[] = new int[] {
- com.android.internal.R.layout.icon_menu_item_layout,
- com.android.internal.R.layout.list_menu_item_layout,
- com.android.internal.R.layout.list_menu_item_layout,
- com.android.internal.R.layout.action_menu_item_layout,
- com.android.internal.R.layout.popup_menu_item_layout,
- };
private static final int[] sCategoryToOrder = new int[] {
1, /* No category */
@@ -160,14 +99,7 @@ public class MenuBuilder implements Menu {
* Contains items that should NOT appear in the Action Bar, if present.
*/
private ArrayList<MenuItemImpl> mNonActionItems;
- /**
- * The number of visible action buttons permitted in this menu
- */
- private int mMaxActionItems;
- /**
- * The total width limit in pixels for all action items within a menu
- */
- private int mActionWidthLimit;
+
/**
* Whether or not the items (or any one item's action state) has changed since it was
* last fetched.
@@ -175,12 +107,6 @@ public class MenuBuilder implements Menu {
private boolean mIsActionItemsStale;
/**
- * Whether the process of granting space as action items should reserve a space for
- * an overflow option in the action list.
- */
- private boolean mReserveActionOverflow;
-
- /**
* Default value for how added items should show in the action list.
*/
private int mDefaultShowAsAction = MenuItem.SHOW_AS_ACTION_NEVER;
@@ -210,100 +136,19 @@ public class MenuBuilder implements Menu {
* that may individually call onItemsChanged.
*/
private boolean mPreventDispatchingItemsChanged = false;
+ private boolean mItemsChangedWhileDispatchPrevented = false;
private boolean mOptionalIconsVisible = false;
- private ViewGroup mMeasureActionButtonParent;
-
- private final WeakReference<MenuAdapter>[] mAdapterCache =
- new WeakReference[NUM_TYPES];
- private final WeakReference<OverflowMenuAdapter>[] mOverflowAdapterCache =
- new WeakReference[NUM_TYPES];
-
- // Group IDs that have been added as actions - used temporarily, allocated here for reuse.
- private final SparseBooleanArray mActionButtonGroups = new SparseBooleanArray();
-
- private static int getAlertDialogTheme(Context context) {
- TypedValue outValue = new TypedValue();
- context.getTheme().resolveAttribute(com.android.internal.R.attr.alertDialogTheme,
- outValue, true);
- return outValue.resourceId;
- }
-
- private MenuType[] mMenuTypes;
- class MenuType {
- private int mMenuType;
-
- /** The layout inflater that uses the menu type's theme */
- private LayoutInflater mInflater;
+ private boolean mIsClosing = false;
- /** The lazily loaded {@link MenuView} */
- private WeakReference<MenuView> mMenuView;
+ private ArrayList<MenuItemImpl> mTempShortcutItemList = new ArrayList<MenuItemImpl>();
- MenuType(int menuType) {
- mMenuType = menuType;
- }
-
- LayoutInflater getInflater() {
- // Create an inflater that uses the given theme for the Views it inflates
- if (mInflater == null) {
- Context wrappedContext;
- int themeResForType = THEME_RES_FOR_TYPE[mMenuType];
- switch (themeResForType) {
- case THEME_APPLICATION:
- wrappedContext = mContext;
- break;
- case THEME_ALERT_DIALOG:
- wrappedContext = new ContextThemeWrapper(mContext,
- getAlertDialogTheme(mContext));
- break;
- default:
- wrappedContext = new ContextThemeWrapper(mContext, themeResForType);
- break;
- }
- mInflater = (LayoutInflater) wrappedContext
- .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- }
-
- return mInflater;
- }
-
- MenuView getMenuView(ViewGroup parent) {
- if (LAYOUT_RES_FOR_TYPE[mMenuType] == 0) {
- return null;
- }
-
- synchronized (this) {
- MenuView menuView = mMenuView != null ? mMenuView.get() : null;
-
- if (menuView == null) {
- menuView = (MenuView) getInflater().inflate(
- LAYOUT_RES_FOR_TYPE[mMenuType], parent, false);
- menuView.initialize(MenuBuilder.this, mMenuType);
-
- // Cache the view
- mMenuView = new WeakReference<MenuView>(menuView);
-
- if (mFrozenViewStates != null) {
- View view = (View) menuView;
- view.restoreHierarchyState(mFrozenViewStates);
-
- // Clear this menu type's frozen state, since we just restored it
- mFrozenViewStates.remove(view.getId());
- }
- }
-
- return menuView;
- }
- }
-
- boolean hasMenuView() {
- return mMenuView != null && mMenuView.get() != null;
- }
- }
+ private CopyOnWriteArrayList<WeakReference<MenuPresenter>> mPresenters =
+ new CopyOnWriteArrayList<WeakReference<MenuPresenter>>();
/**
- * Called by menu to notify of close and selection changes
+ * Called by menu to notify of close and selection changes.
*/
public interface Callback {
/**
@@ -315,30 +160,6 @@ public class MenuBuilder implements Menu {
public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item);
/**
- * Called when a menu is closed.
- * @param menu The menu that was closed.
- * @param allMenusAreClosing Whether the menus are completely closing (true),
- * or whether there is another menu opening shortly
- * (false). For example, if the menu is closing because a
- * sub menu is about to be shown, <var>allMenusAreClosing</var>
- * is false.
- */
- public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing);
-
- /**
- * Called when a sub menu is selected. This is a cue to open the given sub menu's decor.
- * @param subMenu the sub menu that is being opened
- * @return whether the sub menu selection was handled by the callback
- */
- public boolean onSubMenuSelected(SubMenuBuilder subMenu);
-
- /**
- * Called when a sub menu is closed
- * @param menu the sub menu that was closed
- */
- public void onCloseSubMenu(SubMenuBuilder menu);
-
- /**
* Called when the mode of the menu changes (for example, from icon to expanded).
*
* @param menu the menu that has changed modes
@@ -354,8 +175,6 @@ public class MenuBuilder implements Menu {
}
public MenuBuilder(Context context) {
- mMenuTypes = new MenuType[NUM_TYPES];
-
mContext = context;
mResources = context.getResources();
@@ -375,82 +194,68 @@ public class MenuBuilder implements Menu {
mDefaultShowAsAction = defaultShowAsAction;
return this;
}
-
- public void setCallback(Callback callback) {
- mCallback = callback;
- }
- MenuType getMenuType(int menuType) {
- if (mMenuTypes[menuType] == null) {
- mMenuTypes[menuType] = new MenuType(menuType);
- }
-
- return mMenuTypes[menuType];
+ /**
+ * Add a presenter to this menu. This will only hold a WeakReference;
+ * you do not need to explicitly remove a presenter, but you can using
+ * {@link #removeMenuPresenter(MenuPresenter)}.
+ *
+ * @param presenter The presenter to add
+ */
+ public void addMenuPresenter(MenuPresenter presenter) {
+ mPresenters.add(new WeakReference<MenuPresenter>(presenter));
+ presenter.initForMenu(mContext, this);
+ mIsActionItemsStale = true;
}
-
+
/**
- * Gets a menu View that contains this menu's items.
- *
- * @param menuType The type of menu to get a View for (must be one of
- * {@link #TYPE_ICON}, {@link #TYPE_EXPANDED},
- * {@link #TYPE_DIALOG}).
- * @param parent The ViewGroup that provides a set of LayoutParams values
- * for this menu view
- * @return A View for the menu of type <var>menuType</var>
+ * Remove a presenter from this menu. That presenter will no longer
+ * receive notifications of updates to this menu's data.
+ *
+ * @param presenter The presenter to remove
*/
- public View getMenuView(int menuType, ViewGroup parent) {
- // The expanded menu depends on the number if items shown in the icon menu (which
- // is adjustable as setters/XML attributes on IconMenuView [imagine a larger LCD
- // wanting to show more icons]). If, for example, the activity goes through
- // an orientation change while the expanded menu is open, the icon menu's view
- // won't have an instance anymore; so here we make sure we have an icon menu view (matching
- // the same parent so the layout parameters from the XML are used). This
- // will create the icon menu view and cache it (if it doesn't already exist).
- if (menuType == TYPE_EXPANDED
- && (mMenuTypes[TYPE_ICON] == null || !mMenuTypes[TYPE_ICON].hasMenuView())) {
- getMenuType(TYPE_ICON).getMenuView(parent);
+ public void removeMenuPresenter(MenuPresenter presenter) {
+ for (WeakReference<MenuPresenter> ref : mPresenters) {
+ final MenuPresenter item = ref.get();
+ if (item == null || item == presenter) {
+ mPresenters.remove(ref);
+ }
}
-
- return (View) getMenuType(menuType).getMenuView(parent);
}
- private int getNumIconMenuItemsShown() {
- ViewGroup parent = null;
-
- if (!mMenuTypes[TYPE_ICON].hasMenuView()) {
- /*
- * There isn't an icon menu view instantiated, so when we get it
- * below, it will lazily instantiate it. We should pass a proper
- * parent so it uses the layout_ attributes present in the XML
- * layout file.
- */
- if (mMenuTypes[TYPE_EXPANDED].hasMenuView()) {
- View expandedMenuView = (View) mMenuTypes[TYPE_EXPANDED].getMenuView(null);
- parent = (ViewGroup) expandedMenuView.getParent();
+ private void dispatchPresenterUpdate(boolean cleared) {
+ if (mPresenters.isEmpty()) return;
+
+ stopDispatchingItemsChanged();
+ for (WeakReference<MenuPresenter> ref : mPresenters) {
+ final MenuPresenter presenter = ref.get();
+ if (presenter == null) {
+ mPresenters.remove(ref);
+ } else {
+ presenter.updateMenuView(cleared);
}
}
-
- return ((IconMenuView) getMenuView(TYPE_ICON, parent)).getNumActualItemsShown();
+ startDispatchingItemsChanged();
}
- /**
- * Clears the cached menu views. Call this if the menu views need to another
- * layout (for example, if the screen size has changed).
- */
- public void clearMenuViews() {
- for (int i = NUM_TYPES - 1; i >= 0; i--) {
- if (mMenuTypes[i] != null) {
- mMenuTypes[i].mMenuView = null;
- }
- }
-
- for (int i = mItems.size() - 1; i >= 0; i--) {
- MenuItemImpl item = mItems.get(i);
- if (item.hasSubMenu()) {
- ((SubMenuBuilder) item.getSubMenu()).clearMenuViews();
+ private boolean dispatchSubMenuSelected(SubMenuBuilder subMenu) {
+ if (mPresenters.isEmpty()) return false;
+
+ boolean result = false;
+
+ for (WeakReference<MenuPresenter> ref : mPresenters) {
+ final MenuPresenter presenter = ref.get();
+ if (presenter == null) {
+ mPresenters.remove(ref);
+ } else if (!result) {
+ result = presenter.onSubMenuSelected(subMenu);
}
- item.clearItemViews();
}
+ return result;
+ }
+
+ public void setCallback(Callback cb) {
+ mCallback = cb;
}
/**
@@ -468,7 +273,7 @@ public class MenuBuilder implements Menu {
}
mItems.add(findInsertIndex(mItems, ordering), item);
- onItemsChanged(false);
+ onItemsChanged(true);
return item;
}
@@ -554,7 +359,7 @@ public class MenuBuilder implements Menu {
}
// Notify menu views
- onItemsChanged(false);
+ onItemsChanged(true);
}
}
@@ -573,7 +378,7 @@ public class MenuBuilder implements Menu {
mItems.remove(index);
- if (updateChildrenOnMenuViews) onItemsChanged(false);
+ if (updateChildrenOnMenuViews) onItemsChanged(true);
}
public void removeItemAt(int index) {
@@ -585,6 +390,7 @@ public class MenuBuilder implements Menu {
clear();
clearHeader();
mPreventDispatchingItemsChanged = false;
+ mItemsChangedWhileDispatchPrevented = false;
onItemsChanged(true);
}
@@ -725,19 +531,14 @@ public class MenuBuilder implements Menu {
return mItems.get(index);
}
- public MenuItem getOverflowItem(int index) {
- flagActionItems(true);
- return mNonActionItems.get(index);
- }
-
public boolean isShortcutKey(int keyCode, KeyEvent event) {
return findItemWithShortcutForKey(keyCode, event) != null;
}
public void setQwertyMode(boolean isQwerty) {
mQwertyMode = isQwerty;
-
- refreshShortcuts(isShortcutsVisible(), isQwerty);
+
+ onItemsChanged(false);
}
/**
@@ -751,8 +552,7 @@ public class MenuBuilder implements Menu {
* @return An ordering integer that can be used to order this item across
* all the items (even from other categories).
*/
- private static int getOrdering(int categoryOrder)
- {
+ private static int getOrdering(int categoryOrder) {
final int index = (categoryOrder & CATEGORY_MASK) >> CATEGORY_SHIFT;
if (index < 0 || index >= sCategoryToOrder.length) {
@@ -770,23 +570,6 @@ public class MenuBuilder implements Menu {
}
/**
- * Refreshes the shortcut labels on each of the displayed items. Passes the arguments
- * so submenus don't need to call their parent menu for the same values.
- */
- private void refreshShortcuts(boolean shortcutsVisible, boolean qwertyMode) {
- MenuItemImpl item;
- for (int i = mItems.size() - 1; i >= 0; i--) {
- item = mItems.get(i);
-
- if (item.hasSubMenu()) {
- ((MenuBuilder) item.getSubMenu()).refreshShortcuts(shortcutsVisible, qwertyMode);
- }
-
- item.refreshShortcutOnItemViews(shortcutsVisible, qwertyMode);
- }
- }
-
- /**
* Sets whether the shortcuts should be visible on menus. Devices without hardware
* key input will never make shortcuts visible even if this method is passed 'true'.
*
@@ -798,7 +581,7 @@ public class MenuBuilder implements Menu {
if (mShortcutsVisible == shortcutsVisible) return;
setShortcutsVisibleInner(shortcutsVisible);
- refreshShortcuts(mShortcutsVisible, isQwertyMode());
+ onItemsChanged(false);
}
private void setShortcutsVisibleInner(boolean shortcutsVisible) {
@@ -818,15 +601,24 @@ public class MenuBuilder implements Menu {
Resources getResources() {
return mResources;
}
-
- public Callback getCallback() {
- return mCallback;
- }
public Context getContext() {
return mContext;
}
+ boolean dispatchMenuItemSelected(MenuBuilder menu, MenuItem item) {
+ return mCallback != null && mCallback.onMenuItemSelected(menu, item);
+ }
+
+ /**
+ * Dispatch a mode change event to this menu's callback.
+ */
+ public void changeMenuMode() {
+ if (mCallback != null) {
+ mCallback.onMenuModeChange(this);
+ }
+ }
+
private static int findInsertIndex(ArrayList<MenuItemImpl> items, int ordering) {
for (int i = items.size() - 1; i >= 0; i--) {
MenuItemImpl item = items.get(i);
@@ -860,7 +652,7 @@ public class MenuBuilder implements Menu {
* (the ALT-enabled char corresponds to the shortcut) associated
* with the keyCode.
*/
- List<MenuItemImpl> findItemsWithShortcutForKey(int keyCode, KeyEvent event) {
+ void findItemsWithShortcutForKey(List<MenuItemImpl> items, int keyCode, KeyEvent event) {
final boolean qwerty = isQwertyMode();
final int metaState = event.getMetaState();
final KeyCharacterMap.KeyData possibleChars = new KeyCharacterMap.KeyData();
@@ -868,18 +660,15 @@ public class MenuBuilder implements Menu {
final boolean isKeyCodeMapped = event.getKeyData(possibleChars);
// The delete key is not mapped to '\b' so we treat it specially
if (!isKeyCodeMapped && (keyCode != KeyEvent.KEYCODE_DEL)) {
- return null;
+ return;
}
- Vector<MenuItemImpl> items = new Vector();
// Look for an item whose shortcut is this key.
final int N = mItems.size();
for (int i = 0; i < N; i++) {
MenuItemImpl item = mItems.get(i);
if (item.hasSubMenu()) {
- List<MenuItemImpl> subMenuItems = ((MenuBuilder)item.getSubMenu())
- .findItemsWithShortcutForKey(keyCode, event);
- items.addAll(subMenuItems);
+ ((MenuBuilder)item.getSubMenu()).findItemsWithShortcutForKey(items, keyCode, event);
}
final char shortcutChar = qwerty ? item.getAlphabeticShortcut() : item.getNumericShortcut();
if (((metaState & (KeyEvent.META_SHIFT_ON | KeyEvent.META_SYM_ON)) == 0) &&
@@ -892,7 +681,6 @@ public class MenuBuilder implements Menu {
items.add(item);
}
}
- return items;
}
/*
@@ -908,9 +696,11 @@ public class MenuBuilder implements Menu {
*/
MenuItemImpl findItemWithShortcutForKey(int keyCode, KeyEvent event) {
// Get all items that can be associated directly or indirectly with the keyCode
- List<MenuItemImpl> items = findItemsWithShortcutForKey(keyCode, event);
+ ArrayList<MenuItemImpl> items = mTempShortcutItemList;
+ items.clear();
+ findItemsWithShortcutForKey(items, keyCode, event);
- if (items == null) {
+ if (items.isEmpty()) {
return null;
}
@@ -920,15 +710,18 @@ public class MenuBuilder implements Menu {
event.getKeyData(possibleChars);
// If we have only one element, we can safely returns it
- if (items.size() == 1) {
+ final int size = items.size();
+ if (size == 1) {
return items.get(0);
}
final boolean qwerty = isQwertyMode();
// If we found more than one item associated with the key,
// we have to return the exact match
- for (MenuItemImpl item : items) {
- final char shortcutChar = qwerty ? item.getAlphabeticShortcut() : item.getNumericShortcut();
+ for (int i = 0; i < size; i++) {
+ final MenuItemImpl item = items.get(i);
+ final char shortcutChar = qwerty ? item.getAlphabeticShortcut() :
+ item.getNumericShortcut();
if ((shortcutChar == possibleChars.meta[0] &&
(metaState & KeyEvent.META_ALT_ON) == 0)
|| (shortcutChar == possibleChars.meta[2] &&
@@ -958,11 +751,8 @@ public class MenuBuilder implements Menu {
if (item.hasSubMenu()) {
close(false);
- if (mCallback != null) {
- // Return true if the sub menu was invoked or the item was invoked previously
- invoked = mCallback.onSubMenuSelected((SubMenuBuilder) item.getSubMenu())
- || invoked;
- }
+ invoked |= dispatchSubMenuSelected((SubMenuBuilder) item.getSubMenu());
+ if (!invoked) close(true);
} else {
if ((flags & FLAG_PERFORM_NO_CLOSE) == 0) {
close(true);
@@ -982,10 +772,18 @@ public class MenuBuilder implements Menu {
* is false.
*/
final void close(boolean allMenusAreClosing) {
- Callback callback = getCallback();
- if (callback != null) {
- callback.onCloseMenu(this, allMenusAreClosing);
+ if (mIsClosing) return;
+
+ mIsClosing = true;
+ for (WeakReference<MenuPresenter> ref : mPresenters) {
+ final MenuPresenter presenter = ref.get();
+ if (presenter == null) {
+ mPresenters.remove(ref);
+ } else {
+ presenter.onCloseMenu(this, allMenusAreClosing);
+ }
}
+ mIsClosing = false;
}
/** {@inheritDoc} */
@@ -996,26 +794,40 @@ public class MenuBuilder implements Menu {
/**
* Called when an item is added or removed.
*
- * @param cleared Whether the items were cleared or just changed.
+ * @param structureChanged true if the menu structure changed,
+ * false if only item properties changed.
*/
- private void onItemsChanged(boolean cleared) {
+ void onItemsChanged(boolean structureChanged) {
if (!mPreventDispatchingItemsChanged) {
- if (mIsVisibleItemsStale == false) mIsVisibleItemsStale = true;
- if (mIsActionItemsStale == false) mIsActionItemsStale = true;
-
- MenuType[] menuTypes = mMenuTypes;
- for (int i = 0; i < NUM_TYPES; i++) {
- if ((menuTypes[i] != null) && (menuTypes[i].hasMenuView())) {
- MenuView menuView = menuTypes[i].mMenuView.get();
- menuView.updateChildren(cleared);
- }
+ if (structureChanged) {
+ mIsVisibleItemsStale = true;
+ mIsActionItemsStale = true;
+ }
- MenuAdapter adapter = mAdapterCache[i] == null ? null : mAdapterCache[i].get();
- if (adapter != null) adapter.notifyDataSetChanged();
+ dispatchPresenterUpdate(structureChanged);
+ } else {
+ mItemsChangedWhileDispatchPrevented = true;
+ }
+ }
- adapter = mOverflowAdapterCache[i] == null ? null : mOverflowAdapterCache[i].get();
- if (adapter != null) adapter.notifyDataSetChanged();
- }
+ /**
+ * Stop dispatching item changed events to presenters until
+ * {@link #startDispatchingItemsChanged()} is called. Useful when
+ * many menu operations are going to be performed as a batch.
+ */
+ public void stopDispatchingItemsChanged() {
+ if (!mPreventDispatchingItemsChanged) {
+ mPreventDispatchingItemsChanged = true;
+ mItemsChangedWhileDispatchPrevented = false;
+ }
+ }
+
+ public void startDispatchingItemsChanged() {
+ mPreventDispatchingItemsChanged = false;
+
+ if (mItemsChangedWhileDispatchPrevented) {
+ mItemsChangedWhileDispatchPrevented = false;
+ onItemsChanged(true);
}
}
@@ -1025,6 +837,7 @@ public class MenuBuilder implements Menu {
*/
void onItemVisibleChanged(MenuItemImpl item) {
// Notify of items being changed
+ mIsVisibleItemsStale = true;
onItemsChanged(false);
}
@@ -1034,6 +847,7 @@ public class MenuBuilder implements Menu {
*/
void onItemActionRequestChanged(MenuItemImpl item) {
// Notify of items being changed
+ mIsActionItemsStale = true;
onItemsChanged(false);
}
@@ -1055,17 +869,6 @@ public class MenuBuilder implements Menu {
return mVisibleItems;
}
-
- /**
- * @return A fake action button parent view for obtaining child views.
- */
- private ViewGroup getMeasureActionButtonParent() {
- if (mMeasureActionButtonParent == null) {
- mMeasureActionButtonParent = (ViewGroup) getMenuType(TYPE_ACTION_BUTTON).getInflater()
- .inflate(LAYOUT_RES_FOR_TYPE[TYPE_ACTION_BUTTON], null, false);
- }
- return mMeasureActionButtonParent;
- }
/**
* This method determines which menu items get to be 'action items' that will appear
@@ -1090,147 +893,56 @@ public class MenuBuilder implements Menu {
* <p>The space freed by demoting a full group cannot be consumed by future menu items.
* Once items begin to overflow, all future items become overflow items as well. This is
* to avoid inadvertent reordering that may break the app's intended design.
- *
- * @param reserveActionOverflow true if an overflow button should consume one space
- * in the available item count
*/
- private void flagActionItems(boolean reserveActionOverflow) {
- if (reserveActionOverflow != mReserveActionOverflow) {
- mReserveActionOverflow = reserveActionOverflow;
- mIsActionItemsStale = true;
- }
-
+ public void flagActionItems() {
if (!mIsActionItemsStale) {
return;
}
- final ArrayList<MenuItemImpl> visibleItems = getVisibleItems();
- final int itemsSize = visibleItems.size();
- int maxActions = mMaxActionItems;
- int widthLimit = mActionWidthLimit;
- final int querySpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
- final ViewGroup parent = getMeasureActionButtonParent();
-
- int requiredItems = 0;
- int requestedItems = 0;
- int firstActionWidth = 0;
- boolean hasOverflow = false;
- for (int i = 0; i < itemsSize; i++) {
- MenuItemImpl item = visibleItems.get(i);
- if (item.requiresActionButton()) {
- requiredItems++;
- } else if (item.requestsActionButton()) {
- requestedItems++;
+ // Presenters flag action items as needed.
+ boolean flagged = false;
+ for (WeakReference<MenuPresenter> ref : mPresenters) {
+ final MenuPresenter presenter = ref.get();
+ if (presenter == null) {
+ mPresenters.remove(ref);
} else {
- hasOverflow = true;
+ flagged |= presenter.flagActionItems();
}
}
- // Reserve a spot for the overflow item if needed.
- if (reserveActionOverflow &&
- (hasOverflow || requiredItems + requestedItems > maxActions)) {
- maxActions--;
- }
- maxActions -= requiredItems;
-
- final SparseBooleanArray seenGroups = mActionButtonGroups;
- seenGroups.clear();
-
- // Flag as many more requested items as will fit.
- for (int i = 0; i < itemsSize; i++) {
- MenuItemImpl item = visibleItems.get(i);
-
- if (item.requiresActionButton()) {
- View v = item.getActionView();
- if (v == null) {
- v = item.getItemView(TYPE_ACTION_BUTTON, parent);
- }
- v.measure(querySpec, querySpec);
- final int measuredWidth = v.getMeasuredWidth();
- widthLimit -= measuredWidth;
- if (firstActionWidth == 0) {
- firstActionWidth = measuredWidth;
- }
- final int groupId = item.getGroupId();
- if (groupId != 0) {
- seenGroups.put(groupId, true);
- }
- } else if (item.requestsActionButton()) {
- // Items in a group with other items that already have an action slot
- // can break the max actions rule, but not the width limit.
- final int groupId = item.getGroupId();
- final boolean inGroup = seenGroups.get(groupId);
- boolean isAction = (maxActions > 0 || inGroup) && widthLimit > 0;
- maxActions--;
-
- if (isAction) {
- View v = item.getActionView();
- if (v == null) {
- v = item.getItemView(TYPE_ACTION_BUTTON, parent);
- }
- v.measure(querySpec, querySpec);
- final int measuredWidth = v.getMeasuredWidth();
- widthLimit -= measuredWidth;
- if (firstActionWidth == 0) {
- firstActionWidth = measuredWidth;
- }
-
- // Did this push the entire first item past halfway?
- if (widthLimit + firstActionWidth <= 0) {
- isAction = false;
- }
- }
-
- if (isAction && groupId != 0) {
- seenGroups.put(groupId, true);
- } else if (inGroup) {
- // We broke the width limit. Demote the whole group, they all overflow now.
- seenGroups.put(groupId, false);
- for (int j = 0; j < i; j++) {
- MenuItemImpl areYouMyGroupie = visibleItems.get(j);
- if (areYouMyGroupie.getGroupId() == groupId) {
- areYouMyGroupie.setIsActionButton(false);
- }
- }
+ if (flagged) {
+ mActionItems.clear();
+ mNonActionItems.clear();
+ ArrayList<MenuItemImpl> visibleItems = getVisibleItems();
+ final int itemsSize = visibleItems.size();
+ for (int i = 0; i < itemsSize; i++) {
+ MenuItemImpl item = visibleItems.get(i);
+ if (item.isActionButton()) {
+ mActionItems.add(item);
+ } else {
+ mNonActionItems.add(item);
}
-
- item.setIsActionButton(isAction);
}
+ } else if (mActionItems.size() + mNonActionItems.size() != getVisibleItems().size()) {
+ // Nobody flagged anything, but if something doesn't add up then treat everything
+ // as non-action items.
+ // (This happens during a first pass with no action-item presenters.)
+ mActionItems.clear();
+ mNonActionItems.clear();
+ mNonActionItems.addAll(getVisibleItems());
}
-
- mActionItems.clear();
- mNonActionItems.clear();
- for (int i = 0; i < itemsSize; i++) {
- MenuItemImpl item = visibleItems.get(i);
- if (item.isActionButton()) {
- mActionItems.add(item);
- } else {
- mNonActionItems.add(item);
- }
- }
-
mIsActionItemsStale = false;
}
- ArrayList<MenuItemImpl> getActionItems(boolean reserveActionOverflow) {
- flagActionItems(reserveActionOverflow);
+ ArrayList<MenuItemImpl> getActionItems() {
+ flagActionItems();
return mActionItems;
}
- ArrayList<MenuItemImpl> getNonActionItems(boolean reserveActionOverflow) {
- flagActionItems(reserveActionOverflow);
+ ArrayList<MenuItemImpl> getNonActionItems() {
+ flagActionItems();
return mNonActionItems;
}
-
- void setMaxActionItems(int maxActionItems) {
- mMaxActionItems = maxActionItems;
- mIsActionItemsStale = true;
- }
-
- void setActionWidthLimit(int widthLimit) {
- mActionWidthLimit = widthLimit;
- mIsActionItemsStale = true;
- }
public void clearHeader() {
mHeaderIcon = null;
@@ -1362,38 +1074,6 @@ public class MenuBuilder implements Menu {
mCurrentMenuInfo = menuInfo;
}
- /**
- * Gets an adapter for providing items and their views.
- *
- * @param menuType The type of menu to get an adapter for.
- * @return A {@link MenuAdapter} for this menu with the given menu type.
- */
- public MenuAdapter getMenuAdapter(int menuType) {
- MenuAdapter adapter = mAdapterCache[menuType] == null ?
- null : mAdapterCache[menuType].get();
- if (adapter != null) return adapter;
-
- adapter = new MenuAdapter(menuType);
- mAdapterCache[menuType] = new WeakReference<MenuAdapter>(adapter);
- return adapter;
- }
-
- /**
- * Gets an adapter for providing overflow (non-action) items and their views.
- *
- * @param menuType The type of menu to get an adapter for.
- * @return A {@link MenuAdapter} for this menu with the given menu type.
- */
- public MenuAdapter getOverflowMenuAdapter(int menuType) {
- OverflowMenuAdapter adapter = mOverflowAdapterCache[menuType] == null ?
- null : mOverflowAdapterCache[menuType].get();
- if (adapter != null) return adapter;
-
- adapter = new OverflowMenuAdapter(menuType);
- mOverflowAdapterCache[menuType] = new WeakReference<OverflowMenuAdapter>(adapter);
- return adapter;
- }
-
void setOptionalIconsVisible(boolean visible) {
mOptionalIconsVisible = visible;
}
@@ -1401,109 +1081,4 @@ public class MenuBuilder implements Menu {
boolean getOptionalIconsVisible() {
return mOptionalIconsVisible;
}
-
- public void saveHierarchyState(Bundle outState) {
- SparseArray<Parcelable> viewStates = new SparseArray<Parcelable>();
-
- MenuType[] menuTypes = mMenuTypes;
- for (int i = NUM_TYPES - 1; i >= 0; i--) {
- if (menuTypes[i] == null) {
- continue;
- }
-
- if (menuTypes[i].hasMenuView()) {
- ((View) menuTypes[i].getMenuView(null)).saveHierarchyState(viewStates);
- }
- }
-
- outState.putSparseParcelableArray(VIEWS_TAG, viewStates);
- }
-
- public void restoreHierarchyState(Bundle inState) {
- // Save this for menu views opened later
- SparseArray<Parcelable> viewStates = mFrozenViewStates = inState
- .getSparseParcelableArray(VIEWS_TAG);
-
- // Thaw those menu views already open
- MenuType[] menuTypes = mMenuTypes;
- for (int i = NUM_TYPES - 1; i >= 0; i--) {
- if (menuTypes[i] == null) {
- continue;
- }
-
- if (menuTypes[i].hasMenuView()) {
- ((View) menuTypes[i].getMenuView(null)).restoreHierarchyState(viewStates);
- }
- }
- }
-
- /**
- * An adapter that allows an {@link AdapterView} to use this {@link MenuBuilder} as a data
- * source. This adapter will use only the visible/shown items from the menu.
- */
- public class MenuAdapter extends BaseAdapter {
- private int mMenuType;
-
- public MenuAdapter(int menuType) {
- mMenuType = menuType;
- }
-
- public int getOffset() {
- if (mMenuType == TYPE_EXPANDED) {
- return getNumIconMenuItemsShown();
- } else {
- return 0;
- }
- }
-
- public int getCount() {
- return getVisibleItems().size() - getOffset();
- }
-
- public MenuItemImpl getItem(int position) {
- return getVisibleItems().get(position + getOffset());
- }
-
- public long getItemId(int position) {
- // Since a menu item's ID is optional, we'll use the position as an
- // ID for the item in the AdapterView
- return position;
- }
-
- public View getView(int position, View convertView, ViewGroup parent) {
- if (convertView != null) {
- MenuView.ItemView itemView = (MenuView.ItemView) convertView;
- itemView.getItemData().setItemView(mMenuType, null);
-
- MenuItemImpl item = (MenuItemImpl) getItem(position);
- itemView.initialize(item, mMenuType);
- item.setItemView(mMenuType, itemView);
- return convertView;
- } else {
- MenuItemImpl item = (MenuItemImpl) getItem(position);
- item.setItemView(mMenuType, null);
- return item.getItemView(mMenuType, parent);
- }
- }
- }
-
- /**
- * An adapter that allows an {@link AdapterView} to use this {@link MenuBuilder} as a data
- * source for overflow menu items that do not fit in the list of action items.
- */
- private class OverflowMenuAdapter extends MenuAdapter {
- public OverflowMenuAdapter(int menuType) {
- super(menuType);
- }
-
- @Override
- public MenuItemImpl getItem(int position) {
- return getNonActionItems(true).get(position);
- }
-
- @Override
- public int getCount() {
- return getNonActionItems(true).size();
- }
- }
}
diff --git a/core/java/com/android/internal/view/menu/MenuDialogHelper.java b/core/java/com/android/internal/view/menu/MenuDialogHelper.java
index d7438d6..5c8e057 100644
--- a/core/java/com/android/internal/view/menu/MenuDialogHelper.java
+++ b/core/java/com/android/internal/view/menu/MenuDialogHelper.java
@@ -24,17 +24,20 @@ import android.view.KeyEvent;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
-import android.widget.ListAdapter;
/**
* Helper for menus that appear as Dialogs (context and submenus).
*
* @hide
*/
-public class MenuDialogHelper implements DialogInterface.OnKeyListener, DialogInterface.OnClickListener {
+public class MenuDialogHelper implements DialogInterface.OnKeyListener,
+ DialogInterface.OnClickListener,
+ DialogInterface.OnDismissListener,
+ MenuPresenter.Callback {
private MenuBuilder mMenu;
- private ListAdapter mAdapter;
private AlertDialog mDialog;
+ ListMenuPresenter mPresenter;
+ private MenuPresenter.Callback mPresenterCallback;
public MenuDialogHelper(MenuBuilder menu) {
mMenu = menu;
@@ -49,12 +52,15 @@ public class MenuDialogHelper implements DialogInterface.OnKeyListener, DialogIn
// Many references to mMenu, create local reference
final MenuBuilder menu = mMenu;
- // Get an adapter for the menu item views
- mAdapter = menu.getMenuAdapter(MenuBuilder.TYPE_DIALOG);
-
// Get the builder for the dialog
- final AlertDialog.Builder builder = new AlertDialog.Builder(menu.getContext())
- .setAdapter(mAdapter, this);
+ final AlertDialog.Builder builder = new AlertDialog.Builder(menu.getContext());
+
+ mPresenter = new ListMenuPresenter(builder.getContext(),
+ com.android.internal.R.layout.list_menu_item_layout);
+
+ mPresenter.setCallback(this);
+ mMenu.addMenuPresenter(mPresenter);
+ builder.setAdapter(mPresenter.getAdapter(), this);
// Set the title
final View headerView = menu.getHeaderView();
@@ -68,13 +74,10 @@ public class MenuDialogHelper implements DialogInterface.OnKeyListener, DialogIn
// Set the key listener
builder.setOnKeyListener(this);
-
- // Since this is for a menu, disable the recycling of views
- // This is done by the menu framework anyway
- builder.setRecycleOnMeasureEnabled(false);
// Show the menu
mDialog = builder.create();
+ mDialog.setOnDismissListener(this);
WindowManager.LayoutParams lp = mDialog.getWindow().getAttributes();
lp.type = WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
@@ -122,6 +125,10 @@ public class MenuDialogHelper implements DialogInterface.OnKeyListener, DialogIn
}
+ public void setPresenterCallback(MenuPresenter.Callback cb) {
+ mPresenterCallback = cb;
+ }
+
/**
* Dismisses the menu's dialog.
*
@@ -132,9 +139,31 @@ public class MenuDialogHelper implements DialogInterface.OnKeyListener, DialogIn
mDialog.dismiss();
}
}
-
+
+ @Override
+ public void onDismiss(DialogInterface dialog) {
+ mPresenter.onCloseMenu(mMenu, true);
+ }
+
+ @Override
+ public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
+ if (allMenusAreClosing || menu == mMenu) {
+ dismiss();
+ }
+ if (mPresenterCallback != null) {
+ mPresenterCallback.onCloseMenu(menu, allMenusAreClosing);
+ }
+ }
+
+ @Override
+ public boolean onOpenSubMenu(MenuBuilder subMenu) {
+ if (mPresenterCallback != null) {
+ return mPresenterCallback.onOpenSubMenu(subMenu);
+ }
+ return false;
+ }
+
public void onClick(DialogInterface dialog, int which) {
- mMenu.performItemAction((MenuItemImpl) mAdapter.getItem(which), 0);
+ mMenu.performItemAction((MenuItemImpl) mPresenter.getAdapter().getItem(which), 0);
}
-
}
diff --git a/core/java/com/android/internal/view/menu/MenuItemImpl.java b/core/java/com/android/internal/view/menu/MenuItemImpl.java
index 305115f..c6d386d 100644
--- a/core/java/com/android/internal/view/menu/MenuItemImpl.java
+++ b/core/java/com/android/internal/view/menu/MenuItemImpl.java
@@ -16,21 +16,18 @@
package com.android.internal.view.menu;
-import java.lang.ref.WeakReference;
+import com.android.internal.view.menu.MenuView.ItemView;
import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.util.Log;
+import android.view.ContextMenu.ContextMenuInfo;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.SubMenu;
import android.view.View;
import android.view.ViewDebug;
-import android.view.ViewGroup;
-import android.view.ContextMenu.ContextMenuInfo;
-
-import com.android.internal.view.menu.MenuView.ItemView;
/**
* @hide
@@ -60,9 +57,6 @@ public final class MenuItemImpl implements MenuItem {
* needed).
*/
private int mIconResId = NO_ICON;
-
- /** The (cached) menu item views for this item */
- private WeakReference<ItemView> mItemViews[];
/** The menu to which this item belongs */
private MenuBuilder mMenu;
@@ -128,7 +122,6 @@ public final class MenuItemImpl implements MenuItem {
com.android.internal.R.string.menu_space_shortcut_label);
}
- mItemViews = new WeakReference[MenuBuilder.NUM_TYPES];
mMenu = menu;
mId = id;
mGroup = group;
@@ -149,9 +142,7 @@ public final class MenuItemImpl implements MenuItem {
return true;
}
- MenuBuilder.Callback callback = mMenu.getCallback();
- if (callback != null &&
- callback.onMenuItemSelected(mMenu.getRootMenu(), this)) {
+ if (mMenu.dispatchMenuItemSelected(mMenu.getRootMenu(), this)) {
return true;
}
@@ -172,10 +163,6 @@ public final class MenuItemImpl implements MenuItem {
return false;
}
- private boolean hasItemView(int menuType) {
- return mItemViews[menuType] != null && mItemViews[menuType].get() != null;
- }
-
public boolean isEnabled() {
return (mFlags & ENABLED) != 0;
}
@@ -187,13 +174,7 @@ public final class MenuItemImpl implements MenuItem {
mFlags &= ~ENABLED;
}
- for (int i = MenuBuilder.NUM_TYPES - 1; i >= 0; i--) {
- // If the item view prefers a condensed title, only set this title if there
- // is no condensed title for this item
- if (hasItemView(i)) {
- mItemViews[i].get().setEnabled(enabled);
- }
- }
+ mMenu.onItemsChanged(false);
return this;
}
@@ -242,7 +223,7 @@ public final class MenuItemImpl implements MenuItem {
mShortcutAlphabeticChar = Character.toLowerCase(alphaChar);
- refreshShortcutOnItemViews();
+ mMenu.onItemsChanged(false);
return this;
}
@@ -256,7 +237,7 @@ public final class MenuItemImpl implements MenuItem {
mShortcutNumericChar = numericChar;
- refreshShortcutOnItemViews();
+ mMenu.onItemsChanged(false);
return this;
}
@@ -265,7 +246,7 @@ public final class MenuItemImpl implements MenuItem {
mShortcutNumericChar = numericChar;
mShortcutAlphabeticChar = Character.toLowerCase(alphaChar);
- refreshShortcutOnItemViews();
+ mMenu.onItemsChanged(false);
return this;
}
@@ -322,38 +303,6 @@ public final class MenuItemImpl implements MenuItem {
return mMenu.isShortcutsVisible() && (getShortcut() != 0);
}
- /**
- * Refreshes the shortcut shown on the ItemViews. This method retrieves current
- * shortcut state (mode and shown) from the menu that contains this item.
- */
- private void refreshShortcutOnItemViews() {
- refreshShortcutOnItemViews(mMenu.isShortcutsVisible(), mMenu.isQwertyMode());
- }
-
- /**
- * Refreshes the shortcut shown on the ItemViews. This is usually called by
- * the {@link MenuBuilder} when it is refreshing the shortcuts on all item
- * views, so it passes arguments rather than each item calling a method on the menu to get
- * the same values.
- *
- * @param menuShortcutShown The menu's shortcut shown mode. In addition,
- * this method will ensure this item has a shortcut before it
- * displays the shortcut.
- * @param isQwertyMode Whether the shortcut mode is qwerty mode
- */
- void refreshShortcutOnItemViews(boolean menuShortcutShown, boolean isQwertyMode) {
- final char shortcutKey = (isQwertyMode) ? mShortcutAlphabeticChar : mShortcutNumericChar;
-
- // Show shortcuts if the menu is supposed to show shortcuts AND this item has a shortcut
- final boolean showShortcut = menuShortcutShown && (shortcutKey != 0);
-
- for (int i = MenuBuilder.NUM_TYPES - 1; i >= 0; i--) {
- if (hasItemView(i)) {
- mItemViews[i].get().setShortcut(showShortcut, shortcutKey);
- }
- }
- }
-
public SubMenu getSubMenu() {
return mSubMenu;
}
@@ -394,18 +343,7 @@ public final class MenuItemImpl implements MenuItem {
public MenuItem setTitle(CharSequence title) {
mTitle = title;
- for (int i = MenuBuilder.NUM_TYPES - 1; i >= 0; i--) {
- // If the item view prefers a condensed title, only set this title if there
- // is no condensed title for this item
- if (!hasItemView(i)) {
- continue;
- }
-
- ItemView itemView = mItemViews[i].get();
- if (!itemView.prefersCondensedTitle() || mTitleCondensed == null) {
- itemView.setTitle(title);
- }
- }
+ mMenu.onItemsChanged(false);
if (mSubMenu != null) {
mSubMenu.setHeaderTitle(title);
@@ -430,18 +368,12 @@ public final class MenuItemImpl implements MenuItem {
title = mTitle;
}
- for (int i = MenuBuilder.NUM_TYPES - 1; i >= 0; i--) {
- // Refresh those item views that prefer a condensed title
- if (hasItemView(i) && (mItemViews[i].get().prefersCondensedTitle())) {
- mItemViews[i].get().setTitle(title);
- }
- }
+ mMenu.onItemsChanged(false);
return this;
}
public Drawable getIcon() {
-
if (mIconDrawable != null) {
return mIconDrawable;
}
@@ -456,7 +388,7 @@ public final class MenuItemImpl implements MenuItem {
public MenuItem setIcon(Drawable icon) {
mIconResId = NO_ICON;
mIconDrawable = icon;
- setIconOnViews(icon);
+ mMenu.onItemsChanged(false);
return this;
}
@@ -466,33 +398,10 @@ public final class MenuItemImpl implements MenuItem {
mIconResId = iconResId;
// If we have a view, we need to push the Drawable to them
- if (haveAnyOpenedIconCapableItemViews()) {
- Drawable drawable = iconResId != NO_ICON ? mMenu.getResources().getDrawable(iconResId)
- : null;
- setIconOnViews(drawable);
- }
+ mMenu.onItemsChanged(false);
return this;
}
-
- private void setIconOnViews(Drawable icon) {
- for (int i = MenuBuilder.NUM_TYPES - 1; i >= 0; i--) {
- // Refresh those item views that are able to display an icon
- if (hasItemView(i) && mItemViews[i].get().showsIcon()) {
- mItemViews[i].get().setIcon(icon);
- }
- }
- }
-
- private boolean haveAnyOpenedIconCapableItemViews() {
- for (int i = MenuBuilder.NUM_TYPES - 1; i >= 0; i--) {
- if (hasItemView(i) && mItemViews[i].get().showsIcon()) {
- return true;
- }
- }
-
- return false;
- }
public boolean isCheckable() {
return (mFlags & CHECKABLE) == CHECKABLE;
@@ -502,19 +411,14 @@ public final class MenuItemImpl implements MenuItem {
final int oldFlags = mFlags;
mFlags = (mFlags & ~CHECKABLE) | (checkable ? CHECKABLE : 0);
if (oldFlags != mFlags) {
- for (int i = MenuBuilder.NUM_TYPES - 1; i >= 0; i--) {
- if (hasItemView(i)) {
- mItemViews[i].get().setCheckable(checkable);
- }
- }
+ mMenu.onItemsChanged(false);
}
return this;
}
- public void setExclusiveCheckable(boolean exclusive)
- {
- mFlags = (mFlags&~EXCLUSIVE) | (exclusive ? EXCLUSIVE : 0);
+ public void setExclusiveCheckable(boolean exclusive) {
+ mFlags = (mFlags & ~EXCLUSIVE) | (exclusive ? EXCLUSIVE : 0);
}
public boolean isExclusiveCheckable() {
@@ -541,11 +445,7 @@ public final class MenuItemImpl implements MenuItem {
final int oldFlags = mFlags;
mFlags = (mFlags & ~CHECKED) | (checked ? CHECKED : 0);
if (oldFlags != mFlags) {
- for (int i = MenuBuilder.NUM_TYPES - 1; i >= 0; i--) {
- if (hasItemView(i)) {
- mItemViews[i].get().setChecked(checked);
- }
- }
+ mMenu.onItemsChanged(false);
}
}
@@ -581,39 +481,6 @@ public final class MenuItemImpl implements MenuItem {
mClickListener = clickListener;
return this;
}
-
- View getItemView(int menuType, ViewGroup parent) {
- if (!hasItemView(menuType)) {
- mItemViews[menuType] = new WeakReference<ItemView>(createItemView(menuType, parent));
- }
-
- return (View) mItemViews[menuType].get();
- }
-
- void setItemView(int menuType, ItemView view) {
- mItemViews[menuType] = new WeakReference<ItemView>(view);
- }
-
- /**
- * Create and initializes a menu item view that implements {@link MenuView.ItemView}.
- * @param menuType The type of menu to get a View for (must be one of
- * {@link MenuBuilder#TYPE_ICON}, {@link MenuBuilder#TYPE_EXPANDED},
- * {@link MenuBuilder#TYPE_SUB}, {@link MenuBuilder#TYPE_CONTEXT}).
- * @return The inflated {@link MenuView.ItemView} that is ready for use
- */
- private MenuView.ItemView createItemView(int menuType, ViewGroup parent) {
- // Create the MenuView
- MenuView.ItemView itemView = (MenuView.ItemView) getLayoutInflater(menuType)
- .inflate(MenuBuilder.ITEM_LAYOUT_RES_FOR_TYPE[menuType], parent, false);
- itemView.initialize(this, menuType);
- return itemView;
- }
-
- void clearItemViews() {
- for (int i = mItemViews.length - 1; i >= 0; i--) {
- mItemViews[i] = null;
- }
- }
@Override
public String toString() {
@@ -627,24 +494,12 @@ public final class MenuItemImpl implements MenuItem {
public ContextMenuInfo getMenuInfo() {
return mMenuInfo;
}
-
- /**
- * Returns a LayoutInflater that is themed for the given menu type.
- *
- * @param menuType The type of menu.
- * @return A LayoutInflater.
- */
- public LayoutInflater getLayoutInflater(int menuType) {
- return mMenu.getMenuType(menuType).getInflater();
- }
/**
- * @return Whether the given menu type should show icons for menu items.
+ * @return Whether the menu should show icons for menu items.
*/
- public boolean shouldShowIcon(int menuType) {
- return menuType == MenuBuilder.TYPE_ICON ||
- menuType == MenuBuilder.TYPE_ACTION_BUTTON ||
- mMenu.getOptionalIconsVisible();
+ public boolean shouldShowIcon() {
+ return mMenu.getOptionalIconsVisible();
}
public boolean isActionButton() {
@@ -668,7 +523,9 @@ public final class MenuItemImpl implements MenuItem {
}
public boolean showsTextAsAction() {
- return (mShowAsAction & SHOW_AS_ACTION_WITH_TEXT) == SHOW_AS_ACTION_WITH_TEXT;
+ return (mShowAsAction & SHOW_AS_ACTION_WITH_TEXT) == SHOW_AS_ACTION_WITH_TEXT &&
+ mMenu.getContext().getResources().getBoolean(
+ com.android.internal.R.bool.allow_action_menu_item_text_with_icon);
}
public void setShowAsAction(int actionEnum) {
@@ -696,8 +553,8 @@ public final class MenuItemImpl implements MenuItem {
public MenuItem setActionView(int resId) {
LayoutInflater inflater = LayoutInflater.from(mMenu.getContext());
- ViewGroup parent = (ViewGroup) mMenu.getMenuView(MenuBuilder.TYPE_ACTION_BUTTON, null);
- setActionView(inflater.inflate(resId, parent, false));
+ // TODO - Fix for proper parent. Lazily inflate in the presenter.
+ setActionView(inflater.inflate(resId, null));
return this;
}
diff --git a/core/java/com/android/internal/view/menu/MenuPopupHelper.java b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
index 04a059e..38cec29 100644
--- a/core/java/com/android/internal/view/menu/MenuPopupHelper.java
+++ b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
@@ -16,31 +16,35 @@
package com.android.internal.view.menu;
-import com.android.internal.view.menu.MenuBuilder.MenuAdapter;
-
import android.content.Context;
-import android.os.Handler;
import android.util.DisplayMetrics;
import android.view.KeyEvent;
-import android.view.MenuItem;
+import android.view.LayoutInflater;
import android.view.View;
import android.view.View.MeasureSpec;
+import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.AdapterView;
+import android.widget.BaseAdapter;
+import android.widget.ListAdapter;
import android.widget.ListPopupWindow;
import android.widget.PopupWindow;
-import java.lang.ref.WeakReference;
+import java.util.ArrayList;
/**
+ * Presents a menu as a small, simple popup anchored to another view.
* @hide
*/
public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.OnKeyListener,
ViewTreeObserver.OnGlobalLayoutListener, PopupWindow.OnDismissListener,
- View.OnAttachStateChangeListener {
+ View.OnAttachStateChangeListener, MenuPresenter {
private static final String TAG = "MenuPopupHelper";
+ static final int ITEM_LAYOUT = com.android.internal.R.layout.popup_menu_item_layout;
+
private Context mContext;
+ private LayoutInflater mInflater;
private ListPopupWindow mPopup;
private MenuBuilder mMenu;
private int mPopupMaxWidth;
@@ -48,7 +52,9 @@ public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.On
private boolean mOverflowOnly;
private ViewTreeObserver mTreeObserver;
- private final Handler mHandler = new Handler();
+ private MenuAdapter mAdapter;
+
+ private Callback mPresenterCallback;
public MenuPopupHelper(Context context, MenuBuilder menu) {
this(context, menu, null, false);
@@ -61,6 +67,7 @@ public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.On
public MenuPopupHelper(Context context, MenuBuilder menu,
View anchorView, boolean overflowOnly) {
mContext = context;
+ mInflater = LayoutInflater.from(context);
mMenu = menu;
mOverflowOnly = overflowOnly;
@@ -68,6 +75,8 @@ public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.On
mPopupMaxWidth = metrics.widthPixels / 2;
mAnchorView = anchorView;
+
+ menu.addMenuPresenter(this);
}
public void setAnchorView(View anchor) {
@@ -82,23 +91,14 @@ public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.On
public boolean tryShow() {
mPopup = new ListPopupWindow(mContext, null, com.android.internal.R.attr.popupMenuStyle);
- mPopup.setOnItemClickListener(this);
mPopup.setOnDismissListener(this);
+ mPopup.setOnItemClickListener(this);
- final MenuAdapter adapter = mOverflowOnly ?
- mMenu.getOverflowMenuAdapter(MenuBuilder.TYPE_POPUP) :
- mMenu.getMenuAdapter(MenuBuilder.TYPE_POPUP);
- mPopup.setAdapter(adapter);
+ mAdapter = new MenuAdapter(mMenu);
+ mPopup.setAdapter(mAdapter);
mPopup.setModal(true);
View anchor = mAnchorView;
- if (anchor == null && mMenu instanceof SubMenuBuilder) {
- SubMenuBuilder subMenu = (SubMenuBuilder) mMenu;
- final MenuItemImpl itemImpl = (MenuItemImpl) subMenu.getItem();
- anchor = itemImpl.getItemView(MenuBuilder.TYPE_ACTION_BUTTON, null);
- mAnchorView = anchor;
- }
-
if (anchor != null) {
final boolean addGlobalListener = mTreeObserver == null;
mTreeObserver = anchor.getViewTreeObserver(); // Refresh to latest
@@ -109,7 +109,7 @@ public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.On
return false;
}
- mPopup.setContentWidth(Math.min(measureContentWidth(adapter), mPopupMaxWidth));
+ mPopup.setContentWidth(Math.min(measureContentWidth(mAdapter), mPopupMaxWidth));
mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);
mPopup.show();
mPopup.getListView().setOnKeyListener(this);
@@ -136,23 +136,10 @@ public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.On
return mPopup != null && mPopup.isShowing();
}
+ @Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- if (!isShowing()) return;
-
- MenuItem item = null;
- if (mOverflowOnly) {
- item = mMenu.getOverflowItem(position);
- } else {
- item = mMenu.getVisibleItems().get(position);
- }
- dismiss();
-
- final MenuItem performItem = item;
- mHandler.post(new Runnable() {
- public void run() {
- mMenu.performItemAction(performItem, 0);
- }
- });
+ MenuAdapter adapter = mAdapter;
+ adapter.mAdapterMenu.performItemAction(adapter.getItem(position), 0);
}
public boolean onKey(View v, int keyCode, KeyEvent event) {
@@ -163,7 +150,7 @@ public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.On
return false;
}
- private int measureContentWidth(MenuAdapter adapter) {
+ private int measureContentWidth(ListAdapter adapter) {
// Menus don't tend to be long, so this is more sane than it looks.
int width = 0;
View itemView = null;
@@ -211,4 +198,91 @@ public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.On
}
v.removeOnAttachStateChangeListener(this);
}
+
+ @Override
+ public void initForMenu(Context context, MenuBuilder menu) {
+ // Don't need to do anything; we added as a presenter in the constructor.
+ }
+
+ @Override
+ public MenuView getMenuView(ViewGroup root) {
+ throw new UnsupportedOperationException("MenuPopupHelpers manage their own views");
+ }
+
+ @Override
+ public void updateMenuView(boolean cleared) {
+ if (mAdapter != null) mAdapter.notifyDataSetChanged();
+ }
+
+ @Override
+ public void setCallback(Callback cb) {
+ mPresenterCallback = cb;
+ }
+
+ @Override
+ public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
+ if (subMenu.hasVisibleItems()) {
+ MenuPopupHelper subPopup = new MenuPopupHelper(mContext, subMenu, mAnchorView, false);
+ subPopup.setCallback(mPresenterCallback);
+ if (subPopup.tryShow()) {
+ if (mPresenterCallback != null) {
+ mPresenterCallback.onOpenSubMenu(subMenu);
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
+ // Only care about the (sub)menu we're presenting.
+ if (menu != mMenu) return;
+
+ dismiss();
+ if (mPresenterCallback != null) {
+ mPresenterCallback.onCloseMenu(menu, allMenusAreClosing);
+ }
+ }
+
+ @Override
+ public boolean flagActionItems() {
+ return false;
+ }
+
+ private class MenuAdapter extends BaseAdapter {
+ private MenuBuilder mAdapterMenu;
+
+ public MenuAdapter(MenuBuilder menu) {
+ mAdapterMenu = menu;
+ }
+
+ public int getCount() {
+ ArrayList<MenuItemImpl> items = mOverflowOnly ?
+ mAdapterMenu.getNonActionItems() : mAdapterMenu.getVisibleItems();
+ return items.size();
+ }
+
+ public MenuItemImpl getItem(int position) {
+ ArrayList<MenuItemImpl> items = mOverflowOnly ?
+ mAdapterMenu.getNonActionItems() : mAdapterMenu.getVisibleItems();
+ return items.get(position);
+ }
+
+ public long getItemId(int position) {
+ // Since a menu item's ID is optional, we'll use the position as an
+ // ID for the item in the AdapterView
+ return position;
+ }
+
+ public View getView(int position, View convertView, ViewGroup parent) {
+ if (convertView == null) {
+ convertView = mInflater.inflate(ITEM_LAYOUT, parent, false);
+ }
+
+ MenuView.ItemView itemView = (MenuView.ItemView) convertView;
+ itemView.initialize(getItem(position), 0);
+ return convertView;
+ }
+ }
}
diff --git a/core/java/com/android/internal/view/menu/MenuPresenter.java b/core/java/com/android/internal/view/menu/MenuPresenter.java
new file mode 100644
index 0000000..5baf419
--- /dev/null
+++ b/core/java/com/android/internal/view/menu/MenuPresenter.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2011 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.view.menu;
+
+import android.content.Context;
+import android.view.Menu;
+import android.view.ViewGroup;
+
+/**
+ * A MenuPresenter is responsible for building views for a Menu object.
+ * It takes over some responsibility from the old style monolithic MenuBuilder class.
+ */
+public interface MenuPresenter {
+ /**
+ * Called by menu implementation to notify another component of open/close events.
+ */
+ public interface Callback {
+ /**
+ * Called when a menu is closing.
+ * @param menu
+ * @param allMenusAreClosing
+ */
+ public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing);
+
+ /**
+ * Called when a submenu opens. Useful for notifying the application
+ * of menu state so that it does not attempt to hide the action bar
+ * while a submenu is open or similar.
+ *
+ * @param subMenu Submenu currently being opened
+ * @return true if the Callback will handle presenting the submenu, false if
+ * the presenter should attempt to do so.
+ */
+ public boolean onOpenSubMenu(MenuBuilder subMenu);
+ }
+
+ /**
+ * Initialize this presenter for the given context and menu.
+ * This method is called by MenuBuilder when a presenter is
+ * added. See {@link MenuBuilder#addMenuPresenter(MenuPresenter)}
+ *
+ * @param context Context for this presenter; used for view creation and resource management
+ * @param menu Menu to host
+ */
+ public void initForMenu(Context context, MenuBuilder menu);
+
+ /**
+ * Retrieve a MenuView to display the menu specified in
+ * {@link #initForMenu(Context, Menu)}.
+ *
+ * @param root Intended parent of the MenuView.
+ * @return A freshly created MenuView.
+ */
+ public MenuView getMenuView(ViewGroup root);
+
+ /**
+ * Update the menu UI in response to a change. Called by
+ * MenuBuilder during the normal course of operation.
+ *
+ * @param cleared true if the menu was entirely cleared
+ */
+ public void updateMenuView(boolean cleared);
+
+ /**
+ * Set a callback object that will be notified of menu events
+ * related to this specific presentation.
+ * @param cb Callback that will be notified of future events
+ */
+ public void setCallback(Callback cb);
+
+ /**
+ * Called by Menu implementations to indicate that a submenu item
+ * has been selected. An active Callback should be notified, and
+ * if applicable the presenter should present the submenu.
+ *
+ * @param subMenu SubMenu being opened
+ * @return true if the the event was handled, false otherwise.
+ */
+ public boolean onSubMenuSelected(SubMenuBuilder subMenu);
+
+ /**
+ * Called by Menu implementations to indicate that a menu or submenu is
+ * closing. Presenter implementations should close the representation
+ * of the menu indicated as necessary and notify a registered callback.
+ *
+ * @param menu Menu or submenu that is closing.
+ * @param allMenusAreClosing True if all associated menus are closing.
+ */
+ public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing);
+
+ /**
+ * Called by Menu implementations to flag items that will be shown as actions.
+ * @return true if this presenter changed the action status of any items.
+ */
+ public boolean flagActionItems();
+}
diff --git a/core/java/com/android/internal/view/menu/MenuView.java b/core/java/com/android/internal/view/menu/MenuView.java
index 5090400..407caae 100644
--- a/core/java/com/android/internal/view/menu/MenuView.java
+++ b/core/java/com/android/internal/view/menu/MenuView.java
@@ -22,7 +22,7 @@ import com.android.internal.view.menu.MenuItemImpl;
import android.graphics.drawable.Drawable;
/**
- * Minimal interface for a menu view. {@link #initialize(MenuBuilder, int)} must be called for the
+ * Minimal interface for a menu view. {@link #initialize(MenuBuilder)} must be called for the
* menu to be functional.
*
* @hide
@@ -33,18 +33,8 @@ public interface MenuView {
* view is inflated.
*
* @param menu The menu that this MenuView should display.
- * @param menuType The type of this menu, one of
- * {@link MenuBuilder#TYPE_ICON}, {@link MenuBuilder#TYPE_EXPANDED},
- * {@link MenuBuilder#TYPE_DIALOG}).
*/
- public void initialize(MenuBuilder menu, int menuType);
-
- /**
- * Forces the menu view to update its view to reflect the new state of the menu.
- *
- * @param cleared Whether the menu was cleared or just modified.
- */
- public void updateChildren(boolean cleared);
+ public void initialize(MenuBuilder menu);
/**
* Returns the default animations to be used for this menu when entering/exiting.
diff --git a/core/java/com/android/internal/view/menu/SubMenuBuilder.java b/core/java/com/android/internal/view/menu/SubMenuBuilder.java
index af1b996..834041f 100644
--- a/core/java/com/android/internal/view/menu/SubMenuBuilder.java
+++ b/core/java/com/android/internal/view/menu/SubMenuBuilder.java
@@ -67,11 +67,6 @@ public class SubMenuBuilder extends MenuBuilder implements SubMenu {
}
@Override
- public Callback getCallback() {
- return mParentMenu.getCallback();
- }
-
- @Override
public void setCallback(Callback callback) {
mParentMenu.setCallback(callback);
}
@@ -81,6 +76,12 @@ public class SubMenuBuilder extends MenuBuilder implements SubMenu {
return mParentMenu;
}
+ @Override
+ boolean dispatchMenuItemSelected(MenuBuilder menu, MenuItem item) {
+ return super.dispatchMenuItemSelected(menu, item) ||
+ mParentMenu.dispatchMenuItemSelected(menu, item);
+ }
+
public SubMenu setIcon(Drawable icon) {
mItem.setIcon(icon);
return this;
@@ -110,5 +111,4 @@ public class SubMenuBuilder extends MenuBuilder implements SubMenu {
public SubMenu setHeaderView(View view) {
return (SubMenu) super.setHeaderViewInt(view);
}
-
}
diff --git a/core/java/com/android/internal/widget/AbsActionBarView.java b/core/java/com/android/internal/widget/AbsActionBarView.java
new file mode 100644
index 0000000..788883b
--- /dev/null
+++ b/core/java/com/android/internal/widget/AbsActionBarView.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2011 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.widget;
+
+import com.android.internal.view.menu.ActionMenuPresenter;
+import com.android.internal.view.menu.ActionMenuView;
+
+import android.animation.Animator;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.TimeInterpolator;
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.DecelerateInterpolator;
+
+public abstract class AbsActionBarView extends ViewGroup {
+ protected ActionMenuView mMenuView;
+ protected ActionMenuPresenter mMenuPresenter;
+ protected ActionBarContainer mSplitView;
+
+ protected Animator mVisibilityAnim;
+ protected final VisibilityAnimListener mVisAnimListener = new VisibilityAnimListener();
+
+ private static final TimeInterpolator sAlphaInterpolator = new DecelerateInterpolator();
+
+ private static final int FADE_DURATION = 200;
+
+ public AbsActionBarView(Context context) {
+ super(context);
+ }
+
+ public AbsActionBarView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public AbsActionBarView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ public void setSplitView(ActionBarContainer splitView) {
+ mSplitView = splitView;
+ }
+
+ public void animateToVisibility(int visibility) {
+ if (mVisibilityAnim != null) {
+ mVisibilityAnim.cancel();
+ }
+ if (visibility == VISIBLE) {
+ if (getVisibility() != VISIBLE) {
+ setAlpha(0);
+ if (mSplitView != null && mMenuView != null) {
+ mMenuView.setAlpha(0);
+ }
+ }
+ ObjectAnimator anim = ObjectAnimator.ofFloat(this, "alpha", 1);
+ anim.setDuration(FADE_DURATION);
+ anim.setInterpolator(sAlphaInterpolator);
+ if (mSplitView != null && mMenuView != null) {
+ AnimatorSet set = new AnimatorSet();
+ ObjectAnimator splitAnim = ObjectAnimator.ofFloat(mMenuView, "alpha", 1);
+ splitAnim.setDuration(FADE_DURATION);
+ set.addListener(mVisAnimListener.withFinalVisibility(visibility));
+ set.play(anim).with(splitAnim);
+ set.start();
+ } else {
+ anim.addListener(mVisAnimListener.withFinalVisibility(visibility));
+ anim.start();
+ }
+ } else {
+ ObjectAnimator anim = ObjectAnimator.ofFloat(this, "alpha", 0);
+ anim.setDuration(FADE_DURATION);
+ anim.setInterpolator(sAlphaInterpolator);
+ if (mSplitView != null && mMenuView != null) {
+ AnimatorSet set = new AnimatorSet();
+ ObjectAnimator splitAnim = ObjectAnimator.ofFloat(mMenuView, "alpha", 0);
+ splitAnim.setDuration(FADE_DURATION);
+ set.addListener(mVisAnimListener.withFinalVisibility(visibility));
+ set.play(anim).with(splitAnim);
+ set.start();
+ } else {
+ anim.addListener(mVisAnimListener.withFinalVisibility(visibility));
+ anim.start();
+ }
+ }
+ }
+
+ @Override
+ public void setVisibility(int visibility) {
+ if (mVisibilityAnim != null) {
+ mVisibilityAnim.end();
+ }
+ super.setVisibility(visibility);
+ }
+
+ public boolean showOverflowMenu() {
+ if (mMenuPresenter != null) {
+ return mMenuPresenter.showOverflowMenu();
+ }
+ return false;
+ }
+
+ public void postShowOverflowMenu() {
+ post(new Runnable() {
+ public void run() {
+ showOverflowMenu();
+ }
+ });
+ }
+
+ public boolean hideOverflowMenu() {
+ if (mMenuPresenter != null) {
+ return mMenuPresenter.hideOverflowMenu();
+ }
+ return false;
+ }
+
+ public boolean isOverflowMenuShowing() {
+ if (mMenuPresenter != null) {
+ return mMenuPresenter.isOverflowMenuShowing();
+ }
+ return false;
+ }
+
+ public boolean isOverflowReserved() {
+ return mMenuPresenter != null && mMenuPresenter.isOverflowReserved();
+ }
+
+ public void dismissPopupMenus() {
+ if (mMenuPresenter != null) {
+ mMenuPresenter.dismissPopupMenus();
+ }
+ }
+
+ protected int measureChildView(View child, int availableWidth, int childSpecHeight,
+ int spacing) {
+ child.measure(MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST),
+ childSpecHeight);
+
+ availableWidth -= child.getMeasuredWidth();
+ availableWidth -= spacing;
+
+ return Math.max(0, availableWidth);
+ }
+
+ protected int positionChild(View child, int x, int y, int contentHeight) {
+ int childWidth = child.getMeasuredWidth();
+ int childHeight = child.getMeasuredHeight();
+ int childTop = y + (contentHeight - childHeight) / 2;
+
+ child.layout(x, childTop, x + childWidth, childTop + childHeight);
+
+ return childWidth;
+ }
+
+ protected int positionChildInverse(View child, int x, int y, int contentHeight) {
+ int childWidth = child.getMeasuredWidth();
+ int childHeight = child.getMeasuredHeight();
+ int childTop = y + (contentHeight - childHeight) / 2;
+
+ child.layout(x - childWidth, childTop, x, childTop + childHeight);
+
+ return childWidth;
+ }
+
+ protected class VisibilityAnimListener implements Animator.AnimatorListener {
+ private boolean mCanceled = false;
+ private int mFinalVisibility;
+
+ public VisibilityAnimListener withFinalVisibility(int visibility) {
+ mFinalVisibility = visibility;
+ return this;
+ }
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+ setVisibility(VISIBLE);
+ mVisibilityAnim = animation;
+ mCanceled = false;
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (mCanceled) return;
+
+ mVisibilityAnim = null;
+ setVisibility(mFinalVisibility);
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ mCanceled = true;
+ }
+
+ @Override
+ public void onAnimationRepeat(Animator animation) {
+ }
+ }
+}
diff --git a/core/java/com/android/internal/widget/ActionBarContainer.java b/core/java/com/android/internal/widget/ActionBarContainer.java
index c9b0ec9..c18565d 100644
--- a/core/java/com/android/internal/widget/ActionBarContainer.java
+++ b/core/java/com/android/internal/widget/ActionBarContainer.java
@@ -19,7 +19,9 @@ package com.android.internal.widget;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
+import android.view.ActionMode;
import android.view.MotionEvent;
+import android.view.View;
import android.widget.FrameLayout;
/**
@@ -29,6 +31,7 @@ import android.widget.FrameLayout;
*/
public class ActionBarContainer extends FrameLayout {
private boolean mIsTransitioning;
+ private View mTabContainer;
public ActionBarContainer(Context context) {
this(context, null);
@@ -65,6 +68,50 @@ public class ActionBarContainer extends FrameLayout {
@Override
public boolean onTouchEvent(MotionEvent ev) {
super.onTouchEvent(ev);
+
+ // An action bar always eats touch events.
return true;
}
+
+ public void setTabContainer(View tabView) {
+ if (mTabContainer != null) {
+ removeView(mTabContainer);
+ }
+ mTabContainer = tabView;
+ addView(tabView);
+ }
+
+ public View getTabContainer() {
+ return mTabContainer;
+ }
+
+ @Override
+ public ActionMode startActionModeForChild(View child, ActionMode.Callback callback) {
+ // No starting an action mode for an action bar child! (Where would it go?)
+ return null;
+ }
+
+ @Override
+ public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ if (mTabContainer != null && mTabContainer.getVisibility() != GONE) {
+ final int mode = MeasureSpec.getMode(heightMeasureSpec);
+ if (mode == MeasureSpec.AT_MOST) {
+ final int measuredHeight = getMeasuredHeight();
+ final int maxHeight = MeasureSpec.getSize(heightMeasureSpec);
+ setMeasuredDimension(getMeasuredWidth(),
+ Math.min(measuredHeight + mTabContainer.getMeasuredHeight(), maxHeight));
+ }
+ }
+ }
+
+ @Override
+ public void onLayout(boolean changed, int l, int t, int r, int b) {
+ super.onLayout(changed, l, t, r, b);
+ if (mTabContainer != null && mTabContainer.getVisibility() != GONE) {
+ final int containerHeight = getMeasuredHeight();
+ mTabContainer.layout(l, containerHeight - mTabContainer.getMeasuredHeight(),
+ r, containerHeight);
+ }
+ }
}
diff --git a/core/java/com/android/internal/widget/ActionBarContextView.java b/core/java/com/android/internal/widget/ActionBarContextView.java
index f762265..c82323e 100644
--- a/core/java/com/android/internal/widget/ActionBarContextView.java
+++ b/core/java/com/android/internal/widget/ActionBarContextView.java
@@ -16,6 +16,7 @@
package com.android.internal.widget;
import com.android.internal.R;
+import com.android.internal.view.menu.ActionMenuPresenter;
import com.android.internal.view.menu.ActionMenuView;
import com.android.internal.view.menu.MenuBuilder;
@@ -29,7 +30,7 @@ import android.util.AttributeSet;
import android.view.ActionMode;
import android.view.LayoutInflater;
import android.view.View;
-import android.view.ViewGroup;
+import android.view.ViewGroup.LayoutParams;
import android.view.animation.DecelerateInterpolator;
import android.widget.LinearLayout;
import android.widget.TextView;
@@ -37,7 +38,7 @@ import android.widget.TextView;
/**
* @hide
*/
-public class ActionBarContextView extends ViewGroup implements AnimatorListener {
+public class ActionBarContextView extends AbsActionBarView implements AnimatorListener {
private static final String TAG = "ActionBarContextView";
private int mContentHeight;
@@ -52,7 +53,6 @@ public class ActionBarContextView extends ViewGroup implements AnimatorListener
private TextView mSubtitleView;
private int mTitleStyleRes;
private int mSubtitleStyleRes;
- private ActionMenuView mMenuView;
private Animator mCurrentAnimation;
private boolean mAnimateInOnLayout;
@@ -85,12 +85,6 @@ public class ActionBarContextView extends ViewGroup implements AnimatorListener
com.android.internal.R.styleable.ActionMode_height, 0);
a.recycle();
}
-
- @Override
- public ActionMode startActionModeForChild(View child, ActionMode.Callback callback) {
- // No starting an action mode for an existing action mode UI child! (Where would it go?)
- return null;
- }
public void setHeight(int height) {
mContentHeight = height;
@@ -176,10 +170,25 @@ public class ActionBarContextView extends ViewGroup implements AnimatorListener
});
final MenuBuilder menu = (MenuBuilder) mode.getMenu();
- mMenuView = (ActionMenuView) menu.getMenuView(MenuBuilder.TYPE_ACTION_BUTTON, this);
- mMenuView.setOverflowReserved(true);
- mMenuView.updateChildren(false);
- addView(mMenuView);
+ mMenuPresenter = new ActionMenuPresenter();
+ menu.addMenuPresenter(mMenuPresenter);
+ mMenuView = (ActionMenuView) mMenuPresenter.getMenuView(this);
+
+ final LayoutParams layoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT,
+ LayoutParams.MATCH_PARENT);
+ mMenuView.setLayoutParams(layoutParams);
+ if (mSplitView == null) {
+ addView(mMenuView);
+ } else {
+ // Allow full screen width in split mode.
+ mMenuPresenter.setWidthLimit(
+ getContext().getResources().getDisplayMetrics().widthPixels, true);
+ // No limit to the item count; use whatever will fit.
+ mMenuPresenter.setItemLimit(Integer.MAX_VALUE);
+ // Span the whole width
+ layoutParams.width = LayoutParams.MATCH_PARENT;
+ mSplitView.addView(mMenuView);
+ }
mAnimateInOnLayout = true;
}
@@ -211,34 +220,31 @@ public class ActionBarContextView extends ViewGroup implements AnimatorListener
public void killMode() {
finishAnimation();
removeAllViews();
+ if (mSplitView != null) {
+ mSplitView.removeView(mMenuView);
+ }
mCustomView = null;
mMenuView = null;
mAnimateInOnLayout = false;
}
public boolean showOverflowMenu() {
- if (mMenuView != null) {
- return mMenuView.showOverflowMenu();
+ if (mMenuPresenter != null) {
+ return mMenuPresenter.showOverflowMenu();
}
return false;
}
- public void openOverflowMenu() {
- if (mMenuView != null) {
- mMenuView.openOverflowMenu();
- }
- }
-
public boolean hideOverflowMenu() {
- if (mMenuView != null) {
- return mMenuView.hideOverflowMenu();
+ if (mMenuPresenter != null) {
+ return mMenuPresenter.hideOverflowMenu();
}
return false;
}
public boolean isOverflowMenuShowing() {
- if (mMenuView != null) {
- return mMenuView.isOverflowMenuShowing();
+ if (mMenuPresenter != null) {
+ return mMenuPresenter.isOverflowMenuShowing();
}
return false;
}
@@ -346,7 +352,7 @@ public class ActionBarContextView extends ViewGroup implements AnimatorListener
private Animator makeOutAnimation() {
ObjectAnimator buttonAnimator = ObjectAnimator.ofFloat(mClose, "translationX",
- 0, -mClose.getWidth());
+ -mClose.getWidth());
buttonAnimator.setDuration(200);
buttonAnimator.addListener(this);
buttonAnimator.setInterpolator(new DecelerateInterpolator());
@@ -360,7 +366,7 @@ public class ActionBarContextView extends ViewGroup implements AnimatorListener
for (int i = 0; i < 0; i++) {
View child = mMenuView.getChildAt(i);
child.setScaleY(0);
- ObjectAnimator a = ObjectAnimator.ofFloat(child, "scaleY", 1, 0);
+ ObjectAnimator a = ObjectAnimator.ofFloat(child, "scaleY", 0);
a.setDuration(100);
a.setStartDelay(i * 70);
b.with(a);
@@ -387,7 +393,7 @@ public class ActionBarContextView extends ViewGroup implements AnimatorListener
mAnimateInOnLayout = false;
}
}
-
+
if (mTitleLayout != null && mCustomView == null) {
x += positionChild(mTitleLayout, x, y, contentHeight);
}
@@ -403,36 +409,6 @@ public class ActionBarContextView extends ViewGroup implements AnimatorListener
}
}
- private int measureChildView(View child, int availableWidth, int childSpecHeight, int spacing) {
- child.measure(MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST),
- childSpecHeight);
-
- availableWidth -= child.getMeasuredWidth();
- availableWidth -= spacing;
-
- return Math.max(0, availableWidth);
- }
-
- private int positionChild(View child, int x, int y, int contentHeight) {
- int childWidth = child.getMeasuredWidth();
- int childHeight = child.getMeasuredHeight();
- int childTop = y + (contentHeight - childHeight) / 2;
-
- child.layout(x, childTop, x + childWidth, childTop + childHeight);
-
- return childWidth;
- }
-
- private int positionChildInverse(View child, int x, int y, int contentHeight) {
- int childWidth = child.getMeasuredWidth();
- int childHeight = child.getMeasuredHeight();
- int childTop = y + (contentHeight - childHeight) / 2;
-
- child.layout(x - childWidth, childTop, x, childTop + childHeight);
-
- return childWidth;
- }
-
@Override
public void onAnimationStart(Animator animation) {
}
@@ -452,4 +428,9 @@ public class ActionBarContextView extends ViewGroup implements AnimatorListener
@Override
public void onAnimationRepeat(Animator animation) {
}
+
+ @Override
+ public boolean shouldDelayChildPressedState() {
+ return false;
+ }
}
diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java
index 891557d..f1887eb 100644
--- a/core/java/com/android/internal/widget/ActionBarView.java
+++ b/core/java/com/android/internal/widget/ActionBarView.java
@@ -18,8 +18,10 @@ package com.android.internal.widget;
import com.android.internal.R;
import com.android.internal.view.menu.ActionMenuItem;
+import com.android.internal.view.menu.ActionMenuPresenter;
import com.android.internal.view.menu.ActionMenuView;
import com.android.internal.view.menu.MenuBuilder;
+import com.android.internal.view.menu.MenuPresenter;
import android.app.ActionBar;
import android.app.ActionBar.OnNavigationListener;
@@ -28,15 +30,14 @@ import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.text.TextUtils;
import android.text.TextUtils.TruncateAt;
import android.util.AttributeSet;
+import android.util.DisplayMetrics;
import android.util.Log;
-import android.view.ActionMode;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.Menu;
@@ -57,7 +58,7 @@ import android.widget.TextView;
/**
* @hide
*/
-public class ActionBarView extends ViewGroup {
+public class ActionBarView extends AbsActionBarView {
private static final String TAG = "ActionBarView";
/**
@@ -85,7 +86,6 @@ public class ActionBarView extends ViewGroup {
private CharSequence mSubtitle;
private Drawable mIcon;
private Drawable mLogo;
- private Drawable mDivider;
private View mHomeLayout;
private View mHomeAsUpView;
@@ -96,7 +96,7 @@ public class ActionBarView extends ViewGroup {
private Spinner mSpinner;
private LinearLayout mListNavLayout;
private HorizontalScrollView mTabScrollView;
- private LinearLayout mTabLayout;
+ private ViewGroup mTabLayout;
private View mCustomNavView;
private ProgressBar mProgressView;
private ProgressBar mIndeterminateProgressView;
@@ -109,11 +109,11 @@ public class ActionBarView extends ViewGroup {
private int mProgressStyle;
private int mIndeterminateProgressStyle;
- private boolean mShowMenu;
+ private boolean mSplitActionBar;
private boolean mUserTitle;
+ private boolean mIncludeTabs;
private MenuBuilder mOptionsMenu;
- private ActionMenuView mMenuView;
private ActionBarContextView mContextView;
@@ -199,6 +199,8 @@ public class ActionBarView extends ViewGroup {
mProgressBarPadding = a.getDimensionPixelOffset(R.styleable.ActionBar_progressBarPadding, 0);
mItemPadding = a.getDimensionPixelOffset(R.styleable.ActionBar_itemPadding, 0);
+ mIncludeTabs = a.getBoolean(R.styleable.ActionBar_embeddedTabs, true);
+
setDisplayOptions(a.getInt(R.styleable.ActionBar_displayOptions, DISPLAY_DEFAULT));
final int customNavId = a.getResourceId(R.styleable.ActionBar_customNavigationLayout, 0);
@@ -210,8 +212,6 @@ public class ActionBarView extends ViewGroup {
mContentHeight = a.getLayoutDimension(R.styleable.ActionBar_height, 0);
- mDivider = a.getDrawable(R.styleable.ActionBar_divider);
-
a.recycle();
mLogoNavItem = new ActionMenuItem(context, 0, android.R.id.home, 0, 0, mTitle);
@@ -228,6 +228,11 @@ public class ActionBarView extends ViewGroup {
mHomeLayout.setFocusable(true);
}
+ @Override
+ public boolean shouldDelayChildPressedState() {
+ return false;
+ }
+
public void initProgress() {
mProgressView = new ProgressBar(mContext, null, 0, mProgressStyle);
mProgressView.setId(R.id.progress_horizontal);
@@ -242,77 +247,74 @@ public class ActionBarView extends ViewGroup {
addView(mIndeterminateProgressView);
}
- @Override
- public ActionMode startActionModeForChild(View child, ActionMode.Callback callback) {
- // No starting an action mode for an action bar child! (Where would it go?)
- return null;
+ public void setSplitActionBar(boolean splitActionBar) {
+ if (mSplitActionBar != splitActionBar) {
+ if (mMenuView != null) {
+ if (splitActionBar) {
+ removeView(mMenuView);
+ if (mSplitView != null) {
+ mSplitView.addView(mMenuView);
+ }
+ } else {
+ addView(mMenuView);
+ }
+ }
+ mSplitActionBar = splitActionBar;
+ }
+ }
+
+ public boolean isSplitActionBar() {
+ return mSplitActionBar;
+ }
+
+ public boolean hasEmbeddedTabs() {
+ return mIncludeTabs;
+ }
+
+ public void setExternalTabLayout(ViewGroup tabLayout) {
+ mTabLayout = tabLayout;
}
public void setCallback(OnNavigationListener callback) {
mCallback = callback;
}
- public void setMenu(Menu menu) {
+ public void setMenu(Menu menu, MenuPresenter.Callback cb) {
if (menu == mOptionsMenu) return;
+ if (mOptionsMenu != null) {
+ mOptionsMenu.removeMenuPresenter(mMenuPresenter);
+ }
+
MenuBuilder builder = (MenuBuilder) menu;
mOptionsMenu = builder;
if (mMenuView != null) {
removeView(mMenuView);
}
- final ActionMenuView menuView = (ActionMenuView) builder.getMenuView(
- MenuBuilder.TYPE_ACTION_BUTTON, null);
+ if (mMenuPresenter == null) {
+ mMenuPresenter = new ActionMenuPresenter();
+ mMenuPresenter.setCallback(cb);
+ builder.addMenuPresenter(mMenuPresenter);
+ }
+ final ActionMenuView menuView = (ActionMenuView) mMenuPresenter.getMenuView(this);
final LayoutParams layoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.MATCH_PARENT);
menuView.setLayoutParams(layoutParams);
- addView(menuView);
- mMenuView = menuView;
- }
-
- public boolean showOverflowMenu() {
- if (mMenuView != null) {
- return mMenuView.showOverflowMenu();
- }
- return false;
- }
-
- public void openOverflowMenu() {
- if (mMenuView != null) {
- mMenuView.openOverflowMenu();
- }
- }
-
- public void postShowOverflowMenu() {
- post(new Runnable() {
- public void run() {
- showOverflowMenu();
- }
- });
- }
-
- public boolean hideOverflowMenu() {
- if (mMenuView != null) {
- return mMenuView.hideOverflowMenu();
- }
- return false;
- }
-
- public boolean isOverflowMenuShowing() {
- if (mMenuView != null) {
- return mMenuView.isOverflowMenuShowing();
- }
- return false;
- }
-
- public boolean isOverflowMenuOpen() {
- if (mMenuView != null) {
- return mMenuView.isOverflowMenuOpen();
+ if (!mSplitActionBar) {
+ addView(menuView);
+ } else {
+ // Allow full screen width in split mode.
+ mMenuPresenter.setWidthLimit(
+ getContext().getResources().getDisplayMetrics().widthPixels, true);
+ // No limit to the item count; use whatever will fit.
+ mMenuPresenter.setItemLimit(Integer.MAX_VALUE);
+ // Span the whole width
+ layoutParams.width = LayoutParams.MATCH_PARENT;
+ if (mSplitView != null) {
+ mSplitView.addView(menuView);
+ } // We'll add this later if we missed it this time.
}
- return false;
- }
-
- public boolean isOverflowReserved() {
- return mMenuView != null && mMenuView.isOverflowReserved();
+ mMenuView = menuView;
}
public void setCustomNavigationView(View view) {
@@ -382,13 +384,19 @@ public class ActionBarView extends ViewGroup {
public void setDisplayOptions(int options) {
final int flagsChanged = options ^ mDisplayOptions;
mDisplayOptions = options;
+
+ if ((flagsChanged & ActionBar.DISPLAY_DISABLE_HOME) != 0) {
+ final boolean disableHome = (options & ActionBar.DISPLAY_DISABLE_HOME) != 0;
+ mHomeLayout.setEnabled(!disableHome);
+ }
+
if ((flagsChanged & DISPLAY_RELAYOUT_MASK) != 0) {
final int vis = (options & ActionBar.DISPLAY_SHOW_HOME) != 0 ? VISIBLE : GONE;
mHomeLayout.setVisibility(vis);
if ((flagsChanged & ActionBar.DISPLAY_HOME_AS_UP) != 0) {
- mHomeAsUpView.setVisibility((options & ActionBar.DISPLAY_HOME_AS_UP) != 0
- ? VISIBLE : GONE);
+ final boolean isUp = (options & ActionBar.DISPLAY_HOME_AS_UP) != 0;
+ mHomeAsUpView.setVisibility(isUp ? VISIBLE : GONE);
}
if ((flagsChanged & ActionBar.DISPLAY_USE_LOGO) != 0) {
@@ -416,6 +424,59 @@ public class ActionBarView extends ViewGroup {
} else {
invalidate();
}
+
+ // Make sure the home button has an accurate content description for accessibility.
+ if ((options & ActionBar.DISPLAY_DISABLE_HOME) != 0) {
+ mHomeLayout.setContentDescription(null);
+ } else if ((options & ActionBar.DISPLAY_HOME_AS_UP) != 0) {
+ mHomeLayout.setContentDescription(mContext.getResources().getText(
+ R.string.action_bar_up_description));
+ } else {
+ mHomeLayout.setContentDescription(mContext.getResources().getText(
+ R.string.action_bar_home_description));
+ }
+ }
+
+ public void setIcon(Drawable icon) {
+ mIcon = icon;
+ if (icon != null &&
+ ((mDisplayOptions & ActionBar.DISPLAY_USE_LOGO) == 0 || mLogo == null)) {
+ mIconView.setImageDrawable(icon);
+ }
+ }
+
+ public void setIcon(int resId) {
+ setIcon(mContext.getResources().getDrawableForDensity(resId, getPreferredIconDensity()));
+ }
+
+ public void setLogo(Drawable logo) {
+ mLogo = logo;
+ if (logo != null && (mDisplayOptions & ActionBar.DISPLAY_USE_LOGO) != 0) {
+ mIconView.setImageDrawable(logo);
+ }
+ }
+
+ public void setLogo(int resId) {
+ mContext.getResources().getDrawable(resId);
+ }
+
+ /**
+ * @return Drawable density to load that will best fit the available height.
+ */
+ private int getPreferredIconDensity() {
+ final Resources res = mContext.getResources();
+ final int availableHeight = getLayoutParams().height -
+ mIconView.getPaddingTop() - mIconView.getPaddingBottom();
+ int iconSize = res.getDimensionPixelSize(android.R.dimen.app_icon_size);
+
+ if (iconSize * DisplayMetrics.DENSITY_LOW >= availableHeight) {
+ return DisplayMetrics.DENSITY_LOW;
+ } else if (iconSize * DisplayMetrics.DENSITY_MEDIUM >= availableHeight) {
+ return DisplayMetrics.DENSITY_MEDIUM;
+ } else if (iconSize * DisplayMetrics.DENSITY_HIGH >= availableHeight) {
+ return DisplayMetrics.DENSITY_HIGH;
+ }
+ return DisplayMetrics.DENSITY_XHIGH;
}
public void setNavigationMode(int mode) {
@@ -428,7 +489,7 @@ public class ActionBarView extends ViewGroup {
}
break;
case ActionBar.NAVIGATION_MODE_TABS:
- if (mTabLayout != null) {
+ if (mTabScrollView != null) {
removeView(mTabScrollView);
}
}
@@ -453,7 +514,9 @@ public class ActionBarView extends ViewGroup {
break;
case ActionBar.NAVIGATION_MODE_TABS:
ensureTabsExist();
- addView(mTabScrollView);
+ if (mTabScrollView != null) {
+ addView(mTabScrollView);
+ }
break;
}
mNavigationMode = mode;
@@ -462,15 +525,24 @@ public class ActionBarView extends ViewGroup {
}
private void ensureTabsExist() {
+ if (!mIncludeTabs) return;
+
if (mTabScrollView == null) {
mTabScrollView = new HorizontalScrollView(getContext());
mTabScrollView.setHorizontalFadingEdgeEnabled(true);
- mTabLayout = new LinearLayout(getContext(), null,
- com.android.internal.R.attr.actionBarTabBarStyle);
+ mTabLayout = createTabContainer();
mTabScrollView.addView(mTabLayout);
}
}
+ public ViewGroup createTabContainer() {
+ ViewGroup result = new LinearLayout(getContext(), null,
+ com.android.internal.R.attr.actionBarTabBarStyle);
+ result.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,
+ mContentHeight));
+ return result;
+ }
+
public void setDropdownAdapter(SpinnerAdapter adapter) {
mSpinnerAdapter = adapter;
if (mSpinner != null) {
@@ -531,6 +603,10 @@ public class ActionBarView extends ViewGroup {
}
}
+ public void updateTab(int position) {
+ ((TabView) mTabLayout.getChildAt(position)).update();
+ }
+
public void removeTabAt(int position) {
if (mTabLayout != null) {
mTabLayout.removeViewAt(position);
@@ -641,7 +717,7 @@ public class ActionBarView extends ViewGroup {
leftOfCenter = Math.max(0, availableWidth - homeWidth);
}
- if (mMenuView != null) {
+ if (mMenuView != null && mMenuView.getParent() == this) {
availableWidth = measureChildView(mMenuView, availableWidth,
childSpecHeight, 0);
rightOfCenter = Math.max(0, rightOfCenter - mMenuView.getMeasuredWidth());
@@ -760,16 +836,6 @@ public class ActionBarView extends ViewGroup {
}
}
- private int measureChildView(View child, int availableWidth, int childSpecHeight, int spacing) {
- child.measure(MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST),
- childSpecHeight);
-
- availableWidth -= child.getMeasuredWidth();
- availableWidth -= spacing;
-
- return Math.max(0, availableWidth);
- }
-
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int x = getPaddingLeft();
@@ -804,7 +870,7 @@ public class ActionBarView extends ViewGroup {
}
int menuLeft = r - l - getPaddingRight();
- if (mMenuView != null) {
+ if (mMenuView != null && mMenuView.getParent() == this) {
positionChildInverse(mMenuView, menuLeft, y, contentHeight);
menuLeft -= mMenuView.getMeasuredWidth();
}
@@ -882,68 +948,78 @@ public class ActionBarView extends ViewGroup {
}
}
- private int positionChild(View child, int x, int y, int contentHeight) {
- int childWidth = child.getMeasuredWidth();
- int childHeight = child.getMeasuredHeight();
- int childTop = y + (contentHeight - childHeight) / 2;
-
- child.layout(x, childTop, x + childWidth, childTop + childHeight);
-
- return childWidth;
- }
-
- private int positionChildInverse(View child, int x, int y, int contentHeight) {
- int childWidth = child.getMeasuredWidth();
- int childHeight = child.getMeasuredHeight();
- int childTop = y + (contentHeight - childHeight) / 2;
-
- child.layout(x - childWidth, childTop, x, childTop + childHeight);
-
- return childWidth;
- }
-
private static class TabView extends LinearLayout {
private ActionBar.Tab mTab;
+ private TextView mTextView;
+ private ImageView mIconView;
+ private View mCustomView;
public TabView(Context context, ActionBar.Tab tab) {
super(context, null, com.android.internal.R.attr.actionBarTabStyle);
mTab = tab;
+ update();
+
+ setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
+ LayoutParams.MATCH_PARENT, 1));
+ }
+
+ public void update() {
+ final ActionBar.Tab tab = mTab;
final View custom = tab.getCustomView();
if (custom != null) {
addView(custom);
+ mCustomView = custom;
+ if (mTextView != null) mTextView.setVisibility(GONE);
+ if (mIconView != null) {
+ mIconView.setVisibility(GONE);
+ mIconView.setImageDrawable(null);
+ }
} else {
- // TODO Style tabs based on the theme
+ if (mCustomView != null) {
+ removeView(mCustomView);
+ mCustomView = null;
+ }
final Drawable icon = tab.getIcon();
final CharSequence text = tab.getText();
if (icon != null) {
- ImageView iconView = new ImageView(context);
- iconView.setImageDrawable(icon);
- LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT,
- LayoutParams.WRAP_CONTENT);
- lp.gravity = Gravity.CENTER_VERTICAL;
- iconView.setLayoutParams(lp);
- addView(iconView);
+ if (mIconView == null) {
+ ImageView iconView = new ImageView(getContext());
+ LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT,
+ LayoutParams.WRAP_CONTENT);
+ lp.gravity = Gravity.CENTER_VERTICAL;
+ iconView.setLayoutParams(lp);
+ addView(iconView, 0);
+ mIconView = iconView;
+ }
+ mIconView.setImageDrawable(icon);
+ mIconView.setVisibility(VISIBLE);
+ } else if (mIconView != null) {
+ mIconView.setVisibility(GONE);
+ mIconView.setImageDrawable(null);
}
if (text != null) {
- TextView textView = new TextView(context, null,
- com.android.internal.R.attr.actionBarTabTextStyle);
- textView.setText(text);
- textView.setSingleLine();
- textView.setEllipsize(TruncateAt.END);
- LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT,
- LayoutParams.WRAP_CONTENT);
- lp.gravity = Gravity.CENTER_VERTICAL;
- textView.setLayoutParams(lp);
- addView(textView);
+ if (mTextView == null) {
+ TextView textView = new TextView(getContext(), null,
+ com.android.internal.R.attr.actionBarTabTextStyle);
+ textView.setSingleLine();
+ textView.setEllipsize(TruncateAt.END);
+ LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT,
+ LayoutParams.WRAP_CONTENT);
+ lp.gravity = Gravity.CENTER_VERTICAL;
+ textView.setLayoutParams(lp);
+ addView(textView);
+ mTextView = textView;
+ }
+ mTextView.setText(text);
+ mTextView.setVisibility(VISIBLE);
+ } else {
+ mTextView.setVisibility(GONE);
}
}
-
- setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
- LayoutParams.MATCH_PARENT, 1));
}
public ActionBar.Tab getTab() {
@@ -982,21 +1058,6 @@ public class ActionBarView extends ViewGroup {
}
@Override
- public void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
-
- // Make sure we reload positioning elements that may change with configuration.
- Resources res = getContext().getResources();
- final int imagePadding = res.getDimensionPixelSize(
- com.android.internal.R.dimen.action_bar_home_image_padding);
- final int upMargin = res.getDimensionPixelSize(
- com.android.internal.R.dimen.action_bar_home_up_margin);
- mIconView.setPadding(imagePadding, getPaddingTop(), imagePadding, getPaddingBottom());
- ((LayoutParams) mUpView.getLayoutParams()).rightMargin = upMargin;
- mUpView.requestLayout();
- }
-
- @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
measureChildWithMargins(mUpView, widthMeasureSpec, 0, heightMeasureSpec, 0);
final LayoutParams upLp = (LayoutParams) mUpView.getLayoutParams();
diff --git a/core/java/com/android/internal/widget/EditableInputConnection.java b/core/java/com/android/internal/widget/EditableInputConnection.java
index 9f9f020..0d32d4b 100644
--- a/core/java/com/android/internal/widget/EditableInputConnection.java
+++ b/core/java/com/android/internal/widget/EditableInputConnection.java
@@ -17,6 +17,7 @@
package com.android.internal.widget;
import android.os.Bundle;
+import android.os.IBinder;
import android.text.Editable;
import android.text.method.KeyListener;
import android.util.Log;
diff --git a/core/java/com/android/internal/widget/IRemoteViewsFactory.aidl b/core/java/com/android/internal/widget/IRemoteViewsFactory.aidl
index 5857acb..18076c4 100644
--- a/core/java/com/android/internal/widget/IRemoteViewsFactory.aidl
+++ b/core/java/com/android/internal/widget/IRemoteViewsFactory.aidl
@@ -22,7 +22,7 @@ import android.widget.RemoteViews;
/** {@hide} */
interface IRemoteViewsFactory {
void onDataSetChanged();
- void onDestroy(in Intent intent);
+ oneway void onDestroy(in Intent intent);
int getCount();
RemoteViews getViewAt(int position);
RemoteViews getLoadingView();
diff --git a/core/java/com/android/internal/widget/PasswordEntryKeyboardHelper.java b/core/java/com/android/internal/widget/PasswordEntryKeyboardHelper.java
index 65973b6..3070e3e 100644
--- a/core/java/com/android/internal/widget/PasswordEntryKeyboardHelper.java
+++ b/core/java/com/android/internal/widget/PasswordEntryKeyboardHelper.java
@@ -29,7 +29,7 @@ import android.util.Log;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.View;
-import android.view.ViewRoot;
+import android.view.ViewAncestor;
import com.android.internal.R;
public class PasswordEntryKeyboardHelper implements OnKeyboardActionListener {
@@ -150,7 +150,7 @@ public class PasswordEntryKeyboardHelper implements OnKeyboardActionListener {
KeyEvent event = events[i];
event = KeyEvent.changeFlags(event, event.getFlags()
| KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE);
- handler.sendMessage(handler.obtainMessage(ViewRoot.DISPATCH_KEY, event));
+ handler.sendMessage(handler.obtainMessage(ViewAncestor.DISPATCH_KEY, event));
}
}
}
@@ -158,11 +158,11 @@ public class PasswordEntryKeyboardHelper implements OnKeyboardActionListener {
public void sendDownUpKeyEvents(int keyEventCode) {
long eventTime = SystemClock.uptimeMillis();
Handler handler = mTargetView.getHandler();
- handler.sendMessage(handler.obtainMessage(ViewRoot.DISPATCH_KEY_FROM_IME,
+ handler.sendMessage(handler.obtainMessage(ViewAncestor.DISPATCH_KEY_FROM_IME,
new KeyEvent(eventTime, eventTime, KeyEvent.ACTION_DOWN, keyEventCode, 0, 0,
KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE)));
- handler.sendMessage(handler.obtainMessage(ViewRoot.DISPATCH_KEY_FROM_IME,
+ handler.sendMessage(handler.obtainMessage(ViewAncestor.DISPATCH_KEY_FROM_IME,
new KeyEvent(eventTime, eventTime, KeyEvent.ACTION_UP, keyEventCode, 0, 0,
KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE)));
diff --git a/core/java/com/android/internal/widget/PointerLocationView.java b/core/java/com/android/internal/widget/PointerLocationView.java
index 076a1cb..d789584 100644
--- a/core/java/com/android/internal/widget/PointerLocationView.java
+++ b/core/java/com/android/internal/widget/PointerLocationView.java
@@ -320,7 +320,8 @@ public class PointerLocationView extends View {
}
}
- private void logPointerCoords(int action, int index, MotionEvent.PointerCoords coords, int id) {
+ private void logPointerCoords(int action, int index, MotionEvent.PointerCoords coords, int id,
+ int toolType, int buttonState) {
final String prefix;
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
@@ -357,6 +358,12 @@ public class PointerLocationView extends View {
case MotionEvent.ACTION_HOVER_MOVE:
prefix = "HOVER MOVE";
break;
+ case MotionEvent.ACTION_HOVER_ENTER:
+ prefix = "HOVER ENTER";
+ break;
+ case MotionEvent.ACTION_HOVER_EXIT:
+ prefix = "HOVER EXIT";
+ break;
case MotionEvent.ACTION_SCROLL:
prefix = "SCROLL";
break;
@@ -380,26 +387,16 @@ public class PointerLocationView extends View {
.append("deg")
.append(" VScroll=").append(coords.getAxisValue(MotionEvent.AXIS_VSCROLL), 1)
.append(" HScroll=").append(coords.getAxisValue(MotionEvent.AXIS_HSCROLL), 1)
+ .append(" ToolType=").append(MotionEvent.toolTypeToString(toolType))
+ .append(" ButtonState=").append(MotionEvent.buttonStateToString(buttonState))
.toString());
}
public void addPointerEvent(MotionEvent event) {
synchronized (mPointers) {
- int action = event.getAction();
-
- //Log.i(TAG, "Motion: action=0x" + Integer.toHexString(action)
- // + " pointers=" + event.getPointerCount());
-
+ final int action = event.getAction();
int NP = mPointers.size();
-
- //mRect.set(0, 0, getWidth(), mHeaderBottom+1);
- //invalidate(mRect);
- //if (mCurDown) {
- // mRect.set(mCurX-mCurWidth-3, mCurY-mCurWidth-3,
- // mCurX+mCurWidth+3, mCurY+mCurWidth+3);
- //} else {
- // mRect.setEmpty();
- //}
+
if (action == MotionEvent.ACTION_DOWN
|| (action & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_DOWN) {
final int index = (action & MotionEvent.ACTION_POINTER_INDEX_MASK)
@@ -450,7 +447,8 @@ public class PointerLocationView extends View {
final PointerCoords coords = ps != null ? ps.mCoords : mHoverCoords;
event.getHistoricalPointerCoords(i, historyPos, coords);
if (mPrintCoords) {
- logPointerCoords(action, i, coords, id);
+ logPointerCoords(action, i, coords, id,
+ event.getToolType(i), event.getButtonState());
}
if (ps != null) {
ps.addTrace(coords.x, coords.y);
@@ -463,7 +461,8 @@ public class PointerLocationView extends View {
final PointerCoords coords = ps != null ? ps.mCoords : mHoverCoords;
event.getPointerCoords(i, coords);
if (mPrintCoords) {
- logPointerCoords(action, i, coords, id);
+ logPointerCoords(action, i, coords, id,
+ event.getToolType(i), event.getButtonState());
}
if (ps != null) {
ps.addTrace(coords.x, coords.y);
@@ -494,12 +493,7 @@ public class PointerLocationView extends View {
ps.addTrace(Float.NaN, Float.NaN);
}
}
-
- //if (mCurDown) {
- // mRect.union(mCurX-mCurWidth-3, mCurY-mCurWidth-3,
- // mCurX+mCurWidth+3, mCurY+mCurWidth+3);
- //}
- //invalidate(mRect);
+
postInvalidate();
}
}
diff --git a/core/java/com/google/android/mms/pdu/EncodedStringValue.java b/core/java/com/google/android/mms/pdu/EncodedStringValue.java
index a27962d..9495c1c 100644
--- a/core/java/com/google/android/mms/pdu/EncodedStringValue.java
+++ b/core/java/com/google/android/mms/pdu/EncodedStringValue.java
@@ -17,7 +17,6 @@
package com.google.android.mms.pdu;
-import android.util.Config;
import android.util.Log;
import java.io.ByteArrayOutputStream;
@@ -31,7 +30,7 @@ import java.util.ArrayList;
public class EncodedStringValue implements Cloneable {
private static final String TAG = "EncodedStringValue";
private static final boolean DEBUG = false;
- private static final boolean LOCAL_LOGV = DEBUG ? Config.LOGD : Config.LOGV;
+ private static final boolean LOCAL_LOGV = false;
/**
* The Char-set value.
diff --git a/core/java/com/google/android/mms/pdu/PduParser.java b/core/java/com/google/android/mms/pdu/PduParser.java
index 3f185aa..f7f71ed 100755
--- a/core/java/com/google/android/mms/pdu/PduParser.java
+++ b/core/java/com/google/android/mms/pdu/PduParser.java
@@ -20,7 +20,6 @@ package com.google.android.mms.pdu;
import com.google.android.mms.ContentType;
import com.google.android.mms.InvalidHeaderValueException;
-import android.util.Config;
import android.util.Log;
import java.io.ByteArrayInputStream;
@@ -86,7 +85,7 @@ public class PduParser {
*/
private static final String LOG_TAG = "PduParser";
private static final boolean DEBUG = false;
- private static final boolean LOCAL_LOGV = DEBUG ? Config.LOGD : Config.LOGV;
+ private static final boolean LOCAL_LOGV = false;
/**
* Constructor.
diff --git a/core/java/com/google/android/mms/pdu/PduPersister.java b/core/java/com/google/android/mms/pdu/PduPersister.java
index 9fdd204..4d2d535 100644
--- a/core/java/com/google/android/mms/pdu/PduPersister.java
+++ b/core/java/com/google/android/mms/pdu/PduPersister.java
@@ -39,7 +39,6 @@ import android.provider.Telephony.Mms.Addr;
import android.provider.Telephony.Mms.Part;
import android.provider.Telephony.MmsSms.PendingMessages;
import android.text.TextUtils;
-import android.util.Config;
import android.util.Log;
import java.io.ByteArrayOutputStream;
@@ -63,7 +62,7 @@ import com.google.android.mms.pdu.EncodedStringValue;
public class PduPersister {
private static final String TAG = "PduPersister";
private static final boolean DEBUG = false;
- private static final boolean LOCAL_LOGV = DEBUG ? Config.LOGD : Config.LOGV;
+ private static final boolean LOCAL_LOGV = false;
private static final long DUMMY_THREAD_ID = Long.MAX_VALUE;
diff --git a/core/java/com/google/android/mms/util/AbstractCache.java b/core/java/com/google/android/mms/util/AbstractCache.java
index 670439c..39b2abf 100644
--- a/core/java/com/google/android/mms/util/AbstractCache.java
+++ b/core/java/com/google/android/mms/util/AbstractCache.java
@@ -17,7 +17,6 @@
package com.google.android.mms.util;
-import android.util.Config;
import android.util.Log;
import java.util.HashMap;
@@ -25,7 +24,7 @@ import java.util.HashMap;
public abstract class AbstractCache<K, V> {
private static final String TAG = "AbstractCache";
private static final boolean DEBUG = false;
- private static final boolean LOCAL_LOGV = DEBUG ? Config.LOGD : Config.LOGV;
+ private static final boolean LOCAL_LOGV = false;
private static final int MAX_CACHED_ITEMS = 500;
diff --git a/core/java/com/google/android/mms/util/PduCache.java b/core/java/com/google/android/mms/util/PduCache.java
index 866ca1e..059af72 100644
--- a/core/java/com/google/android/mms/util/PduCache.java
+++ b/core/java/com/google/android/mms/util/PduCache.java
@@ -21,7 +21,6 @@ import android.content.ContentUris;
import android.content.UriMatcher;
import android.net.Uri;
import android.provider.Telephony.Mms;
-import android.util.Config;
import android.util.Log;
import java.util.HashMap;
@@ -30,7 +29,7 @@ import java.util.HashSet;
public final class PduCache extends AbstractCache<Uri, PduCacheEntry> {
private static final String TAG = "PduCache";
private static final boolean DEBUG = false;
- private static final boolean LOCAL_LOGV = DEBUG ? Config.LOGD : Config.LOGV;
+ private static final boolean LOCAL_LOGV = false;
private static final int MMS_ALL = 0;
private static final int MMS_ALL_ID = 1;
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index dda5a81..223008c 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -47,13 +47,16 @@ LOCAL_SRC_FILES:= \
android_emoji_EmojiFactory.cpp \
android_view_Display.cpp \
android_view_Surface.cpp \
- android_view_ViewRoot.cpp \
+ android_view_TextureView.cpp \
+ android_view_ViewAncestor.cpp \
android_view_InputChannel.cpp \
android_view_InputQueue.cpp \
android_view_KeyEvent.cpp \
android_view_KeyCharacterMap.cpp \
android_view_GLES20Canvas.cpp \
android_view_MotionEvent.cpp \
+ android_view_PointerIcon.cpp \
+ android_view_VelocityTracker.cpp \
android_text_AndroidCharacter.cpp \
android_text_AndroidBidi.cpp \
android_os_Debug.cpp \
@@ -92,6 +95,7 @@ LOCAL_SRC_FILES:= \
android/graphics/DrawFilter.cpp \
android/graphics/CreateJavaOutputStreamAdaptor.cpp \
android/graphics/Graphics.cpp \
+ android/graphics/HarfbuzzSkia.cpp \
android/graphics/Interpolator.cpp \
android/graphics/LayerRasterizer.cpp \
android/graphics/MaskFilter.cpp \
@@ -113,6 +117,7 @@ LOCAL_SRC_FILES:= \
android/graphics/Shader.cpp \
android/graphics/SurfaceTexture.cpp \
android/graphics/TextLayout.cpp \
+ android/graphics/TextLayoutCache.cpp \
android/graphics/Typeface.cpp \
android/graphics/Utils.cpp \
android/graphics/Xfermode.cpp \
@@ -146,6 +151,7 @@ LOCAL_SRC_FILES:= \
android_backup_BackupDataOutput.cpp \
android_backup_FileBackupHelperBase.cpp \
android_backup_BackupHelperDispatcher.cpp \
+ android_app_backup_FullBackup.cpp \
android_content_res_ObbScanner.cpp \
android_content_res_Configuration.cpp \
android_animation_PropertyValuesHolder.cpp
@@ -172,7 +178,9 @@ LOCAL_C_INCLUDES += \
external/icu4c/i18n \
external/icu4c/common \
external/jpeg \
- frameworks/opt/emoji
+ external/harfbuzz/src \
+ frameworks/opt/emoji \
+ libcore/include
LOCAL_SHARED_LIBRARIES := \
libexpat \
@@ -183,7 +191,6 @@ LOCAL_SHARED_LIBRARIES := \
libnetutils \
libui \
libgui \
- libsurfaceflinger_client \
libcamera_client \
libskia \
libsqlite \
@@ -204,6 +211,7 @@ LOCAL_SHARED_LIBRARIES := \
libjpeg \
libnfc_ndef \
libusbhost \
+ libharfbuzz \
ifeq ($(USE_OPENGL_RENDERER),true)
LOCAL_SHARED_LIBRARIES += libhwui
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 0e071a4..b787e9f 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -1,19 +1,18 @@
-/* //device/libs/android_runtime/AndroidRuntime.cpp
-**
-** Copyright 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.
-*/
+/*
+ * 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.
+ */
#define LOG_TAG "AndroidRuntime"
//#define LOG_NDEBUG 0
@@ -119,7 +118,8 @@ extern int register_com_android_internal_graphics_NativeUtils(JNIEnv *env);
extern int register_android_view_Display(JNIEnv* env);
extern int register_android_view_GLES20Canvas(JNIEnv* env);
extern int register_android_view_Surface(JNIEnv* env);
-extern int register_android_view_ViewRoot(JNIEnv* env);
+extern int register_android_view_TextureView(JNIEnv* env);
+extern int register_android_view_ViewAncestor(JNIEnv* env);
extern int register_android_database_CursorWindow(JNIEnv* env);
extern int register_android_database_SQLiteCompiledSql(JNIEnv* env);
extern int register_android_database_SQLiteDatabase(JNIEnv* env);
@@ -165,11 +165,15 @@ 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);
+extern int register_android_app_backup_FullBackup(JNIEnv *env);
+extern int register_android_app_ActivityThread(JNIEnv *env);
extern int register_android_app_NativeActivity(JNIEnv *env);
extern int register_android_view_InputChannel(JNIEnv* env);
extern int register_android_view_InputQueue(JNIEnv* env);
extern int register_android_view_KeyEvent(JNIEnv* env);
extern int register_android_view_MotionEvent(JNIEnv* env);
+extern int register_android_view_PointerIcon(JNIEnv* env);
+extern int register_android_view_VelocityTracker(JNIEnv* env);
extern int register_android_content_res_ObbScanner(JNIEnv* env);
extern int register_android_content_res_Configuration(JNIEnv* env);
extern int register_android_animation_PropertyValuesHolder(JNIEnv *env);
@@ -210,7 +214,7 @@ static jint com_android_internal_os_RuntimeInit_getQwertyKeyboard(JNIEnv* env, j
if (value != NULL && strcmp(value, "true") == 0) {
return 1;
}
-
+
return 0;
}
@@ -225,7 +229,7 @@ static JNINativeMethod gMethods[] = {
{ "isComputerOn", "()I",
(void*) com_android_internal_os_RuntimeInit_isComputerOn },
{ "turnComputerOn", "()V",
- (void*) com_android_internal_os_RuntimeInit_turnComputerOn },
+ (void*) com_android_internal_os_RuntimeInit_turnComputerOn },
{ "getQwertyKeyboard", "()I",
(void*) com_android_internal_os_RuntimeInit_getQwertyKeyboard },
};
@@ -276,51 +280,16 @@ AndroidRuntime::~AndroidRuntime()
return jniRegisterNativeMethods(env, className, gMethods, numMethods);
}
-/*
- * Call a static Java Programming Language function that takes no arguments and returns void.
- */
-status_t AndroidRuntime::callStatic(const char* className, const char* methodName)
-{
- JNIEnv* env;
- jclass clazz;
- jmethodID methodId;
-
- env = getJNIEnv();
- if (env == NULL)
- return UNKNOWN_ERROR;
-
- clazz = findClass(env, className);
- if (clazz == NULL) {
- LOGE("ERROR: could not find class '%s'\n", className);
- return UNKNOWN_ERROR;
- }
- methodId = env->GetStaticMethodID(clazz, methodName, "()V");
- if (methodId == NULL) {
- LOGE("ERROR: could not find method %s.%s\n", className, methodName);
- return UNKNOWN_ERROR;
- }
-
- env->CallStaticVoidMethod(clazz, methodId);
-
- return NO_ERROR;
-}
-
-status_t AndroidRuntime::callMain(
- const char* className, int argc, const char* const argv[])
+status_t AndroidRuntime::callMain(const char* className,
+ jclass clazz, int argc, const char* const argv[])
{
JNIEnv* env;
- jclass clazz;
jmethodID methodId;
LOGD("Calling main entry %s", className);
env = getJNIEnv();
- if (env == NULL)
- return UNKNOWN_ERROR;
-
- clazz = findClass(env, className);
- if (clazz == NULL) {
- LOGE("ERROR: could not find class '%s'\n", className);
+ if (clazz == NULL || env == NULL) {
return UNKNOWN_ERROR;
}
@@ -350,70 +319,6 @@ status_t AndroidRuntime::callMain(
}
/*
- * Find the named class.
- */
-jclass AndroidRuntime::findClass(JNIEnv* env, const char* className)
-{
- if (env->ExceptionCheck()) {
- LOGE("ERROR: exception pending on entry to findClass()");
- return NULL;
- }
-
- /*
- * This is a little awkward because the JNI FindClass call uses the
- * class loader associated with the native method we're executing in.
- * Because this native method is part of a "boot" class, JNI doesn't
- * look for the class in CLASSPATH, which unfortunately is a likely
- * location for it. (Had we issued the FindClass call before calling
- * into the VM -- at which point there isn't a native method frame on
- * the stack -- the VM would have checked CLASSPATH. We have to do
- * this because we call into Java Programming Language code and
- * bounce back out.)
- *
- * JNI lacks a "find class in a specific class loader" operation, so we
- * have to do things the hard way.
- */
- jclass cls = NULL;
-
- jclass javaLangClassLoader;
- jmethodID getSystemClassLoader, loadClass;
- jobject systemClassLoader;
- jstring strClassName;
-
- /* find the "system" class loader; none of this is expected to fail */
- javaLangClassLoader = env->FindClass("java/lang/ClassLoader");
- assert(javaLangClassLoader != NULL);
- getSystemClassLoader = env->GetStaticMethodID(javaLangClassLoader,
- "getSystemClassLoader", "()Ljava/lang/ClassLoader;");
- loadClass = env->GetMethodID(javaLangClassLoader,
- "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;");
- assert(getSystemClassLoader != NULL && loadClass != NULL);
- systemClassLoader = env->CallStaticObjectMethod(javaLangClassLoader,
- getSystemClassLoader);
- assert(systemClassLoader != NULL);
-
- /* create an object for the class name string; alloc could fail */
- strClassName = env->NewStringUTF(className);
- if (env->ExceptionCheck()) {
- LOGE("ERROR: unable to convert '%s' to string", className);
- return NULL;
- }
- LOGV("system class loader is %p, loading %p (%s)",
- systemClassLoader, strClassName, className);
-
- /* try to find the named class */
- cls = (jclass) env->CallObjectMethod(systemClassLoader, loadClass,
- strClassName);
- if (env->ExceptionCheck()) {
- LOGE("ERROR: unable to load class '%s' from %p",
- className, systemClassLoader);
- return NULL;
- }
-
- return cls;
-}
-
-/*
* The VM calls this through the "exit" hook.
*/
static void runtime_exit(int code)
@@ -455,7 +360,7 @@ static bool runtime_isSensitiveThread() {
int AndroidRuntime::addVmArguments(int argc, const char* const argv[])
{
int i;
-
+
for (i = 0; i<argc; i++) {
if (argv[i][0] != '-') {
return i;
@@ -697,6 +602,7 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv)
case 'n': val = "-Xdexopt:none"; break;
case 'v': val = "-Xdexopt:verified"; break;
case 'a': val = "-Xdexopt:all"; break;
+ case 'f': val = "-Xdexopt:full"; break;
default: val = NULL; break;
}
@@ -887,6 +793,17 @@ bail:
return result;
}
+char* AndroidRuntime::toSlashClassName(const char* className)
+{
+ char* result = strdup(className);
+ for (char* cp = result; *cp != '\0'; cp++) {
+ if (*cp == '.') {
+ *cp = '/';
+ }
+ }
+ return result;
+}
+
/*
* Start the Android runtime. This involves starting the virtual machine
* and calling the "static void main(String[] args)" method in the class
@@ -897,20 +814,16 @@ void AndroidRuntime::start(const char* className, const bool startSystemServer)
LOGD("\n>>>>>> AndroidRuntime START %s <<<<<<\n",
className != NULL ? className : "(unknown)");
- char* slashClassName = NULL;
- char* cp;
- JNIEnv* env;
-
blockSigpipe();
- /*
- * 'startSystemServer == true' means runtime is obslete and not run from
+ /*
+ * 'startSystemServer == true' means runtime is obsolete and not run from
* init.rc anymore, so we print out the boot start event here.
*/
if (startSystemServer) {
/* track our progress through the boot sequence */
const int LOG_BOOT_PROGRESS_START = 3000;
- LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START,
+ LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START,
ns2ms(systemTime(SYSTEM_TIME_MONOTONIC)));
}
@@ -919,7 +832,7 @@ void AndroidRuntime::start(const char* className, const bool startSystemServer)
rootDir = "/system";
if (!hasDir("/system")) {
LOG_FATAL("No root directory specified, and /android does not exist.");
- goto bail;
+ return;
}
setenv("ANDROID_ROOT", rootDir, 1);
}
@@ -928,15 +841,18 @@ void AndroidRuntime::start(const char* className, const bool startSystemServer)
//LOGD("Found LD_ASSUME_KERNEL='%s'\n", kernelHack);
/* start the virtual machine */
- if (startVm(&mJavaVM, &env) != 0)
- goto bail;
+ JNIEnv* env;
+ if (startVm(&mJavaVM, &env) != 0) {
+ return;
+ }
+ onVmCreated(env);
/*
* Register android functions.
*/
if (startReg(env) < 0) {
LOGE("Unable to register all android natives\n");
- goto bail;
+ return;
}
/*
@@ -956,7 +872,7 @@ void AndroidRuntime::start(const char* className, const bool startSystemServer)
classNameStr = env->NewStringUTF(className);
assert(classNameStr != NULL);
env->SetObjectArrayElement(strArray, 0, classNameStr);
- startSystemServerStr = env->NewStringUTF(startSystemServer ?
+ startSystemServerStr = env->NewStringUTF(startSystemServer ?
"true" : "false");
env->SetObjectArrayElement(strArray, 1, startSystemServerStr);
@@ -964,20 +880,13 @@ void AndroidRuntime::start(const char* className, const bool startSystemServer)
* Start VM. This thread becomes the main thread of the VM, and will
* not return until the VM exits.
*/
- jclass startClass;
- jmethodID startMeth;
-
- slashClassName = strdup(className);
- for (cp = slashClassName; *cp != '\0'; cp++)
- if (*cp == '.')
- *cp = '/';
-
- startClass = env->FindClass(slashClassName);
+ char* slashClassName = toSlashClassName(className);
+ jclass startClass = env->FindClass(slashClassName);
if (startClass == NULL) {
LOGE("JavaVM unable to locate class '%s'\n", slashClassName);
/* keep going */
} else {
- startMeth = env->GetStaticMethodID(startClass, "main",
+ jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
"([Ljava/lang/String;)V");
if (startMeth == NULL) {
LOGE("JavaVM unable to find main() in '%s'\n", className);
@@ -991,15 +900,13 @@ void AndroidRuntime::start(const char* className, const bool startSystemServer)
#endif
}
}
+ free(slashClassName);
LOGD("Shutting down VM\n");
if (mJavaVM->DetachCurrentThread() != JNI_OK)
LOGW("Warning: unable to detach main thread\n");
if (mJavaVM->DestroyJavaVM() != 0)
LOGW("Warning: VM did not shut down cleanly\n");
-
-bail:
- free(slashClassName);
}
void AndroidRuntime::start()
@@ -1014,6 +921,11 @@ void AndroidRuntime::onExit(int code)
exit(code);
}
+void AndroidRuntime::onVmCreated(JNIEnv* env)
+{
+ // If AndroidRuntime had anything to do here, we'd have done it in 'start'.
+}
+
/*
* Get the JNIEnv pointer for this thread.
*
@@ -1108,7 +1020,7 @@ static int javaDetachThread(void)
* into the VM before it really starts executing.
*/
/*static*/ int AndroidRuntime::javaCreateThreadEtc(
- android_thread_func_t entryFunction,
+ android_thread_func_t entryFunction,
void* userData,
const char* threadName,
int32_t threadPriority,
@@ -1212,7 +1124,8 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_graphics_Graphics),
REG_JNI(register_android_view_GLES20Canvas),
REG_JNI(register_android_view_Surface),
- REG_JNI(register_android_view_ViewRoot),
+ REG_JNI(register_android_view_TextureView),
+ REG_JNI(register_android_view_ViewAncestor),
REG_JNI(register_com_google_android_gles_jni_EGLImpl),
REG_JNI(register_com_google_android_gles_jni_GLImpl),
REG_JNI(register_android_opengl_jni_GLES10),
@@ -1296,12 +1209,15 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_backup_BackupDataOutput),
REG_JNI(register_android_backup_FileBackupHelperBase),
REG_JNI(register_android_backup_BackupHelperDispatcher),
-
+ REG_JNI(register_android_app_backup_FullBackup),
+ REG_JNI(register_android_app_ActivityThread),
REG_JNI(register_android_app_NativeActivity),
REG_JNI(register_android_view_InputChannel),
REG_JNI(register_android_view_InputQueue),
REG_JNI(register_android_view_KeyEvent),
REG_JNI(register_android_view_MotionEvent),
+ REG_JNI(register_android_view_PointerIcon),
+ REG_JNI(register_android_view_VelocityTracker),
REG_JNI(register_android_content_res_ObbScanner),
REG_JNI(register_android_content_res_Configuration),
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index 8064836..1b22e2d 100644
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -100,6 +100,8 @@ bool GraphicsJNI::SetPixels(JNIEnv* env, jintArray srcColors,
dst = (char*)dst + dstBitmap.rowBytes();
}
+ dstBitmap.notifyPixelsChanged();
+
env->ReleaseIntArrayElements(srcColors, const_cast<jint*>(array),
JNI_ABORT);
return true;
@@ -524,6 +526,7 @@ static void Bitmap_setPixel(JNIEnv* env, jobject, const SkBitmap* bitmap,
}
proc(bitmap->getAddr(x, y), &color, 1, x, y);
+ bitmap->notifyPixelsChanged();
}
static void Bitmap_setPixels(JNIEnv* env, jobject, const SkBitmap* bitmap,
@@ -652,7 +655,6 @@ static JNINativeMethod gBitmapMethods[] = {
#define kClassPathName "android/graphics/Bitmap"
-int register_android_graphics_Bitmap(JNIEnv* env);
int register_android_graphics_Bitmap(JNIEnv* env)
{
return android::AndroidRuntime::registerNativeMethods(env, kClassPathName,
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index 491a388..258ffa5 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -1,6 +1,7 @@
#define LOG_TAG "BitmapFactory"
#include "BitmapFactory.h"
+#include "NinePatchPeeker.h"
#include "SkImageDecoder.h"
#include "SkImageRef_ashmem.h"
#include "SkImageRef_GlobalPool.h"
@@ -11,6 +12,7 @@
#include "CreateJavaOutputStreamAdaptor.h"
#include "AutoDecodeCancel.h"
#include "Utils.h"
+#include "JNIHelp.h"
#include <android_runtime/AndroidRuntime.h>
#include <utils/Asset.h>
@@ -19,7 +21,6 @@
#include <sys/mman.h>
#include <sys/stat.h>
-jclass gOptions_class;
jfieldID gOptions_justBoundsFieldID;
jfieldID gOptions_sampleSizeFieldID;
jfieldID gOptions_configFieldID;
@@ -33,12 +34,8 @@ jfieldID gOptions_heightFieldID;
jfieldID gOptions_mimeFieldID;
jfieldID gOptions_mCancelID;
jfieldID gOptions_bitmapFieldID;
-jclass gBitmap_class;
jfieldID gBitmap_nativeBitmapFieldID;
-static jclass gFileDescriptor_class;
-static jfieldID gFileDescriptor_descriptor;
-
#if 0
#define TRACE_BITMAP(code) code
#else
@@ -47,65 +44,6 @@ static jfieldID gFileDescriptor_descriptor;
using namespace android;
-class NinePatchPeeker : public SkImageDecoder::Peeker {
- SkImageDecoder* fHost;
-public:
- NinePatchPeeker(SkImageDecoder* host) {
- // the host lives longer than we do, so a raw ptr is safe
- fHost = host;
- fPatchIsValid = false;
- }
-
- ~NinePatchPeeker() {
- if (fPatchIsValid) {
- free(fPatch);
- }
- }
-
- bool fPatchIsValid;
- Res_png_9patch* fPatch;
-
- virtual bool peek(const char tag[], const void* data, size_t length) {
- if (strcmp("npTc", tag) == 0 && length >= sizeof(Res_png_9patch)) {
- Res_png_9patch* patch = (Res_png_9patch*) data;
- size_t patchSize = patch->serializedSize();
- assert(length == patchSize);
- // You have to copy the data because it is owned by the png reader
- Res_png_9patch* patchNew = (Res_png_9patch*) malloc(patchSize);
- memcpy(patchNew, patch, patchSize);
- // this relies on deserialization being done in place
- Res_png_9patch::deserialize(patchNew);
- patchNew->fileToDevice();
- if (fPatchIsValid) {
- free(fPatch);
- }
- fPatch = patchNew;
- //printf("9patch: (%d,%d)-(%d,%d)\n",
- // fPatch.sizeLeft, fPatch.sizeTop,
- // fPatch.sizeRight, fPatch.sizeBottom);
- fPatchIsValid = true;
-
- // now update our host to force index or 32bit config
- // 'cause we don't want 565 predithered, since as a 9patch, we know
- // we will be stretched, and therefore we want to dither afterwards.
- static const SkBitmap::Config gNo565Pref[] = {
- SkBitmap::kIndex8_Config,
- SkBitmap::kIndex8_Config,
- SkBitmap::kARGB_8888_Config,
- SkBitmap::kARGB_8888_Config,
- SkBitmap::kARGB_8888_Config,
- SkBitmap::kARGB_8888_Config,
- };
- fHost->setPrefConfigTable(gNo565Pref);
- } else {
- fPatch = NULL;
- }
- return true; // keep on decoding
- }
-};
-
-///////////////////////////////////////////////////////////////////////////////
-
static inline int32_t validOrNeg1(bool isValid, int32_t value) {
// return isValid ? value : -1;
SkASSERT((int)isValid == 0 || (int)isValid == 1);
@@ -124,7 +62,7 @@ jstring getMimeTypeString(JNIEnv* env, SkImageDecoder::Format format) {
{ SkImageDecoder::kPNG_Format, "image/png" },
{ SkImageDecoder::kWBMP_Format, "image/vnd.wap.wbmp" }
};
-
+
const char* cstr = NULL;
for (size_t i = 0; i < SK_ARRAY_COUNT(gMimeTypes); i++) {
if (gMimeTypes[i].fFormat == format) {
@@ -185,7 +123,7 @@ static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding,
(allowPurgeable && optionsPurgeable(env, options));
bool preferQualityOverSpeed = false;
jobject javaBitmap = NULL;
-
+
if (NULL != options) {
sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID);
if (optionsJustBounds(env, options)) {
@@ -195,7 +133,7 @@ static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding,
env->SetIntField(options, gOptions_widthFieldID, -1);
env->SetIntField(options, gOptions_heightFieldID, -1);
env->SetObjectField(options, gOptions_mimeFieldID, 0);
-
+
jobject jconfig = env->GetObjectField(options, gOptions_configFieldID);
prefConfig = GraphicsJNI::getNativeBitmapConfig(env, jconfig);
isMutable = env->GetBooleanField(options, gOptions_mutableFieldID);
@@ -209,7 +147,7 @@ static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding,
if (NULL == decoder) {
return nullObjectReturn("SkImageDecoder::Factory returned null");
}
-
+
decoder->setSampleSize(sampleSize);
decoder->setDitherImage(doDither);
decoder->setPreferQualityOverSpeed(preferQualityOverSpeed);
@@ -356,8 +294,7 @@ static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz,
jobject bitmapFactoryOptions) {
NPE_CHECK_RETURN_ZERO(env, fileDescriptor);
- jint descriptor = env->GetIntField(fileDescriptor,
- gFileDescriptor_descriptor);
+ jint descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor);
bool isPurgeable = optionsPurgeable(env, bitmapFactoryOptions);
bool isShareable = optionsShareable(env, bitmapFactoryOptions);
@@ -482,7 +419,7 @@ static jbyteArray nativeScaleNinePatch(JNIEnv* env, jobject, jbyteArray chunkObj
}
for (int i = 0; i < chunk->numYDivs; i++) {
- chunk->yDivs[i] = int(chunk->yDivs[i] * scale + 0.5f);
+ chunk->yDivs[i] = int(chunk->yDivs[i] * scale + 0.5f);
if (i > 0 && chunk->yDivs[i] == chunk->yDivs[i - 1]) {
chunk->yDivs[i]++;
}
@@ -518,7 +455,7 @@ static void nativeSetDefaultConfig(JNIEnv* env, jobject, int nativeConfig) {
}
static jboolean nativeIsSeekable(JNIEnv* env, jobject, jobject fileDescriptor) {
- jint descriptor = env->GetIntField(fileDescriptor, gFileDescriptor_descriptor);
+ jint descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor);
return ::lseek64(descriptor, 0, SEEK_CUR) != -1 ? JNI_TRUE : JNI_FALSE;
}
@@ -562,12 +499,6 @@ static JNINativeMethod gOptionsMethods[] = {
{ "requestCancel", "()V", (void*)nativeRequestCancel }
};
-static jclass make_globalref(JNIEnv* env, const char classname[]) {
- jclass c = env->FindClass(classname);
- SkASSERT(c);
- return (jclass)env->NewGlobalRef(c);
-}
-
static jfieldID getFieldIDCheck(JNIEnv* env, jclass clazz,
const char fieldname[], const char type[]) {
jfieldID id = env->GetFieldID(clazz, fieldname, type);
@@ -575,35 +506,29 @@ static jfieldID getFieldIDCheck(JNIEnv* env, jclass clazz,
return id;
}
-#define kClassPathName "android/graphics/BitmapFactory"
-
-#define RETURN_ERR_IF_NULL(value) \
- do { if (!(value)) { assert(0); return -1; } } while (false)
-
-int register_android_graphics_BitmapFactory(JNIEnv* env);
int register_android_graphics_BitmapFactory(JNIEnv* env) {
- gOptions_class = make_globalref(env, "android/graphics/BitmapFactory$Options");
- gOptions_bitmapFieldID = getFieldIDCheck(env, gOptions_class, "inBitmap",
+ jclass options_class = env->FindClass("android/graphics/BitmapFactory$Options");
+ SkASSERT(options_class);
+ gOptions_bitmapFieldID = getFieldIDCheck(env, options_class, "inBitmap",
"Landroid/graphics/Bitmap;");
- gOptions_justBoundsFieldID = getFieldIDCheck(env, gOptions_class, "inJustDecodeBounds", "Z");
- gOptions_sampleSizeFieldID = getFieldIDCheck(env, gOptions_class, "inSampleSize", "I");
- gOptions_configFieldID = getFieldIDCheck(env, gOptions_class, "inPreferredConfig",
+ gOptions_justBoundsFieldID = getFieldIDCheck(env, options_class, "inJustDecodeBounds", "Z");
+ gOptions_sampleSizeFieldID = getFieldIDCheck(env, options_class, "inSampleSize", "I");
+ gOptions_configFieldID = getFieldIDCheck(env, options_class, "inPreferredConfig",
"Landroid/graphics/Bitmap$Config;");
- gOptions_mutableFieldID = getFieldIDCheck(env, gOptions_class, "inMutable", "Z");
- gOptions_ditherFieldID = getFieldIDCheck(env, gOptions_class, "inDither", "Z");
- gOptions_purgeableFieldID = getFieldIDCheck(env, gOptions_class, "inPurgeable", "Z");
- gOptions_shareableFieldID = getFieldIDCheck(env, gOptions_class, "inInputShareable", "Z");
- gOptions_preferQualityOverSpeedFieldID = getFieldIDCheck(env, gOptions_class,
+ gOptions_mutableFieldID = getFieldIDCheck(env, options_class, "inMutable", "Z");
+ gOptions_ditherFieldID = getFieldIDCheck(env, options_class, "inDither", "Z");
+ gOptions_purgeableFieldID = getFieldIDCheck(env, options_class, "inPurgeable", "Z");
+ gOptions_shareableFieldID = getFieldIDCheck(env, options_class, "inInputShareable", "Z");
+ gOptions_preferQualityOverSpeedFieldID = getFieldIDCheck(env, options_class,
"inPreferQualityOverSpeed", "Z");
- gOptions_widthFieldID = getFieldIDCheck(env, gOptions_class, "outWidth", "I");
- gOptions_heightFieldID = getFieldIDCheck(env, gOptions_class, "outHeight", "I");
- gOptions_mimeFieldID = getFieldIDCheck(env, gOptions_class, "outMimeType", "Ljava/lang/String;");
- gOptions_mCancelID = getFieldIDCheck(env, gOptions_class, "mCancel", "Z");
+ gOptions_widthFieldID = getFieldIDCheck(env, options_class, "outWidth", "I");
+ gOptions_heightFieldID = getFieldIDCheck(env, options_class, "outHeight", "I");
+ gOptions_mimeFieldID = getFieldIDCheck(env, options_class, "outMimeType", "Ljava/lang/String;");
+ gOptions_mCancelID = getFieldIDCheck(env, options_class, "mCancel", "Z");
- gBitmap_class = make_globalref(env, "android/graphics/Bitmap");
- gBitmap_nativeBitmapFieldID = getFieldIDCheck(env, gBitmap_class, "mNativeBitmap", "I");
- gFileDescriptor_class = make_globalref(env, "java/io/FileDescriptor");
- gFileDescriptor_descriptor = getFieldIDCheck(env, gFileDescriptor_class, "descriptor", "I");
+ jclass bitmap_class = env->FindClass("android/graphics/Bitmap");
+ SkASSERT(bitmap_class);
+ gBitmap_nativeBitmapFieldID = getFieldIDCheck(env, bitmap_class, "mNativeBitmap", "I");
int ret = AndroidRuntime::registerNativeMethods(env,
"android/graphics/BitmapFactory$Options",
@@ -612,6 +537,6 @@ int register_android_graphics_BitmapFactory(JNIEnv* env) {
if (ret) {
return ret;
}
- return android::AndroidRuntime::registerNativeMethods(env, kClassPathName,
+ return android::AndroidRuntime::registerNativeMethods(env, "android/graphics/BitmapFactory",
gMethods, SK_ARRAY_COUNT(gMethods));
}
diff --git a/core/jni/android/graphics/BitmapRegionDecoder.cpp b/core/jni/android/graphics/BitmapRegionDecoder.cpp
index ee3e209..d437929 100644
--- a/core/jni/android/graphics/BitmapRegionDecoder.cpp
+++ b/core/jni/android/graphics/BitmapRegionDecoder.cpp
@@ -28,6 +28,7 @@
#include "SkBitmapRegionDecoder.h"
#include "CreateJavaOutputStreamAdaptor.h"
#include "Utils.h"
+#include "JNIHelp.h"
#include <android_runtime/AndroidRuntime.h>
#include "android_util_Binder.h"
@@ -39,9 +40,6 @@
#include <utils/Asset.h>
#include <sys/stat.h>
-static jclass gFileDescriptor_class;
-static jfieldID gFileDescriptor_descriptor;
-
#if 0
#define TRACE_BITMAP(code) code
#else
@@ -111,8 +109,7 @@ static jobject nativeNewInstanceFromFileDescriptor(JNIEnv* env, jobject clazz,
jobject fileDescriptor, jboolean isShareable) {
NPE_CHECK_RETURN_ZERO(env, fileDescriptor);
- jint descriptor = env->GetIntField(fileDescriptor,
- gFileDescriptor_descriptor);
+ jint descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor);
SkStream *stream = NULL;
struct stat fdStat;
int newFD;
@@ -300,25 +297,8 @@ static JNINativeMethod gBitmapRegionDecoderMethods[] = {
#define kClassPathName "android/graphics/BitmapRegionDecoder"
-static jclass make_globalref(JNIEnv* env, const char classname[]) {
- jclass c = env->FindClass(classname);
- SkASSERT(c);
- return (jclass)env->NewGlobalRef(c);
-}
-
-static jfieldID getFieldIDCheck(JNIEnv* env, jclass clazz,
- const char fieldname[], const char type[]) {
- jfieldID id = env->GetFieldID(clazz, fieldname, type);
- SkASSERT(id);
- return id;
-}
-
-int register_android_graphics_BitmapRegionDecoder(JNIEnv* env);
int register_android_graphics_BitmapRegionDecoder(JNIEnv* env)
{
-
- gFileDescriptor_class = make_globalref(env, "java/io/FileDescriptor");
- gFileDescriptor_descriptor = getFieldIDCheck(env, gFileDescriptor_class, "descriptor", "I");
return android::AndroidRuntime::registerNativeMethods(env, kClassPathName,
gBitmapRegionDecoderMethods, SK_ARRAY_COUNT(gBitmapRegionDecoderMethods));
}
diff --git a/core/jni/android/graphics/Camera.cpp b/core/jni/android/graphics/Camera.cpp
index 0d715fd..76d415a 100644
--- a/core/jni/android/graphics/Camera.cpp
+++ b/core/jni/android/graphics/Camera.cpp
@@ -98,7 +98,6 @@ static JNINativeMethod gCameraMethods[] = {
{ "dotWithNormal", "(FFF)F", (void*)Camera_dotWithNormal }
};
-int register_android_graphics_Camera(JNIEnv* env);
int register_android_graphics_Camera(JNIEnv* env) {
jclass clazz = env->FindClass("android/graphics/Camera");
if (clazz == 0) {
@@ -113,4 +112,3 @@ int register_android_graphics_Camera(JNIEnv* env) {
gCameraMethods,
SK_ARRAY_COUNT(gCameraMethods));
}
-
diff --git a/core/jni/android/graphics/Canvas.cpp b/core/jni/android/graphics/Canvas.cpp
index 0cdb357..4a09232 100644
--- a/core/jni/android/graphics/Canvas.cpp
+++ b/core/jni/android/graphics/Canvas.cpp
@@ -27,6 +27,7 @@
#include "SkTemplates.h"
#include "TextLayout.h"
+#include "TextLayoutCache.h"
#include "unicode/ubidi.h"
#include "unicode/ushape.h"
@@ -72,20 +73,6 @@ public:
static jboolean isOpaque(JNIEnv* env, jobject jcanvas) {
NPE_CHECK_RETURN_ZERO(env, jcanvas);
SkCanvas* canvas = GraphicsJNI::getNativeCanvas(env, jcanvas);
-
- /*
- Currently we cannot support transparency in GL-based canvas' at
- the view level. Therefore we cannot base our answer on the device's
- bitmap, but need to hard-code the answer. If we relax this
- limitation in views, we can simplify the following code as well.
-
- Use the getViewport() call to find out if we're gl-based...
- */
- if (canvas->getViewport(NULL)) {
- return true;
- }
-
- // normal technique, rely on the device's bitmap for the answer
return canvas->getDevice()->accessBitmap(false).isOpaque();
}
@@ -742,7 +729,11 @@ public:
jcharArray text, int index, int count,
jfloat x, jfloat y, int flags, SkPaint* paint) {
jchar* textArray = env->GetCharArrayElements(text, NULL);
+#if RTL_USE_HARFBUZZ
+ drawTextWithGlyphs(canvas, textArray + index, 0, count, x, y, flags, paint);
+#else
TextLayout::drawText(paint, textArray + index, count, flags, x, y, canvas);
+#endif
env->ReleaseCharArrayElements(text, textArray, JNI_ABORT);
}
@@ -751,18 +742,106 @@ public:
int start, int end,
jfloat x, jfloat y, int flags, SkPaint* paint) {
const jchar* textArray = env->GetStringChars(text, NULL);
+#if RTL_USE_HARFBUZZ
+ drawTextWithGlyphs(canvas, textArray, start, end, x, y, flags, paint);
+#else
TextLayout::drawText(paint, textArray + start, end - start, flags, x, y, canvas);
+#endif
+ env->ReleaseStringChars(text, textArray);
+ }
+
+ static void drawTextWithGlyphs(SkCanvas* canvas, const jchar* textArray,
+ int start, int end,
+ jfloat x, jfloat y, int flags, SkPaint* paint) {
+
+ jint count = end - start;
+ sp<TextLayoutCacheValue> value = gTextLayoutCache.getValue(
+ paint, textArray, start, count, end, flags);
+ if (value == NULL) {
+ LOGE("Cannot get TextLayoutCache value");
+ return ;
+ }
+#if DEBUG_GLYPHS
+ logGlyphs(value);
+#endif
+ doDrawGlyphs(canvas, value->getGlyphs(), 0, value->getGlyphsCount(),
+ x, y, flags, paint);
+ }
+
+ static void drawTextWithGlyphs(SkCanvas* canvas, const jchar* textArray,
+ int start, int count, int contextCount,
+ jfloat x, jfloat y, int flags, SkPaint* paint) {
+
+ sp<TextLayoutCacheValue> value = gTextLayoutCache.getValue(
+ paint, textArray, start, count, contextCount, flags);
+ if (value == NULL) {
+ LOGE("Cannot get TextLayoutCache value");
+ return ;
+ }
+#if DEBUG_GLYPHS
+ logGlyphs(value);
+#endif
+ doDrawGlyphs(canvas, value->getGlyphs(), 0, value->getGlyphsCount(),
+ x, y, flags, paint);
+ }
+
+ static void drawTextWithGlyphs___CIIFFIPaint(JNIEnv* env, jobject, SkCanvas* canvas,
+ jcharArray text, int index, int count,
+ jfloat x, jfloat y, int flags, SkPaint* paint) {
+ jchar* textArray = env->GetCharArrayElements(text, NULL);
+ drawTextWithGlyphs(canvas, textArray + index, 0, count, x, y, flags, paint);
+ env->ReleaseCharArrayElements(text, textArray, JNI_ABORT);
+ }
+
+ static void drawTextWithGlyphs__StringIIFFIPaint(JNIEnv* env, jobject,
+ SkCanvas* canvas, jstring text,
+ int start, int end,
+ jfloat x, jfloat y, int flags, SkPaint* paint) {
+
+ const jchar* textArray = env->GetStringChars(text, NULL);
+ drawTextWithGlyphs(canvas, textArray, start, end, x, y, flags, paint);
env->ReleaseStringChars(text, textArray);
}
+ static void doDrawGlyphs(SkCanvas* canvas, const jchar* glyphArray, int index, int count,
+ jfloat x, jfloat y, int flags, SkPaint* paint) {
+ // TODO: need to suppress this code after the GL renderer is modified for not
+ // copying the paint
+
+ // Save old text encoding
+ SkPaint::TextEncoding oldEncoding = paint->getTextEncoding();
+ // Define Glyph encoding
+ paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+
+ canvas->drawText(glyphArray + index * 2, count * 2, x, y, *paint);
+
+ // Get back old encoding
+ paint->setTextEncoding(oldEncoding);
+ }
+
+ static void drawGlyphs___CIIFFIPaint(JNIEnv* env, jobject, SkCanvas* canvas,
+ jcharArray glyphs, int index, int count,
+ jfloat x, jfloat y, int flags, SkPaint* paint) {
+ jchar* glyphArray = env->GetCharArrayElements(glyphs, NULL);
+
+ doDrawGlyphs(canvas, glyphArray, index, count, x, y, flags, paint);
+
+ env->ReleaseCharArrayElements(glyphs, glyphArray, JNI_ABORT);
+ }
+
static void drawTextRun___CIIIIFFIPaint(
JNIEnv* env, jobject, SkCanvas* canvas, jcharArray text, int index,
int count, int contextIndex, int contextCount,
jfloat x, jfloat y, int dirFlags, SkPaint* paint) {
jchar* chars = env->GetCharArrayElements(text, NULL);
+#if RTL_USE_HARFBUZZ
+ drawTextWithGlyphs(canvas, chars + contextIndex, index - contextIndex,
+ count, contextCount, x, y, dirFlags, paint);
+#else
TextLayout::drawTextRun(paint, chars + contextIndex, index - contextIndex,
- count, contextCount, dirFlags, x, y, canvas);
+ count, contextCount, dirFlags, x, y, canvas);
+#endif
env->ReleaseCharArrayElements(text, chars, JNI_ABORT);
}
@@ -774,8 +853,13 @@ public:
jint count = end - start;
jint contextCount = contextEnd - contextStart;
const jchar* chars = env->GetStringChars(text, NULL);
+#if RTL_USE_HARFBUZZ
+ drawTextWithGlyphs(canvas, chars + contextStart, start - contextStart,
+ count, contextCount, x, y, dirFlags, paint);
+#else
TextLayout::drawTextRun(paint, chars + contextStart, start - contextStart,
- count, contextCount, dirFlags, x, y, canvas);
+ count, contextCount, dirFlags, x, y, canvas);
+#endif
env->ReleaseStringChars(text, chars);
}
@@ -831,7 +915,7 @@ public:
SkPath* path, jfloat hOffset, jfloat vOffset, jint bidiFlags, SkPaint* paint) {
jchar* textArray = env->GetCharArrayElements(text, NULL);
- TextLayout::drawTextOnPath(paint, textArray, count, bidiFlags, hOffset, vOffset,
+ TextLayout::drawTextOnPath(paint, textArray + index, count, bidiFlags, hOffset, vOffset,
path, canvas);
env->ReleaseCharArrayElements(text, textArray, 0);
}
@@ -946,6 +1030,12 @@ static JNINativeMethod gCanvasMethods[] = {
(void*) SkCanvasGlue::drawText___CIIFFIPaint},
{"native_drawText","(ILjava/lang/String;IIFFII)V",
(void*) SkCanvasGlue::drawText__StringIIFFIPaint},
+ {"native_drawTextWithGlyphs","(I[CIIFFII)V",
+ (void*) SkCanvasGlue::drawTextWithGlyphs___CIIFFIPaint},
+ {"native_drawTextWithGlyphs","(ILjava/lang/String;IIFFII)V",
+ (void*) SkCanvasGlue::drawTextWithGlyphs__StringIIFFIPaint},
+ {"native_drawGlyphs","(I[CIIFFII)V",
+ (void*) SkCanvasGlue::drawGlyphs___CIIFFIPaint},
{"native_drawTextRun","(I[CIIIIFFII)V",
(void*) SkCanvasGlue::drawTextRun___CIIIIFFIPaint},
{"native_drawTextRun","(ILjava/lang/String;IIIIFFII)V",
diff --git a/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp b/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp
index 137acc6..6ce3f51 100644
--- a/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp
+++ b/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp
@@ -3,7 +3,6 @@
#define RETURN_NULL_IF_NULL(value) \
do { if (!(value)) { SkASSERT(0); return NULL; } } while (false)
-static jclass gInputStream_Clazz;
static jmethodID gInputStream_resetMethodID;
static jmethodID gInputStream_markMethodID;
static jmethodID gInputStream_availableMethodID;
@@ -19,10 +18,10 @@ public:
SkASSERT(fCapacity > 0);
fBytesRead = 0;
}
-
+
virtual bool rewind() {
JNIEnv* env = fEnv;
-
+
fBytesRead = 0;
env->CallVoidMethod(fJavaInputStream, gInputStream_resetMethodID);
@@ -34,7 +33,7 @@ public:
}
return true;
}
-
+
size_t doRead(void* buffer, size_t size) {
JNIEnv* env = fEnv;
size_t bytesRead = 0;
@@ -43,7 +42,7 @@ public:
size_t requested = size;
if (requested > fCapacity)
requested = fCapacity;
-
+
jint n = env->CallIntMethod(fJavaInputStream,
gInputStream_readMethodID, fJavaByteArray, 0, requested);
if (env->ExceptionCheck()) {
@@ -52,11 +51,11 @@ public:
SkDebugf("---- read threw an exception\n");
return 0;
}
-
+
if (n < 0) { // n == 0 should not be possible, see InputStream read() specifications.
break; // eof
}
-
+
env->GetByteArrayRegion(fJavaByteArray, 0, n,
reinterpret_cast<jbyte*>(buffer));
if (env->ExceptionCheck()) {
@@ -65,16 +64,16 @@ public:
SkDebugf("---- read:GetByteArrayRegion threw an exception\n");
return 0;
}
-
+
buffer = (void*)((char*)buffer + n);
bytesRead += n;
size -= n;
fBytesRead += n;
} while (size != 0);
-
+
return bytesRead;
}
-
+
size_t doSkip(size_t size) {
JNIEnv* env = fEnv;
@@ -92,7 +91,7 @@ public:
return (size_t)skipped;
}
-
+
size_t doSize() {
JNIEnv* env = fEnv;
jint avail = env->CallIntMethod(fJavaInputStream,
@@ -105,7 +104,7 @@ public:
}
return avail;
}
-
+
virtual size_t read(void* buffer, size_t size) {
JNIEnv* env = fEnv;
if (NULL == buffer) {
@@ -134,7 +133,7 @@ public:
}
return this->doRead(buffer, size);
}
-
+
private:
JNIEnv* fEnv;
jobject fJavaInputStream; // the caller owns this object
@@ -148,19 +147,18 @@ SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream,
static bool gInited;
if (!gInited) {
- gInputStream_Clazz = env->FindClass("java/io/InputStream");
- RETURN_NULL_IF_NULL(gInputStream_Clazz);
- gInputStream_Clazz = (jclass)env->NewGlobalRef(gInputStream_Clazz);
+ jclass inputStream_Clazz = env->FindClass("java/io/InputStream");
+ RETURN_NULL_IF_NULL(inputStream_Clazz);
- gInputStream_resetMethodID = env->GetMethodID(gInputStream_Clazz,
+ gInputStream_resetMethodID = env->GetMethodID(inputStream_Clazz,
"reset", "()V");
- gInputStream_markMethodID = env->GetMethodID(gInputStream_Clazz,
+ gInputStream_markMethodID = env->GetMethodID(inputStream_Clazz,
"mark", "(I)V");
- gInputStream_availableMethodID = env->GetMethodID(gInputStream_Clazz,
+ gInputStream_availableMethodID = env->GetMethodID(inputStream_Clazz,
"available", "()I");
- gInputStream_readMethodID = env->GetMethodID(gInputStream_Clazz,
+ gInputStream_readMethodID = env->GetMethodID(inputStream_Clazz,
"read", "([BII)I");
- gInputStream_skipMethodID = env->GetMethodID(gInputStream_Clazz,
+ gInputStream_skipMethodID = env->GetMethodID(inputStream_Clazz,
"skip", "(J)J");
RETURN_NULL_IF_NULL(gInputStream_resetMethodID);
@@ -181,7 +179,6 @@ SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream,
///////////////////////////////////////////////////////////////////////////////
-static jclass gOutputStream_Clazz;
static jmethodID gOutputStream_writeMethodID;
static jmethodID gOutputStream_flushMethodID;
@@ -191,11 +188,11 @@ public:
: fEnv(env), fJavaOutputStream(stream), fJavaByteArray(storage) {
fCapacity = env->GetArrayLength(storage);
}
-
+
virtual bool write(const void* buffer, size_t size) {
JNIEnv* env = fEnv;
jbyteArray storage = fJavaByteArray;
-
+
while (size > 0) {
size_t requested = size;
if (requested > fCapacity) {
@@ -210,7 +207,7 @@ public:
SkDebugf("--- write:SetByteArrayElements threw an exception\n");
return false;
}
-
+
fEnv->CallVoidMethod(fJavaOutputStream, gOutputStream_writeMethodID,
storage, 0, requested);
if (env->ExceptionCheck()) {
@@ -219,17 +216,17 @@ public:
SkDebugf("------- write threw an exception\n");
return false;
}
-
+
buffer = (void*)((char*)buffer + requested);
size -= requested;
}
return true;
}
-
+
virtual void flush() {
fEnv->CallVoidMethod(fJavaOutputStream, gOutputStream_flushMethodID);
}
-
+
private:
JNIEnv* fEnv;
jobject fJavaOutputStream; // the caller owns this object
@@ -242,14 +239,13 @@ SkWStream* CreateJavaOutputStreamAdaptor(JNIEnv* env, jobject stream,
static bool gInited;
if (!gInited) {
- gOutputStream_Clazz = env->FindClass("java/io/OutputStream");
- RETURN_NULL_IF_NULL(gOutputStream_Clazz);
- gOutputStream_Clazz = (jclass)env->NewGlobalRef(gOutputStream_Clazz);
+ jclass outputStream_Clazz = env->FindClass("java/io/OutputStream");
+ RETURN_NULL_IF_NULL(outputStream_Clazz);
- gOutputStream_writeMethodID = env->GetMethodID(gOutputStream_Clazz,
+ gOutputStream_writeMethodID = env->GetMethodID(outputStream_Clazz,
"write", "([BII)V");
RETURN_NULL_IF_NULL(gOutputStream_writeMethodID);
- gOutputStream_flushMethodID = env->GetMethodID(gOutputStream_Clazz,
+ gOutputStream_flushMethodID = env->GetMethodID(outputStream_Clazz,
"flush", "()V");
RETURN_NULL_IF_NULL(gOutputStream_flushMethodID);
@@ -258,4 +254,3 @@ SkWStream* CreateJavaOutputStreamAdaptor(JNIEnv* env, jobject stream,
return new SkJavaOutputStream(env, stream, storage);
}
-
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index 6fedde2..64bd207 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -1,6 +1,7 @@
#define LOG_TAG "GraphicsJNI"
#include "jni.h"
+#include "JNIHelp.h"
#include "GraphicsJNI.h"
#include "SkCanvas.h"
@@ -9,44 +10,32 @@
#include "SkRegion.h"
#include <android_runtime/AndroidRuntime.h>
-void doThrow(JNIEnv* env, const char* exc, const char* msg) {
- // don't throw a new exception if we already have one pending
- if (env->ExceptionCheck() == JNI_FALSE) {
- jclass npeClazz;
-
- npeClazz = env->FindClass(exc);
- LOG_FATAL_IF(npeClazz == NULL, "Unable to find class %s", exc);
-
- env->ThrowNew(npeClazz, msg);
- }
-}
-
void doThrowNPE(JNIEnv* env) {
- doThrow(env, "java/lang/NullPointerException");
+ jniThrowNullPointerException(env, NULL);
}
void doThrowAIOOBE(JNIEnv* env) {
- doThrow(env, "java/lang/ArrayIndexOutOfBoundsException");
+ jniThrowException(env, "java/lang/ArrayIndexOutOfBoundsException", NULL);
}
void doThrowRE(JNIEnv* env, const char* msg) {
- doThrow(env, "java/lang/RuntimeException", msg);
+ jniThrowRuntimeException(env, msg);
}
void doThrowIAE(JNIEnv* env, const char* msg) {
- doThrow(env, "java/lang/IllegalArgumentException", msg);
+ jniThrowException(env, "java/lang/IllegalArgumentException", msg);
}
void doThrowISE(JNIEnv* env, const char* msg) {
- doThrow(env, "java/lang/IllegalStateException", msg);
+ jniThrowException(env, "java/lang/IllegalStateException", msg);
}
void doThrowOOME(JNIEnv* env, const char* msg) {
- doThrow(env, "java/lang/OutOfMemoryError", msg);
+ jniThrowException(env, "java/lang/OutOfMemoryError", msg);
}
void doThrowIOE(JNIEnv* env, const char* msg) {
- doThrow(env, "java/io/IOException", msg);
+ jniThrowException(env, "java/io/IOException", msg);
}
bool GraphicsJNI::hasException(JNIEnv *env) {
@@ -229,7 +218,7 @@ void GraphicsJNI::irect_to_jrect(const SkIRect& ir, JNIEnv* env, jobject obj)
SkRect* GraphicsJNI::jrectf_to_rect(JNIEnv* env, jobject obj, SkRect* r)
{
SkASSERT(env->IsInstanceOf(obj, gRectF_class));
-
+
r->set(SkFloatToScalar(env->GetFloatField(obj, gRectF_leftFieldID)),
SkFloatToScalar(env->GetFloatField(obj, gRectF_topFieldID)),
SkFloatToScalar(env->GetFloatField(obj, gRectF_rightFieldID)),
@@ -240,7 +229,7 @@ SkRect* GraphicsJNI::jrectf_to_rect(JNIEnv* env, jobject obj, SkRect* r)
SkRect* GraphicsJNI::jrect_to_rect(JNIEnv* env, jobject obj, SkRect* r)
{
SkASSERT(env->IsInstanceOf(obj, gRect_class));
-
+
r->set(SkIntToScalar(env->GetIntField(obj, gRect_leftFieldID)),
SkIntToScalar(env->GetIntField(obj, gRect_topFieldID)),
SkIntToScalar(env->GetIntField(obj, gRect_rightFieldID)),
@@ -261,7 +250,7 @@ void GraphicsJNI::rect_to_jrectf(const SkRect& r, JNIEnv* env, jobject obj)
SkIPoint* GraphicsJNI::jpoint_to_ipoint(JNIEnv* env, jobject obj, SkIPoint* point)
{
SkASSERT(env->IsInstanceOf(obj, gPoint_class));
-
+
point->set(env->GetIntField(obj, gPoint_xFieldID),
env->GetIntField(obj, gPoint_yFieldID));
return point;
@@ -278,7 +267,7 @@ void GraphicsJNI::ipoint_to_jpoint(const SkIPoint& ir, JNIEnv* env, jobject obj)
SkPoint* GraphicsJNI::jpointf_to_point(JNIEnv* env, jobject obj, SkPoint* point)
{
SkASSERT(env->IsInstanceOf(obj, gPointF_class));
-
+
point->set(SkFloatToScalar(env->GetIntField(obj, gPointF_xFieldID)),
SkFloatToScalar(env->GetIntField(obj, gPointF_yFieldID)));
return point;
@@ -360,15 +349,11 @@ jobject GraphicsJNI::createBitmap(JNIEnv* env, SkBitmap* bitmap, jbyteArray buff
{
SkASSERT(bitmap);
SkASSERT(bitmap->pixelRef());
-
- jobject obj = env->AllocObject(gBitmap_class);
- if (obj) {
- env->CallVoidMethod(obj, gBitmap_constructorMethodID,
- (jint)bitmap, buffer, isMutable, ninepatch, density);
- if (hasException(env)) {
- obj = NULL;
- }
- }
+
+ jobject obj = env->NewObject(gBitmap_class, gBitmap_constructorMethodID,
+ static_cast<jint>(reinterpret_cast<uintptr_t>(bitmap)),
+ buffer, isMutable, ninepatch, density);
+ hasException(env); // For the side effect of logging.
return obj;
}
@@ -383,30 +368,19 @@ jobject GraphicsJNI::createBitmapRegionDecoder(JNIEnv* env, SkBitmapRegionDecode
{
SkASSERT(bitmap != NULL);
- jobject obj = env->AllocObject(gBitmapRegionDecoder_class);
- if (hasException(env)) {
- obj = NULL;
- return obj;
- }
- if (obj) {
- env->CallVoidMethod(obj, gBitmapRegionDecoder_constructorMethodID, (jint)bitmap);
- if (hasException(env)) {
- obj = NULL;
- }
- }
+ jobject obj = env->NewObject(gBitmapRegionDecoder_class,
+ gBitmapRegionDecoder_constructorMethodID,
+ static_cast<jint>(reinterpret_cast<uintptr_t>(bitmap)));
+ hasException(env); // For the side effect of logging.
return obj;
}
jobject GraphicsJNI::createRegion(JNIEnv* env, SkRegion* region)
{
SkASSERT(region != NULL);
- jobject obj = env->AllocObject(gRegion_class);
- if (obj) {
- env->CallVoidMethod(obj, gRegion_constructorMethodID, (jint)region, 0);
- if (hasException(env)) {
- obj = NULL;
- }
- }
+ jobject obj = env->NewObject(gRegion_class, gRegion_constructorMethodID,
+ static_cast<jint>(reinterpret_cast<uintptr_t>(region)), 0);
+ hasException(env); // For the side effect of logging.
return obj;
}
@@ -438,7 +412,7 @@ AndroidPixelRef::AndroidPixelRef(JNIEnv* env, void* storage, size_t size, jbyteA
// If storageObj is NULL, the memory was NOT allocated on the Java heap
fOnJavaHeap = (storageObj != NULL);
-
+
}
AndroidPixelRef::~AndroidPixelRef() {
@@ -508,11 +482,11 @@ jbyteArray GraphicsJNI::allocateJavaPixelRef(JNIEnv* env, SkBitmap* bitmap,
SkColorTable* ctable) {
Sk64 size64 = bitmap->getSize64();
if (size64.isNeg() || !size64.is32()) {
- doThrow(env, "java/lang/IllegalArgumentException",
- "bitmap size exceeds 32bits");
+ jniThrowException(env, "java/lang/IllegalArgumentException",
+ "bitmap size exceeds 32bits");
return NULL;
}
-
+
size_t size = size64.get32();
jbyteArray arrayObj = env->NewByteArray(size);
if (arrayObj) {
@@ -540,7 +514,7 @@ JavaPixelAllocator::JavaPixelAllocator(JNIEnv* env)
sk_throw();
}
}
-
+
bool JavaPixelAllocator::allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) {
JNIEnv* env = vm2env(fVM);
@@ -625,14 +599,14 @@ int register_android_graphics_Graphics(JNIEnv* env)
gBitmapConfig_class = make_globalref(env, "android/graphics/Bitmap$Config");
gBitmapConfig_nativeInstanceID = getFieldIDCheck(env, gBitmapConfig_class,
- "nativeInt", "I");
+ "nativeInt", "I");
gCanvas_class = make_globalref(env, "android/graphics/Canvas");
gCanvas_nativeInstanceID = getFieldIDCheck(env, gCanvas_class, "mNativeCanvas", "I");
gPaint_class = make_globalref(env, "android/graphics/Paint");
gPaint_nativeInstanceID = getFieldIDCheck(env, gPaint_class, "mNativePaint", "I");
-
+
gPicture_class = make_globalref(env, "android/graphics/Picture");
gPicture_nativeInstanceID = getFieldIDCheck(env, gPicture_class, "mNativePicture", "I");
@@ -640,6 +614,6 @@ int register_android_graphics_Graphics(JNIEnv* env)
gRegion_nativeInstanceID = getFieldIDCheck(env, gRegion_class, "mNativeRegion", "I");
gRegion_constructorMethodID = env->GetMethodID(gRegion_class, "<init>",
"(II)V");
-
+
return 0;
}
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index d678a5d..cc32f44 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -29,26 +29,26 @@ public:
static SkRect* jrectf_to_rect(JNIEnv*, jobject jrectf, SkRect*);
static SkRect* jrect_to_rect(JNIEnv*, jobject jrect, SkRect*);
static void rect_to_jrectf(const SkRect&, JNIEnv*, jobject jrectf);
-
+
static void set_jpoint(JNIEnv*, jobject jrect, int x, int y);
-
+
static SkIPoint* jpoint_to_ipoint(JNIEnv*, jobject jpoint, SkIPoint* point);
static void ipoint_to_jpoint(const SkIPoint& point, JNIEnv*, jobject jpoint);
-
+
static SkPoint* jpointf_to_point(JNIEnv*, jobject jpointf, SkPoint* point);
static void point_to_jpointf(const SkPoint& point, JNIEnv*, jobject jpointf);
-
+
static SkCanvas* getNativeCanvas(JNIEnv*, jobject canvas);
static SkPaint* getNativePaint(JNIEnv*, jobject paint);
static SkBitmap* getNativeBitmap(JNIEnv*, jobject bitmap);
static SkPicture* getNativePicture(JNIEnv*, jobject picture);
static SkRegion* getNativeRegion(JNIEnv*, jobject region);
-
+
/** Return the corresponding native config from the java Config enum,
or kNo_Config if the java object is null.
*/
static SkBitmap::Config getNativeBitmapConfig(JNIEnv*, jobject jconfig);
-
+
/** Create a java Bitmap object given the native bitmap (required) and optional
storage array (may be null).
*/
@@ -57,7 +57,7 @@ public:
static jobject createBitmap(JNIEnv* env, SkBitmap* bitmap, bool isMutable,
jbyteArray ninepatch, int density = -1);
-
+
static jobject createRegion(JNIEnv* env, SkRegion* region);
static jobject createBitmapRegionDecoder(JNIEnv* env, SkBitmapRegionDecoder* bitmap);
@@ -138,7 +138,7 @@ public:
JavaPixelAllocator(JNIEnv* env);
// overrides
virtual bool allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable);
-
+
/** Return the Java array object created for the last allocation.
* This returns a local JNI reference which the caller is responsible
* for storing appropriately (usually by passing it to the Bitmap
@@ -173,10 +173,10 @@ public:
AutoJavaFloatArray(JNIEnv* env, jfloatArray array,
int minLength = 0, JNIAccess = kRW_JNIAccess);
~AutoJavaFloatArray();
-
+
float* ptr() const { return fPtr; }
int length() const { return fLen; }
-
+
private:
JNIEnv* fEnv;
jfloatArray fArray;
@@ -189,10 +189,10 @@ class AutoJavaIntArray {
public:
AutoJavaIntArray(JNIEnv* env, jintArray array, int minLength = 0);
~AutoJavaIntArray();
-
+
jint* ptr() const { return fPtr; }
int length() const { return fLen; }
-
+
private:
JNIEnv* fEnv;
jintArray fArray;
@@ -205,10 +205,10 @@ public:
AutoJavaShortArray(JNIEnv* env, jshortArray array,
int minLength = 0, JNIAccess = kRW_JNIAccess);
~AutoJavaShortArray();
-
+
jshort* ptr() const { return fPtr; }
int length() const { return fLen; }
-
+
private:
JNIEnv* fEnv;
jshortArray fArray;
@@ -221,10 +221,10 @@ class AutoJavaByteArray {
public:
AutoJavaByteArray(JNIEnv* env, jbyteArray array, int minLength = 0);
~AutoJavaByteArray();
-
+
jbyte* ptr() const { return fPtr; }
int length() const { return fLen; }
-
+
private:
JNIEnv* fEnv;
jbyteArray fArray;
@@ -232,7 +232,6 @@ private:
int fLen;
};
-void doThrow(JNIEnv* env, const char* exc, const char* msg = NULL);
void doThrowNPE(JNIEnv* env);
void doThrowAIOOBE(JNIEnv* env); // Array Index Out Of Bounds Exception
void doThrowIAE(JNIEnv* env, const char* msg = NULL); // Illegal Argument
diff --git a/core/jni/android/graphics/HarfbuzzSkia.cpp b/core/jni/android/graphics/HarfbuzzSkia.cpp
new file mode 100644
index 0000000..92c743f
--- /dev/null
+++ b/core/jni/android/graphics/HarfbuzzSkia.cpp
@@ -0,0 +1,232 @@
+/*
+ * Copyright 2011, The Android Open Source Project
+ * Copyright 2011, Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "HarfbuzzSkia.h"
+
+#include "SkFontHost.h"
+
+#include "SkPaint.h"
+#include "SkPath.h"
+#include "SkPoint.h"
+#include "SkRect.h"
+#include "SkTypeface.h"
+
+#include <utils/Log.h>
+
+extern "C" {
+#include "harfbuzz-shaper.h"
+}
+
+// This file implements the callbacks which Harfbuzz requires by using Skia
+// calls. See the Harfbuzz source for references about what these callbacks do.
+
+namespace android {
+
+static void setupPaintWithFontData(SkPaint* paint, FontData* data) {
+ paint->setTypeface(data->typeFace);
+ paint->setTextSize(data->textSize);
+ paint->setTextSkewX(data->textSkewX);
+ paint->setTextScaleX(data->textScaleX);
+ paint->setFlags(data->flags);
+ paint->setHinting(data->hinting);
+}
+
+static HB_Bool stringToGlyphs(HB_Font hbFont, const HB_UChar16* characters, hb_uint32 length,
+ HB_Glyph* glyphs, hb_uint32* glyphsSize, HB_Bool isRTL)
+{
+ FontData* data = reinterpret_cast<FontData*>(hbFont->userData);
+ SkPaint paint;
+ setupPaintWithFontData(&paint, data);
+
+ paint.setTextEncoding(SkPaint::kUTF16_TextEncoding);
+ uint16_t* skiaGlyphs = reinterpret_cast<uint16_t*>(glyphs);
+ int numGlyphs = paint.textToGlyphs(characters, length * sizeof(uint16_t), skiaGlyphs);
+
+ // HB_Glyph is 32-bit, but Skia outputs only 16-bit numbers. So our
+ // |glyphs| array needs to be converted.
+ for (int i = numGlyphs - 1; i >= 0; --i) {
+ glyphs[i] = skiaGlyphs[i];
+ }
+
+ *glyphsSize = numGlyphs;
+ return 1;
+}
+
+static void glyphsToAdvances(HB_Font hbFont, const HB_Glyph* glyphs, hb_uint32 numGlyphs,
+ HB_Fixed* advances, int flags)
+{
+ FontData* data = reinterpret_cast<FontData*>(hbFont->userData);
+ SkPaint paint;
+ setupPaintWithFontData(&paint, data);
+
+ paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+
+ uint16_t* glyphs16 = new uint16_t[numGlyphs];
+ if (!glyphs16)
+ return;
+ for (unsigned i = 0; i < numGlyphs; ++i)
+ glyphs16[i] = glyphs[i];
+ SkScalar* scalarAdvances = reinterpret_cast<SkScalar*>(advances);
+ paint.getTextWidths(glyphs16, numGlyphs * sizeof(uint16_t), scalarAdvances);
+
+ // The |advances| values which Skia outputs are SkScalars, which are floats
+ // in Chromium. However, Harfbuzz wants them in 26.6 fixed point format.
+ // These two formats are both 32-bits long.
+ for (unsigned i = 0; i < numGlyphs; ++i) {
+ advances[i] = SkScalarToHBFixed(scalarAdvances[i]);
+#if DEBUG_ADVANCES
+ LOGD("glyphsToAdvances -- advances[%d]=%d", i, advances[i]);
+#endif
+ }
+ delete glyphs16;
+}
+
+static HB_Bool canRender(HB_Font hbFont, const HB_UChar16* characters, hb_uint32 length)
+{
+ FontData* data = reinterpret_cast<FontData*>(hbFont->userData);
+ SkPaint paint;
+ setupPaintWithFontData(&paint, data);
+
+ paint.setTextEncoding(SkPaint::kUTF16_TextEncoding);
+
+ uint16_t* glyphs16 = new uint16_t[length];
+ int numGlyphs = paint.textToGlyphs(characters, length * sizeof(uint16_t), glyphs16);
+
+ bool result = true;
+ for (int i = 0; i < numGlyphs; ++i) {
+ if (!glyphs16[i]) {
+ result = false;
+ break;
+ }
+ }
+ delete glyphs16;
+ return result;
+}
+
+static HB_Error getOutlinePoint(HB_Font hbFont, HB_Glyph glyph, int flags, hb_uint32 point,
+ HB_Fixed* xPos, HB_Fixed* yPos, hb_uint32* resultingNumPoints)
+{
+ FontData* data = reinterpret_cast<FontData*>(hbFont->userData);
+ SkPaint paint;
+ setupPaintWithFontData(&paint, data);
+
+ if (flags & HB_ShaperFlag_UseDesignMetrics)
+ // This is requesting pre-hinted positions. We can't support this.
+ return HB_Err_Invalid_Argument;
+
+ paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+ uint16_t glyph16 = glyph;
+ SkPath path;
+ paint.getTextPath(&glyph16, sizeof(glyph16), 0, 0, &path);
+ uint32_t numPoints = path.getPoints(0, 0);
+ if (point >= numPoints)
+ return HB_Err_Invalid_SubTable;
+ SkPoint* points = reinterpret_cast<SkPoint*>(malloc(sizeof(SkPoint) * (point + 1)));
+ if (!points)
+ return HB_Err_Invalid_SubTable;
+ // Skia does let us get a single point from the path.
+ path.getPoints(points, point + 1);
+ *xPos = SkScalarToHBFixed(points[point].fX);
+ *yPos = SkScalarToHBFixed(points[point].fY);
+ *resultingNumPoints = numPoints;
+ delete points;
+
+ return HB_Err_Ok;
+}
+
+static void getGlyphMetrics(HB_Font hbFont, HB_Glyph glyph, HB_GlyphMetrics* metrics)
+{
+ FontData* data = reinterpret_cast<FontData*>(hbFont->userData);
+ SkPaint paint;
+ setupPaintWithFontData(&paint, data);
+
+ paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+ uint16_t glyph16 = glyph;
+ SkScalar width;
+ SkRect bounds;
+ paint.getTextWidths(&glyph16, sizeof(glyph16), &width, &bounds);
+
+ metrics->x = SkScalarToHBFixed(bounds.fLeft);
+ metrics->y = SkScalarToHBFixed(bounds.fTop);
+ metrics->width = SkScalarToHBFixed(bounds.width());
+ metrics->height = SkScalarToHBFixed(bounds.height());
+
+ metrics->xOffset = SkScalarToHBFixed(width);
+ // We can't actually get the |y| correct because Skia doesn't export
+ // the vertical advance. However, nor we do ever render vertical text at
+ // the moment so it's unimportant.
+ metrics->yOffset = 0;
+}
+
+static HB_Fixed getFontMetric(HB_Font hbFont, HB_FontMetric metric)
+{
+ FontData* data = reinterpret_cast<FontData*>(hbFont->userData);
+ SkPaint paint;
+ setupPaintWithFontData(&paint, data);
+
+ SkPaint::FontMetrics skiaMetrics;
+ paint.getFontMetrics(&skiaMetrics);
+
+ switch (metric) {
+ case HB_FontAscent:
+ return SkScalarToHBFixed(-skiaMetrics.fAscent);
+ // We don't support getting the rest of the metrics and Harfbuzz doesn't seem to need them.
+ default:
+ return 0;
+ }
+ return 0;
+}
+
+const HB_FontClass harfbuzzSkiaClass = {
+ stringToGlyphs,
+ glyphsToAdvances,
+ canRender,
+ getOutlinePoint,
+ getGlyphMetrics,
+ getFontMetric,
+};
+
+HB_Error harfbuzzSkiaGetTable(void* voidface, const HB_Tag tag, HB_Byte* buffer, HB_UInt* len)
+{
+ FontData* data = reinterpret_cast<FontData*>(voidface);
+ SkTypeface* typeface = data->typeFace;
+
+ const size_t tableSize = SkFontHost::GetTableSize(typeface->uniqueID(), tag);
+ if (!tableSize)
+ return HB_Err_Invalid_Argument;
+ // If Harfbuzz specified a NULL buffer then it's asking for the size of the table.
+ if (!buffer) {
+ *len = tableSize;
+ return HB_Err_Ok;
+ }
+
+ if (*len < tableSize)
+ return HB_Err_Invalid_Argument;
+ SkFontHost::GetTableData(typeface->uniqueID(), tag, 0, tableSize, buffer);
+ return HB_Err_Ok;
+}
+
+} // namespace android
diff --git a/core/jni/android/graphics/HarfbuzzSkia.h b/core/jni/android/graphics/HarfbuzzSkia.h
new file mode 100644
index 0000000..99b389a
--- /dev/null
+++ b/core/jni/android/graphics/HarfbuzzSkia.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2011, The Android Open Source Project
+ * Copyright 2011, Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef HarfbuzzSkia_h
+#define HarfbuzzSkia_h
+
+#include "SkScalar.h"
+#include "SkTypeface.h"
+#include "SkPaint.h"
+
+extern "C" {
+#include "harfbuzz-shaper.h"
+}
+
+namespace android {
+
+static inline float HBFixedToFloat(HB_Fixed v) {
+ // Harfbuzz uses 26.6 fixed point values for pixel offsets
+ return v * (1.0f / 64);
+}
+
+static inline HB_Fixed SkScalarToHBFixed(SkScalar value) {
+ // HB_Fixed is a 26.6 fixed point format.
+ return SkScalarToFloat(value) * 64.0f;
+}
+
+typedef struct {
+ SkTypeface* typeFace;
+ SkScalar textSize;
+ SkScalar textSkewX;
+ SkScalar textScaleX;
+ uint32_t flags;
+ SkPaint::Hinting hinting;
+} FontData;
+
+HB_Error harfbuzzSkiaGetTable(void* voidface, const HB_Tag, HB_Byte* buffer, HB_UInt* len);
+extern const HB_FontClass harfbuzzSkiaClass;
+
+} // namespace android
+
+#endif
diff --git a/core/jni/android/graphics/Interpolator.cpp b/core/jni/android/graphics/Interpolator.cpp
index beec351..aa33c3d 100644
--- a/core/jni/android/graphics/Interpolator.cpp
+++ b/core/jni/android/graphics/Interpolator.cpp
@@ -61,7 +61,7 @@ static int Interpolator_timeToValues(JNIEnv* env, jobject clazz, SkInterpolator*
float* values = valueArray ? env->GetFloatArrayElements(valueArray, NULL) : NULL;
result = interp->timeToValues(msec, (SkScalar*)values);
-
+
if (valueArray) {
int n = env->GetArrayLength(valueArray);
for (int i = 0; i < n; i++) {
@@ -69,7 +69,7 @@ static int Interpolator_timeToValues(JNIEnv* env, jobject clazz, SkInterpolator*
}
env->ReleaseFloatArrayElements(valueArray, values, 0);
}
-
+
return result;
}
@@ -87,7 +87,6 @@ static JNINativeMethod gInterpolatorMethods[] = {
{ "nativeTimeToValues", "(II[F)I", (void*)Interpolator_timeToValues }
};
-int register_android_graphics_Interpolator(JNIEnv* env);
int register_android_graphics_Interpolator(JNIEnv* env)
{
return android::AndroidRuntime::registerNativeMethods(env,
diff --git a/core/jni/android/graphics/LayerRasterizer.cpp b/core/jni/android/graphics/LayerRasterizer.cpp
index 7c45889..e5bc6f8 100644
--- a/core/jni/android/graphics/LayerRasterizer.cpp
+++ b/core/jni/android/graphics/LayerRasterizer.cpp
@@ -11,7 +11,7 @@ public:
SkASSERT(layer);
SkASSERT(paint);
layer->addLayer(*paint, SkFloatToScalar(dx), SkFloatToScalar(dy));
- }
+ }
};
/////////////////////////////////////////////////////////////////////////////////////////
@@ -23,7 +23,6 @@ static JNINativeMethod gLayerRasterizerMethods[] = {
{ "nativeAddLayer", "(IIFF)V", (void*)SkLayerRasterizerGlue::addLayer }
};
-int register_android_graphics_LayerRasterizer(JNIEnv* env);
int register_android_graphics_LayerRasterizer(JNIEnv* env)
{
return android::AndroidRuntime::registerNativeMethods(env,
@@ -31,4 +30,3 @@ int register_android_graphics_LayerRasterizer(JNIEnv* env)
gLayerRasterizerMethods,
SK_ARRAY_COUNT(gLayerRasterizerMethods));
}
-
diff --git a/core/jni/android/graphics/MaskFilter.cpp b/core/jni/android/graphics/MaskFilter.cpp
index d3f9b78..d954ddf 100644
--- a/core/jni/android/graphics/MaskFilter.cpp
+++ b/core/jni/android/graphics/MaskFilter.cpp
@@ -23,7 +23,7 @@ public:
ThrowIAE_IfNull(env, filter);
return filter;
}
-
+
static SkMaskFilter* createEmboss(JNIEnv* env, jobject, jfloatArray dirArray, float ambient, float specular, float radius) {
SkScalar direction[3];
@@ -79,16 +79,14 @@ static JNINativeMethod gTableMaskFilterMethods[] = {
result = android::AndroidRuntime::registerNativeMethods(env, name, array, SK_ARRAY_COUNT(array)); \
if (result < 0) return result
-int register_android_graphics_MaskFilter(JNIEnv* env);
int register_android_graphics_MaskFilter(JNIEnv* env)
{
int result;
-
+
REG(env, "android/graphics/MaskFilter", gMaskFilterMethods);
REG(env, "android/graphics/BlurMaskFilter", gBlurMaskFilterMethods);
REG(env, "android/graphics/EmbossMaskFilter", gEmbossMaskFilterMethods);
REG(env, "android/graphics/TableMaskFilter", gTableMaskFilterMethods);
-
+
return 0;
}
-
diff --git a/core/jni/android/graphics/Movie.cpp b/core/jni/android/graphics/Movie.cpp
index d1a5546..c1acaa3 100644
--- a/core/jni/android/graphics/Movie.cpp
+++ b/core/jni/android/graphics/Movie.cpp
@@ -23,11 +23,8 @@ jobject create_jmovie(JNIEnv* env, SkMovie* moov) {
if (NULL == moov) {
return NULL;
}
- jobject obj = env->AllocObject(gMovie_class);
- if (obj) {
- env->CallVoidMethod(obj, gMovie_constructorMethodID, (jint)moov);
- }
- return obj;
+ return env->NewObject(gMovie_class, gMovie_constructorMethodID,
+ static_cast<jint>(reinterpret_cast<uintptr_t>(moov)));
}
static SkMovie* J2Movie(JNIEnv* env, jobject movie) {
@@ -83,7 +80,7 @@ static void movie_draw(JNIEnv* env, jobject movie, jobject canvas,
}
static jobject movie_decodeStream(JNIEnv* env, jobject clazz, jobject istream) {
-
+
NPE_CHECK_RETURN_ZERO(env, istream);
// what is the lifetime of the array? Can the skstream hold onto it?
@@ -101,12 +98,12 @@ static jobject movie_decodeStream(JNIEnv* env, jobject clazz, jobject istream) {
static jobject movie_decodeByteArray(JNIEnv* env, jobject clazz,
jbyteArray byteArray,
int offset, int length) {
-
+
NPE_CHECK_RETURN_ZERO(env, byteArray);
int totalLength = env->GetArrayLength(byteArray);
if ((offset | length) < 0 || offset + length > totalLength) {
- doThrow(env, "java/lang/ArrayIndexOutOfBoundsException");
+ doThrowAIOOBE(env);
return 0;
}
@@ -142,13 +139,12 @@ static JNINativeMethod gMethods[] = {
#define RETURN_ERR_IF_NULL(value) do { if (!(value)) { assert(0); return -1; } } while (false)
-int register_android_graphics_Movie(JNIEnv* env);
int register_android_graphics_Movie(JNIEnv* env)
{
gMovie_class = env->FindClass(kClassPathName);
RETURN_ERR_IF_NULL(gMovie_class);
gMovie_class = (jclass)env->NewGlobalRef(gMovie_class);
-
+
gMovie_constructorMethodID = env->GetMethodID(gMovie_class, "<init>", "(I)V");
RETURN_ERR_IF_NULL(gMovie_constructorMethodID);
diff --git a/core/jni/android/graphics/NinePatch.cpp b/core/jni/android/graphics/NinePatch.cpp
index 50df0f7..0c8a8a3 100644
--- a/core/jni/android/graphics/NinePatch.cpp
+++ b/core/jni/android/graphics/NinePatch.cpp
@@ -30,7 +30,7 @@
extern void NinePatch_Draw(SkCanvas* canvas, const SkRect& bounds,
const SkBitmap& bitmap, const android::Res_png_9patch& chunk,
const SkPaint* paint, SkRegion** outRegion);
-
+
using namespace android;
class SkNinePatchGlue {
@@ -58,8 +58,7 @@ public:
static void validateNinePatchChunk(JNIEnv* env, jobject, jint, jbyteArray obj)
{
if (env->GetArrayLength(obj) < (int) (sizeof(Res_png_9patch))) {
- jniThrowException(env, "java/lang/RuntimeException",
- "Array too small for chunk.");
+ jniThrowRuntimeException(env, "Array too small for chunk.");
return;
}
@@ -80,7 +79,7 @@ public:
assert(chunkSize == chunk->serializedSize());
// this relies on deserialization being done in place
Res_png_9patch::deserialize(chunk);
-
+
if (destDensity == srcDensity || destDensity == 0
|| srcDensity == 0) {
LOGV("Drawing unscaled 9-patch: (%g,%g)-(%g,%g)",
@@ -89,26 +88,26 @@ public:
NinePatch_Draw(canvas, bounds, *bitmap, *chunk, paint, NULL);
} else {
canvas->save();
-
+
SkScalar scale = SkFloatToScalar(destDensity / (float)srcDensity);
canvas->translate(bounds.fLeft, bounds.fTop);
canvas->scale(scale, scale);
-
+
bounds.fRight = SkScalarDiv(bounds.fRight-bounds.fLeft, scale);
bounds.fBottom = SkScalarDiv(bounds.fBottom-bounds.fTop, scale);
bounds.fLeft = bounds.fTop = 0;
-
+
LOGV("Drawing scaled 9-patch: (%g,%g)-(%g,%g) srcDensity=%d destDensity=%d",
SkScalarToFloat(bounds.fLeft), SkScalarToFloat(bounds.fTop),
SkScalarToFloat(bounds.fRight), SkScalarToFloat(bounds.fBottom),
srcDensity, destDensity);
-
+
NinePatch_Draw(canvas, bounds, *bitmap, *chunk, paint, NULL);
-
+
canvas->restore();
}
}
- }
+ }
static void drawF(JNIEnv* env, jobject, SkCanvas* canvas, jobject boundsRectF,
const SkBitmap* bitmap, jbyteArray chunkObj, const SkPaint* paint,
@@ -125,7 +124,7 @@ public:
draw(env, canvas, bounds, bitmap, chunkObj, paint, destDensity, srcDensity);
}
-
+
static void drawI(JNIEnv* env, jobject, SkCanvas* canvas, jobject boundsRect,
const SkBitmap* bitmap, jbyteArray chunkObj, const SkPaint* paint,
jint destDensity, jint srcDensity)
@@ -140,7 +139,7 @@ public:
GraphicsJNI::jrect_to_rect(env, boundsRect, &bounds);
draw(env, canvas, bounds, bitmap, chunkObj, paint, destDensity, srcDensity);
}
-
+
static jint getTransparentRegion(JNIEnv* env, jobject,
const SkBitmap* bitmap, jbyteArray chunkObj,
jobject boundsRect)
@@ -148,7 +147,7 @@ public:
SkASSERT(bitmap);
SkASSERT(chunkObj);
SkASSERT(boundsRect);
-
+
SkRect bounds;
GraphicsJNI::jrect_to_rect(env, boundsRect, &bounds);
size_t chunkSize = env->GetArrayLength(chunkObj);
@@ -179,11 +178,10 @@ static JNINativeMethod gNinePatchMethods[] = {
{ "validateNinePatchChunk", "(I[B)V", (void*)SkNinePatchGlue::validateNinePatchChunk },
{ "nativeDraw", "(ILandroid/graphics/RectF;I[BIII)V", (void*)SkNinePatchGlue::drawF },
{ "nativeDraw", "(ILandroid/graphics/Rect;I[BIII)V", (void*)SkNinePatchGlue::drawI },
- { "nativeGetTransparentRegion", "(I[BLandroid/graphics/Rect;)I",
+ { "nativeGetTransparentRegion", "(I[BLandroid/graphics/Rect;)I",
(void*)SkNinePatchGlue::getTransparentRegion }
};
-int register_android_graphics_NinePatch(JNIEnv* env);
int register_android_graphics_NinePatch(JNIEnv* env)
{
return android::AndroidRuntime::registerNativeMethods(env,
diff --git a/core/jni/android/graphics/NinePatchImpl.cpp b/core/jni/android/graphics/NinePatchImpl.cpp
index ff24a87..a3e36ee 100644
--- a/core/jni/android/graphics/NinePatchImpl.cpp
+++ b/core/jni/android/graphics/NinePatchImpl.cpp
@@ -116,9 +116,9 @@ void NinePatch_Draw(SkCanvas* canvas, const SkRect& bounds,
paint = &defaultPaint;
}
- // if our canvas is GL, draw this as a mesh, which will be faster than
- // in parts (which is faster for raster)
- if (canvas && canvas->getViewport(NULL)) {
+ // if our SkCanvas were back by GL we should enable this and draw this as
+ // a mesh, which will be faster in most cases.
+ if (false) {
SkNinePatch::DrawMesh(canvas, bounds, bitmap,
chunk.xDivs, chunk.numXDivs,
chunk.yDivs, chunk.numYDivs,
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index e62b034..64749e9 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -2,16 +2,16 @@
**
** Copyright 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
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
**
-** http://www.apache.org/licenses/LICENSE-2.0
+** http://www.apache.org/licenses/LICENSE-2.0
**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
** limitations under the License.
*/
@@ -76,214 +76,214 @@ public:
SkPaint* obj = new SkPaint(*paint);
return obj;
}
-
+
static void reset(JNIEnv* env, jobject clazz, SkPaint* obj) {
obj->reset();
defaultSettingsForAndroid(obj);
}
-
+
static void assign(JNIEnv* env, jobject clazz, SkPaint* dst, const SkPaint* src) {
*dst = *src;
}
-
+
static jint getFlags(JNIEnv* env, jobject paint) {
NPE_CHECK_RETURN_ZERO(env, paint);
return GraphicsJNI::getNativePaint(env, paint)->getFlags();
}
-
+
static void setFlags(JNIEnv* env, jobject paint, jint flags) {
NPE_CHECK_RETURN_VOID(env, paint);
GraphicsJNI::getNativePaint(env, paint)->setFlags(flags);
}
-
+
static void setAntiAlias(JNIEnv* env, jobject paint, jboolean aa) {
NPE_CHECK_RETURN_VOID(env, paint);
GraphicsJNI::getNativePaint(env, paint)->setAntiAlias(aa);
}
-
+
static void setLinearText(JNIEnv* env, jobject paint, jboolean linearText) {
NPE_CHECK_RETURN_VOID(env, paint);
GraphicsJNI::getNativePaint(env, paint)->setLinearText(linearText);
}
-
+
static void setSubpixelText(JNIEnv* env, jobject paint, jboolean subpixelText) {
NPE_CHECK_RETURN_VOID(env, paint);
GraphicsJNI::getNativePaint(env, paint)->setSubpixelText(subpixelText);
}
-
+
static void setUnderlineText(JNIEnv* env, jobject paint, jboolean underlineText) {
NPE_CHECK_RETURN_VOID(env, paint);
GraphicsJNI::getNativePaint(env, paint)->setUnderlineText(underlineText);
}
-
+
static void setStrikeThruText(JNIEnv* env, jobject paint, jboolean strikeThruText) {
NPE_CHECK_RETURN_VOID(env, paint);
GraphicsJNI::getNativePaint(env, paint)->setStrikeThruText(strikeThruText);
}
-
+
static void setFakeBoldText(JNIEnv* env, jobject paint, jboolean fakeBoldText) {
NPE_CHECK_RETURN_VOID(env, paint);
GraphicsJNI::getNativePaint(env, paint)->setFakeBoldText(fakeBoldText);
}
-
+
static void setFilterBitmap(JNIEnv* env, jobject paint, jboolean filterBitmap) {
NPE_CHECK_RETURN_VOID(env, paint);
GraphicsJNI::getNativePaint(env, paint)->setFilterBitmap(filterBitmap);
}
-
+
static void setDither(JNIEnv* env, jobject paint, jboolean dither) {
NPE_CHECK_RETURN_VOID(env, paint);
GraphicsJNI::getNativePaint(env, paint)->setDither(dither);
}
-
+
static jint getStyle(JNIEnv* env, jobject clazz, SkPaint* obj) {
return obj->getStyle();
}
-
+
static void setStyle(JNIEnv* env, jobject clazz, SkPaint* obj, SkPaint::Style style) {
obj->setStyle(style);
}
-
+
static jint getColor(JNIEnv* env, jobject paint) {
NPE_CHECK_RETURN_ZERO(env, paint);
return GraphicsJNI::getNativePaint(env, paint)->getColor();
}
-
+
static jint getAlpha(JNIEnv* env, jobject paint) {
NPE_CHECK_RETURN_ZERO(env, paint);
return GraphicsJNI::getNativePaint(env, paint)->getAlpha();
}
-
+
static void setColor(JNIEnv* env, jobject paint, jint color) {
NPE_CHECK_RETURN_VOID(env, paint);
GraphicsJNI::getNativePaint(env, paint)->setColor(color);
}
-
+
static void setAlpha(JNIEnv* env, jobject paint, jint a) {
NPE_CHECK_RETURN_VOID(env, paint);
GraphicsJNI::getNativePaint(env, paint)->setAlpha(a);
}
-
+
static jfloat getStrokeWidth(JNIEnv* env, jobject paint) {
NPE_CHECK_RETURN_ZERO(env, paint);
return SkScalarToFloat(GraphicsJNI::getNativePaint(env, paint)->getStrokeWidth());
}
-
+
static void setStrokeWidth(JNIEnv* env, jobject paint, jfloat width) {
NPE_CHECK_RETURN_VOID(env, paint);
GraphicsJNI::getNativePaint(env, paint)->setStrokeWidth(SkFloatToScalar(width));
}
-
+
static jfloat getStrokeMiter(JNIEnv* env, jobject paint) {
NPE_CHECK_RETURN_ZERO(env, paint);
return SkScalarToFloat(GraphicsJNI::getNativePaint(env, paint)->getStrokeMiter());
}
-
+
static void setStrokeMiter(JNIEnv* env, jobject paint, jfloat miter) {
NPE_CHECK_RETURN_VOID(env, paint);
GraphicsJNI::getNativePaint(env, paint)->setStrokeMiter(SkFloatToScalar(miter));
}
-
+
static jint getStrokeCap(JNIEnv* env, jobject clazz, SkPaint* obj) {
return obj->getStrokeCap();
}
-
+
static void setStrokeCap(JNIEnv* env, jobject clazz, SkPaint* obj, SkPaint::Cap cap) {
obj->setStrokeCap(cap);
}
-
+
static jint getStrokeJoin(JNIEnv* env, jobject clazz, SkPaint* obj) {
return obj->getStrokeJoin();
}
-
+
static void setStrokeJoin(JNIEnv* env, jobject clazz, SkPaint* obj, SkPaint::Join join) {
obj->setStrokeJoin(join);
}
-
+
static jboolean getFillPath(JNIEnv* env, jobject clazz, SkPaint* obj, SkPath* src, SkPath* dst) {
return obj->getFillPath(*src, dst);
}
-
+
static SkShader* setShader(JNIEnv* env, jobject clazz, SkPaint* obj, SkShader* shader) {
return obj->setShader(shader);
}
-
+
static SkColorFilter* setColorFilter(JNIEnv* env, jobject clazz, SkPaint* obj, SkColorFilter* filter) {
return obj->setColorFilter(filter);
}
-
+
static SkXfermode* setXfermode(JNIEnv* env, jobject clazz, SkPaint* obj, SkXfermode* xfermode) {
return obj->setXfermode(xfermode);
}
-
+
static SkPathEffect* setPathEffect(JNIEnv* env, jobject clazz, SkPaint* obj, SkPathEffect* effect) {
return obj->setPathEffect(effect);
}
-
+
static SkMaskFilter* setMaskFilter(JNIEnv* env, jobject clazz, SkPaint* obj, SkMaskFilter* maskfilter) {
return obj->setMaskFilter(maskfilter);
}
-
+
static SkTypeface* setTypeface(JNIEnv* env, jobject clazz, SkPaint* obj, SkTypeface* typeface) {
return obj->setTypeface(typeface);
}
-
+
static SkRasterizer* setRasterizer(JNIEnv* env, jobject clazz, SkPaint* obj, SkRasterizer* rasterizer) {
return obj->setRasterizer(rasterizer);
}
-
+
static jint getTextAlign(JNIEnv* env, jobject clazz, SkPaint* obj) {
return obj->getTextAlign();
}
-
+
static void setTextAlign(JNIEnv* env, jobject clazz, SkPaint* obj, SkPaint::Align align) {
obj->setTextAlign(align);
}
-
+
static jfloat getTextSize(JNIEnv* env, jobject paint) {
NPE_CHECK_RETURN_ZERO(env, paint);
return SkScalarToFloat(GraphicsJNI::getNativePaint(env, paint)->getTextSize());
}
-
+
static void setTextSize(JNIEnv* env, jobject paint, jfloat textSize) {
NPE_CHECK_RETURN_VOID(env, paint);
GraphicsJNI::getNativePaint(env, paint)->setTextSize(SkFloatToScalar(textSize));
}
-
+
static jfloat getTextScaleX(JNIEnv* env, jobject paint) {
NPE_CHECK_RETURN_ZERO(env, paint);
return SkScalarToFloat(GraphicsJNI::getNativePaint(env, paint)->getTextScaleX());
}
-
+
static void setTextScaleX(JNIEnv* env, jobject paint, jfloat scaleX) {
NPE_CHECK_RETURN_VOID(env, paint);
GraphicsJNI::getNativePaint(env, paint)->setTextScaleX(SkFloatToScalar(scaleX));
}
-
+
static jfloat getTextSkewX(JNIEnv* env, jobject paint) {
NPE_CHECK_RETURN_ZERO(env, paint);
return SkScalarToFloat(GraphicsJNI::getNativePaint(env, paint)->getTextSkewX());
}
-
+
static void setTextSkewX(JNIEnv* env, jobject paint, jfloat skewX) {
NPE_CHECK_RETURN_VOID(env, paint);
GraphicsJNI::getNativePaint(env, paint)->setTextSkewX(SkFloatToScalar(skewX));
}
-
+
static jfloat ascent(JNIEnv* env, jobject paint) {
NPE_CHECK_RETURN_ZERO(env, paint);
SkPaint::FontMetrics metrics;
(void)GraphicsJNI::getNativePaint(env, paint)->getFontMetrics(&metrics);
return SkScalarToFloat(metrics.fAscent);
}
-
+
static jfloat descent(JNIEnv* env, jobject paint) {
NPE_CHECK_RETURN_ZERO(env, paint);
SkPaint::FontMetrics metrics;
(void)GraphicsJNI::getNativePaint(env, paint)->getFontMetrics(&metrics);
return SkScalarToFloat(metrics.fDescent);
}
-
+
static jfloat getFontMetrics(JNIEnv* env, jobject paint, jobject metricsObj) {
NPE_CHECK_RETURN_ZERO(env, paint);
SkPaint::FontMetrics metrics;
@@ -299,11 +299,11 @@ public:
}
return SkScalarToFloat(spacing);
}
-
+
static jint getFontMetricsInt(JNIEnv* env, jobject paint, jobject metricsObj) {
NPE_CHECK_RETURN_ZERO(env, paint);
SkPaint::FontMetrics metrics;
-
+
GraphicsJNI::getNativePaint(env, paint)->getFontMetrics(&metrics);
int ascent = SkScalarRound(metrics.fAscent);
int descent = SkScalarRound(metrics.fDescent);
@@ -325,81 +325,133 @@ public:
NPE_CHECK_RETURN_ZERO(env, text);
size_t textLength = env->GetArrayLength(text);
-
+
if ((index | count) < 0 || (size_t)(index + count) > textLength) {
- doThrow(env, "java/lang/ArrayIndexOutOfBoundsException");
+ doThrowAIOOBE(env);
return 0;
}
- const SkPaint* paint = GraphicsJNI::getNativePaint(env, jpaint);
+ SkPaint* paint = GraphicsJNI::getNativePaint(env, jpaint);
const jchar* textArray = env->GetCharArrayElements(text, NULL);
+ jfloat result = 0;
+#if RTL_USE_HARFBUZZ
+ TextLayout::getTextRunAdvances(paint, textArray, index, count, textLength,
+ paint->getFlags(), NULL /* dont need all advances */, result);
+#else
// we double count, since measureText wants a byteLength
SkScalar width = paint->measureText(textArray + index, count << 1);
- env->ReleaseCharArrayElements(text, const_cast<jchar*>(textArray),
- JNI_ABORT);
-
- return SkScalarToFloat(width);
+ result = SkScalarToFloat(width);
+#endif
+ env->ReleaseCharArrayElements(text, const_cast<jchar*>(textArray), JNI_ABORT);
+ return result;
}
-
+
static jfloat measureText_StringII(JNIEnv* env, jobject jpaint, jstring text, int start, int end) {
NPE_CHECK_RETURN_ZERO(env, jpaint);
NPE_CHECK_RETURN_ZERO(env, text);
-
- SkPaint* paint = GraphicsJNI::getNativePaint(env, jpaint);
+
+ SkPaint* paint = GraphicsJNI::getNativePaint(env, jpaint);
const jchar* textArray = env->GetStringChars(text, NULL);
- size_t textLength = env->GetStringLength(text);
-
+
int count = end - start;
+ size_t textLength = env->GetStringLength(text);
if ((start | count) < 0 || (size_t)count > textLength) {
- doThrow(env, "java/lang/IndexOutOfBoundsException");
+ doThrowAIOOBE(env);
return 0;
}
-
- jfloat width = SkScalarToFloat(paint->measureText(textArray + start, count << 1));
+ jfloat width = 0;
+
+#if RTL_USE_HARFBUZZ
+ TextLayout::getTextRunAdvances(paint, textArray, start, count, end,
+ paint->getFlags(), NULL /* dont need all advances */, width);
+#else
+
+ width = SkScalarToFloat(paint->measureText(textArray + start, count << 1));
+#endif
env->ReleaseStringChars(text, textArray);
return width;
}
-
+
static jfloat measureText_String(JNIEnv* env, jobject jpaint, jstring text) {
NPE_CHECK_RETURN_ZERO(env, jpaint);
NPE_CHECK_RETURN_ZERO(env, text);
-
- SkPaint* paint = GraphicsJNI::getNativePaint(env, jpaint);
+
+ SkPaint* paint = GraphicsJNI::getNativePaint(env, jpaint);
const jchar* textArray = env->GetStringChars(text, NULL);
size_t textLength = env->GetStringLength(text);
-
- jfloat width = SkScalarToFloat(paint->measureText(textArray, textLength << 1));
+ jfloat width = 0;
+#if RTL_USE_HARFBUZZ
+ TextLayout::getTextRunAdvances(paint, textArray, 0, textLength, textLength,
+ paint->getFlags(), NULL /* dont need all advances */, width);
+#else
+ width = SkScalarToFloat(paint->measureText(textArray, textLength << 1));
+#endif
env->ReleaseStringChars(text, textArray);
return width;
}
-
+
static int dotextwidths(JNIEnv* env, SkPaint* paint, const jchar text[], int count, jfloatArray widths) {
AutoJavaFloatArray autoWidths(env, widths, count);
jfloat* widthsArray = autoWidths.ptr();
+#if RTL_USE_HARFBUZZ
+ jfloat totalAdvance;
+
+ TextLayout::getTextRunAdvances(paint, text, 0, count, count,
+ paint->getFlags(), widthsArray, totalAdvance);
+#else
SkScalar* scalarArray = (SkScalar*)widthsArray;
- count = paint->getTextWidths(text, count << 1, scalarArray);
+ count = paint->getTextWidths(text, count << 1, scalarArray);
for (int i = 0; i < count; i++) {
widthsArray[i] = SkScalarToFloat(scalarArray[i]);
}
+#endif
return count;
}
-
+
static int getTextWidths___CII_F(JNIEnv* env, jobject clazz, SkPaint* paint, jcharArray text, int index, int count, jfloatArray widths) {
const jchar* textArray = env->GetCharArrayElements(text, NULL);
count = dotextwidths(env, paint, textArray + index, count, widths);
env->ReleaseCharArrayElements(text, const_cast<jchar*>(textArray),
- JNI_ABORT);
+ JNI_ABORT);
return count;
}
-
- static int getTextWidths__StringII_F(JNIEnv* env, jobject clazz, SkPaint* paint, jstring text, int start, int end, jfloatArray widths) {
+
+ static int getTextWidths__StringII_F(JNIEnv* env, jobject clazz, SkPaint* paint, jstring text,
+ int start, int end, jfloatArray widths) {
const jchar* textArray = env->GetStringChars(text, NULL);
int count = dotextwidths(env, paint, textArray + start, end - start, widths);
env->ReleaseStringChars(text, textArray);
return count;
}
+ static int doTextGlyphs(JNIEnv* env, SkPaint* paint, const jchar* text, jint start, jint count,
+ jint contextCount, jint flags, jcharArray glyphs) {
+ jchar* glyphsArray = env->GetCharArrayElements(glyphs, NULL);
+ HB_ShaperItem shaperItem;
+ HB_FontRec font;
+ FontData fontData;
+ TextLayoutCacheValue::shapeWithHarfbuzz(&shaperItem, &font, &fontData, paint, text,
+ start, count, contextCount, flags);
+
+ int glyphCount = shaperItem.num_glyphs;
+ for (int i = 0; i < glyphCount; i++) {
+ glyphsArray[i] = (jchar) shaperItem.glyphs[i];
+ }
+ env->ReleaseCharArrayElements(glyphs, glyphsArray, JNI_ABORT);
+ return glyphCount;
+ }
+
+ static int getTextGlyphs__StringIIIII_C(JNIEnv* env, jobject clazz, SkPaint* paint,
+ jstring text, jint start, jint end, jint contextStart, jint contextEnd, jint flags,
+ jcharArray glyphs) {
+ const jchar* textArray = env->GetStringChars(text, NULL);
+ int count = doTextGlyphs(env, paint, textArray + contextStart, start - contextStart,
+ end - start, contextEnd - contextStart, flags, glyphs);
+ env->ReleaseStringChars(text, textArray);
+ return count;
+ }
+
static jfloat doTextRunAdvances(JNIEnv *env, SkPaint *paint, const jchar *text,
jint start, jint count, jint contextCount, jint flags,
jfloatArray advances, jint advancesIndex) {
@@ -415,29 +467,56 @@ public:
return totalAdvance;
}
- static float getTextRunAdvances___CIIIII_FI(JNIEnv* env, jobject clazz, SkPaint* paint,
+ static jfloat doTextRunAdvancesICU(JNIEnv *env, SkPaint *paint, const jchar *text,
+ jint start, jint count, jint contextCount, jint flags,
+ jfloatArray advances, jint advancesIndex) {
+ jfloat advancesArray[count];
+ jfloat totalAdvance;
+
+ TextLayout::getTextRunAdvancesICU(paint, text, start, count, contextCount, flags,
+ advancesArray, totalAdvance);
+
+ if (advances != NULL) {
+ env->SetFloatArrayRegion(advances, advancesIndex, count, advancesArray);
+ }
+ return totalAdvance;
+ }
+
+ static float getTextRunAdvances___CIIIII_FII(JNIEnv* env, jobject clazz, SkPaint* paint,
jcharArray text, jint index, jint count, jint contextIndex, jint contextCount,
- jint flags, jfloatArray advances, jint advancesIndex) {
+ jint flags, jfloatArray advances, jint advancesIndex, jint reserved) {
jchar* textArray = env->GetCharArrayElements(text, NULL);
- jfloat result = doTextRunAdvances(env, paint, textArray + contextIndex,
- index - contextIndex, count, contextCount, flags, advances, advancesIndex);
+ jfloat result = (reserved == 0) ?
+ doTextRunAdvances(env, paint, textArray + contextIndex, index - contextIndex,
+ count, contextCount, flags, advances, advancesIndex) :
+ doTextRunAdvancesICU(env, paint, textArray + contextIndex, index - contextIndex,
+ count, contextCount, flags, advances, advancesIndex);
env->ReleaseCharArrayElements(text, textArray, JNI_ABORT);
return result;
}
- static float getTextRunAdvances__StringIIIII_FI(JNIEnv* env, jobject clazz, SkPaint* paint,
+ static float getTextRunAdvances__StringIIIII_FII(JNIEnv* env, jobject clazz, SkPaint* paint,
jstring text, jint start, jint end, jint contextStart, jint contextEnd, jint flags,
- jfloatArray advances, jint advancesIndex) {
+ jfloatArray advances, jint advancesIndex, jint reserved) {
const jchar* textArray = env->GetStringChars(text, NULL);
- jfloat result = doTextRunAdvances(env, paint, textArray + contextStart,
- start - contextStart, end - start, contextEnd - contextStart, flags, advances,
- advancesIndex);
+ jfloat result = (reserved == 0) ?
+ doTextRunAdvances(env, paint, textArray + contextStart, start - contextStart,
+ end - start, contextEnd - contextStart, flags, advances, advancesIndex) :
+ doTextRunAdvancesICU(env, paint, textArray + contextStart, start - contextStart,
+ end - start, contextEnd - contextStart, flags, advances, advancesIndex);
env->ReleaseStringChars(text, textArray);
return result;
}
static jint doTextRunCursor(JNIEnv *env, SkPaint* paint, const jchar *text, jint start,
jint count, jint flags, jint offset, jint opt) {
+#if RTL_USE_HARFBUZZ
+ jfloat scalarArray[count];
+ jfloat totalAdvance;
+
+ TextLayout::getTextRunAdvances(paint, text, start, count, count, flags,
+ scalarArray, totalAdvance);
+#else
SkScalar scalarArray[count];
jchar buffer[count];
@@ -479,7 +558,7 @@ public:
}
}
}
-
+#endif
jint pos = offset - start;
switch (opt) {
case AFTER:
@@ -557,8 +636,8 @@ public:
static void setShadowLayer(JNIEnv* env, jobject jpaint, jfloat radius,
jfloat dx, jfloat dy, int color) {
NPE_CHECK_RETURN_VOID(env, jpaint);
-
- SkPaint* paint = GraphicsJNI::getNativePaint(env, jpaint);
+
+ SkPaint* paint = GraphicsJNI::getNativePaint(env, jpaint);
if (radius <= 0) {
paint->setLooper(NULL);
}
@@ -603,7 +682,7 @@ public:
}
if ((index < 0) || (index + count > env->GetArrayLength(jtext))) {
- doThrow(env, "java/lang/ArrayIndexOutOfBoundsException");
+ doThrowAIOOBE(env);
return 0;
}
@@ -639,7 +718,7 @@ public:
{
SkRect r;
SkIRect ir;
-
+
paint.measureText(text, count << 1, &r);
r.roundOut(&ir);
GraphicsJNI::irect_to_jrect(ir, env, bounds);
@@ -652,7 +731,7 @@ public:
doTextBounds(env, textArray + start, end - start, bounds, *paint);
env->ReleaseStringChars(text, textArray);
}
-
+
static void getCharArrayBounds(JNIEnv* env, jobject, const SkPaint* paint,
jcharArray text, int index, int count, jobject bounds)
{
@@ -661,7 +740,7 @@ public:
env->ReleaseCharArrayElements(text, const_cast<jchar*>(textArray),
JNI_ABORT);
}
-
+
};
static JNINativeMethod methods[] = {
@@ -721,10 +800,14 @@ static JNINativeMethod methods[] = {
{"native_breakText","(Ljava/lang/String;ZF[F)I", (void*) SkPaintGlue::breakTextS},
{"native_getTextWidths","(I[CII[F)I", (void*) SkPaintGlue::getTextWidths___CII_F},
{"native_getTextWidths","(ILjava/lang/String;II[F)I", (void*) SkPaintGlue::getTextWidths__StringII_F},
- {"native_getTextRunAdvances","(I[CIIIII[FI)F", (void*)
- SkPaintGlue::getTextRunAdvances___CIIIII_FI},
- {"native_getTextRunAdvances","(ILjava/lang/String;IIIII[FI)F",
- (void*) SkPaintGlue::getTextRunAdvances__StringIIIII_FI},
+ {"native_getTextRunAdvances","(I[CIIIII[FII)F",
+ (void*) SkPaintGlue::getTextRunAdvances___CIIIII_FII},
+ {"native_getTextRunAdvances","(ILjava/lang/String;IIIII[FII)F",
+ (void*) SkPaintGlue::getTextRunAdvances__StringIIIII_FII},
+
+
+ {"native_getTextGlyphs","(ILjava/lang/String;IIIII[C)I",
+ (void*) SkPaintGlue::getTextGlyphs__StringIIIII_C},
{"native_getTextRunCursor", "(I[CIIIII)I", (void*) SkPaintGlue::getTextRunCursor___C},
{"native_getTextRunCursor", "(ILjava/lang/String;IIIII)I",
(void*) SkPaintGlue::getTextRunCursor__String},
diff --git a/core/jni/android/graphics/PathEffect.cpp b/core/jni/android/graphics/PathEffect.cpp
index cfa9ce4..0503614 100644
--- a/core/jni/android/graphics/PathEffect.cpp
+++ b/core/jni/android/graphics/PathEffect.cpp
@@ -19,12 +19,12 @@ public:
SkPathEffect* outer, SkPathEffect* inner) {
return new SkComposePathEffect(outer, inner);
}
-
+
static SkPathEffect* Sum_constructor(JNIEnv* env, jobject,
SkPathEffect* first, SkPathEffect* second) {
return new SkSumPathEffect(first, second);
}
-
+
static SkPathEffect* Dash_constructor(JNIEnv* env, jobject,
jfloatArray intervalArray, float phase) {
AutoJavaFloatArray autoInterval(env, intervalArray);
@@ -32,30 +32,30 @@ public:
float* values = autoInterval.ptr();
SkAutoSTMalloc<32, SkScalar> storage(count);
- SkScalar* intervals = storage.get();
+ SkScalar* intervals = storage.get();
for (int i = 0; i < count; i++) {
intervals[i] = SkFloatToScalar(values[i]);
}
return new SkDashPathEffect(intervals, count, SkFloatToScalar(phase));
}
-
+
static SkPathEffect* OneD_constructor(JNIEnv* env, jobject,
const SkPath* shape, float advance, float phase, int style) {
SkASSERT(shape != NULL);
return new SkPath1DPathEffect(*shape, SkFloatToScalar(advance),
SkFloatToScalar(phase), (SkPath1DPathEffect::Style)style);
}
-
+
static SkPathEffect* Corner_constructor(JNIEnv* env, jobject, float radius){
return new SkCornerPathEffect(SkFloatToScalar(radius));
}
-
+
static SkPathEffect* Discrete_constructor(JNIEnv* env, jobject,
float length, float deviation) {
return new SkDiscretePathEffect(SkFloatToScalar(length),
SkFloatToScalar(deviation));
}
-
+
};
////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -95,11 +95,10 @@ static JNINativeMethod gDiscretePathEffectMethods[] = {
SK_ARRAY_COUNT(array)); \
if (result < 0) return result
-int register_android_graphics_PathEffect(JNIEnv* env);
int register_android_graphics_PathEffect(JNIEnv* env)
{
int result;
-
+
REG(env, "android/graphics/PathEffect", gPathEffectMethods);
REG(env, "android/graphics/ComposePathEffect", gComposePathEffectMethods);
REG(env, "android/graphics/SumPathEffect", gSumPathEffectMethods);
@@ -107,7 +106,6 @@ int register_android_graphics_PathEffect(JNIEnv* env)
REG(env, "android/graphics/PathDashPathEffect", gPathDashPathEffectMethods);
REG(env, "android/graphics/CornerPathEffect", gCornerPathEffectMethods);
REG(env, "android/graphics/DiscretePathEffect", gDiscretePathEffectMethods);
-
+
return 0;
}
-
diff --git a/core/jni/android/graphics/Region.cpp b/core/jni/android/graphics/Region.cpp
index 2445229..5c6ebdf 100644
--- a/core/jni/android/graphics/Region.cpp
+++ b/core/jni/android/graphics/Region.cpp
@@ -69,7 +69,7 @@ static jboolean Region_getBoundaryPath(JNIEnv* env, jobject, const SkRegion* reg
static jboolean Region_op0(JNIEnv* env, jobject, SkRegion* dst, int left, int top, int right, int bottom, int op) {
SkIRect ir;
-
+
ir.set(left, top, right, bottom);
return dst->op(ir, (SkRegion::Op)op);
}
@@ -84,16 +84,16 @@ static jboolean Region_op2(JNIEnv* env, jobject, SkRegion* dst, const SkRegion*
return dst->op(*region1, *region2, (SkRegion::Op)op);
}
-//////////////////////////////////// These are methods, not static
+//////////////////////////////////// These are methods, not static
static jboolean Region_isEmpty(JNIEnv* env, jobject region) {
return GetSkRegion(env, region)->isEmpty();
}
-
+
static jboolean Region_isRect(JNIEnv* env, jobject region) {
return GetSkRegion(env, region)->isRect();
}
-
+
static jboolean Region_isComplex(JNIEnv* env, jobject region) {
return GetSkRegion(env, region)->isComplex();
}
@@ -101,21 +101,21 @@ static jboolean Region_isComplex(JNIEnv* env, jobject region) {
static jboolean Region_contains(JNIEnv* env, jobject region, int x, int y) {
return GetSkRegion(env, region)->contains(x, y);
}
-
+
static jboolean Region_quickContains(JNIEnv* env, jobject region, int left, int top, int right, int bottom) {
return GetSkRegion(env, region)->quickContains(left, top, right, bottom);
}
-
+
static jboolean Region_quickRejectIIII(JNIEnv* env, jobject region, int left, int top, int right, int bottom) {
SkIRect ir;
ir.set(left, top, right, bottom);
return GetSkRegion(env, region)->quickReject(ir);
}
-
+
static jboolean Region_quickRejectRgn(JNIEnv* env, jobject region, jobject other) {
return GetSkRegion(env, region)->quickReject(*GetSkRegion(env, other));
}
-
+
static void Region_translate(JNIEnv* env, jobject region, int x, int y, jobject dst) {
SkRegion* rgn = GetSkRegion(env, region);
if (dst)
@@ -171,13 +171,13 @@ static SkRegion* Region_createFromParcel(JNIEnv* env, jobject clazz, jobject par
if (parcel == NULL) {
return NULL;
}
-
+
android::Parcel* p = android::parcelForJavaObject(env, parcel);
-
+
SkRegion* region = new SkRegion;
size_t size = p->readInt32();
region->unflatten(p->readInplace(size));
-
+
return region;
}
@@ -186,7 +186,7 @@ static jboolean Region_writeToParcel(JNIEnv* env, jobject clazz, const SkRegion*
if (parcel == NULL) {
return false;
}
-
+
android::Parcel* p = android::parcelForJavaObject(env, parcel);
size_t size = region->flatten(NULL);
@@ -208,7 +208,7 @@ static jboolean Region_equals(JNIEnv* env, jobject clazz, const SkRegion *r1, co
struct RgnIterPair {
SkRegion fRgn; // a copy of the caller's region
SkRegion::Iterator fIter; // an iterator acting upon the copy (fRgn)
-
+
RgnIterPair(const SkRegion& rgn) : fRgn(rgn) {
// have our iterator reference our copy (fRgn), so we know it will be
// unchanged for the lifetime of the iterator
@@ -218,7 +218,7 @@ struct RgnIterPair {
static RgnIterPair* RegionIter_constructor(JNIEnv* env, jobject, const SkRegion* region)
{
- SkASSERT(region);
+ SkASSERT(region);
return new RgnIterPair(*region);
}
@@ -279,12 +279,11 @@ static JNINativeMethod gRegionMethods[] = {
{ "nativeEquals", "(II)Z", (void*)Region_equals },
};
-int register_android_graphics_Region(JNIEnv* env);
int register_android_graphics_Region(JNIEnv* env)
{
jclass clazz = env->FindClass("android/graphics/Region");
SkASSERT(clazz);
-
+
gRegion_nativeInstanceFieldID = env->GetFieldID(clazz, "mNativeRegion", "I");
SkASSERT(gRegion_nativeInstanceFieldID);
diff --git a/core/jni/android/graphics/RtlProperties.h b/core/jni/android/graphics/RtlProperties.h
new file mode 100644
index 0000000..a41c91b
--- /dev/null
+++ b/core/jni/android/graphics/RtlProperties.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_RTL_PROPERTIES_H
+#define ANDROID_RTL_PROPERTIES_H
+
+#include <cutils/properties.h>
+#include <stdlib.h>
+
+namespace android {
+
+/**
+ * Debug level for app developers.
+ */
+#define RTL_PROPERTY_DEBUG "rtl.debug_level"
+
+/**
+ * Debug levels. Debug levels are used as flags.
+ */
+enum RtlDebugLevel {
+ kRtlDebugDisabled = 0,
+ kRtlDebugMemory = 1,
+ kRtlDebugCaches = 2,
+ kRtlDebugAllocations = 3
+};
+
+static RtlDebugLevel readRtlDebugLevel() {
+ char property[PROPERTY_VALUE_MAX];
+ if (property_get(RTL_PROPERTY_DEBUG, property, NULL) > 0) {
+ return (RtlDebugLevel) atoi(property);
+ }
+ return kRtlDebugDisabled;
+}
+
+// Define if we want to use Harfbuzz (1) or not (0)
+#define RTL_USE_HARFBUZZ 1
+
+// Define if we want (1) to have Advances debug values or not (0)
+#define DEBUG_ADVANCES 0
+
+// Define if we want (1) to have Glyphs debug values or not (0)
+#define DEBUG_GLYPHS 0
+
+} // namespace android
+#endif // ANDROID_RTL_PROPERTIES_H
diff --git a/core/jni/android/graphics/Shader.cpp b/core/jni/android/graphics/Shader.cpp
index 7fdad10..f4cc9e4 100644
--- a/core/jni/android/graphics/Shader.cpp
+++ b/core/jni/android/graphics/Shader.cpp
@@ -35,7 +35,7 @@ static void Color_RGBToHSV(JNIEnv* env, jobject, int red, int green, int blue, j
values[i] = SkScalarToFloat(hsv[i]);
}
}
-
+
static int Color_HSVToColor(JNIEnv* env, jobject, int alpha, jfloatArray hsvArray)
{
AutoJavaFloatArray autoHSV(env, hsvArray, 3);
@@ -45,7 +45,7 @@ static int Color_HSVToColor(JNIEnv* env, jobject, int alpha, jfloatArray hsvArra
for (int i = 0; i < 3; i++) {
hsv[i] = SkFloatToScalar(values[i]);
}
-
+
return SkHSVToColor(alpha, hsv);
}
@@ -104,7 +104,7 @@ static SkiaShader* BitmapShader_postConstructor(JNIEnv* env, jobject o, SkShader
return NULL;
#endif
}
-
+
///////////////////////////////////////////////////////////////////////////////////////////////
static SkShader* LinearGradient_create1(JNIEnv* env, jobject o,
@@ -129,7 +129,7 @@ static SkShader* LinearGradient_create1(JNIEnv* env, jobject o,
pos[i] = SkFloatToScalar(posValues[i]);
}
}
-
+
SkShader* shader = SkGradientShader::CreateLinear(pts,
reinterpret_cast<const SkColor*>(colorValues),
pos, count,
@@ -218,7 +218,7 @@ static SkShader* LinearGradient_create2(JNIEnv* env, jobject o,
SkColor colors[2];
colors[0] = color0;
colors[1] = color1;
-
+
SkShader* s = SkGradientShader::CreateLinear(pts, colors, NULL, 2, (SkShader::TileMode)tileMode);
ThrowIAE_IfNull(env, s);
@@ -237,7 +237,7 @@ static SkShader* RadialGradient_create1(JNIEnv* env, jobject, float x, float y,
SkAutoSTMalloc<8, SkScalar> storage(posArray ? count : 0);
SkScalar* pos = NULL;
-
+
if (posArray) {
AutoJavaFloatArray autoPos(env, posArray, count);
const float* posValues = autoPos.ptr();
@@ -338,10 +338,10 @@ static SkShader* SweepGradient_create1(JNIEnv* env, jobject, float x, float y,
jintArray jcolors, jfloatArray jpositions) {
size_t count = env->GetArrayLength(jcolors);
const jint* colors = env->GetIntArrayElements(jcolors, NULL);
-
+
SkAutoSTMalloc<8, SkScalar> storage(jpositions ? count : 0);
SkScalar* pos = NULL;
-
+
if (NULL != jpositions) {
AutoJavaFloatArray autoPos(env, jpositions, count);
const float* posValues = autoPos.ptr();
@@ -520,11 +520,10 @@ static JNINativeMethod gComposeShaderMethods[] = {
result = android::AndroidRuntime::registerNativeMethods(env, name, array, SK_ARRAY_COUNT(array)); \
if (result < 0) return result
-int register_android_graphics_Shader(JNIEnv* env);
int register_android_graphics_Shader(JNIEnv* env)
{
int result;
-
+
REG(env, "android/graphics/Color", gColorMethods);
REG(env, "android/graphics/Shader", gShaderMethods);
REG(env, "android/graphics/BitmapShader", gBitmapShaderMethods);
@@ -532,7 +531,6 @@ int register_android_graphics_Shader(JNIEnv* env)
REG(env, "android/graphics/RadialGradient", gRadialGradientMethods);
REG(env, "android/graphics/SweepGradient", gSweepGradientMethods);
REG(env, "android/graphics/ComposeShader", gComposeShaderMethods);
-
+
return result;
}
-
diff --git a/core/jni/android/graphics/SurfaceTexture.cpp b/core/jni/android/graphics/SurfaceTexture.cpp
index c4e5878..2f70190 100644
--- a/core/jni/android/graphics/SurfaceTexture.cpp
+++ b/core/jni/android/graphics/SurfaceTexture.cpp
@@ -171,6 +171,12 @@ static void SurfaceTexture_getTransformMatrix(JNIEnv* env, jobject thiz,
env->ReleaseFloatArrayElements(jmtx, mtx, 0);
}
+static jlong SurfaceTexture_getTimestamp(JNIEnv* env, jobject thiz)
+{
+ sp<SurfaceTexture> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
+ return surfaceTexture->getTimestamp();
+}
+
// ----------------------------------------------------------------------------
const char* const kSurfaceTextureClassPathName = "android/graphics/SurfaceTexture";
@@ -178,9 +184,10 @@ const char* const kSurfaceTextureClassPathName = "android/graphics/SurfaceTextur
static JNINativeMethod gSurfaceTextureMethods[] = {
{"nativeClassInit", "()V", (void*)SurfaceTexture_classInit },
{"nativeInit", "(ILjava/lang/Object;)V", (void*)SurfaceTexture_init },
- {"nativeFinalize", "()V", (void*)SurfaceTexture_finalize },
+ {"nativeFinalize", "()V", (void*)SurfaceTexture_finalize },
{"nativeUpdateTexImage", "()V", (void*)SurfaceTexture_updateTexImage },
{"nativeGetTransformMatrix", "([F)V", (void*)SurfaceTexture_getTransformMatrix },
+ {"nativeGetTimestamp", "()J", (void*)SurfaceTexture_getTimestamp }
};
int register_android_graphics_SurfaceTexture(JNIEnv* env)
diff --git a/core/jni/android/graphics/TextLayout.cpp b/core/jni/android/graphics/TextLayout.cpp
index e957635..46e6c2b 100644
--- a/core/jni/android/graphics/TextLayout.cpp
+++ b/core/jni/android/graphics/TextLayout.cpp
@@ -15,6 +15,7 @@
*/
#include "TextLayout.h"
+#include "TextLayoutCache.h"
#include <android_runtime/AndroidRuntime.h>
@@ -23,10 +24,8 @@
#include "unicode/ushape.h"
#include <utils/Log.h>
-// Log debug messages from RTL related allocations
-#define DEBUG_RTL_ALLOCATIONS 0
-
namespace android {
+
// Returns true if we might need layout. If bidiFlags force LTR, assume no layout, if
// bidiFlags indicate there probably is RTL, assume we do, otherwise scan the text
// looking for a character >= the first RTL character in unicode and assume we do if
@@ -60,14 +59,10 @@ bool TextLayout::needsLayout(const jchar* text, jint len, jint bidiFlags) {
* @return the length of the shaped text, or -1 if error
*/
int TextLayout::shapeRtlText(const jchar* context, jsize start, jsize count, jsize contextCount,
- jchar* shaped, UErrorCode &status) {
+ jchar* shaped, UErrorCode& status) {
SkAutoSTMalloc<CHAR_BUFFER_SIZE, jchar> tempBuffer(contextCount);
jchar* buffer = tempBuffer.get();
-#if DEBUG_RTL_ALLOCATIONS
- LOGD("TextLayout::shapeRtlText - allocated buffer with size: %d", contextCount);
-#endif
-
// Use fixed length since we need to keep start and count valid
u_shapeArabic(context, contextCount, buffer, contextCount,
U_SHAPE_LENGTH_FIXED_SPACES_NEAR |
@@ -105,8 +100,8 @@ int TextLayout::shapeRtlText(const jchar* context, jsize start, jsize count, jsi
* @flags line bidi flags
* @return the length of the reordered, shaped line, or -1 if error
*/
-jint TextLayout::layoutLine(const jchar* text, jint len, jint flags, int &dir, jchar* buffer,
- UErrorCode &status) {
+jint TextLayout::layoutLine(const jchar* text, jint len, jint flags, int& dir, jchar* buffer,
+ UErrorCode& status) {
static const int RTL_OPTS = UBIDI_DO_MIRRORING | UBIDI_KEEP_BASE_COMBINING |
UBIDI_REMOVE_BIDI_CONTROLS | UBIDI_OUTPUT_REVERSE;
@@ -156,7 +151,7 @@ jint TextLayout::layoutLine(const jchar* text, jint len, jint flags, int &dir, j
return result;
}
-bool TextLayout::prepareText(SkPaint *paint, const jchar* text, jsize len, jint bidiFlags,
+bool TextLayout::prepareText(SkPaint* paint, const jchar* text, jsize len, jint bidiFlags,
const jchar** outText, int32_t* outBytes, jchar** outBuffer) {
const jchar *workText = text;
jchar *buffer = NULL;
@@ -166,11 +161,6 @@ bool TextLayout::prepareText(SkPaint *paint, const jchar* text, jsize len, jint
if (!buffer) {
return false;
}
-
-#if DEBUG_RTL_ALLOCATIONS
- LOGD("TextLayout::prepareText - allocated buffer with size: %d", len);
-#endif
-
UErrorCode status = U_ZERO_ERROR;
len = layoutLine(text, len, bidiFlags, dir, buffer, status); // might change len, dir
if (!U_SUCCESS(status)) {
@@ -178,7 +168,6 @@ bool TextLayout::prepareText(SkPaint *paint, const jchar* text, jsize len, jint
free(buffer);
return false; // can't render
}
-
workText = buffer; // use the shaped text
}
@@ -262,74 +251,45 @@ void TextLayout::drawTextRun(SkPaint* paint, const jchar* chars,
}
}
-void TextLayout::getTextRunAdvances(SkPaint *paint, const jchar *chars, jint start,
+void TextLayout::getTextRunAdvances(SkPaint* paint, const jchar* chars, jint start,
jint count, jint contextCount, jint dirFlags,
- jfloat *resultAdvances, jfloat &resultTotalAdvance) {
- resultTotalAdvance = 0;
-
- SkAutoSTMalloc<CHAR_BUFFER_SIZE, jchar> tempBuffer(contextCount);
- jchar* buffer = tempBuffer.get();
-
-#if DEBUG_RTL_ALLOCATIONS
- LOGD("TextLayout::getTextRunAdvances - allocated buffer with size: %d", contextCount);
-#endif
-
- SkScalar* scalarArray = (SkScalar*)resultAdvances;
-
- // this is where we'd call harfbuzz
- // for now we just use ushape.c
-
- int widths;
- const jchar* text;
- if (dirFlags & 0x1) { // rtl, call arabic shaping in case
- UErrorCode status = U_ZERO_ERROR;
- // Use fixed length since we need to keep start and count valid
- u_shapeArabic(chars, contextCount, buffer, contextCount,
- U_SHAPE_LENGTH_FIXED_SPACES_NEAR |
- U_SHAPE_TEXT_DIRECTION_LOGICAL | U_SHAPE_LETTERS_SHAPE |
- U_SHAPE_X_LAMALEF_SUB_ALTERNATE, &status);
- // we shouldn't fail unless there's an out of memory condition,
- // in which case we're hosed anyway
- for (int i = start, e = i + count; i < e; ++i) {
- if (buffer[i] == UNICODE_NOT_A_CHAR) {
- buffer[i] = UNICODE_ZWSP; // zero-width-space for skia
- }
+ jfloat* resultAdvances, jfloat& resultTotalAdvance) {
+#if USE_TEXT_LAYOUT_CACHE
+ // Return advances from the cache. Compute them if needed
+ sp<TextLayoutCacheValue> layout = gTextLayoutCache.getValue(
+ paint, chars, start, count, contextCount, dirFlags);
+ if (layout != NULL) {
+ if (resultAdvances != NULL) {
+ memcpy(resultAdvances, layout->getAdvances(), layout->getAdvancesCount() * sizeof(jfloat));
}
- text = buffer + start;
- widths = paint->getTextWidths(text, count << 1, scalarArray);
- } else {
- text = chars + start;
- widths = paint->getTextWidths(text, count << 1, scalarArray);
+ resultTotalAdvance = layout->getTotalAdvance();
}
+#else
+ // Compute advances and return them
+ TextLayoutCacheValue::computeValuesWithHarfbuzz(paint, chars, start, count, contextCount,
+ dirFlags, resultAdvances, &resultTotalAdvance, NULL, NULL );
+#endif
+}
- if (widths < count) {
- // Skia operates on code points, not code units, so surrogate pairs return only
- // one value. Expand the result so we have one value per UTF-16 code unit.
-
- // Note, skia's getTextWidth gets confused if it encounters a surrogate pair,
- // leaving the remaining widths zero. Not nice.
- for (int i = 0, p = 0; i < widths; ++i) {
- resultTotalAdvance += resultAdvances[p++] = SkScalarToFloat(scalarArray[i]);
- if (p < count &&
- text[p] >= UNICODE_FIRST_LOW_SURROGATE &&
- text[p] < UNICODE_FIRST_PRIVATE_USE &&
- text[p-1] >= UNICODE_FIRST_HIGH_SURROGATE &&
- text[p-1] < UNICODE_FIRST_LOW_SURROGATE) {
- resultAdvances[p++] = 0;
- }
- }
- } else {
- for (int i = 0; i < count; i++) {
- resultTotalAdvance += resultAdvances[i] = SkScalarToFloat(scalarArray[i]);
- }
- }
+void TextLayout::getTextRunAdvancesHB(SkPaint* paint, const jchar* chars, jint start,
+ jint count, jint contextCount, jint dirFlags,
+ jfloat* resultAdvances, jfloat& resultTotalAdvance) {
+ // Compute advances and return them
+ TextLayoutCacheValue::computeValuesWithHarfbuzz(paint, chars, start, count, contextCount,
+ dirFlags, resultAdvances, &resultTotalAdvance, NULL, NULL);
}
+void TextLayout::getTextRunAdvancesICU(SkPaint* paint, const jchar* chars, jint start,
+ jint count, jint contextCount, jint dirFlags,
+ jfloat* resultAdvances, jfloat& resultTotalAdvance) {
+ // Compute advances and return them
+ TextLayoutCacheValue::computeAdvancesWithICU(paint, chars, start, count, contextCount, dirFlags,
+ resultAdvances, &resultTotalAdvance);
+}
// Draws a paragraph of text on a single line, running bidi and shaping
void TextLayout::drawText(SkPaint* paint, const jchar* text, jsize len,
int bidiFlags, jfloat x, jfloat y, SkCanvas* canvas) {
-
handleText(paint, text, len, bidiFlags, x, y, canvas, NULL);
}
@@ -353,10 +313,6 @@ void TextLayout::drawTextOnPath(SkPaint* paint, const jchar* text, int count,
SkAutoSTMalloc<CHAR_BUFFER_SIZE, jchar> buffer(count);
-#if DEBUG_RTL_ALLOCATIONS
- LOGD("TextLayout::drawTextOnPath - allocated buffer with size: %d", count);
-#endif
-
int dir = kDirection_LTR;
UErrorCode status = U_ZERO_ERROR;
count = layoutLine(text, count, bidiFlags, dir, buffer.get(), status);
diff --git a/core/jni/android/graphics/TextLayout.h b/core/jni/android/graphics/TextLayout.h
index c98f745..d197d04 100644
--- a/core/jni/android/graphics/TextLayout.h
+++ b/core/jni/android/graphics/TextLayout.h
@@ -20,6 +20,8 @@
#include "SkPaint.h"
#include "unicode/utypes.h"
+#include "TextLayoutCache.h"
+
namespace android {
#define UNICODE_NOT_A_CHAR 0xffff
@@ -34,26 +36,44 @@ namespace android {
*/
#define CHAR_BUFFER_SIZE 80
-class TextLayout {
-public:
+/**
+ * Turn on for using the Cache
+ */
+#define USE_TEXT_LAYOUT_CACHE 1
+
- enum {
- kDirection_LTR = 0,
- kDirection_RTL = 1,
+#if USE_TEXT_LAYOUT_CACHE
+ static TextLayoutCache gTextLayoutCache;
+#endif
- kDirection_Mask = 0x1
- };
+enum {
+ kBidi_LTR = 0,
+ kBidi_RTL = 1,
+ kBidi_Default_LTR = 2,
+ kBidi_Default_RTL = 3,
+ kBidi_Force_LTR = 4,
+ kBidi_Force_RTL = 5,
+
+ kBidi_Mask = 0x7
+};
+
+enum {
+ kDirection_LTR = 0,
+ kDirection_RTL = 1,
+
+ kDirection_Mask = 0x1
+};
- enum {
- kBidi_LTR = 0,
- kBidi_RTL = 1,
- kBidi_Default_LTR = 2,
- kBidi_Default_RTL = 3,
- kBidi_Force_LTR = 4,
- kBidi_Force_RTL = 5,
+static void logGlyphs(sp<TextLayoutCacheValue> value) {
+ if (value == NULL) return;
+ LOGD("Got glyphs - count=%d", value->getGlyphsCount());
+ for (size_t i = 0; i < value->getGlyphsCount(); i++) {
+ LOGD(" glyphs[%d]=%d", i, value->getGlyphs()[i]);
+ }
+}
- kBidi_Mask = 0x7
- };
+class TextLayout {
+public:
/*
* Draws a unidirectional run of text.
@@ -62,22 +82,31 @@ public:
jint start, jint count, jint contextCount,
int dirFlags, jfloat x, jfloat y, SkCanvas* canvas);
- static void getTextRunAdvances(SkPaint *paint, const jchar *chars, jint start,
+ static void getTextRunAdvances(SkPaint* paint, const jchar* chars, jint start,
+ jint count, jint contextCount, jint dirFlags,
+ jfloat* resultAdvances, jfloat& resultTotalAdvance);
+
+ static void getTextRunAdvancesICU(SkPaint* paint, const jchar* chars, jint start,
jint count, jint contextCount, jint dirFlags,
- jfloat *resultAdvances, jfloat &resultTotalAdvance);
+ jfloat* resultAdvances, jfloat& resultTotalAdvance);
+
+ static void getTextRunAdvancesHB(SkPaint* paint, const jchar* chars, jint start,
+ jint count, jint contextCount, jint dirFlags,
+ jfloat* resultAdvances, jfloat& resultTotalAdvance);
static void drawText(SkPaint* paint, const jchar* text, jsize len,
jint bidiFlags, jfloat x, jfloat y, SkCanvas* canvas);
- static void getTextPath(SkPaint *paint, const jchar *text, jsize len,
- jint bidiFlags, jfloat x, jfloat y, SkPath *path);
+ static void getTextPath(SkPaint* paint, const jchar* text, jsize len,
+ jint bidiFlags, jfloat x, jfloat y, SkPath* path);
static void drawTextOnPath(SkPaint* paint, const jchar* text, jsize len,
int bidiFlags, jfloat hOffset, jfloat vOffset,
SkPath* path, SkCanvas* canvas);
- static bool prepareText(SkPaint *paint, const jchar* text, jsize len, jint bidiFlags,
+ static bool prepareText(SkPaint* paint, const jchar* text, jsize len, jint bidiFlags,
const jchar** outText, int32_t* outBytes, jchar** outBuffer);
+
static bool prepareRtlTextRun(const jchar* context, jsize start, jsize& count,
jsize contextCount, jchar* shaped);
@@ -85,11 +114,10 @@ public:
private:
static bool needsLayout(const jchar* text, jint len, jint bidiFlags);
static int shapeRtlText(const jchar* context, jsize start, jsize count, jsize contextCount,
- jchar* shaped, UErrorCode &status);
+ jchar* shaped, UErrorCode& status);
static jint layoutLine(const jchar* text, jint len, jint flags, int &dir, jchar* buffer,
UErrorCode &status);
- static void handleText(SkPaint *paint, const jchar* text, jsize len,
- int bidiFlags, jfloat x, jfloat y,SkCanvas *canvas, SkPath *path);
+ static void handleText(SkPaint* paint, const jchar* text, jsize len,
+ int bidiFlags, jfloat x, jfloat y, SkCanvas* canvas, SkPath* path);
};
-
-}
+} // namespace android
diff --git a/core/jni/android/graphics/TextLayoutCache.cpp b/core/jni/android/graphics/TextLayoutCache.cpp
new file mode 100644
index 0000000..a5b2006
--- /dev/null
+++ b/core/jni/android/graphics/TextLayoutCache.cpp
@@ -0,0 +1,698 @@
+/*
+ * Copyright (C) 2011 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 "TextLayoutCache.h"
+#include "TextLayout.h"
+
+namespace android {
+
+TextLayoutCache::TextLayoutCache() :
+ mCache(GenerationCache<TextLayoutCacheKey, sp<TextLayoutCacheValue> >::kUnlimitedCapacity),
+ mSize(0), mMaxSize(MB(DEFAULT_TEXT_LAYOUT_CACHE_SIZE_IN_MB)),
+ mCacheHitCount(0), mNanosecondsSaved(0) {
+ init();
+}
+
+TextLayoutCache::TextLayoutCache(uint32_t max):
+ mCache(GenerationCache<TextLayoutCacheKey, sp<TextLayoutCacheValue> >::kUnlimitedCapacity),
+ mSize(0), mMaxSize(max),
+ mCacheHitCount(0), mNanosecondsSaved(0) {
+ init();
+}
+
+TextLayoutCache::~TextLayoutCache() {
+ mCache.clear();
+}
+
+void TextLayoutCache::init() {
+ mCache.setOnEntryRemovedListener(this);
+
+ mDebugLevel = readRtlDebugLevel();
+ mDebugEnabled = mDebugLevel & kRtlDebugCaches;
+ LOGD("Using TextLayoutCache debug level: %d - Debug Enabled: %d", mDebugLevel, mDebugEnabled);
+
+ mCacheStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+ if (mDebugEnabled) {
+ LOGD("TextLayoutCache start time: %lld", mCacheStartTime);
+ }
+ mInitialized = true;
+
+ if (mDebugEnabled) {
+#if RTL_USE_HARFBUZZ
+ LOGD("TextLayoutCache is using HARFBUZZ");
+#else
+ LOGD("TextLayoutCache is using ICU");
+#endif
+ }
+
+ if (mDebugEnabled) {
+ LOGD("TextLayoutCache initialization is done");
+ }
+}
+
+/*
+ * Size management
+ */
+
+uint32_t TextLayoutCache::getSize() {
+ return mSize;
+}
+
+uint32_t TextLayoutCache::getMaxSize() {
+ return mMaxSize;
+}
+
+void TextLayoutCache::setMaxSize(uint32_t maxSize) {
+ mMaxSize = maxSize;
+ removeOldests();
+}
+
+void TextLayoutCache::removeOldests() {
+ while (mSize > mMaxSize) {
+ mCache.removeOldest();
+ }
+}
+
+/**
+ * Callbacks
+ */
+void TextLayoutCache::operator()(TextLayoutCacheKey& text, sp<TextLayoutCacheValue>& desc) {
+ if (desc != NULL) {
+ size_t totalSizeToDelete = text.getSize() + desc->getSize();
+ mSize -= totalSizeToDelete;
+ if (mDebugEnabled) {
+ LOGD("Cache value deleted, size = %d", totalSizeToDelete);
+ }
+ desc.clear();
+ }
+}
+
+/*
+ * Cache clearing
+ */
+void TextLayoutCache::clear() {
+ mCache.clear();
+}
+
+/*
+ * Caching
+ */
+sp<TextLayoutCacheValue> TextLayoutCache::getValue(SkPaint* paint,
+ const jchar* text, jint start, jint count, jint contextCount, jint dirFlags) {
+ AutoMutex _l(mLock);
+ nsecs_t startTime = 0;
+ if (mDebugEnabled) {
+ startTime = systemTime(SYSTEM_TIME_MONOTONIC);
+ }
+
+ // Create the key
+ TextLayoutCacheKey key(paint, text, start, count, contextCount, dirFlags);
+
+ // Get value from cache if possible
+ sp<TextLayoutCacheValue> value = mCache.get(key);
+
+ // Value not found for the key, we need to add a new value in the cache
+ if (value == NULL) {
+ if (mDebugEnabled) {
+ startTime = systemTime(SYSTEM_TIME_MONOTONIC);
+ }
+
+ value = new TextLayoutCacheValue();
+
+ // Compute advances and store them
+ value->computeValues(paint, text, start, count, contextCount, dirFlags);
+
+ nsecs_t endTime = systemTime(SYSTEM_TIME_MONOTONIC);
+
+ // Don't bother to add in the cache if the entry is too big
+ size_t size = key.getSize() + value->getSize();
+ if (size <= mMaxSize) {
+ // Cleanup to make some room if needed
+ if (mSize + size > mMaxSize) {
+ if (mDebugEnabled) {
+ LOGD("TextLayoutCache: need to clean some entries "
+ "for making some room for a new entry");
+ }
+ while (mSize + size > mMaxSize) {
+ // This will call the callback
+ mCache.removeOldest();
+ }
+ }
+
+ // Update current cache size
+ mSize += size;
+
+ // Copy the text when we insert the new entry
+ key.internalTextCopy();
+ mCache.put(key, value);
+
+ if (mDebugEnabled) {
+ // Update timing information for statistics
+ value->setElapsedTime(endTime - startTime);
+
+ LOGD("CACHE MISS: Added entry for text='%s' with start=%d, count=%d, "
+ "contextCount=%d, entry size %d bytes, remaining space %d bytes"
+ " - Compute time in nanos: %d",
+ String8(text, contextCount).string(), start, count, contextCount,
+ size, mMaxSize - mSize, value->getElapsedTime());
+ }
+ } else {
+ if (mDebugEnabled) {
+ LOGD("CACHE MISS: Calculated but not storing entry because it is too big "
+ "for text='%s' with start=%d, count=%d, contextCount=%d, "
+ "entry size %d bytes, remaining space %d bytes"
+ " - Compute time in nanos: %lld",
+ String8(text, contextCount).string(), start, count, contextCount,
+ size, mMaxSize - mSize, endTime);
+ }
+ value.clear();
+ }
+ } else {
+ // This is a cache hit, just log timestamp and user infos
+ if (mDebugEnabled) {
+ nsecs_t elapsedTimeThruCacheGet = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
+ mNanosecondsSaved += (value->getElapsedTime() - elapsedTimeThruCacheGet);
+ ++mCacheHitCount;
+
+ if (value->getElapsedTime() > 0) {
+ float deltaPercent = 100 * ((value->getElapsedTime() - elapsedTimeThruCacheGet)
+ / ((float)value->getElapsedTime()));
+ LOGD("CACHE HIT #%d for text='%s' with start=%d, count=%d, contextCount=%d "
+ "- Compute time in nanos: %d - "
+ "Cache get time in nanos: %lld - Gain in percent: %2.2f",
+ mCacheHitCount, String8(text, contextCount).string(), start, count,
+ contextCount,
+ value->getElapsedTime(), elapsedTimeThruCacheGet, deltaPercent);
+ }
+ if (mCacheHitCount % DEFAULT_DUMP_STATS_CACHE_HIT_INTERVAL == 0) {
+ dumpCacheStats();
+ }
+ }
+ }
+ return value;
+}
+
+void TextLayoutCache::dumpCacheStats() {
+ float remainingPercent = 100 * ((mMaxSize - mSize) / ((float)mMaxSize));
+ float timeRunningInSec = (systemTime(SYSTEM_TIME_MONOTONIC) - mCacheStartTime) / 1000000000;
+ LOGD("------------------------------------------------");
+ LOGD("TextLayoutCache stats");
+ LOGD("------------------------------------------------");
+ LOGD("pid : %d", getpid());
+ LOGD("running : %.0f seconds", timeRunningInSec);
+ LOGD("entries : %d", mCache.size());
+ LOGD("size : %d bytes", mMaxSize);
+ LOGD("remaining : %d bytes or %2.2f percent", mMaxSize - mSize, remainingPercent);
+ LOGD("hits : %d", mCacheHitCount);
+ LOGD("saved : %lld milliseconds", mNanosecondsSaved / 1000000);
+ LOGD("------------------------------------------------");
+}
+
+/**
+ * TextLayoutCacheKey
+ */
+TextLayoutCacheKey::TextLayoutCacheKey(): text(NULL), start(0), count(0), contextCount(0),
+ dirFlags(0), typeface(NULL), textSize(0), textSkewX(0), textScaleX(0), flags(0),
+ hinting(SkPaint::kNo_Hinting) {
+}
+
+TextLayoutCacheKey::TextLayoutCacheKey(const SkPaint* paint,
+ const UChar* text, size_t start, size_t count,
+ size_t contextCount, int dirFlags) :
+ text(text), start(start), count(count), contextCount(contextCount),
+ dirFlags(dirFlags) {
+ typeface = paint->getTypeface();
+ textSize = paint->getTextSize();
+ textSkewX = paint->getTextSkewX();
+ textScaleX = paint->getTextScaleX();
+ flags = paint->getFlags();
+ hinting = paint->getHinting();
+}
+
+bool TextLayoutCacheKey::operator<(const TextLayoutCacheKey& rhs) const {
+ LTE_INT(count) {
+ LTE_INT(contextCount) {
+ LTE_INT(start) {
+ LTE_INT(typeface) {
+ LTE_FLOAT(textSize) {
+ LTE_FLOAT(textSkewX) {
+ LTE_FLOAT(textScaleX) {
+ LTE_INT(flags) {
+ LTE_INT(hinting) {
+ LTE_INT(dirFlags) {
+ return strncmp16(text, rhs.text, contextCount) < 0;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return false;
+}
+
+void TextLayoutCacheKey::internalTextCopy() {
+ textCopy.setTo(text, contextCount);
+ text = textCopy.string();
+}
+
+size_t TextLayoutCacheKey::getSize() {
+ return sizeof(TextLayoutCacheKey) + sizeof(UChar) * contextCount;
+}
+
+/**
+ * TextLayoutCacheValue
+ */
+TextLayoutCacheValue::TextLayoutCacheValue() :
+ mAdvances(NULL), mTotalAdvance(0), mAdvancesCount(0),
+ mGlyphs(NULL), mGlyphsCount(0), mElapsedTime(0) {
+}
+
+TextLayoutCacheValue::~TextLayoutCacheValue() {
+ delete[] mAdvances;
+ delete[] mGlyphs;
+}
+
+void TextLayoutCacheValue::setElapsedTime(uint32_t time) {
+ mElapsedTime = time;
+}
+
+uint32_t TextLayoutCacheValue::getElapsedTime() {
+ return mElapsedTime;
+}
+
+void TextLayoutCacheValue::computeValues(SkPaint* paint, const UChar* chars, size_t start,
+ size_t count, size_t contextCount, int dirFlags) {
+ mAdvancesCount = count;
+ mAdvances = new float[count];
+
+#if RTL_USE_HARFBUZZ
+ computeValuesWithHarfbuzz(paint, chars, start, count, contextCount, dirFlags,
+ mAdvances, &mTotalAdvance, &mGlyphs, &mGlyphsCount);
+#else
+ computeAdvancesWithICU(paint, chars, start, count, contextCount, dirFlags,
+ mAdvances, &mTotalAdvance);
+#endif
+#if DEBUG_ADVANCES
+ LOGD("Advances - count=%d - countextCount=%d - totalAdvance=%f - "
+ "adv[0]=%f adv[1]=%f adv[2]=%f adv[3]=%f", count, contextCount, mTotalAdvance,
+ mAdvances[0], mAdvances[1], mAdvances[2], mAdvances[3]);
+#endif
+}
+
+size_t TextLayoutCacheValue::getSize() {
+ return sizeof(TextLayoutCacheValue) + sizeof(jfloat) * mAdvancesCount +
+ sizeof(jchar) * mGlyphsCount;
+}
+
+void TextLayoutCacheValue::setupShaperItem(HB_ShaperItem* shaperItem, HB_FontRec* font,
+ FontData* fontData, SkPaint* paint, const UChar* chars, size_t start, size_t count,
+ size_t contextCount, int dirFlags) {
+ bool isRTL = dirFlags & 0x1;
+
+ font->klass = &harfbuzzSkiaClass;
+ font->userData = 0;
+ // The values which harfbuzzSkiaClass returns are already scaled to
+ // pixel units, so we just set all these to one to disable further
+ // scaling.
+ font->x_ppem = 1;
+ font->y_ppem = 1;
+ font->x_scale = 1;
+ font->y_scale = 1;
+
+ memset(shaperItem, 0, sizeof(*shaperItem));
+ shaperItem->font = font;
+ shaperItem->face = HB_NewFace(shaperItem->font, harfbuzzSkiaGetTable);
+
+ shaperItem->kerning_applied = false;
+
+ // We cannot know, ahead of time, how many glyphs a given script run
+ // will produce. We take a guess that script runs will not produce more
+ // than twice as many glyphs as there are code points plus a bit of
+ // padding and fallback if we find that we are wrong.
+ createGlyphArrays(shaperItem, (contextCount + 2) * 2);
+
+ // Free memory for clusters if needed and recreate the clusters array
+ if (shaperItem->log_clusters) {
+ delete shaperItem->log_clusters;
+ }
+ shaperItem->log_clusters = new unsigned short[contextCount];
+
+ shaperItem->item.pos = start;
+ shaperItem->item.length = count;
+ shaperItem->item.bidiLevel = isRTL;
+ shaperItem->item.script = isRTL ? HB_Script_Arabic : HB_Script_Common;
+
+ shaperItem->string = chars;
+ shaperItem->stringLength = contextCount;
+
+ fontData->typeFace = paint->getTypeface();
+ fontData->textSize = paint->getTextSize();
+ fontData->textSkewX = paint->getTextSkewX();
+ fontData->textScaleX = paint->getTextScaleX();
+ fontData->flags = paint->getFlags();
+ fontData->hinting = paint->getHinting();
+
+ shaperItem->font->userData = fontData;
+}
+
+void TextLayoutCacheValue::shapeWithHarfbuzz(HB_ShaperItem* shaperItem, HB_FontRec* font,
+ FontData* fontData, SkPaint* paint, const UChar* chars, size_t start, size_t count,
+ size_t contextCount, int dirFlags) {
+ // Setup Harfbuzz Shaper
+ setupShaperItem(shaperItem, font, fontData, paint, chars, start, count,
+ contextCount, dirFlags);
+
+ // Shape
+ resetGlyphArrays(shaperItem);
+ while (!HB_ShapeItem(shaperItem)) {
+ // We overflowed our arrays. Resize and retry.
+ // HB_ShapeItem fills in shaperItem.num_glyphs with the needed size.
+ deleteGlyphArrays(shaperItem);
+ createGlyphArrays(shaperItem, shaperItem->num_glyphs << 1);
+ resetGlyphArrays(shaperItem);
+ }
+}
+
+struct GlyphRun {
+ inline GlyphRun() {}
+ inline GlyphRun(jchar* glyphs, size_t glyphsCount, bool isRTL) :
+ glyphs(glyphs), glyphsCount(glyphsCount), isRTL(isRTL) { }
+ jchar* glyphs;
+ size_t glyphsCount;
+ int isRTL;
+};
+
+void static reverseGlyphArray(jchar* glyphs, size_t count) {
+ for (size_t i = 0; i < count / 2; i++) {
+ jchar temp = glyphs[i];
+ glyphs[i] = glyphs[count - 1 - i];
+ glyphs[count - 1 - i] = temp;
+ }
+}
+
+void TextLayoutCacheValue::computeValuesWithHarfbuzz(SkPaint* paint, const UChar* chars,
+ size_t start, size_t count, size_t contextCount, int dirFlags,
+ jfloat* outAdvances, jfloat* outTotalAdvance,
+ jchar** outGlyphs, size_t* outGlyphsCount) {
+
+ UBiDiLevel bidiReq = 0;
+ bool forceLTR = false;
+ bool forceRTL = false;
+
+ switch (dirFlags) {
+ case kBidi_LTR: bidiReq = 0; break; // no ICU constant, canonical LTR level
+ case kBidi_RTL: bidiReq = 1; break; // no ICU constant, canonical RTL level
+ case kBidi_Default_LTR: bidiReq = UBIDI_DEFAULT_LTR; break;
+ case kBidi_Default_RTL: bidiReq = UBIDI_DEFAULT_RTL; break;
+ case kBidi_Force_LTR: forceLTR = true; break; // every char is LTR
+ case kBidi_Force_RTL: forceRTL = true; break; // every char is RTL
+ }
+
+ if (forceLTR || forceRTL) {
+#if DEBUG_GLYPHS
+ LOGD("computeValuesWithHarfbuzz -- forcing run with LTR=%d RTL=%d",
+ forceLTR, forceRTL);
+#endif
+ computeRunValuesWithHarfbuzz(paint, chars, start, count, contextCount, dirFlags,
+ outAdvances, outTotalAdvance, outGlyphs, outGlyphsCount);
+
+ if (forceRTL && *outGlyphsCount > 1) {
+ reverseGlyphArray(*outGlyphs, *outGlyphsCount);
+ }
+ } else {
+ UBiDi* bidi = ubidi_open();
+ if (bidi) {
+ UErrorCode status = U_ZERO_ERROR;
+#if DEBUG_GLYPHS
+ LOGD("computeValuesWithHarfbuzz -- bidiReq=%d", bidiReq);
+#endif
+ ubidi_setPara(bidi, chars, contextCount, bidiReq, NULL, &status);
+ if (U_SUCCESS(status)) {
+ int paraDir = ubidi_getParaLevel(bidi) & kDirection_Mask; // 0 if ltr, 1 if rtl
+ size_t rc = ubidi_countRuns(bidi, &status);
+#if DEBUG_GLYPHS
+ LOGD("computeValuesWithHarfbuzz -- dirFlags=%d run-count=%d paraDir=%d", dirFlags, rc, paraDir);
+#endif
+ if (rc == 1 || !U_SUCCESS(status)) {
+ computeRunValuesWithHarfbuzz(paint, chars, start, count, contextCount,
+ dirFlags, outAdvances, outTotalAdvance, outGlyphs, outGlyphsCount);
+
+ if (dirFlags == 1 && *outGlyphsCount > 1) {
+ reverseGlyphArray(*outGlyphs, *outGlyphsCount);
+ }
+ } else {
+ Vector<GlyphRun> glyphRuns;
+ jchar* runGlyphs;
+ size_t runGlyphsCount = 0;
+ size_t runIndex = 0;
+ for (size_t i = 0; i < rc; ++i) {
+ int32_t startRun;
+ int32_t lengthRun;
+ UBiDiDirection runDir = ubidi_getVisualRun(bidi, i, &startRun, &lengthRun);
+
+ int newFlags = (runDir == UBIDI_RTL) ? kDirection_RTL : kDirection_LTR;
+ jfloat runTotalAdvance = 0;
+#if DEBUG_GLYPHS
+ LOGD("computeValuesWithHarfbuzz -- run-start=%d run-len=%d newFlags=%d",
+ startRun, lengthRun, newFlags);
+#endif
+ computeRunValuesWithHarfbuzz(paint, chars, startRun,
+ lengthRun, contextCount, newFlags,
+ outAdvances + runIndex, &runTotalAdvance,
+ &runGlyphs, &runGlyphsCount);
+
+ runIndex += lengthRun;
+
+ *outTotalAdvance += runTotalAdvance;
+ *outGlyphsCount += runGlyphsCount;
+#if DEBUG_GLYPHS
+ LOGD("computeValuesWithHarfbuzz -- run=%d run-glyphs-count=%d",
+ i, runGlyphsCount);
+ for (size_t j = 0; j < runGlyphsCount; j++) {
+ LOGD(" -- glyphs[%d]=%d", j, runGlyphs[j]);
+ }
+#endif
+ glyphRuns.push(GlyphRun(runGlyphs, runGlyphsCount, newFlags));
+ }
+ *outGlyphs = new jchar[*outGlyphsCount];
+
+ jchar* glyphs = *outGlyphs;
+ for (size_t i = 0; i < glyphRuns.size(); i++) {
+ const GlyphRun& glyphRun = glyphRuns.itemAt(i);
+ if (glyphRun.isRTL) {
+ for (size_t n = 0; n < glyphRun.glyphsCount; n++) {
+ glyphs[glyphRun.glyphsCount - n - 1] = glyphRun.glyphs[n];
+ }
+ } else {
+ memcpy(glyphs, glyphRun.glyphs, glyphRun.glyphsCount * sizeof(jchar));
+ }
+ glyphs += glyphRun.glyphsCount;
+ delete[] glyphRun.glyphs;
+ }
+ }
+ }
+ ubidi_close(bidi);
+ } else {
+ // Cannot run BiDi, just consider one Run
+#if DEBUG_GLYPHS
+ LOGD("computeValuesWithHarfbuzz -- cannot run BiDi, considering only one Run");
+#endif
+ computeRunValuesWithHarfbuzz(paint, chars, start, count, contextCount, dirFlags,
+ outAdvances, outTotalAdvance, outGlyphs, outGlyphsCount);
+
+ if (dirFlags == 1 && *outGlyphsCount > 1) {
+ reverseGlyphArray(*outGlyphs, *outGlyphsCount);
+ }
+ }
+ }
+#if DEBUG_GLYPHS
+ LOGD("computeValuesWithHarfbuzz -- total-glyphs-count=%d", *outGlyphsCount);
+#endif
+}
+
+void TextLayoutCacheValue::computeRunValuesWithHarfbuzz(SkPaint* paint, const UChar* chars,
+ size_t start, size_t count, size_t contextCount, int dirFlags,
+ jfloat* outAdvances, jfloat* outTotalAdvance,
+ jchar** outGlyphs, size_t* outGlyphsCount) {
+
+ bool isRTL = dirFlags & 0x1;
+
+ HB_ShaperItem shaperItem;
+ HB_FontRec font;
+ FontData fontData;
+ shapeWithHarfbuzz(&shaperItem, &font, &fontData, paint, chars, start, count,
+ contextCount, dirFlags);
+
+#if DEBUG_GLYPHS
+ LOGD("HARFBUZZ -- num_glypth=%d - kerning_applied=%d", shaperItem.num_glyphs,
+ shaperItem.kerning_applied);
+ LOGD(" -- string= '%s'", String8(chars + start, count).string());
+ LOGD(" -- isDevKernText=%d", paint->isDevKernText());
+#endif
+
+ if (shaperItem.advances == NULL || shaperItem.num_glyphs == 0) {
+#if DEBUG_GLYPHS
+ LOGD("HARFBUZZ -- advances array is empty or num_glypth = 0");
+#endif
+ for (size_t i = 0; i < count; i++) {
+ outAdvances[i] = 0;
+ }
+ *outTotalAdvance = 0;
+
+ if (outGlyphs) {
+ *outGlyphsCount = 0;
+ *outGlyphs = new jchar[0];
+ }
+
+ // Cleaning
+ deleteGlyphArrays(&shaperItem);
+ HB_FreeFace(shaperItem.face);
+ return;
+ }
+ // Get Advances and their total
+ jfloat totalAdvance = outAdvances[0] = HBFixedToFloat(shaperItem.advances[shaperItem.log_clusters[0]]);
+ for (size_t i = 1; i < count; i++) {
+ size_t clusterPrevious = shaperItem.log_clusters[i - 1];
+ size_t cluster = shaperItem.log_clusters[i];
+ if (cluster == clusterPrevious) {
+ outAdvances[i] = 0;
+ } else {
+ totalAdvance += outAdvances[i] = HBFixedToFloat(shaperItem.advances[shaperItem.log_clusters[i]]);
+ }
+ }
+ *outTotalAdvance = totalAdvance;
+
+#if DEBUG_ADVANCES
+ for (size_t i = 0; i < count; i++) {
+ LOGD("hb-adv[%d] = %f - log_clusters = %d - total = %f", i,
+ outAdvances[i], shaperItem.log_clusters[i], totalAdvance);
+ }
+#endif
+
+ // Get Glyphs
+ if (outGlyphs) {
+ *outGlyphsCount = shaperItem.num_glyphs;
+ *outGlyphs = new jchar[shaperItem.num_glyphs];
+ for (size_t i = 0; i < shaperItem.num_glyphs; i++) {
+ (*outGlyphs)[i] = (jchar) shaperItem.glyphs[i];
+ }
+ }
+
+ // Cleaning
+ deleteGlyphArrays(&shaperItem);
+ HB_FreeFace(shaperItem.face);
+}
+
+void TextLayoutCacheValue::computeAdvancesWithICU(SkPaint* paint, const UChar* chars,
+ size_t start, size_t count, size_t contextCount, int dirFlags,
+ jfloat* outAdvances, jfloat* outTotalAdvance) {
+ SkAutoSTMalloc<CHAR_BUFFER_SIZE, jchar> tempBuffer(contextCount);
+ jchar* buffer = tempBuffer.get();
+ SkScalar* scalarArray = (SkScalar*)outAdvances;
+
+ // this is where we'd call harfbuzz
+ // for now we just use ushape.c
+ size_t widths;
+ const jchar* text;
+ if (dirFlags & 0x1) { // rtl, call arabic shaping in case
+ UErrorCode status = U_ZERO_ERROR;
+ // Use fixed length since we need to keep start and count valid
+ u_shapeArabic(chars, contextCount, buffer, contextCount,
+ U_SHAPE_LENGTH_FIXED_SPACES_NEAR |
+ U_SHAPE_TEXT_DIRECTION_LOGICAL | U_SHAPE_LETTERS_SHAPE |
+ U_SHAPE_X_LAMALEF_SUB_ALTERNATE, &status);
+ // we shouldn't fail unless there's an out of memory condition,
+ // in which case we're hosed anyway
+ for (int i = start, e = i + count; i < e; ++i) {
+ if (buffer[i] == UNICODE_NOT_A_CHAR) {
+ buffer[i] = UNICODE_ZWSP; // zero-width-space for skia
+ }
+ }
+ text = buffer + start;
+ widths = paint->getTextWidths(text, count << 1, scalarArray);
+ } else {
+ text = chars + start;
+ widths = paint->getTextWidths(text, count << 1, scalarArray);
+ }
+
+ jfloat totalAdvance = 0;
+ if (widths < count) {
+#if DEBUG_ADVANCES
+ LOGD("ICU -- count=%d", widths);
+#endif
+ // Skia operates on code points, not code units, so surrogate pairs return only
+ // one value. Expand the result so we have one value per UTF-16 code unit.
+
+ // Note, skia's getTextWidth gets confused if it encounters a surrogate pair,
+ // leaving the remaining widths zero. Not nice.
+ for (size_t i = 0, p = 0; i < widths; ++i) {
+ totalAdvance += outAdvances[p++] = SkScalarToFloat(scalarArray[i]);
+ if (p < count &&
+ text[p] >= UNICODE_FIRST_LOW_SURROGATE &&
+ text[p] < UNICODE_FIRST_PRIVATE_USE &&
+ text[p-1] >= UNICODE_FIRST_HIGH_SURROGATE &&
+ text[p-1] < UNICODE_FIRST_LOW_SURROGATE) {
+ outAdvances[p++] = 0;
+ }
+#if DEBUG_ADVANCES
+ LOGD("icu-adv = %f - total = %f", outAdvances[i], totalAdvance);
+#endif
+ }
+ } else {
+#if DEBUG_ADVANCES
+ LOGD("ICU -- count=%d", count);
+#endif
+ for (size_t i = 0; i < count; i++) {
+ totalAdvance += outAdvances[i] = SkScalarToFloat(scalarArray[i]);
+#if DEBUG_ADVANCES
+ LOGD("icu-adv = %f - total = %f", outAdvances[i], totalAdvance);
+#endif
+ }
+ }
+ *outTotalAdvance = totalAdvance;
+}
+
+void TextLayoutCacheValue::deleteGlyphArrays(HB_ShaperItem* shaperItem) {
+ delete[] shaperItem->glyphs;
+ delete[] shaperItem->attributes;
+ delete[] shaperItem->advances;
+ delete[] shaperItem->offsets;
+}
+
+void TextLayoutCacheValue::createGlyphArrays(HB_ShaperItem* shaperItem, int size) {
+ shaperItem->glyphs = new HB_Glyph[size];
+ shaperItem->attributes = new HB_GlyphAttributes[size];
+ shaperItem->advances = new HB_Fixed[size];
+ shaperItem->offsets = new HB_FixedPoint[size];
+ shaperItem->num_glyphs = size;
+}
+
+void TextLayoutCacheValue::resetGlyphArrays(HB_ShaperItem* shaperItem) {
+ int size = shaperItem->num_glyphs;
+ // All the types here don't have pointers. It is safe to reset to
+ // zero unless Harfbuzz breaks the compatibility in the future.
+ memset(shaperItem->glyphs, 0, size * sizeof(shaperItem->glyphs[0]));
+ memset(shaperItem->attributes, 0, size * sizeof(shaperItem->attributes[0]));
+ memset(shaperItem->advances, 0, size * sizeof(shaperItem->advances[0]));
+ memset(shaperItem->offsets, 0, size * sizeof(shaperItem->offsets[0]));
+}
+
+
+} // namespace android
diff --git a/core/jni/android/graphics/TextLayoutCache.h b/core/jni/android/graphics/TextLayoutCache.h
new file mode 100644
index 0000000..690caac
--- /dev/null
+++ b/core/jni/android/graphics/TextLayoutCache.h
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_TEXT_LAYOUT_CACHE_H
+#define ANDROID_TEXT_LAYOUT_CACHE_H
+
+#include "RtlProperties.h"
+
+#include <stddef.h>
+#include <utils/threads.h>
+#include <utils/String16.h>
+#include <utils/GenerationCache.h>
+#include <utils/Compare.h>
+#include <utils/RefBase.h>
+
+#include <SkPaint.h>
+#include <SkTemplates.h>
+#include <SkUtils.h>
+#include <SkScalerContext.h>
+#include <SkAutoKern.h>
+
+#include <unicode/ubidi.h>
+#include <unicode/ushape.h>
+#include "HarfbuzzSkia.h"
+#include "harfbuzz-shaper.h"
+
+#include <android_runtime/AndroidRuntime.h>
+
+#define UNICODE_NOT_A_CHAR 0xffff
+#define UNICODE_ZWSP 0x200b
+#define UNICODE_FIRST_LOW_SURROGATE 0xdc00
+#define UNICODE_FIRST_HIGH_SURROGATE 0xd800
+#define UNICODE_FIRST_PRIVATE_USE 0xe000
+#define UNICODE_FIRST_RTL_CHAR 0x0590
+
+// Temporary buffer size
+#define CHAR_BUFFER_SIZE 80
+
+// Converts a number of mega-bytes into bytes
+#define MB(s) s * 1024 * 1024
+
+// Define the default cache size in Mb
+#define DEFAULT_TEXT_LAYOUT_CACHE_SIZE_IN_MB 0.250f
+
+// Define the interval in number of cache hits between two statistics dump
+#define DEFAULT_DUMP_STATS_CACHE_HIT_INTERVAL 100
+
+namespace android {
+
+/**
+ * TextLayoutCacheKey is the Cache key
+ */
+class TextLayoutCacheKey {
+public:
+ TextLayoutCacheKey();
+
+ TextLayoutCacheKey(const SkPaint* paint,
+ const UChar* text, size_t start, size_t count,
+ size_t contextCount, int dirFlags);
+
+ bool operator<(const TextLayoutCacheKey& rhs) const;
+
+ /**
+ * We need to copy the text when we insert the key into the cache itself.
+ * We don't need to copy the text when we are only comparing keys.
+ */
+ void internalTextCopy();
+
+ /**
+ * Get the size of the Cache key.
+ */
+ size_t getSize();
+
+private:
+ const UChar* text;
+ String16 textCopy;
+ size_t start;
+ size_t count;
+ size_t contextCount;
+ int dirFlags;
+ SkTypeface* typeface;
+ SkScalar textSize;
+ SkScalar textSkewX;
+ SkScalar textScaleX;
+ uint32_t flags;
+ SkPaint::Hinting hinting;
+}; // TextLayoutCacheKey
+
+/*
+ * TextLayoutCacheValue is the Cache value
+ */
+class TextLayoutCacheValue : public RefBase {
+protected:
+ ~TextLayoutCacheValue();
+
+public:
+ TextLayoutCacheValue();
+
+ void setElapsedTime(uint32_t time);
+ uint32_t getElapsedTime();
+
+ void computeValues(SkPaint* paint, const UChar* chars, size_t start, size_t count,
+ size_t contextCount, int dirFlags);
+
+ inline const jfloat* getAdvances() const { return mAdvances; }
+ inline size_t getAdvancesCount() const { return mAdvancesCount; }
+ inline jfloat getTotalAdvance() const { return mTotalAdvance; }
+ inline const jchar* getGlyphs() const { return mGlyphs; }
+ inline size_t getGlyphsCount() const { return mGlyphsCount; }
+
+ /**
+ * Get the size of the Cache entry
+ */
+ size_t getSize();
+
+ static void setupShaperItem(HB_ShaperItem* shaperItem, HB_FontRec* font, FontData* fontData,
+ SkPaint* paint, const UChar* chars, size_t start, size_t count, size_t contextCount,
+ int dirFlags);
+
+ static void shapeWithHarfbuzz(HB_ShaperItem* shaperItem, HB_FontRec* font, FontData* fontData,
+ SkPaint* paint, const UChar* chars, size_t start, size_t count, size_t contextCount,
+ int dirFlags);
+
+ static void computeValuesWithHarfbuzz(SkPaint* paint, const UChar* chars, size_t start,
+ size_t count, size_t contextCount, int dirFlags,
+ jfloat* outAdvances, jfloat* outTotalAdvance,
+ jchar** outGlyphs, size_t* outGlyphsCount);
+
+ static void computeAdvancesWithICU(SkPaint* paint, const UChar* chars, size_t start,
+ size_t count, size_t contextCount, int dirFlags,
+ jfloat* outAdvances, jfloat* outTotalAdvance);
+
+private:
+ /**
+ * Advances array
+ */
+ jfloat* mAdvances;
+
+ /**
+ * Total number of advances
+ */
+ jfloat mTotalAdvance;
+
+ /**
+ * Allocated size for advances array
+ */
+ size_t mAdvancesCount;
+
+ /**
+ * Glyphs array
+ */
+ jchar* mGlyphs;
+
+ /**
+ * Total number of glyphs
+ */
+ size_t mGlyphsCount;
+
+ /**
+ * Time for computing the values (in milliseconds)
+ */
+ uint32_t mElapsedTime;
+
+ static void deleteGlyphArrays(HB_ShaperItem* shaperItem);
+ static void createGlyphArrays(HB_ShaperItem* shaperItem, int size);
+ static void resetGlyphArrays(HB_ShaperItem* shaperItem);
+
+ static void computeRunValuesWithHarfbuzz(SkPaint* paint, const UChar* chars, size_t start,
+ size_t count, size_t contextCount, int dirFlags,
+ jfloat* outAdvances, jfloat* outTotalAdvance,
+ jchar** outGlyphs, size_t* outGlyphsCount);
+}; // TextLayoutCacheValue
+
+/**
+ * Cache of text layout information.
+ */
+class TextLayoutCache : public OnEntryRemoved<TextLayoutCacheKey, sp<TextLayoutCacheValue> >
+{
+public:
+ TextLayoutCache();
+ TextLayoutCache(uint32_t maxByteSize);
+
+ virtual ~TextLayoutCache();
+
+ bool isInitialized() {
+ return mInitialized;
+ }
+
+ /**
+ * Used as a callback when an entry is removed from the cache
+ * Do not invoke directly
+ */
+ void operator()(TextLayoutCacheKey& text, sp<TextLayoutCacheValue>& desc);
+
+ sp<TextLayoutCacheValue> getValue(SkPaint* paint,
+ const jchar* text, jint start, jint count, jint contextCount, jint dirFlags);
+
+ /**
+ * Clear the cache
+ */
+ void clear();
+
+ /**
+ * Sets the maximum size of the cache in bytes
+ */
+ void setMaxSize(uint32_t maxSize);
+
+ /**
+ * Returns the maximum size of the cache in bytes
+ */
+ uint32_t getMaxSize();
+
+ /**
+ * Returns the current size of the cache in bytes
+ */
+ uint32_t getSize();
+
+private:
+ Mutex mLock;
+ bool mInitialized;
+
+ GenerationCache<TextLayoutCacheKey, sp<TextLayoutCacheValue> > mCache;
+
+ uint32_t mSize;
+ uint32_t mMaxSize;
+
+ uint32_t mCacheHitCount;
+ uint64_t mNanosecondsSaved;
+
+ uint64_t mCacheStartTime;
+
+ RtlDebugLevel mDebugLevel;
+ bool mDebugEnabled;
+
+ /*
+ * Class initialization
+ */
+ void init();
+
+ /**
+ * Remove oldest entries until we are having enough space
+ */
+ void removeOldests();
+
+ /**
+ * Dump Cache statistics
+ */
+ void dumpCacheStats();
+}; // TextLayoutCache
+
+} // namespace android
+#endif /* ANDROID_TEXT_LAYOUT_CACHE_H */
+
diff --git a/core/jni/android/graphics/Typeface.cpp b/core/jni/android/graphics/Typeface.cpp
index 53a5c0a..b25598a 100644
--- a/core/jni/android/graphics/Typeface.cpp
+++ b/core/jni/android/graphics/Typeface.cpp
@@ -44,7 +44,7 @@ static SkTypeface* Typeface_create(JNIEnv* env, jobject, jstring name,
static SkTypeface* Typeface_createFromTypeface(JNIEnv* env, jobject, SkTypeface* family, int style) {
return SkTypeface::CreateFromTypeface(family, (SkTypeface::Style)style);
}
-
+
static void Typeface_unref(JNIEnv* env, jobject obj, SkTypeface* face) {
SkSafeUnref(face);
}
@@ -64,7 +64,7 @@ public:
{
delete fAsset;
}
-
+
virtual const void* getMemoryBase()
{
return fMemoryBase;
@@ -75,38 +75,38 @@ public:
off64_t pos = fAsset->seek(0, SEEK_SET);
return pos != (off64_t)-1;
}
-
+
virtual size_t read(void* buffer, size_t size)
{
ssize_t amount;
-
+
if (NULL == buffer)
{
if (0 == size) // caller is asking us for our total length
return fAsset->getLength();
-
+
// asset->seek returns new total offset
// we want to return amount that was skipped
-
+
off64_t oldOffset = fAsset->seek(0, SEEK_CUR);
if (-1 == oldOffset)
return 0;
off64_t newOffset = fAsset->seek(size, SEEK_CUR);
if (-1 == newOffset)
return 0;
-
+
amount = newOffset - oldOffset;
}
else
{
amount = fAsset->read(buffer, size);
}
-
+
if (amount < 0)
amount = 0;
return amount;
}
-
+
private:
Asset* fAsset;
const void* fMemoryBase;
@@ -115,21 +115,21 @@ private:
static SkTypeface* Typeface_createFromAsset(JNIEnv* env, jobject,
jobject jassetMgr,
jstring jpath) {
-
+
NPE_CHECK_RETURN_ZERO(env, jassetMgr);
NPE_CHECK_RETURN_ZERO(env, jpath);
-
+
AssetManager* mgr = assetManagerForJavaObject(env, jassetMgr);
if (NULL == mgr) {
return NULL;
}
-
+
AutoJavaStringToUTF8 str(env, jpath);
Asset* asset = mgr->open(str.c_str(), Asset::ACCESS_BUFFER);
if (NULL == asset) {
return NULL;
}
-
+
SkStream* stream = new AssetStream(asset, true);
SkTypeface* face = SkTypeface::CreateFromStream(stream);
// SkTypeFace::CreateFromStream calls ref() on the stream, so we
@@ -180,7 +180,6 @@ static JNINativeMethod gTypefaceMethods[] = {
{ "setGammaForText", "(FF)V", (void*)Typeface_setGammaForText },
};
-int register_android_graphics_Typeface(JNIEnv* env);
int register_android_graphics_Typeface(JNIEnv* env)
{
return android::AndroidRuntime::registerNativeMethods(env,
diff --git a/core/jni/android/graphics/YuvToJpegEncoder.cpp b/core/jni/android/graphics/YuvToJpegEncoder.cpp
index 0a0c5b3..8333e00 100644
--- a/core/jni/android/graphics/YuvToJpegEncoder.cpp
+++ b/core/jni/android/graphics/YuvToJpegEncoder.cpp
@@ -242,7 +242,6 @@ static JNINativeMethod gYuvImageMethods[] = {
#define kClassPathName "android/graphics/YuvImage"
-int register_android_graphics_YuvImage(JNIEnv* env);
int register_android_graphics_YuvImage(JNIEnv* env)
{
return android::AndroidRuntime::registerNativeMethods(env, kClassPathName,
diff --git a/core/jni/android/opengl/util.cpp b/core/jni/android/opengl/util.cpp
index 589b255..6a049e0 100644
--- a/core/jni/android/opengl/util.cpp
+++ b/core/jni/android/opengl/util.cpp
@@ -14,7 +14,9 @@
** limitations under the License.
*/
-#include <nativehelper/jni.h>
+#include "jni.h"
+#include "JNIHelp.h"
+
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
@@ -38,10 +40,6 @@
namespace android {
-static jclass gIAEClass;
-static jclass gUOEClass;
-static jclass gAIOOBEClass;
-
static inline
void mx4transform(float x, float y, float z, float w, const float* pM, float* pDest) {
pDest[0] = pM[0 + 4 * 0] * x + pM[0 + 4 * 1] * y + pM[0 + 4 * 2] * z + pM[0 + 4 * 3] * w;
@@ -151,6 +149,10 @@ int visibilityTest(float* pWS, float* pPositions, int positionsLength,
return result;
}
+static void doThrowIAE(JNIEnv* env, const char* msg) {
+ jniThrowException(env, "java/lang/IllegalArgumentException", msg);
+}
+
template<class JArray, class T>
class ArrayHelper {
public:
@@ -178,16 +180,16 @@ public:
bool check() {
if ( ! mRef) {
- mEnv->ThrowNew(gIAEClass, "array == null");
+ doThrowIAE(mEnv, "array == null");
return false;
}
if ( mOffset < 0) {
- mEnv->ThrowNew(gIAEClass, "offset < 0");
+ doThrowIAE(mEnv, "offset < 0");
return false;
}
mLength = mEnv->GetArrayLength(mRef) - mOffset;
if (mLength < mMinSize ) {
- mEnv->ThrowNew(gIAEClass, "length - offset < n");
+ doThrowIAE(mEnv, "length - offset < n");
return false;
}
return true;
@@ -245,7 +247,7 @@ void util_computeBoundingSphere(JNIEnv *env, jclass clazz,
sphere.bind();
if ( positionsCount < 1 ) {
- env->ThrowNew(gIAEClass, "positionsCount < 1");
+ doThrowIAE(env, "positionsCount < 1");
return;
}
@@ -450,8 +452,7 @@ int util_visibilityTest(JNIEnv *env, jclass clazz,
}
if (indices.mLength < indexCount) {
- env->ThrowNew(gIAEClass, "length < offset + indexCount");
- // Return value will be ignored, because an exception has been thrown.
+ doThrowIAE(env, "length < offset + indexCount");
return -1;
}
@@ -784,11 +785,11 @@ public:
if (mBuffer) {
mData = getPointer(mEnv, mBuffer, &mRemaining);
if (mData == NULL) {
- mEnv->ThrowNew(gIAEClass, errorMessage);
+ doThrowIAE(mEnv, errorMessage);
}
return mData != NULL;
} else {
- mEnv->ThrowNew(gIAEClass, errorMessage);
+ doThrowIAE(mEnv, errorMessage);
return false;
}
}
@@ -824,16 +825,16 @@ private:
static void etc1_encodeBlock(JNIEnv *env, jclass clazz,
jobject in, jint validPixelMask, jobject out) {
if (validPixelMask < 0 || validPixelMask > 15) {
- env->ThrowNew(gIAEClass, "validPixelMask");
+ doThrowIAE(env, "validPixelMask");
return;
}
BufferHelper inB(env, in);
BufferHelper outB(env, out);
if (inB.checkPointer("in") && outB.checkPointer("out")) {
if (inB.remaining() < ETC1_DECODED_BLOCK_SIZE) {
- env->ThrowNew(gIAEClass, "in's remaining data < DECODED_BLOCK_SIZE");
+ doThrowIAE(env, "in's remaining data < DECODED_BLOCK_SIZE");
} else if (outB.remaining() < ETC1_ENCODED_BLOCK_SIZE) {
- env->ThrowNew(gIAEClass, "out's remaining data < ENCODED_BLOCK_SIZE");
+ doThrowIAE(env, "out's remaining data < ENCODED_BLOCK_SIZE");
} else {
etc1_encode_block((etc1_byte*) inB.getData(), validPixelMask,
(etc1_byte*) outB.getData());
@@ -856,9 +857,9 @@ static void etc1_decodeBlock(JNIEnv *env, jclass clazz,
BufferHelper outB(env, out);
if (inB.checkPointer("in") && outB.checkPointer("out")) {
if (inB.remaining() < ETC1_ENCODED_BLOCK_SIZE) {
- env->ThrowNew(gIAEClass, "in's remaining data < ENCODED_BLOCK_SIZE");
+ doThrowIAE(env, "in's remaining data < ENCODED_BLOCK_SIZE");
} else if (outB.remaining() < ETC1_DECODED_BLOCK_SIZE) {
- env->ThrowNew(gIAEClass, "out's remaining data < DECODED_BLOCK_SIZE");
+ doThrowIAE(env, "out's remaining data < DECODED_BLOCK_SIZE");
} else {
etc1_decode_block((etc1_byte*) inB.getData(),
(etc1_byte*) outB.getData());
@@ -884,7 +885,7 @@ static void etc1_encodeImage(JNIEnv *env, jclass clazz,
jobject in, jint width, jint height,
jint pixelSize, jint stride, jobject out) {
if (pixelSize < 2 || pixelSize > 3) {
- env->ThrowNew(gIAEClass, "pixelSize must be 2 or 3");
+ doThrowIAE(env, "pixelSize must be 2 or 3");
return;
}
BufferHelper inB(env, in);
@@ -893,9 +894,9 @@ static void etc1_encodeImage(JNIEnv *env, jclass clazz,
jint imageSize = stride * height;
jint encodedImageSize = etc1_get_encoded_data_size(width, height);
if (inB.remaining() < imageSize) {
- env->ThrowNew(gIAEClass, "in's remaining data < image size");
+ doThrowIAE(env, "in's remaining data < image size");
} else if (outB.remaining() < encodedImageSize) {
- env->ThrowNew(gIAEClass, "out's remaining data < encoded image size");
+ doThrowIAE(env, "out's remaining data < encoded image size");
} else {
int result = etc1_encode_image((etc1_byte*) inB.getData(),
width, height, pixelSize,
@@ -917,7 +918,7 @@ static void etc1_decodeImage(JNIEnv *env, jclass clazz,
jint width, jint height,
jint pixelSize, jint stride) {
if (pixelSize < 2 || pixelSize > 3) {
- env->ThrowNew(gIAEClass, "pixelSize must be 2 or 3");
+ doThrowIAE(env, "pixelSize must be 2 or 3");
return;
}
BufferHelper inB(env, in);
@@ -926,9 +927,9 @@ static void etc1_decodeImage(JNIEnv *env, jclass clazz,
jint imageSize = stride * height;
jint encodedImageSize = etc1_get_encoded_data_size(width, height);
if (inB.remaining() < encodedImageSize) {
- env->ThrowNew(gIAEClass, "in's remaining data < encoded image size");
+ doThrowIAE(env, "in's remaining data < encoded image size");
} else if (outB.remaining() < imageSize) {
- env->ThrowNew(gIAEClass, "out's remaining data < image size");
+ doThrowIAE(env, "out's remaining data < image size");
} else {
int result = etc1_decode_image((etc1_byte*) inB.getData(),
(etc1_byte*) outB.getData(),
@@ -946,7 +947,7 @@ static void etc1_formatHeader(JNIEnv *env, jclass clazz,
BufferHelper headerB(env, header);
if (headerB.checkPointer("header") ){
if (headerB.remaining() < ETC_PKM_HEADER_SIZE) {
- env->ThrowNew(gIAEClass, "header's remaining data < ETC_PKM_HEADER_SIZE");
+ doThrowIAE(env, "header's remaining data < ETC_PKM_HEADER_SIZE");
} else {
etc1_pkm_format_header((etc1_byte*) headerB.getData(), width, height);
}
@@ -962,7 +963,7 @@ static jboolean etc1_isValid(JNIEnv *env, jclass clazz,
BufferHelper headerB(env, header);
if (headerB.checkPointer("header") ){
if (headerB.remaining() < ETC_PKM_HEADER_SIZE) {
- env->ThrowNew(gIAEClass, "header's remaining data < ETC_PKM_HEADER_SIZE");
+ doThrowIAE(env, "header's remaining data < ETC_PKM_HEADER_SIZE");
} else {
result = etc1_pkm_is_valid((etc1_byte*) headerB.getData());
}
@@ -979,7 +980,7 @@ static jint etc1_getWidth(JNIEnv *env, jclass clazz,
BufferHelper headerB(env, header);
if (headerB.checkPointer("header") ){
if (headerB.remaining() < ETC_PKM_HEADER_SIZE) {
- env->ThrowNew(gIAEClass, "header's remaining data < ETC_PKM_HEADER_SIZE");
+ doThrowIAE(env, "header's remaining data < ETC_PKM_HEADER_SIZE");
} else {
result = etc1_pkm_get_width((etc1_byte*) headerB.getData());
}
@@ -996,7 +997,7 @@ static int etc1_getHeight(JNIEnv *env, jclass clazz,
BufferHelper headerB(env, header);
if (headerB.checkPointer("header") ){
if (headerB.remaining() < ETC_PKM_HEADER_SIZE) {
- env->ThrowNew(gIAEClass, "header's remaining data < ETC_PKM_HEADER_SIZE");
+ doThrowIAE(env, "header's remaining data < ETC_PKM_HEADER_SIZE");
} else {
result = etc1_pkm_get_height((etc1_byte*) headerB.getData());
}
@@ -1008,22 +1009,12 @@ static int etc1_getHeight(JNIEnv *env, jclass clazz,
* JNI registration
*/
-static void
-lookupClasses(JNIEnv* env) {
- gIAEClass = (jclass) env->NewGlobalRef(
- env->FindClass("java/lang/IllegalArgumentException"));
- gUOEClass = (jclass) env->NewGlobalRef(
- env->FindClass("java/lang/UnsupportedOperationException"));
- gAIOOBEClass = (jclass) env->NewGlobalRef(
- env->FindClass("java/lang/ArrayIndexOutOfBoundsException"));
-}
-
static JNINativeMethod gMatrixMethods[] = {
{ "multiplyMM", "([FI[FI[FI)V", (void*)util_multiplyMM },
{ "multiplyMV", "([FI[FI[FI)V", (void*)util_multiplyMV },
};
-static JNINativeMethod gVisiblityMethods[] = {
+static JNINativeMethod gVisibilityMethods[] = {
{ "computeBoundingSphere", "([FII[FI)V", (void*)util_computeBoundingSphere },
{ "frustumCullSpheres", "([FI[FII[III)I", (void*)util_frustumCullSpheres },
{ "visibilityTest", "([FI[FI[CII)I", (void*)util_visibilityTest },
@@ -1056,15 +1047,14 @@ typedef struct _ClassRegistrationInfo {
} ClassRegistrationInfo;
static ClassRegistrationInfo gClasses[] = {
- {"android/opengl/Matrix", gMatrixMethods, NELEM(gMatrixMethods)},
- {"android/opengl/Visibility", gVisiblityMethods, NELEM(gVisiblityMethods)},
- {"android/opengl/GLUtils", gUtilsMethods, NELEM(gUtilsMethods)},
- {"android/opengl/ETC1", gEtc1Methods, NELEM(gEtc1Methods)},
+ {"android/opengl/Matrix", gMatrixMethods, NELEM(gMatrixMethods)},
+ {"android/opengl/Visibility", gVisibilityMethods, NELEM(gVisibilityMethods)},
+ {"android/opengl/GLUtils", gUtilsMethods, NELEM(gUtilsMethods)},
+ {"android/opengl/ETC1", gEtc1Methods, NELEM(gEtc1Methods)},
};
int register_android_opengl_classes(JNIEnv* env)
{
- lookupClasses(env);
nativeClassInitBuffer(env);
int result = 0;
for (int i = 0; i < NELEM(gClasses); i++) {
@@ -1080,4 +1070,3 @@ int register_android_opengl_classes(JNIEnv* env)
}
} // namespace android
-
diff --git a/core/jni/android_app_NativeActivity.cpp b/core/jni/android_app_NativeActivity.cpp
index 56f2646..b1ea90b 100644
--- a/core/jni/android_app_NativeActivity.cpp
+++ b/core/jni/android_app_NativeActivity.cpp
@@ -42,8 +42,6 @@ namespace android
{
static struct {
- jclass clazz;
-
jmethodID dispatchUnhandledKeyEvent;
jmethodID preDispatchKeyEvent;
jmethodID finish;
@@ -1054,8 +1052,7 @@ static const char* const kNativeActivityPathName = "android/app/NativeActivity";
#define FIND_CLASS(var, className) \
var = env->FindClass(className); \
- LOG_FATAL_IF(! var, "Unable to find class %s", className); \
- var = jclass(env->NewGlobalRef(var));
+ LOG_FATAL_IF(! var, "Unable to find class %s", className);
#define GET_METHOD_ID(var, clazz, methodName, fieldDescriptor) \
var = env->GetMethodID(clazz, methodName, fieldDescriptor); \
@@ -1064,30 +1061,30 @@ static const char* const kNativeActivityPathName = "android/app/NativeActivity";
int register_android_app_NativeActivity(JNIEnv* env)
{
//LOGD("register_android_app_NativeActivity");
+ jclass clazz;
+ FIND_CLASS(clazz, kNativeActivityPathName);
- FIND_CLASS(gNativeActivityClassInfo.clazz, kNativeActivityPathName);
-
GET_METHOD_ID(gNativeActivityClassInfo.dispatchUnhandledKeyEvent,
- gNativeActivityClassInfo.clazz,
+ clazz,
"dispatchUnhandledKeyEvent", "(Landroid/view/KeyEvent;)Z");
GET_METHOD_ID(gNativeActivityClassInfo.preDispatchKeyEvent,
- gNativeActivityClassInfo.clazz,
+ clazz,
"preDispatchKeyEvent", "(Landroid/view/KeyEvent;I)V");
GET_METHOD_ID(gNativeActivityClassInfo.finish,
- gNativeActivityClassInfo.clazz,
+ clazz,
"finish", "()V");
GET_METHOD_ID(gNativeActivityClassInfo.setWindowFlags,
- gNativeActivityClassInfo.clazz,
+ clazz,
"setWindowFlags", "(II)V");
GET_METHOD_ID(gNativeActivityClassInfo.setWindowFormat,
- gNativeActivityClassInfo.clazz,
+ clazz,
"setWindowFormat", "(I)V");
GET_METHOD_ID(gNativeActivityClassInfo.showIme,
- gNativeActivityClassInfo.clazz,
+ clazz,
"showIme", "(I)V");
GET_METHOD_ID(gNativeActivityClassInfo.hideIme,
- gNativeActivityClassInfo.clazz,
+ clazz,
"hideIme", "(I)V");
return AndroidRuntime::registerNativeMethods(
diff --git a/core/jni/android_app_backup_FullBackup.cpp b/core/jni/android_app_backup_FullBackup.cpp
new file mode 100644
index 0000000..ecfe5ff
--- /dev/null
+++ b/core/jni/android_app_backup_FullBackup.cpp
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2011 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 "FullBackup_native"
+#include <utils/Log.h>
+#include <utils/String8.h>
+
+#include "JNIHelp.h"
+#include <android_runtime/AndroidRuntime.h>
+
+#include <utils/BackupHelpers.h>
+
+#include <string.h>
+
+namespace android
+{
+
+// android.app.backup.BackupDataOutput
+static struct {
+ // This is actually a native pointer to the underlying BackupDataWriter instance
+ jfieldID mBackupWriter;
+} sBackupDataOutput;
+
+/*
+ * Write files to the given output. This implementation does *not* create
+ * a standalone archive suitable for restore on its own. In particular, the identification of
+ * the application's name etc is not in-band here; it's assumed that the calling code has
+ * taken care of supplying that information previously in the output stream.
+ *
+ * The file format is 'tar's, with special semantics applied by use of a "fake" directory
+ * hierarchy within the tar stream:
+ *
+ * apps/packagename/a/Filename.apk - this is an actual application binary, which will be
+ * installed on the target device at restore time. These need to appear first in the tar
+ * stream.
+ * apps/packagename/obb/[relpath] - OBB containers belonging the app
+ * apps/packagename/r/[relpath] - these are files at the root of the app's data tree
+ * apps/packagename/f/[relpath] - this is a file within the app's getFilesDir() tree, stored
+ * at [relpath] relative to the top of that tree.
+ * apps/packagename/db/[relpath] - as with "files" but for the getDatabasePath() tree
+ * apps/packagename/sp/[relpath] - as with "files" but for the getSharedPrefsFile() tree
+ * apps/packagename/c/[relpath] - as with "files" but for the getCacheDir() tree
+ *
+ * and for the shared storage hierarchy:
+ *
+ * shared/[relpaths] - files belonging in the device's shared storage location. This will
+ * *not* include .obb files; those are saved with their owning apps.
+ *
+ * This method writes one file data block. 'domain' is the name of the appropriate pseudo-
+ * directory to be applied for this file; 'linkdomain' is the pseudo-dir for a relative
+ * symlink's antecedent.
+ *
+ * packagename: the package name to use as the top level directory tag
+ * domain: which semantic name the file is to be stored under (a, r, f, db, etc)
+ * linkdomain: where a symlink points for purposes of rewriting; current unused
+ * rootpath: prefix to be snipped from full path when encoding in tar
+ * path: absolute path to the file to be saved
+ * dataOutput: the BackupDataOutput object that we're saving into
+ */
+static int backupToTar(JNIEnv* env, jobject clazz, jstring packageNameObj,
+ jstring domainObj, jstring linkdomain,
+ jstring rootpathObj, jstring pathObj, jobject dataOutputObj) {
+ // Extract the various strings, allowing for null object pointers
+ const char* packagenamechars = env->GetStringUTFChars(packageNameObj, NULL);
+ const char* rootchars = env->GetStringUTFChars(rootpathObj, NULL);
+ const char* pathchars = env->GetStringUTFChars(pathObj, NULL);
+ const char* domainchars = env->GetStringUTFChars(domainObj, NULL);
+
+ String8 packageName(packagenamechars ? packagenamechars : "");
+ String8 rootpath(rootchars ? rootchars : "");
+ String8 path(pathchars ? pathchars : "");
+ String8 domain(domainchars ? domainchars : "");
+
+ if (domainchars) env->ReleaseStringUTFChars(domainObj, domainchars);
+ if (pathchars) env->ReleaseStringUTFChars(pathObj, pathchars);
+ if (rootchars) env->ReleaseStringUTFChars(rootpathObj, rootchars);
+ if (packagenamechars) env->ReleaseStringUTFChars(packageNameObj, packagenamechars);
+
+ // Extract the data output fd
+ BackupDataWriter* writer = (BackupDataWriter*) env->GetIntField(dataOutputObj,
+ sBackupDataOutput.mBackupWriter);
+
+ // Validate
+ if (!writer) {
+ LOGE("No output stream provided [%s]", path.string());
+ return -1;
+ }
+
+ if (path.length() < rootpath.length()) {
+ LOGE("file path [%s] shorter than root path [%s]",
+ path.string(), rootpath.string());
+ return -1;
+ }
+
+ return write_tarfile(packageName, domain, rootpath, path, writer);
+}
+
+static const JNINativeMethod g_methods[] = {
+ { "backupToTar",
+ "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Landroid/app/backup/BackupDataOutput;)I",
+ (void*)backupToTar },
+};
+
+int register_android_app_backup_FullBackup(JNIEnv* env)
+{
+ jclass clazz = env->FindClass("android/app/backup/BackupDataOutput");
+ LOG_FATAL_IF(clazz == NULL, "Unable to find class android.app.backup.BackupDataOutput");
+
+ sBackupDataOutput.mBackupWriter = env->GetFieldID(clazz, "mBackupWriter", "I");
+ LOG_FATAL_IF(sBackupDataOutput.mBackupwriter == NULL,
+ "Unable to find mBackupWriter field in android.app.backup.BackupDataOutput");
+
+ return AndroidRuntime::registerNativeMethods(env, "android/app/backup/FullBackup",
+ g_methods, NELEM(g_methods));
+}
+
+}
diff --git a/core/jni/android_backup_BackupDataInput.cpp b/core/jni/android_backup_BackupDataInput.cpp
index b03dd16..c174a41 100644
--- a/core/jni/android_backup_BackupDataInput.cpp
+++ b/core/jni/android_backup_BackupDataInput.cpp
@@ -25,9 +25,6 @@
namespace android
{
-// java.io.FileDescriptor
-static jfieldID s_descriptorField = 0;
-
// android.app.backup.BackupDataInput$EntityHeader
static jfieldID s_keyField = 0;
static jfieldID s_dataSizeField = 0;
@@ -35,9 +32,7 @@ static jfieldID s_dataSizeField = 0;
static int
ctor_native(JNIEnv* env, jobject clazz, jobject fileDescriptor)
{
- int err;
-
- int fd = env->GetIntField(fileDescriptor, s_descriptorField);
+ int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
if (fd == -1) {
return NULL;
}
@@ -140,15 +135,7 @@ 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/app/backup/BackupDataInput$EntityHeader");
+ jclass clazz = env->FindClass("android/app/backup/BackupDataInput$EntityHeader");
LOG_FATAL_IF(clazz == NULL, "Unable to find class android.app.backup.BackupDataInput.EntityHeader");
s_keyField = env->GetFieldID(clazz, "key", "Ljava/lang/String;");
LOG_FATAL_IF(s_keyField == NULL,
diff --git a/core/jni/android_backup_BackupDataOutput.cpp b/core/jni/android_backup_BackupDataOutput.cpp
index b895d11..144a10c 100644
--- a/core/jni/android_backup_BackupDataOutput.cpp
+++ b/core/jni/android_backup_BackupDataOutput.cpp
@@ -25,14 +25,10 @@
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);
+ int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
if (fd == -1) {
return NULL;
}
@@ -112,15 +108,6 @@ static const JNINativeMethod g_methods[] = {
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/app/backup/BackupDataOutput",
g_methods, NELEM(g_methods));
}
diff --git a/core/jni/android_backup_BackupHelperDispatcher.cpp b/core/jni/android_backup_BackupHelperDispatcher.cpp
index 26e7d66..49f1cd4 100644
--- a/core/jni/android_backup_BackupHelperDispatcher.cpp
+++ b/core/jni/android_backup_BackupHelperDispatcher.cpp
@@ -37,8 +37,6 @@ struct chunk_header_v1 {
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;
@@ -46,12 +44,11 @@ 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);
+ int fd = jniGetFDFromFileDescriptor(env, fdObj);
amt = read(fd, &flattenedHeader.headerSize, sizeof(flattenedHeader.headerSize));
if (amt != sizeof(flattenedHeader.headerSize)) {
@@ -128,9 +125,7 @@ readHeader_native(JNIEnv* env, jobject clazz, jobject headerObj, jobject fdObj)
static int
skipChunk_native(JNIEnv* env, jobject clazz, jobject fdObj, jint bytesToSkip)
{
- int fd;
-
- fd = env->GetIntField(fdObj, s_descriptorField);
+ int fd = jniGetFDFromFileDescriptor(env, fdObj);
lseek(fd, bytesToSkip, SEEK_CUR);
@@ -152,9 +147,8 @@ allocateHeader_native(JNIEnv* env, jobject clazz, jobject headerObj, jobject fdO
int nameLength;
int namePadding;
int headerSize;
- int fd;
- fd = env->GetIntField(fdObj, s_descriptorField);
+ int fd = jniGetFDFromFileDescriptor(env, fdObj);
nameObj = (jstring)env->GetObjectField(headerObj, s_keyPrefixField);
@@ -166,7 +160,7 @@ allocateHeader_native(JNIEnv* env, jobject clazz, jobject headerObj, jobject fdO
pos = lseek(fd, 0, SEEK_CUR);
lseek(fd, headerSize, SEEK_CUR);
-
+
return pos;
}
@@ -175,13 +169,12 @@ writeHeader_native(JNIEnv* env, jobject clazz, jobject headerObj, jobject fdObj,
{
int err;
chunk_header_v1 header;
- int fd;
int namePadding;
int prevPos;
jstring nameObj;
const char* buf;
- fd = env->GetIntField(fdObj, s_descriptorField);
+ int fd = jniGetFDFromFileDescriptor(env, fdObj);
prevPos = lseek(fd, 0, SEEK_CUR);
nameObj = (jstring)env->GetObjectField(headerObj, s_keyPrefixField);
@@ -234,15 +227,7 @@ static const JNINativeMethod g_methods[] = {
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/app/backup/BackupHelperDispatcher$Header");
+ jclass clazz = env->FindClass("android/app/backup/BackupHelperDispatcher$Header");
LOG_FATAL_IF(clazz == NULL,
"Unable to find class android.app.backup.BackupHelperDispatcher.Header");
s_chunkSizeField = env->GetFieldID(clazz, "chunkSize", "I");
@@ -251,7 +236,7 @@ int register_android_backup_BackupHelperDispatcher(JNIEnv* env)
s_keyPrefixField = env->GetFieldID(clazz, "keyPrefix", "Ljava/lang/String;");
LOG_FATAL_IF(s_keyPrefixField == NULL,
"Unable to find keyPrefix field in android.app.backup.BackupHelperDispatcher.Header");
-
+
return AndroidRuntime::registerNativeMethods(env, "android/app/backup/BackupHelperDispatcher",
g_methods, NELEM(g_methods));
}
diff --git a/core/jni/android_backup_FileBackupHelperBase.cpp b/core/jni/android_backup_FileBackupHelperBase.cpp
index 0137a06..0dfd8db 100644
--- a/core/jni/android_backup_FileBackupHelperBase.cpp
+++ b/core/jni/android_backup_FileBackupHelperBase.cpp
@@ -25,9 +25,6 @@
namespace android
{
-// java.io.FileDescriptor
-static jfieldID s_descriptorField = 0;
-
static int
ctor(JNIEnv* env, jobject clazz)
{
@@ -47,8 +44,8 @@ performBackup_native(JNIEnv* env, jobject clazz, jobject oldState, int data,
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);
+ int oldStateFD = oldState != NULL ? jniGetFDFromFileDescriptor(env, oldState) : -1;
+ int newStateFD = jniGetFDFromFileDescriptor(env, newState);
BackupDataWriter* dataStream = (BackupDataWriter*)data;
const int fileCount = env->GetArrayLength(files);
@@ -102,7 +99,7 @@ writeSnapshot_native(JNIEnv* env, jobject clazz, jint ptr, jobject fileDescripto
int err;
RestoreHelperBase* restore = (RestoreHelperBase*)ptr;
- int fd = env->GetIntField(fileDescriptor, s_descriptorField);
+ int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
err = restore->WriteSnapshot(fd);
@@ -121,14 +118,6 @@ static const JNINativeMethod g_methods[] = {
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/app/backup/FileBackupHelperBase",
g_methods, NELEM(g_methods));
}
diff --git a/core/jni/android_bluetooth_BluetoothAudioGateway.cpp b/core/jni/android_bluetooth_BluetoothAudioGateway.cpp
index acf858a..cb742a3 100755
--- a/core/jni/android_bluetooth_BluetoothAudioGateway.cpp
+++ b/core/jni/android_bluetooth_BluetoothAudioGateway.cpp
@@ -484,21 +484,21 @@ static int setup_listening_socket(int dev, int channel) {
lm = RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT;
}
- if (lm && setsockopt(sk, SOL_RFCOMM, RFCOMM_LM, &lm, sizeof(lm)) < 0) {
- LOGE("Can't set RFCOMM link mode");
- close(sk);
- return -1;
- }
+ if (lm && setsockopt(sk, SOL_RFCOMM, RFCOMM_LM, &lm, sizeof(lm)) < 0) {
+ LOGE("Can't set RFCOMM link mode");
+ close(sk);
+ return -1;
+ }
laddr.rc_family = AF_BLUETOOTH;
- bacpy(&laddr.rc_bdaddr, BDADDR_ANY);
+ memcpy(&laddr.rc_bdaddr, BDADDR_ANY, sizeof(bdaddr_t));
laddr.rc_channel = channel;
- if (bind(sk, (struct sockaddr *)&laddr, sizeof(laddr)) < 0) {
- LOGE("Can't bind RFCOMM socket");
- close(sk);
- return -1;
- }
+ if (bind(sk, (struct sockaddr *)&laddr, sizeof(laddr)) < 0) {
+ LOGE("Can't bind RFCOMM socket");
+ close(sk);
+ return -1;
+ }
listen(sk, 10);
return sk;
diff --git a/core/jni/android_bluetooth_HeadsetBase.cpp b/core/jni/android_bluetooth_HeadsetBase.cpp
index bbf1ae5..5b21c56 100644
--- a/core/jni/android_bluetooth_HeadsetBase.cpp
+++ b/core/jni/android_bluetooth_HeadsetBase.cpp
@@ -96,11 +96,12 @@ static int send_line(int fd, const char* line) {
return 0;
}
-static int is_ascii(char *line) {
- for (;;line++) {
- if (*line == 0) return 1;
- if (*line >> 7) return 0;
- }
+static void mask_eighth_bit(char *line)
+{
+ for (;;line++) {
+ if (0 == *line) return;
+ *line &= 0x7F;
+ }
}
static const char* get_line(int fd, char *buf, int len, int timeout_ms,
@@ -164,16 +165,15 @@ again:
*bufit = NULL;
- // Simple validation. Must be all ASCII.
- // (we sometimes send non-ASCII UTF-8 in address book, but should
- // never receive non-ASCII UTF-8).
- // This was added because of the BMW 2005 E46 which sends binary junk.
- if (is_ascii(buf)) {
- IF_LOGV() LOG(LOG_VERBOSE, "Bluetooth AT recv", "%s", buf);
- } else {
- LOGW("Ignoring invalid AT command: %s", buf);
- buf[0] = NULL;
- }
+ // According to ITU V.250 section 5.1, IA5 7 bit chars are used,
+ // the eighth bit or higher bits are ignored if they exists
+ // We mask out only eighth bit, no higher bit, since we do char
+ // string here, not wide char.
+ // We added this processing due to 2 real world problems.
+ // 1 BMW 2005 E46 which sends binary junk
+ // 2 Audi 2010 A3, dial command use 0xAD (soft-hyphen) as number
+ // formater, which was rejected by the AT handler
+ mask_eighth_bit(buf);
return buf;
}
diff --git a/core/jni/android_bluetooth_common.cpp b/core/jni/android_bluetooth_common.cpp
index aae0f21..6ae3e35 100644
--- a/core/jni/android_bluetooth_common.cpp
+++ b/core/jni/android_bluetooth_common.cpp
@@ -43,6 +43,7 @@ static Properties remote_device_properties[] = {
{"Icon", DBUS_TYPE_STRING},
{"Class", DBUS_TYPE_UINT32},
{"UUIDs", DBUS_TYPE_ARRAY},
+ {"Services", DBUS_TYPE_ARRAY},
{"Paired", DBUS_TYPE_BOOLEAN},
{"Connected", DBUS_TYPE_BOOLEAN},
{"Trusted", DBUS_TYPE_BOOLEAN},
@@ -52,7 +53,8 @@ static Properties remote_device_properties[] = {
{"Adapter", DBUS_TYPE_OBJECT_PATH},
{"LegacyPairing", DBUS_TYPE_BOOLEAN},
{"RSSI", DBUS_TYPE_INT16},
- {"TX", DBUS_TYPE_UINT32}
+ {"TX", DBUS_TYPE_UINT32},
+ {"Broadcaster", DBUS_TYPE_BOOLEAN}
};
static Properties adapter_properties[] = {
diff --git a/core/jni/android_content_res_Configuration.cpp b/core/jni/android_content_res_Configuration.cpp
index 5e09b38..246e3bd 100644
--- a/core/jni/android_content_res_Configuration.cpp
+++ b/core/jni/android_content_res_Configuration.cpp
@@ -26,8 +26,6 @@
namespace android {
static struct {
- jclass clazz;
-
jfieldID mcc;
jfieldID mnc;
jfieldID locale;
@@ -83,8 +81,7 @@ static JNINativeMethod gMethods[] = {
#define FIND_CLASS(var, className) \
var = env->FindClass(className); \
- LOG_FATAL_IF(! var, "Unable to find class " className); \
- var = jclass(env->NewGlobalRef(var));
+ LOG_FATAL_IF(! var, "Unable to find class " className);
#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \
var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \
@@ -92,37 +89,38 @@ static JNINativeMethod gMethods[] = {
int register_android_content_res_Configuration(JNIEnv* env)
{
- FIND_CLASS(gConfigurationClassInfo.clazz, "android/content/res/Configuration");
+ jclass clazz;
+ FIND_CLASS(clazz, "android/content/res/Configuration");
- GET_FIELD_ID(gConfigurationClassInfo.mcc, gConfigurationClassInfo.clazz,
+ GET_FIELD_ID(gConfigurationClassInfo.mcc, clazz,
"mcc", "I");
- GET_FIELD_ID(gConfigurationClassInfo.mnc, gConfigurationClassInfo.clazz,
+ GET_FIELD_ID(gConfigurationClassInfo.mnc, clazz,
"mnc", "I");
- GET_FIELD_ID(gConfigurationClassInfo.locale, gConfigurationClassInfo.clazz,
+ GET_FIELD_ID(gConfigurationClassInfo.locale, clazz,
"locale", "Ljava/util/Locale;");
- GET_FIELD_ID(gConfigurationClassInfo.screenLayout, gConfigurationClassInfo.clazz,
+ GET_FIELD_ID(gConfigurationClassInfo.screenLayout, clazz,
"screenLayout", "I");
- GET_FIELD_ID(gConfigurationClassInfo.touchscreen, gConfigurationClassInfo.clazz,
+ GET_FIELD_ID(gConfigurationClassInfo.touchscreen, clazz,
"touchscreen", "I");
- GET_FIELD_ID(gConfigurationClassInfo.keyboard, gConfigurationClassInfo.clazz,
+ GET_FIELD_ID(gConfigurationClassInfo.keyboard, clazz,
"keyboard", "I");
- GET_FIELD_ID(gConfigurationClassInfo.keyboardHidden, gConfigurationClassInfo.clazz,
+ GET_FIELD_ID(gConfigurationClassInfo.keyboardHidden, clazz,
"keyboardHidden", "I");
- GET_FIELD_ID(gConfigurationClassInfo.hardKeyboardHidden, gConfigurationClassInfo.clazz,
+ GET_FIELD_ID(gConfigurationClassInfo.hardKeyboardHidden, clazz,
"hardKeyboardHidden", "I");
- GET_FIELD_ID(gConfigurationClassInfo.navigation, gConfigurationClassInfo.clazz,
+ GET_FIELD_ID(gConfigurationClassInfo.navigation, clazz,
"navigation", "I");
- GET_FIELD_ID(gConfigurationClassInfo.navigationHidden, gConfigurationClassInfo.clazz,
+ GET_FIELD_ID(gConfigurationClassInfo.navigationHidden, clazz,
"navigationHidden", "I");
- GET_FIELD_ID(gConfigurationClassInfo.orientation, gConfigurationClassInfo.clazz,
+ GET_FIELD_ID(gConfigurationClassInfo.orientation, clazz,
"orientation", "I");
- GET_FIELD_ID(gConfigurationClassInfo.uiMode, gConfigurationClassInfo.clazz,
+ GET_FIELD_ID(gConfigurationClassInfo.uiMode, clazz,
"uiMode", "I");
- GET_FIELD_ID(gConfigurationClassInfo.screenWidthDp, gConfigurationClassInfo.clazz,
+ GET_FIELD_ID(gConfigurationClassInfo.screenWidthDp, clazz,
"screenWidthDp", "I");
- GET_FIELD_ID(gConfigurationClassInfo.screenHeightDp, gConfigurationClassInfo.clazz,
+ GET_FIELD_ID(gConfigurationClassInfo.screenHeightDp, clazz,
"screenHeightDp", "I");
- GET_FIELD_ID(gConfigurationClassInfo.smallestScreenWidthDp, gConfigurationClassInfo.clazz,
+ GET_FIELD_ID(gConfigurationClassInfo.smallestScreenWidthDp, clazz,
"smallestScreenWidthDp", "I");
return AndroidRuntime::registerNativeMethods(env, "android/content/res/Configuration", gMethods,
diff --git a/core/jni/android_content_res_ObbScanner.cpp b/core/jni/android_content_res_ObbScanner.cpp
index 3fd7985..8837538 100644
--- a/core/jni/android_content_res_ObbScanner.cpp
+++ b/core/jni/android_content_res_ObbScanner.cpp
@@ -21,6 +21,7 @@
#include <utils/ObbFile.h>
#include "jni.h"
+#include "JNIHelp.h"
#include "utils/misc.h"
#include "android_runtime/AndroidRuntime.h"
@@ -35,25 +36,15 @@ static struct {
jfieldID salt;
} gObbInfoClassInfo;
-static void doThrow(JNIEnv* env, const char* exc, const char* msg = NULL)
-{
- jclass npeClazz;
-
- npeClazz = env->FindClass(exc);
- LOG_FATAL_IF(npeClazz == NULL, "Unable to find class %s", exc);
-
- env->ThrowNew(npeClazz, msg);
-}
-
static void android_content_res_ObbScanner_getObbInfo(JNIEnv* env, jobject clazz, jstring file,
jobject obbInfo)
{
- const char* filePath = env->GetStringUTFChars(file, JNI_FALSE);
+ const char* filePath = env->GetStringUTFChars(file, NULL);
sp<ObbFile> obb = new ObbFile();
if (!obb->readFrom(filePath)) {
env->ReleaseStringUTFChars(file, filePath);
- doThrow(env, "java/io/IOException", "Could not read OBB file");
+ jniThrowException(env, "java/io/IOException", "Could not read OBB file");
return;
}
@@ -63,7 +54,7 @@ static void android_content_res_ObbScanner_getObbInfo(JNIEnv* env, jobject clazz
jstring packageName = env->NewStringUTF(packageNameStr);
if (packageName == NULL) {
- doThrow(env, "java/io/IOException", "Could not read OBB file");
+ jniThrowException(env, "java/io/IOException", "Could not read OBB file");
return;
}
@@ -91,8 +82,7 @@ static JNINativeMethod gMethods[] = {
#define FIND_CLASS(var, className) \
var = env->FindClass(className); \
- LOG_FATAL_IF(! var, "Unable to find class " className); \
- var = jclass(env->NewGlobalRef(var));
+ LOG_FATAL_IF(! var, "Unable to find class " className);
#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \
var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \
@@ -100,15 +90,16 @@ static JNINativeMethod gMethods[] = {
int register_android_content_res_ObbScanner(JNIEnv* env)
{
- FIND_CLASS(gObbInfoClassInfo.clazz, "android/content/res/ObbInfo");
+ jclass clazz;
+ FIND_CLASS(clazz, "android/content/res/ObbInfo");
- GET_FIELD_ID(gObbInfoClassInfo.packageName, gObbInfoClassInfo.clazz,
+ GET_FIELD_ID(gObbInfoClassInfo.packageName, clazz,
"packageName", "Ljava/lang/String;");
- GET_FIELD_ID(gObbInfoClassInfo.version, gObbInfoClassInfo.clazz,
+ GET_FIELD_ID(gObbInfoClassInfo.version, clazz,
"version", "I");
- GET_FIELD_ID(gObbInfoClassInfo.flags, gObbInfoClassInfo.clazz,
+ GET_FIELD_ID(gObbInfoClassInfo.flags, clazz,
"flags", "I");
- GET_FIELD_ID(gObbInfoClassInfo.salt, gObbInfoClassInfo.clazz,
+ GET_FIELD_ID(gObbInfoClassInfo.salt, clazz,
"salt", "[B");
return AndroidRuntime::registerNativeMethods(env, "android/content/res/ObbScanner", gMethods,
@@ -116,4 +107,3 @@ int register_android_content_res_ObbScanner(JNIEnv* env)
}
}; // namespace android
-
diff --git a/core/jni/android_database_SQLiteStatement.cpp b/core/jni/android_database_SQLiteStatement.cpp
index 97e0483..05ffbb1 100644
--- a/core/jni/android_database_SQLiteStatement.cpp
+++ b/core/jni/android_database_SQLiteStatement.cpp
@@ -143,7 +143,7 @@ static jstring native_1x1_string(JNIEnv* env, jobject object)
static jobject createParcelFileDescriptor(JNIEnv * env, int fd)
{
// Create FileDescriptor object
- jobject fileDesc = newFileDescriptor(env, fd);
+ jobject fileDesc = jniCreateFileDescriptor(env, fd);
if (fileDesc == NULL) {
// FileDescriptor constructor has thrown an exception
close(fd);
diff --git a/core/jni/android_emoji_EmojiFactory.cpp b/core/jni/android_emoji_EmojiFactory.cpp
index 1ebb36c..81dae88 100644
--- a/core/jni/android_emoji_EmojiFactory.cpp
+++ b/core/jni/android_emoji_EmojiFactory.cpp
@@ -93,8 +93,6 @@ static EmojiFactoryCaller* gCaller;
static pthread_once_t g_once = PTHREAD_ONCE_INIT;
static bool lib_emoji_factory_is_ready;
-static jclass gString_class;
-
static jclass gBitmap_class;
static jmethodID gBitmap_constructorMethodID;
@@ -108,15 +106,11 @@ static void InitializeCaller() {
static jobject create_java_EmojiFactory(
JNIEnv* env, EmojiFactory* factory, jstring name) {
- jobject obj = env->AllocObject(gEmojiFactory_class);
- if (obj) {
- env->CallVoidMethod(obj, gEmojiFactory_constructorMethodID,
- (jint)factory, name);
- if (env->ExceptionCheck() != 0) {
- LOGE("*** Uncaught exception returned from Java call!\n");
- env->ExceptionDescribe();
- obj = NULL;
- }
+ jobject obj = env->NewObject(gEmojiFactory_class, gEmojiFactory_constructorMethodID,
+ static_cast<jint>(reinterpret_cast<uintptr_t>(factory)), name);
+ if (env->ExceptionCheck() != 0) {
+ LOGE("*** Uncaught exception returned from Java call!\n");
+ env->ExceptionDescribe();
}
return obj;
}
@@ -182,17 +176,12 @@ static jobject android_emoji_EmojiFactory_getBitmapFromAndroidPua(
return NULL;
}
- jobject obj = env->AllocObject(gBitmap_class);
- if (obj) {
- env->CallVoidMethod(obj, gBitmap_constructorMethodID,
- reinterpret_cast<jint>(bitmap), NULL, false, NULL, -1);
- if (env->ExceptionCheck() != 0) {
- LOGE("*** Uncaught exception returned from Java call!\n");
- env->ExceptionDescribe();
- return NULL;
- }
+ jobject obj = env->NewObject(gBitmap_class, gBitmap_constructorMethodID,
+ static_cast<jint>(reinterpret_cast<uintptr_t>(bitmap)), NULL, false, NULL, -1);
+ if (env->ExceptionCheck() != 0) {
+ LOGE("*** Uncaught exception returned from Java call!\n");
+ env->ExceptionDescribe();
}
-
return obj;
}
diff --git a/core/jni/android_graphics_PixelFormat.cpp b/core/jni/android_graphics_PixelFormat.cpp
index 5b8363c..1fc363b 100644
--- a/core/jni/android_graphics_PixelFormat.cpp
+++ b/core/jni/android_graphics_PixelFormat.cpp
@@ -20,6 +20,7 @@
#include <ui/PixelFormat.h>
#include "jni.h"
+#include "JNIHelp.h"
#include <android_runtime/AndroidRuntime.h>
#include <utils/misc.h>
@@ -36,12 +37,6 @@ struct offsets_t {
static offsets_t offsets;
-static void doThrow(JNIEnv* env, const char* exc, const char* msg = NULL)
-{
- jclass npeClazz = env->FindClass(exc);
- env->ThrowNew(npeClazz, msg);
-}
-
// ----------------------------------------------------------------------------
static void android_graphics_getPixelFormatInfo(
@@ -72,7 +67,7 @@ static void android_graphics_getPixelFormatInfo(
err = getPixelFormatInfo(format, &info);
if (err < 0) {
- doThrow(env, "java/lang/IllegalArgumentException");
+ jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
return;
}
@@ -97,7 +92,7 @@ static JNINativeMethod gMethods[] = {
void nativeClassInit(JNIEnv* env, jclass clazz)
{
offsets.bytesPerPixel = env->GetFieldID(clazz, "bytesPerPixel", "I");
- offsets.bitsPerPixel = env->GetFieldID(clazz, "bitsPerPixel", "I");
+ offsets.bitsPerPixel = env->GetFieldID(clazz, "bitsPerPixel", "I");
}
int register_android_graphics_PixelFormat(JNIEnv* env)
diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp
index bfbfd37..4687ee0 100644
--- a/core/jni/android_hardware_Camera.cpp
+++ b/core/jni/android_hardware_Camera.cpp
@@ -105,7 +105,7 @@ sp<Camera> get_native_camera(JNIEnv *env, jobject thiz, JNICameraContext** pCont
}
LOGV("get_native_camera: context=%p, camera=%p", context, camera.get());
if (camera == 0) {
- jniThrowException(env, "java/lang/RuntimeException", "Method called after release()");
+ jniThrowRuntimeException(env, "Method called after release()");
}
if (pContext != NULL) *pContext = context;
@@ -218,7 +218,7 @@ void JNICameraContext::copyAndPost(JNIEnv* env, const sp<IMemory>& dataPtr, int
if (mCallbackBuffers.isEmpty()) {
LOGV("Out of buffers, clearing callback!");
- mCamera->setPreviewCallbackFlags(FRAME_CALLBACK_FLAG_NOOP);
+ mCamera->setPreviewCallbackFlags(CAMERA_FRAME_CALLBACK_FLAG_NOOP);
mManualCameraCallbackSet = false;
if (obj == NULL) {
@@ -232,8 +232,7 @@ void JNICameraContext::copyAndPost(JNIEnv* env, const sp<IMemory>& dataPtr, int
break;
}
default: {
- jniThrowException(env,
- "java/lang/RuntimeException", "Unsupported message type");
+ jniThrowRuntimeException(env, "Unsupported message type");
return;
}
}
@@ -306,22 +305,22 @@ void JNICameraContext::setCallbackMode(JNIEnv *env, bool installed, bool manualM
mManualCameraCallbackSet = false;
// In order to limit the over usage of binder threads, all non-manual buffer
- // callbacks use FRAME_CALLBACK_FLAG_BARCODE_SCANNER mode now.
+ // callbacks use CAMERA_FRAME_CALLBACK_FLAG_BARCODE_SCANNER mode now.
//
// Continuous callbacks will have the callback re-registered from handleMessage.
// Manual buffer mode will operate as fast as possible, relying on the finite supply
// of buffers for throttling.
if (!installed) {
- mCamera->setPreviewCallbackFlags(FRAME_CALLBACK_FLAG_NOOP);
+ mCamera->setPreviewCallbackFlags(CAMERA_FRAME_CALLBACK_FLAG_NOOP);
clearCallbackBuffers_l(env, &mCallbackBuffers);
} else if (mManualBufferMode) {
if (!mCallbackBuffers.isEmpty()) {
- mCamera->setPreviewCallbackFlags(FRAME_CALLBACK_FLAG_CAMERA);
+ mCamera->setPreviewCallbackFlags(CAMERA_FRAME_CALLBACK_FLAG_CAMERA);
mManualCameraCallbackSet = true;
}
} else {
- mCamera->setPreviewCallbackFlags(FRAME_CALLBACK_FLAG_BARCODE_SCANNER);
+ mCamera->setPreviewCallbackFlags(CAMERA_FRAME_CALLBACK_FLAG_BARCODE_SCANNER);
clearCallbackBuffers_l(env, &mCallbackBuffers);
}
}
@@ -344,7 +343,7 @@ void JNICameraContext::addCallbackBuffer(
// next frame. This may have come unset had we not had a
// callbackbuffer ready for it last time.
if (mManualBufferMode && !mManualCameraCallbackSet) {
- mCamera->setPreviewCallbackFlags(FRAME_CALLBACK_FLAG_CAMERA);
+ mCamera->setPreviewCallbackFlags(CAMERA_FRAME_CALLBACK_FLAG_CAMERA);
mManualCameraCallbackSet = true;
}
break;
@@ -391,8 +390,7 @@ static void android_hardware_Camera_getCameraInfo(JNIEnv *env, jobject thiz,
CameraInfo cameraInfo;
status_t rc = Camera::getCameraInfo(cameraId, &cameraInfo);
if (rc != NO_ERROR) {
- jniThrowException(env, "java/lang/RuntimeException",
- "Fail to get camera info");
+ jniThrowRuntimeException(env, "Fail to get camera info");
return;
}
env->SetIntField(info_obj, fields.facing, cameraInfo.facing);
@@ -406,20 +404,19 @@ static void android_hardware_Camera_native_setup(JNIEnv *env, jobject thiz,
sp<Camera> camera = Camera::connect(cameraId);
if (camera == NULL) {
- jniThrowException(env, "java/lang/RuntimeException",
- "Fail to connect to camera service");
+ jniThrowRuntimeException(env, "Fail to connect to camera service");
return;
}
// make sure camera hardware is alive
if (camera->getStatus() != NO_ERROR) {
- jniThrowException(env, "java/lang/RuntimeException", "Camera initialization failed");
+ jniThrowRuntimeException(env, "Camera initialization failed");
return;
}
jclass clazz = env->GetObjectClass(thiz);
if (clazz == NULL) {
- jniThrowException(env, "java/lang/RuntimeException", "Can't find android/hardware/Camera");
+ jniThrowRuntimeException(env, "Can't find android/hardware/Camera");
return;
}
@@ -459,7 +456,7 @@ static void android_hardware_Camera_release(JNIEnv *env, jobject thiz)
// clear callbacks
if (camera != NULL) {
- camera->setPreviewCallbackFlags(FRAME_CALLBACK_FLAG_NOOP);
+ camera->setPreviewCallbackFlags(CAMERA_FRAME_CALLBACK_FLAG_NOOP);
camera->disconnect();
}
@@ -508,7 +505,7 @@ static void android_hardware_Camera_startPreview(JNIEnv *env, jobject thiz)
if (camera == 0) return;
if (camera->startPreview() != NO_ERROR) {
- jniThrowException(env, "java/lang/RuntimeException", "startPreview failed");
+ jniThrowRuntimeException(env, "startPreview failed");
return;
}
}
@@ -564,7 +561,7 @@ static void android_hardware_Camera_autoFocus(JNIEnv *env, jobject thiz)
if (c == 0) return;
if (c->autoFocus() != NO_ERROR) {
- jniThrowException(env, "java/lang/RuntimeException", "autoFocus failed");
+ jniThrowRuntimeException(env, "autoFocus failed");
}
}
@@ -576,7 +573,7 @@ static void android_hardware_Camera_cancelAutoFocus(JNIEnv *env, jobject thiz)
if (c == 0) return;
if (c->cancelAutoFocus() != NO_ERROR) {
- jniThrowException(env, "java/lang/RuntimeException", "cancelAutoFocus failed");
+ jniThrowRuntimeException(env, "cancelAutoFocus failed");
}
}
@@ -606,7 +603,7 @@ static void android_hardware_Camera_takePicture(JNIEnv *env, jobject thiz, int m
}
if (camera->takePicture(msgType) != NO_ERROR) {
- jniThrowException(env, "java/lang/RuntimeException", "takePicture failed");
+ jniThrowRuntimeException(env, "takePicture failed");
return;
}
}
@@ -624,7 +621,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/RuntimeException", "setParameters failed");
+ jniThrowRuntimeException(env, "setParameters failed");
return;
}
}
@@ -657,7 +654,7 @@ static void android_hardware_Camera_lock(JNIEnv *env, jobject thiz)
if (camera == 0) return;
if (camera->lock() != NO_ERROR) {
- jniThrowException(env, "java/lang/RuntimeException", "lock failed");
+ jniThrowRuntimeException(env, "lock failed");
}
}
@@ -668,7 +665,7 @@ static void android_hardware_Camera_unlock(JNIEnv *env, jobject thiz)
if (camera == 0) return;
if (camera->unlock() != NO_ERROR) {
- jniThrowException(env, "java/lang/RuntimeException", "unlock failed");
+ jniThrowRuntimeException(env, "unlock failed");
}
}
@@ -684,7 +681,7 @@ static void android_hardware_Camera_startSmoothZoom(JNIEnv *env, jobject thiz, j
sprintf(msg, "invalid zoom value=%d", value);
jniThrowException(env, "java/lang/IllegalArgumentException", msg);
} else if (rc != NO_ERROR) {
- jniThrowException(env, "java/lang/RuntimeException", "start smooth zoom failed");
+ jniThrowRuntimeException(env, "start smooth zoom failed");
}
}
@@ -695,7 +692,7 @@ static void android_hardware_Camera_stopSmoothZoom(JNIEnv *env, jobject thiz)
if (camera == 0) return;
if (camera->sendCommand(CAMERA_CMD_STOP_SMOOTH_ZOOM, 0, 0) != NO_ERROR) {
- jniThrowException(env, "java/lang/RuntimeException", "stop smooth zoom failed");
+ jniThrowRuntimeException(env, "stop smooth zoom failed");
}
}
@@ -707,7 +704,7 @@ static void android_hardware_Camera_setDisplayOrientation(JNIEnv *env, jobject t
if (camera == 0) return;
if (camera->sendCommand(CAMERA_CMD_SET_DISPLAY_ORIENTATION, value, 0) != NO_ERROR) {
- jniThrowException(env, "java/lang/RuntimeException", "set display orientation failed");
+ jniThrowRuntimeException(env, "set display orientation failed");
}
}
diff --git a/core/jni/android_hardware_UsbDevice.cpp b/core/jni/android_hardware_UsbDevice.cpp
index c2950ea..25f901b 100644
--- a/core/jni/android_hardware_UsbDevice.cpp
+++ b/core/jni/android_hardware_UsbDevice.cpp
@@ -54,13 +54,6 @@ static JNINativeMethod method_table[] = {
int register_android_hardware_UsbDevice(JNIEnv *env)
{
- jclass clazz = env->FindClass("android/hardware/usb/UsbDevice");
- if (clazz == NULL) {
- LOGE("Can't find android/hardware/usb/UsbDevice");
- return -1;
- }
-
return AndroidRuntime::registerNativeMethods(env, "android/hardware/usb/UsbDevice",
method_table, NELEM(method_table));
}
-
diff --git a/core/jni/android_hardware_UsbDeviceConnection.cpp b/core/jni/android_hardware_UsbDeviceConnection.cpp
index b5d6b91..68be9e1 100644
--- a/core/jni/android_hardware_UsbDeviceConnection.cpp
+++ b/core/jni/android_hardware_UsbDeviceConnection.cpp
@@ -42,7 +42,7 @@ static jboolean
android_hardware_UsbDeviceConnection_open(JNIEnv *env, jobject thiz, jstring deviceName,
jobject fileDescriptor)
{
- int fd = getParcelFileDescriptorFD(env, fileDescriptor);
+ int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
// duplicate the file descriptor, since ParcelFileDescriptor will eventually close its copy
fd = dup(fd);
if (fd < 0)
@@ -153,7 +153,7 @@ android_hardware_UsbDeviceConnection_control_request(JNIEnv *env, jobject thiz,
jbyte* bufferBytes = NULL;
if (buffer) {
if (env->GetArrayLength(buffer) < length) {
- env->ThrowNew(env->FindClass("java/lang/ArrayIndexOutOfBoundsException"), NULL);
+ jniThrowException(env, "java/lang/ArrayIndexOutOfBoundsException", NULL);
return -1;
}
bufferBytes = env->GetByteArrayElements(buffer, 0);
@@ -181,7 +181,7 @@ android_hardware_UsbDeviceConnection_bulk_request(JNIEnv *env, jobject thiz,
jbyte* bufferBytes = NULL;
if (buffer) {
if (env->GetArrayLength(buffer) < length) {
- env->ThrowNew(env->FindClass("java/lang/ArrayIndexOutOfBoundsException"), NULL);
+ jniThrowException(env, "java/lang/ArrayIndexOutOfBoundsException", NULL);
return -1;
}
bufferBytes = env->GetByteArrayElements(buffer, 0);
@@ -261,4 +261,3 @@ int register_android_hardware_UsbDeviceConnection(JNIEnv *env)
return AndroidRuntime::registerNativeMethods(env, "android/hardware/usb/UsbDeviceConnection",
method_table, NELEM(method_table));
}
-
diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp
index b6619ab..50d9ca1 100644
--- a/core/jni/android_media_AudioRecord.cpp
+++ b/core/jni/android_media_AudioRecord.cpp
@@ -31,6 +31,9 @@
#include "media/AudioRecord.h"
#include "media/mediarecorder.h"
+#include <cutils/bitops.h>
+
+#include <system/audio.h>
// ----------------------------------------------------------------------------
@@ -41,7 +44,6 @@ static const char* const kClassPathName = "android/media/AudioRecord";
struct fields_t {
// these fields provide access from C++ to the...
- jclass audioRecordClass; //... AudioRecord class
jmethodID postNativeEventInJava; //... event post callback method
int PCM16; //... format constants
int PCM8; //... format constants
@@ -131,11 +133,11 @@ android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject weak_this,
//LOGV("sampleRate=%d, audioFormat=%d, channels=%x, buffSizeInBytes=%d",
// sampleRateInHertz, audioFormat, channels, buffSizeInBytes);
- if (!AudioSystem::isInputChannel(channels)) {
+ if (!audio_is_input_channel(channels)) {
LOGE("Error creating AudioRecord: channel count is not 1 or 2.");
return AUDIORECORD_ERROR_SETUP_INVALIDCHANNELMASK;
}
- uint32_t nbChannels = AudioSystem::popCount(channels);
+ uint32_t nbChannels = popcount(channels);
// compare the format against the Java constants
if ((audioFormat != javaAudioRecordFields.PCM16)
@@ -146,7 +148,7 @@ android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject weak_this,
int bytesPerSample = audioFormat==javaAudioRecordFields.PCM16 ? 2 : 1;
int format = audioFormat==javaAudioRecordFields.PCM16 ?
- AudioSystem::PCM_16_BIT : AudioSystem::PCM_8_BIT;
+ AUDIO_FORMAT_PCM_16_BIT : AUDIO_FORMAT_PCM_8_BIT;
if (buffSizeInBytes == 0) {
LOGE("Error creating AudioRecord: frameCount is 0.");
@@ -155,7 +157,7 @@ android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject weak_this,
int frameSize = nbChannels * bytesPerSample;
size_t frameCount = buffSizeInBytes / frameSize;
- if (source >= AUDIO_SOURCE_LIST_END) {
+ if (source >= AUDIO_SOURCE_CNT) {
LOGE("Error creating AudioRecord: unknown source.");
return AUDIORECORD_ERROR_SETUP_INVALIDSOURCE;
}
@@ -464,7 +466,7 @@ static jint android_media_AudioRecord_get_min_buff_size(JNIEnv *env, jobject th
status_t result = AudioRecord::getMinFrameCount(&frameCount,
sampleRateInHertz,
(audioFormat == javaAudioRecordFields.PCM16 ?
- AudioSystem::PCM_16_BIT : AudioSystem::PCM_8_BIT),
+ AUDIO_FORMAT_PCM_16_BIT : AUDIO_FORMAT_PCM_8_BIT),
nbChannels);
if (result == BAD_VALUE) {
@@ -520,22 +522,20 @@ extern bool android_media_getIntConstantFromClass(JNIEnv* pEnv,
// ----------------------------------------------------------------------------
int register_android_media_AudioRecord(JNIEnv *env)
{
- javaAudioRecordFields.audioRecordClass = NULL;
javaAudioRecordFields.postNativeEventInJava = NULL;
javaAudioRecordFields.nativeRecorderInJavaObj = NULL;
javaAudioRecordFields.nativeCallbackCookie = NULL;
// Get the AudioRecord class
- javaAudioRecordFields.audioRecordClass = env->FindClass(kClassPathName);
- if (javaAudioRecordFields.audioRecordClass == NULL) {
+ jclass audioRecordClass = env->FindClass(kClassPathName);
+ if (audioRecordClass == NULL) {
LOGE("Can't find %s", kClassPathName);
return -1;
}
-
// Get the postEvent method
javaAudioRecordFields.postNativeEventInJava = env->GetStaticMethodID(
- javaAudioRecordFields.audioRecordClass,
+ audioRecordClass,
JAVA_POSTEVENT_CALLBACK_NAME, "(Ljava/lang/Object;IIILjava/lang/Object;)V");
if (javaAudioRecordFields.postNativeEventInJava == NULL) {
LOGE("Can't find AudioRecord.%s", JAVA_POSTEVENT_CALLBACK_NAME);
@@ -545,7 +545,7 @@ int register_android_media_AudioRecord(JNIEnv *env)
// Get the variables
// mNativeRecorderInJavaObj
javaAudioRecordFields.nativeRecorderInJavaObj =
- env->GetFieldID(javaAudioRecordFields.audioRecordClass,
+ env->GetFieldID(audioRecordClass,
JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME, "I");
if (javaAudioRecordFields.nativeRecorderInJavaObj == NULL) {
LOGE("Can't find AudioRecord.%s", JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME);
@@ -553,7 +553,7 @@ int register_android_media_AudioRecord(JNIEnv *env)
}
// mNativeCallbackCookie
javaAudioRecordFields.nativeCallbackCookie = env->GetFieldID(
- javaAudioRecordFields.audioRecordClass,
+ audioRecordClass,
JAVA_NATIVECALLBACKINFO_FIELD_NAME, "I");
if (javaAudioRecordFields.nativeCallbackCookie == NULL) {
LOGE("Can't find AudioRecord.%s", JAVA_NATIVECALLBACKINFO_FIELD_NAME);
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 5f3fed2..246c0e5 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -30,10 +30,15 @@
#include <media/AudioSystem.h>
#include <media/AudioTrack.h>
+#include <system/audio.h>
+#include <hardware/audio_policy.h>
+
// ----------------------------------------------------------------------------
using namespace android;
+static const char* const kClassPathName = "android/media/AudioSystem";
+
enum AudioError {
kAudioStatusOk = 0,
kAudioStatusError = 1,
@@ -96,14 +101,15 @@ android_media_AudioSystem_getParameters(JNIEnv *env, jobject thiz, jstring keys)
return env->NewStringUTF(AudioSystem::getParameters(0, c_keys8).string());
}
-void android_media_AudioSystem_error_callback(status_t err)
+static void
+android_media_AudioSystem_error_callback(status_t err)
{
JNIEnv *env = AndroidRuntime::getJNIEnv();
if (env == NULL) {
return;
}
- jclass clazz = env->FindClass("android/media/AudioSystem");
+ jclass clazz = env->FindClass(kClassPathName);
int error;
@@ -126,8 +132,8 @@ static int
android_media_AudioSystem_setDeviceConnectionState(JNIEnv *env, jobject thiz, jint device, jint state, jstring device_address)
{
const char *c_address = env->GetStringUTFChars(device_address, NULL);
- int status = check_AudioSystem_Command(AudioSystem::setDeviceConnectionState(static_cast <AudioSystem::audio_devices>(device),
- static_cast <AudioSystem::device_connection_state>(state),
+ int status = check_AudioSystem_Command(AudioSystem::setDeviceConnectionState(static_cast <audio_devices_t>(device),
+ static_cast <audio_policy_dev_state_t>(state),
c_address));
env->ReleaseStringUTFChars(device_address, c_address);
return status;
@@ -137,7 +143,7 @@ static int
android_media_AudioSystem_getDeviceConnectionState(JNIEnv *env, jobject thiz, jint device, jstring device_address)
{
const char *c_address = env->GetStringUTFChars(device_address, NULL);
- int state = static_cast <int>(AudioSystem::getDeviceConnectionState(static_cast <AudioSystem::audio_devices>(device),
+ int state = static_cast <int>(AudioSystem::getDeviceConnectionState(static_cast <audio_devices_t>(device),
c_address));
env->ReleaseStringUTFChars(device_address, c_address);
return state;
@@ -158,20 +164,20 @@ android_media_AudioSystem_setRingerMode(JNIEnv *env, jobject thiz, jint mode, ji
static int
android_media_AudioSystem_setForceUse(JNIEnv *env, jobject thiz, jint usage, jint config)
{
- return check_AudioSystem_Command(AudioSystem::setForceUse(static_cast <AudioSystem::force_use>(usage),
- static_cast <AudioSystem::forced_config>(config)));
+ return check_AudioSystem_Command(AudioSystem::setForceUse(static_cast <audio_policy_force_use_t>(usage),
+ static_cast <audio_policy_forced_cfg_t>(config)));
}
static int
android_media_AudioSystem_getForceUse(JNIEnv *env, jobject thiz, jint usage)
{
- return static_cast <int>(AudioSystem::getForceUse(static_cast <AudioSystem::force_use>(usage)));
+ return static_cast <int>(AudioSystem::getForceUse(static_cast <audio_policy_force_use_t>(usage)));
}
static int
android_media_AudioSystem_initStreamVolume(JNIEnv *env, jobject thiz, jint stream, jint indexMin, jint indexMax)
{
- return check_AudioSystem_Command(AudioSystem::initStreamVolume(static_cast <AudioSystem::stream_type>(stream),
+ return check_AudioSystem_Command(AudioSystem::initStreamVolume(static_cast <audio_stream_type_t>(stream),
indexMin,
indexMax));
}
@@ -179,14 +185,14 @@ android_media_AudioSystem_initStreamVolume(JNIEnv *env, jobject thiz, jint strea
static int
android_media_AudioSystem_setStreamVolumeIndex(JNIEnv *env, jobject thiz, jint stream, jint index)
{
- return check_AudioSystem_Command(AudioSystem::setStreamVolumeIndex(static_cast <AudioSystem::stream_type>(stream), index));
+ return check_AudioSystem_Command(AudioSystem::setStreamVolumeIndex(static_cast <audio_stream_type_t>(stream), index));
}
static int
android_media_AudioSystem_getStreamVolumeIndex(JNIEnv *env, jobject thiz, jint stream)
{
int index;
- if (AudioSystem::getStreamVolumeIndex(static_cast <AudioSystem::stream_type>(stream), &index) != NO_ERROR) {
+ if (AudioSystem::getStreamVolumeIndex(static_cast <audio_stream_type_t>(stream), &index) != NO_ERROR) {
index = -1;
}
return index;
@@ -195,7 +201,7 @@ android_media_AudioSystem_getStreamVolumeIndex(JNIEnv *env, jobject thiz, jint s
static jint
android_media_AudioSystem_getDevicesForStream(JNIEnv *env, jobject thiz, jint stream)
{
- return (jint) AudioSystem::getDevicesForStream(static_cast <AudioSystem::stream_type>(stream));
+ return (jint) AudioSystem::getDevicesForStream(static_cast <audio_stream_type_t>(stream));
}
// ----------------------------------------------------------------------------
@@ -218,12 +224,10 @@ static JNINativeMethod gMethods[] = {
{"getDevicesForStream", "(I)I", (void *)android_media_AudioSystem_getDevicesForStream},
};
-const char* const kClassPathName = "android/media/AudioSystem";
-
int register_android_media_AudioSystem(JNIEnv *env)
{
AudioSystem::setErrorCallback(android_media_AudioSystem_error_callback);
return AndroidRuntime::registerNativeMethods(env,
- "android/media/AudioSystem", gMethods, NELEM(gMethods));
+ kClassPathName, gMethods, NELEM(gMethods));
}
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index 44d2a52..bd70dad 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -33,6 +33,9 @@
#include <binder/MemoryHeapBase.h>
#include <binder/MemoryBase.h>
+#include <cutils/bitops.h>
+
+#include <system/audio.h>
// ----------------------------------------------------------------------------
@@ -43,7 +46,6 @@ static const char* const kClassPathName = "android/media/AudioTrack";
struct fields_t {
// these fields provide access from C++ to the...
- jclass audioTrackClass; //... AudioTrack class
jmethodID postNativeEventInJava; //... event post callback method
int PCM16; //... format constants
int PCM8; //... format constants
@@ -78,7 +80,7 @@ class AudioTrackJniStorage {
AudioTrackJniStorage() {
mCallbackData.audioTrack_class = 0;
mCallbackData.audioTrack_ref = 0;
- mStreamType = AudioSystem::DEFAULT;
+ mStreamType = AUDIO_STREAM_DEFAULT;
}
~AudioTrackJniStorage() {
@@ -182,30 +184,30 @@ android_media_AudioTrack_native_setup(JNIEnv *env, jobject thiz, jobject weak_th
return AUDIOTRACK_ERROR_SETUP_AUDIOSYSTEM;
}
- if (!AudioSystem::isOutputChannel(channels)) {
+ if (!audio_is_output_channel(channels)) {
LOGE("Error creating AudioTrack: invalid channel mask.");
return AUDIOTRACK_ERROR_SETUP_INVALIDCHANNELMASK;
}
- int nbChannels = AudioSystem::popCount(channels);
+ int nbChannels = popcount(channels);
// check the stream type
- AudioSystem::stream_type atStreamType;
+ audio_stream_type_t atStreamType;
if (streamType == javaAudioTrackFields.STREAM_VOICE_CALL) {
- atStreamType = AudioSystem::VOICE_CALL;
+ atStreamType = AUDIO_STREAM_VOICE_CALL;
} else if (streamType == javaAudioTrackFields.STREAM_SYSTEM) {
- atStreamType = AudioSystem::SYSTEM;
+ atStreamType = AUDIO_STREAM_SYSTEM;
} else if (streamType == javaAudioTrackFields.STREAM_RING) {
- atStreamType = AudioSystem::RING;
+ atStreamType = AUDIO_STREAM_RING;
} else if (streamType == javaAudioTrackFields.STREAM_MUSIC) {
- atStreamType = AudioSystem::MUSIC;
+ atStreamType = AUDIO_STREAM_MUSIC;
} else if (streamType == javaAudioTrackFields.STREAM_ALARM) {
- atStreamType = AudioSystem::ALARM;
+ atStreamType = AUDIO_STREAM_ALARM;
} else if (streamType == javaAudioTrackFields.STREAM_NOTIFICATION) {
- atStreamType = AudioSystem::NOTIFICATION;
+ atStreamType = AUDIO_STREAM_NOTIFICATION;
} else if (streamType == javaAudioTrackFields.STREAM_BLUETOOTH_SCO) {
- atStreamType = AudioSystem::BLUETOOTH_SCO;
+ atStreamType = AUDIO_STREAM_BLUETOOTH_SCO;
} else if (streamType == javaAudioTrackFields.STREAM_DTMF) {
- atStreamType = AudioSystem::DTMF;
+ atStreamType = AUDIO_STREAM_DTMF;
} else {
LOGE("Error creating AudioTrack: unknown stream type.");
return AUDIOTRACK_ERROR_SETUP_INVALIDSTREAMTYPE;
@@ -234,7 +236,7 @@ android_media_AudioTrack_native_setup(JNIEnv *env, jobject thiz, jobject weak_th
// compute the frame count
int bytesPerSample = audioFormat == javaAudioTrackFields.PCM16 ? 2 : 1;
int format = audioFormat == javaAudioTrackFields.PCM16 ?
- AudioSystem::PCM_16_BIT : AudioSystem::PCM_8_BIT;
+ AUDIO_FORMAT_PCM_16_BIT : AUDIO_FORMAT_PCM_8_BIT;
int frameCount = buffSizeInBytes / (nbChannels * bytesPerSample);
AudioTrackJniStorage* lpJniStorage = new AudioTrackJniStorage();
@@ -756,25 +758,25 @@ static jint android_media_AudioTrack_get_output_sample_rate(JNIEnv *env, jobjec
int afSamplingRate;
// convert the stream type from Java to native value
// FIXME: code duplication with android_media_AudioTrack_native_setup()
- AudioSystem::stream_type nativeStreamType;
+ audio_stream_type_t nativeStreamType;
if (javaStreamType == javaAudioTrackFields.STREAM_VOICE_CALL) {
- nativeStreamType = AudioSystem::VOICE_CALL;
+ nativeStreamType = AUDIO_STREAM_VOICE_CALL;
} else if (javaStreamType == javaAudioTrackFields.STREAM_SYSTEM) {
- nativeStreamType = AudioSystem::SYSTEM;
+ nativeStreamType = AUDIO_STREAM_SYSTEM;
} else if (javaStreamType == javaAudioTrackFields.STREAM_RING) {
- nativeStreamType = AudioSystem::RING;
+ nativeStreamType = AUDIO_STREAM_RING;
} else if (javaStreamType == javaAudioTrackFields.STREAM_MUSIC) {
- nativeStreamType = AudioSystem::MUSIC;
+ nativeStreamType = AUDIO_STREAM_MUSIC;
} else if (javaStreamType == javaAudioTrackFields.STREAM_ALARM) {
- nativeStreamType = AudioSystem::ALARM;
+ nativeStreamType = AUDIO_STREAM_ALARM;
} else if (javaStreamType == javaAudioTrackFields.STREAM_NOTIFICATION) {
- nativeStreamType = AudioSystem::NOTIFICATION;
+ nativeStreamType = AUDIO_STREAM_NOTIFICATION;
} else if (javaStreamType == javaAudioTrackFields.STREAM_BLUETOOTH_SCO) {
- nativeStreamType = AudioSystem::BLUETOOTH_SCO;
+ nativeStreamType = AUDIO_STREAM_BLUETOOTH_SCO;
} else if (javaStreamType == javaAudioTrackFields.STREAM_DTMF) {
- nativeStreamType = AudioSystem::DTMF;
+ nativeStreamType = AUDIO_STREAM_DTMF;
} else {
- nativeStreamType = AudioSystem::DEFAULT;
+ nativeStreamType = AUDIO_STREAM_DEFAULT;
}
if (AudioSystem::getOutputSamplingRate(&afSamplingRate, nativeStreamType) != NO_ERROR) {
@@ -794,7 +796,7 @@ static jint android_media_AudioTrack_get_min_buff_size(JNIEnv *env, jobject thi
jint sampleRateInHertz, jint nbChannels, jint audioFormat) {
int frameCount = 0;
- if (AudioTrack::getMinFrameCount(&frameCount, AudioSystem::DEFAULT,
+ if (AudioTrack::getMinFrameCount(&frameCount, AUDIO_STREAM_DEFAULT,
sampleRateInHertz) != NO_ERROR) {
return -1;
}
@@ -915,20 +917,19 @@ bool android_media_getIntConstantFromClass(JNIEnv* pEnv, jclass theClass, const
// ----------------------------------------------------------------------------
int register_android_media_AudioTrack(JNIEnv *env)
{
- javaAudioTrackFields.audioTrackClass = NULL;
javaAudioTrackFields.nativeTrackInJavaObj = NULL;
javaAudioTrackFields.postNativeEventInJava = NULL;
// Get the AudioTrack class
- javaAudioTrackFields.audioTrackClass = env->FindClass(kClassPathName);
- if (javaAudioTrackFields.audioTrackClass == NULL) {
+ jclass audioTrackClass = env->FindClass(kClassPathName);
+ if (audioTrackClass == NULL) {
LOGE("Can't find %s", kClassPathName);
return -1;
}
// Get the postEvent method
javaAudioTrackFields.postNativeEventInJava = env->GetStaticMethodID(
- javaAudioTrackFields.audioTrackClass,
+ audioTrackClass,
JAVA_POSTEVENT_CALLBACK_NAME, "(Ljava/lang/Object;IIILjava/lang/Object;)V");
if (javaAudioTrackFields.postNativeEventInJava == NULL) {
LOGE("Can't find AudioTrack.%s", JAVA_POSTEVENT_CALLBACK_NAME);
@@ -938,7 +939,7 @@ int register_android_media_AudioTrack(JNIEnv *env)
// Get the variables fields
// nativeTrackInJavaObj
javaAudioTrackFields.nativeTrackInJavaObj = env->GetFieldID(
- javaAudioTrackFields.audioTrackClass,
+ audioTrackClass,
JAVA_NATIVETRACKINJAVAOBJ_FIELD_NAME, "I");
if (javaAudioTrackFields.nativeTrackInJavaObj == NULL) {
LOGE("Can't find AudioTrack.%s", JAVA_NATIVETRACKINJAVAOBJ_FIELD_NAME);
@@ -946,7 +947,7 @@ int register_android_media_AudioTrack(JNIEnv *env)
}
// jniData;
javaAudioTrackFields.jniData = env->GetFieldID(
- javaAudioTrackFields.audioTrackClass,
+ audioTrackClass,
JAVA_JNIDATA_FIELD_NAME, "I");
if (javaAudioTrackFields.jniData == NULL) {
LOGE("Can't find AudioTrack.%s", JAVA_JNIDATA_FIELD_NAME);
@@ -954,10 +955,10 @@ int register_android_media_AudioTrack(JNIEnv *env)
}
// Get the memory mode constants
- if ( !android_media_getIntConstantFromClass(env, javaAudioTrackFields.audioTrackClass,
+ if ( !android_media_getIntConstantFromClass(env, audioTrackClass,
kClassPathName,
JAVA_CONST_MODE_STATIC_NAME, &(javaAudioTrackFields.MODE_STATIC))
- || !android_media_getIntConstantFromClass(env, javaAudioTrackFields.audioTrackClass,
+ || !android_media_getIntConstantFromClass(env, audioTrackClass,
kClassPathName,
JAVA_CONST_MODE_STREAM_NAME, &(javaAudioTrackFields.MODE_STREAM)) ) {
// error log performed in android_media_getIntConstantFromClass()
diff --git a/core/jni/android_media_JetPlayer.cpp b/core/jni/android_media_JetPlayer.cpp
index 3727f4b..e124069 100644
--- a/core/jni/android_media_JetPlayer.cpp
+++ b/core/jni/android_media_JetPlayer.cpp
@@ -85,7 +85,7 @@ android_media_JetPlayer_setup(JNIEnv *env, jobject thiz, jobject weak_this,
EAS_RESULT result = lpJet->init();
if(result==EAS_SUCCESS) {
- // save our newly created C++ JetPlayer in the "nativePlayerInJavaObj" field
+ // save our newly created C++ JetPlayer in the "nativePlayerInJavaObj" field
// of the Java object (in mNativePlayerInJavaObj)
env->SetIntField(thiz, javaJetPlayerFields.nativePlayerInJavaObj, (int)lpJet);
return JNI_TRUE;
@@ -120,7 +120,7 @@ android_media_JetPlayer_release(JNIEnv *env, jobject thiz)
{
android_media_JetPlayer_finalize(env, thiz);
env->SetIntField(thiz, javaJetPlayerFields.nativePlayerInJavaObj, 0);
- LOGV("android_media_JetPlayer_release() done");
+ LOGV("android_media_JetPlayer_release() done");
}
@@ -134,14 +134,13 @@ android_media_JetPlayer_loadFromFile(JNIEnv *env, jobject thiz, jstring path)
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve JetPlayer pointer for openFile()");
}
-
+
// set up event callback function
lpJet->setEventCallback(jetPlayerEventCallback);
const char *pathStr = env->GetStringUTFChars(path, NULL);
if (pathStr == NULL) { // Out of memory
LOGE("android_media_JetPlayer_openFile(): aborting, out of memory");
- jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
return JNI_FALSE;
}
@@ -171,14 +170,14 @@ android_media_JetPlayer_loadFromFileD(JNIEnv *env, jobject thiz,
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve JetPlayer pointer for openFile()");
}
-
+
// set up event callback function
lpJet->setEventCallback(jetPlayerEventCallback);
-
+
LOGV("android_media_JetPlayer_openFileDescr(): trying to load JET file through its fd" );
- EAS_RESULT result = lpJet->loadFromFD(getParcelFileDescriptorFD(env, fileDescriptor),
+ EAS_RESULT result = lpJet->loadFromFD(jniGetFDFromFileDescriptor(env, fileDescriptor),
(long long)offset, (long long)length); // cast params to types used by EAS_FILE
-
+
if(result==EAS_SUCCESS) {
LOGV("android_media_JetPlayer_openFileDescr(): file successfully opened");
return JNI_TRUE;
@@ -200,7 +199,7 @@ android_media_JetPlayer_closeFile(JNIEnv *env, jobject thiz)
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve JetPlayer pointer for closeFile()");
}
-
+
if( lpJet->closeFile()==EAS_SUCCESS) {
//LOGV("android_media_JetPlayer_closeFile(): file successfully closed");
return JNI_TRUE;
@@ -221,13 +220,13 @@ android_media_JetPlayer_play(JNIEnv *env, jobject thiz)
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve JetPlayer pointer for play()");
}
-
+
EAS_RESULT result = lpJet->play();
if( result==EAS_SUCCESS) {
//LOGV("android_media_JetPlayer_play(): play successful");
return JNI_TRUE;
} else {
- LOGE("android_media_JetPlayer_play(): failed to play with EAS error code %ld",
+ LOGE("android_media_JetPlayer_play(): failed to play with EAS error code %ld",
result);
return JNI_FALSE;
}
@@ -244,7 +243,7 @@ android_media_JetPlayer_pause(JNIEnv *env, jobject thiz)
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve JetPlayer pointer for pause()");
}
-
+
EAS_RESULT result = lpJet->pause();
if( result==EAS_SUCCESS) {
//LOGV("android_media_JetPlayer_pause(): pause successful");
@@ -273,7 +272,7 @@ android_media_JetPlayer_queueSegment(JNIEnv *env, jobject thiz,
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve JetPlayer pointer for queueSegment()");
}
-
+
EAS_RESULT result
= lpJet->queueSegment(segmentNum, libNum, repeatCount, transpose, muteFlags, userID);
if(result==EAS_SUCCESS) {
@@ -299,7 +298,7 @@ android_media_JetPlayer_queueSegmentMuteArray(JNIEnv *env, jobject thiz,
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve JetPlayer pointer for queueSegmentMuteArray()");
}
-
+
EAS_RESULT result=EAS_FAILURE;
jboolean *muteTracks = NULL;
@@ -326,7 +325,7 @@ android_media_JetPlayer_queueSegmentMuteArray(JNIEnv *env, jobject thiz,
//LOGV("android_media_JetPlayer_queueSegmentMuteArray(): segment successfully queued");
return JNI_TRUE;
} else {
- LOGE("android_media_JetPlayer_queueSegmentMuteArray(): failed with EAS error code %ld",
+ LOGE("android_media_JetPlayer_queueSegmentMuteArray(): failed with EAS error code %ld",
result);
return JNI_FALSE;
}
@@ -344,7 +343,7 @@ android_media_JetPlayer_setMuteFlags(JNIEnv *env, jobject thiz,
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve JetPlayer pointer for setMuteFlags()");
}
-
+
EAS_RESULT result;
result = lpJet->setMuteFlags(muteFlags, bSync==JNI_TRUE ? true : false);
if(result==EAS_SUCCESS) {
@@ -368,7 +367,7 @@ android_media_JetPlayer_setMuteArray(JNIEnv *env, jobject thiz,
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve JetPlayer pointer for setMuteArray()");
}
-
+
EAS_RESULT result=EAS_FAILURE;
jboolean *muteTracks = NULL;
@@ -413,9 +412,9 @@ android_media_JetPlayer_setMuteFlag(JNIEnv *env, jobject thiz,
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve JetPlayer pointer for setMuteFlag()");
}
-
+
EAS_RESULT result;
- result = lpJet->setMuteFlag(trackId,
+ result = lpJet->setMuteFlag(trackId,
muteFlag==JNI_TRUE ? true : false, bSync==JNI_TRUE ? true : false);
if(result==EAS_SUCCESS) {
//LOGV("android_media_JetPlayer_setMuteFlag(): mute flag successfully updated for track %d", trackId);
@@ -438,7 +437,7 @@ android_media_JetPlayer_triggerClip(JNIEnv *env, jobject thiz, jint clipId)
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve JetPlayer pointer for triggerClip()");
}
-
+
EAS_RESULT result;
result = lpJet->triggerClip(clipId);
if(result==EAS_SUCCESS) {
@@ -462,7 +461,7 @@ android_media_JetPlayer_clearQueue(JNIEnv *env, jobject thiz)
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve JetPlayer pointer for clearQueue()");
}
-
+
EAS_RESULT result = lpJet->clearQueue();
if(result==EAS_SUCCESS) {
//LOGV("android_media_JetPlayer_clearQueue(): clearQueue successful");
@@ -482,16 +481,16 @@ static JNINativeMethod gMethods[] = {
{"native_setup", "(Ljava/lang/Object;II)Z", (void *)android_media_JetPlayer_setup},
{"native_finalize", "()V", (void *)android_media_JetPlayer_finalize},
{"native_release", "()V", (void *)android_media_JetPlayer_release},
- {"native_loadJetFromFile",
+ {"native_loadJetFromFile",
"(Ljava/lang/String;)Z", (void *)android_media_JetPlayer_loadFromFile},
{"native_loadJetFromFileD", "(Ljava/io/FileDescriptor;JJ)Z",
(void *)android_media_JetPlayer_loadFromFileD},
{"native_closeJetFile","()Z", (void *)android_media_JetPlayer_closeFile},
{"native_playJet", "()Z", (void *)android_media_JetPlayer_play},
{"native_pauseJet", "()Z", (void *)android_media_JetPlayer_pause},
- {"native_queueJetSegment",
+ {"native_queueJetSegment",
"(IIIIIB)Z", (void *)android_media_JetPlayer_queueSegment},
- {"native_queueJetSegmentMuteArray",
+ {"native_queueJetSegmentMuteArray",
"(IIII[ZB)Z", (void *)android_media_JetPlayer_queueSegmentMuteArray},
{"native_setMuteFlags","(IZ)Z", (void *)android_media_JetPlayer_setMuteFlags},
{"native_setMuteArray","([ZZ)Z", (void *)android_media_JetPlayer_setMuteArray},
@@ -510,7 +509,7 @@ int register_android_media_JetPlayer(JNIEnv *env)
javaJetPlayerFields.jetClass = NULL;
javaJetPlayerFields.postNativeEventInJava = NULL;
javaJetPlayerFields.nativePlayerInJavaObj = NULL;
-
+
// Get the JetPlayer java class
jetPlayerClass = env->FindClass(kClassPathName);
if (jetPlayerClass == NULL) {
diff --git a/core/jni/android_media_ToneGenerator.cpp b/core/jni/android_media_ToneGenerator.cpp
index 50aa967..fdd586b 100644
--- a/core/jni/android_media_ToneGenerator.cpp
+++ b/core/jni/android_media_ToneGenerator.cpp
@@ -44,7 +44,7 @@ static jboolean android_media_ToneGenerator_startTone(JNIEnv *env, jobject thiz,
ToneGenerator *lpToneGen = (ToneGenerator *)env->GetIntField(thiz,
fields.context);
if (lpToneGen == NULL) {
- jniThrowException(env, "java/lang/RuntimeException", "Method called after release()");
+ jniThrowRuntimeException(env, "Method called after release()");
return false;
}
@@ -59,7 +59,7 @@ static void android_media_ToneGenerator_stopTone(JNIEnv *env, jobject thiz) {
LOGV("ToneGenerator lpToneGen: %x\n", (unsigned int)lpToneGen);
if (lpToneGen == NULL) {
- jniThrowException(env, "java/lang/RuntimeException", "Method called after release()");
+ jniThrowRuntimeException(env, "Method called after release()");
return;
}
lpToneGen->stopTone();
@@ -94,7 +94,7 @@ static void android_media_ToneGenerator_native_setup(JNIEnv *env, jobject thiz,
if (!lpToneGen->isInited()) {
LOGE("ToneGenerator init failed \n");
- jniThrowException(env, "java/lang/RuntimeException", "Init failed");
+ jniThrowRuntimeException(env, "Init failed");
return;
}
diff --git a/core/jni/android_net_LocalSocketImpl.cpp b/core/jni/android_net_LocalSocketImpl.cpp
index e58794b..c8add70 100644
--- a/core/jni/android_net_LocalSocketImpl.cpp
+++ b/core/jni/android_net_LocalSocketImpl.cpp
@@ -99,9 +99,9 @@ socket_connect_local(JNIEnv *env, jobject object,
#define DEFAULT_BACKLOG 4
-/* private native void bindLocal(FileDescriptor fd, String name, namespace)
- * throws IOException;
- */
+/* private native void bindLocal(FileDescriptor fd, String name, namespace)
+ * throws IOException;
+ */
static void
socket_bind_local (JNIEnv *env, jobject object, jobject fileDescriptor,
@@ -113,7 +113,7 @@ socket_bind_local (JNIEnv *env, jobject object, jobject fileDescriptor,
if (name == NULL) {
- jniThrowException(env, "java/lang/NullPointerException", NULL);
+ jniThrowNullPointerException(env, NULL);
}
fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
@@ -127,7 +127,7 @@ socket_bind_local (JNIEnv *env, jobject object, jobject fileDescriptor,
ret = socket_local_server_bind(fd, nameUtf8, namespaceId);
env->ReleaseStringUTFChars(name, nameUtf8);
-
+
if (ret < 0) {
jniThrowIOException(env, errno);
return;
@@ -166,14 +166,14 @@ socket_accept (JNIEnv *env, jobject object, jobject fileDescriptor, jobject s)
struct sockaddr address;
struct sockaddr_un un_address;
} sa;
-
+
int ret;
int retFD;
int fd;
socklen_t addrlen;
if (s == NULL) {
- jniThrowException(env, "java/lang/NullPointerException", NULL);
+ jniThrowNullPointerException(env, NULL);
return NULL;
}
@@ -345,7 +345,7 @@ static void socket_setOption(
struct timeval timeout;
timeout.tv_sec = intValue / 1000;
timeout.tv_usec = (intValue % 1000) * 1000;
-
+
ret = setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO,
(void *)&timeout, sizeof(timeout));
@@ -353,7 +353,7 @@ static void socket_setOption(
ret = setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO,
(void *)&timeout, sizeof(timeout));
}
-
+
break;
}
default: {
@@ -372,7 +372,7 @@ static void socket_setOption(
}
}
-static jint socket_available (JNIEnv *env, jobject object,
+static jint socket_available (JNIEnv *env, jobject object,
jobject fileDescriptor)
{
int fd;
@@ -398,7 +398,7 @@ static jint socket_available (JNIEnv *env, jobject object,
return (jint)avail;
#else
// there appears to be a bionic bug that prevents this version from working.
-
+
ssize_t ret;
struct msghdr msg;
@@ -408,7 +408,7 @@ static jint socket_available (JNIEnv *env, jobject object,
ret = recvmsg(fd, &msg, MSG_PEEK | MSG_DONTWAIT | MSG_NOSIGNAL);
} while (ret < 0 && errno == EINTR);
-
+
// MSG_PEEK returns 0 on EOF and EWOULDBLOCK on none available
if (ret < 0 && errno == EWOULDBLOCK) {
return 0;
@@ -427,7 +427,7 @@ static void socket_close (JNIEnv *env, jobject object, jobject fileDescriptor)
int err;
if (fileDescriptor == NULL) {
- jniThrowException(env, "java/lang/NullPointerException", NULL);
+ jniThrowNullPointerException(env, NULL);
return;
}
@@ -448,7 +448,7 @@ static void socket_close (JNIEnv *env, jobject object, jobject fileDescriptor)
}
/**
- * Processes ancillary data, handling only
+ * Processes ancillary data, handling only
* SCM_RIGHTS. Creates appropriate objects and sets appropriate
* fields in the LocalSocketImpl object. Returns 0 on success
* or -1 if an exception was thrown.
@@ -457,7 +457,7 @@ static int socket_process_cmsg(JNIEnv *env, jobject thisJ, struct msghdr * pMsg)
{
struct cmsghdr *cmsgptr;
- for (cmsgptr = CMSG_FIRSTHDR(pMsg);
+ for (cmsgptr = CMSG_FIRSTHDR(pMsg);
cmsgptr != NULL; cmsgptr = CMSG_NXTHDR(pMsg, cmsgptr)) {
if (cmsgptr->cmsg_level != SOL_SOCKET) {
@@ -467,11 +467,11 @@ static int socket_process_cmsg(JNIEnv *env, jobject thisJ, struct msghdr * pMsg)
if (cmsgptr->cmsg_type == SCM_RIGHTS) {
int *pDescriptors = (int *)CMSG_DATA(cmsgptr);
jobjectArray fdArray;
- int count
+ int count
= ((cmsgptr->cmsg_len - CMSG_LEN(0)) / sizeof(int));
if (count < 0) {
- jniThrowException(env, "java/io/IOException",
+ jniThrowException(env, "java/io/IOException",
"invalid cmsg length");
}
@@ -482,7 +482,7 @@ static int socket_process_cmsg(JNIEnv *env, jobject thisJ, struct msghdr * pMsg)
}
for (int i = 0; i < count; i++) {
- jobject fdObject
+ jobject fdObject
= jniCreateFileDescriptor(env, pDescriptors[i]);
if (env->ExceptionOccurred() != NULL) {
@@ -514,7 +514,7 @@ static int socket_process_cmsg(JNIEnv *env, jobject thisJ, struct msghdr * pMsg)
* Returns the length of normal data read, or -1 if an exception has
* been thrown in this function.
*/
-static ssize_t socket_read_all(JNIEnv *env, jobject thisJ, int fd,
+static ssize_t socket_read_all(JNIEnv *env, jobject thisJ, int fd,
void *buffer, size_t len)
{
ssize_t ret;
@@ -554,7 +554,7 @@ static ssize_t socket_read_all(JNIEnv *env, jobject thisJ, int fd,
if ((msg.msg_flags & (MSG_CTRUNC | MSG_OOB | MSG_ERRQUEUE)) != 0) {
// To us, any of the above flags are a fatal error
- jniThrowException(env, "java/io/IOException",
+ jniThrowException(env, "java/io/IOException",
"Unexpected error or truncation during recvmsg()");
return -1;
@@ -580,7 +580,7 @@ static int socket_write_all(JNIEnv *env, jobject object, int fd,
unsigned char *buffer = (unsigned char *)buf;
memset(&msg, 0, sizeof(msg));
- jobjectArray outboundFds
+ jobjectArray outboundFds
= (jobjectArray)env->GetObjectField(
object, field_outboundFileDescriptors);
@@ -632,7 +632,7 @@ static int socket_write_all(JNIEnv *env, jobject object, int fd,
msg.msg_iov = &iv;
msg.msg_iovlen = 1;
-
+
do {
ret = sendmsg(fd, &msg, MSG_NOSIGNAL);
} while (ret < 0 && errno == EINTR);
@@ -658,7 +658,7 @@ static jint socket_read (JNIEnv *env, jobject object, jobject fileDescriptor)
int err;
if (fileDescriptor == NULL) {
- jniThrowException(env, "java/lang/NullPointerException", NULL);
+ jniThrowNullPointerException(env, NULL);
return (jint)-1;
}
@@ -685,7 +685,7 @@ static jint socket_read (JNIEnv *env, jobject object, jobject fileDescriptor)
return (jint)buf;
}
-static jint socket_readba (JNIEnv *env, jobject object,
+static jint socket_readba (JNIEnv *env, jobject object,
jbyteArray buffer, jint off, jint len, jobject fileDescriptor)
{
int fd;
@@ -693,7 +693,7 @@ static jint socket_readba (JNIEnv *env, jobject object,
int ret;
if (fileDescriptor == NULL || buffer == NULL) {
- jniThrowException(env, "java/lang/NullPointerException", NULL);
+ jniThrowNullPointerException(env, NULL);
return (jint)-1;
}
@@ -720,7 +720,7 @@ static jint socket_readba (JNIEnv *env, jobject object,
return (jint)-1;
}
- ret = socket_read_all(env, object,
+ ret = socket_read_all(env, object,
fd, byteBuffer + off, len);
// A return of -1 above means an exception is pending
@@ -730,14 +730,14 @@ static jint socket_readba (JNIEnv *env, jobject object,
return (jint) ((ret == 0) ? -1 : ret);
}
-static void socket_write (JNIEnv *env, jobject object,
+static void socket_write (JNIEnv *env, jobject object,
jint b, jobject fileDescriptor)
{
int fd;
int err;
if (fileDescriptor == NULL) {
- jniThrowException(env, "java/lang/NullPointerException", NULL);
+ jniThrowNullPointerException(env, NULL);
return;
}
@@ -752,7 +752,7 @@ static void socket_write (JNIEnv *env, jobject object,
// A return of -1 above means an exception is pending
}
-static void socket_writeba (JNIEnv *env, jobject object,
+static void socket_writeba (JNIEnv *env, jobject object,
jbyteArray buffer, jint off, jint len, jobject fileDescriptor)
{
int fd;
@@ -760,7 +760,7 @@ static void socket_writeba (JNIEnv *env, jobject object,
jbyte* byteBuffer;
if (fileDescriptor == NULL || buffer == NULL) {
- jniThrowException(env, "java/lang/NullPointerException", NULL);
+ jniThrowNullPointerException(env, NULL);
return;
}
@@ -782,7 +782,7 @@ static void socket_writeba (JNIEnv *env, jobject object,
return;
}
- err = socket_write_all(env, object, fd,
+ err = socket_write_all(env, object, fd,
byteBuffer + off, len);
// A return of -1 above means an exception is pending
@@ -790,14 +790,14 @@ static void socket_writeba (JNIEnv *env, jobject object,
env->ReleaseByteArrayElements(buffer, byteBuffer, JNI_ABORT);
}
-static jobject socket_get_peer_credentials(JNIEnv *env,
+static jobject socket_get_peer_credentials(JNIEnv *env,
jobject object, jobject fileDescriptor)
{
int err;
int fd;
if (fileDescriptor == NULL) {
- jniThrowException(env, "java/lang/NullPointerException", NULL);
+ jniThrowNullPointerException(env, NULL);
return NULL;
}
@@ -812,7 +812,7 @@ static jobject socket_get_peer_credentials(JNIEnv *env,
memset(&creds, 0, sizeof(creds));
socklen_t szCreds = sizeof(creds);
- err = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &creds, &szCreds);
+ err = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &creds, &szCreds);
if (err < 0) {
jniThrowIOException(env, errno);
@@ -823,20 +823,20 @@ static jobject socket_get_peer_credentials(JNIEnv *env,
return NULL;
}
- return env->NewObject(class_Credentials, method_CredentialsInit,
+ return env->NewObject(class_Credentials, method_CredentialsInit,
creds.pid, creds.uid, creds.gid);
}
#if 0
//TODO change this to return an instance of LocalSocketAddress
-static jobject socket_getSockName(JNIEnv *env,
+static jobject socket_getSockName(JNIEnv *env,
jobject object, jobject fileDescriptor)
{
int err;
int fd;
if (fileDescriptor == NULL) {
- jniThrowException(env, "java/lang/NullPointerException", NULL);
+ jniThrowNullPointerException(env, NULL);
return NULL;
}
@@ -898,10 +898,10 @@ static JNINativeMethod gMethods[] = {
{"readba_native", "([BIILjava/io/FileDescriptor;)I", (void*) socket_readba},
{"writeba_native", "([BIILjava/io/FileDescriptor;)V", (void*) socket_writeba},
{"write_native", "(ILjava/io/FileDescriptor;)V", (void*) socket_write},
- {"getPeerCredentials_native",
- "(Ljava/io/FileDescriptor;)Landroid/net/Credentials;",
+ {"getPeerCredentials_native",
+ "(Ljava/io/FileDescriptor;)Landroid/net/Credentials;",
(void*) socket_get_peer_credentials}
- //,{"getSockName_native", "(Ljava/io/FileDescriptor;)Ljava/lang/String;",
+ //,{"getSockName_native", "(Ljava/io/FileDescriptor;)Ljava/lang/String;",
// (void *) socket_getSockName}
};
@@ -916,14 +916,14 @@ int register_android_net_LocalSocketImpl(JNIEnv *env)
goto error;
}
- field_inboundFileDescriptors = env->GetFieldID(clazz,
+ field_inboundFileDescriptors = env->GetFieldID(clazz,
"inboundFileDescriptors", "[Ljava/io/FileDescriptor;");
if (field_inboundFileDescriptors == NULL) {
goto error;
}
- field_outboundFileDescriptors = env->GetFieldID(clazz,
+ field_outboundFileDescriptors = env->GetFieldID(clazz,
"outboundFileDescriptors", "[Ljava/io/FileDescriptor;");
if (field_outboundFileDescriptors == NULL) {
@@ -931,7 +931,7 @@ int register_android_net_LocalSocketImpl(JNIEnv *env)
}
class_Credentials = env->FindClass("android/net/Credentials");
-
+
if (class_Credentials == NULL) {
goto error;
}
@@ -946,7 +946,7 @@ int register_android_net_LocalSocketImpl(JNIEnv *env)
class_FileDescriptor = (jclass)env->NewGlobalRef(class_FileDescriptor);
- method_CredentialsInit
+ method_CredentialsInit
= env->GetMethodID(class_Credentials, "<init>", "(III)V");
if (method_CredentialsInit == NULL) {
diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp
index 68d1b3a..ddae505 100644
--- a/core/jni/android_net_NetUtils.cpp
+++ b/core/jni/android_net_NetUtils.cpp
@@ -1,16 +1,16 @@
/*
* Copyright 2008, The Android Open Source Project
*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
* limitations under the License.
*/
@@ -61,7 +61,6 @@ namespace android {
* to look them up every time.
*/
static struct fieldIds {
- jclass dhcpInfoInternalClass;
jmethodID constructorId;
jfieldID ipaddress;
jfieldID prefixLength;
@@ -125,7 +124,7 @@ static jboolean android_net_utils_runDhcpCommon(JNIEnv* env, jobject clazz, jstr
}
env->ReleaseStringUTFChars(ifname, nameStr);
- if (result == 0 && dhcpInfoInternalFieldIds.dhcpInfoInternalClass != NULL) {
+ if (result == 0) {
env->SetObjectField(info, dhcpInfoInternalFieldIds.ipaddress, env->NewStringUTF(ipaddr));
// set the gateway
@@ -217,19 +216,15 @@ static JNINativeMethod gNetworkUtilMethods[] = {
int register_android_net_NetworkUtils(JNIEnv* env)
{
- jclass netutils = env->FindClass(NETUTILS_PKG_NAME);
- LOG_FATAL_IF(netutils == NULL, "Unable to find class " NETUTILS_PKG_NAME);
-
- dhcpInfoInternalFieldIds.dhcpInfoInternalClass = env->FindClass("android/net/DhcpInfoInternal");
- if (dhcpInfoInternalFieldIds.dhcpInfoInternalClass != NULL) {
- dhcpInfoInternalFieldIds.constructorId = env->GetMethodID(dhcpInfoInternalFieldIds.dhcpInfoInternalClass, "<init>", "()V");
- dhcpInfoInternalFieldIds.ipaddress = env->GetFieldID(dhcpInfoInternalFieldIds.dhcpInfoInternalClass, "ipAddress", "Ljava/lang/String;");
- dhcpInfoInternalFieldIds.prefixLength = env->GetFieldID(dhcpInfoInternalFieldIds.dhcpInfoInternalClass, "prefixLength", "I");
- dhcpInfoInternalFieldIds.dns1 = env->GetFieldID(dhcpInfoInternalFieldIds.dhcpInfoInternalClass, "dns1", "Ljava/lang/String;");
- dhcpInfoInternalFieldIds.dns2 = env->GetFieldID(dhcpInfoInternalFieldIds.dhcpInfoInternalClass, "dns2", "Ljava/lang/String;");
- dhcpInfoInternalFieldIds.serverAddress = env->GetFieldID(dhcpInfoInternalFieldIds.dhcpInfoInternalClass, "serverAddress", "Ljava/lang/String;");
- dhcpInfoInternalFieldIds.leaseDuration = env->GetFieldID(dhcpInfoInternalFieldIds.dhcpInfoInternalClass, "leaseDuration", "I");
- }
+ jclass dhcpInfoInternalClass = env->FindClass("android/net/DhcpInfoInternal");
+ LOG_FATAL_IF(dhcpInfoInternalClass == NULL, "Unable to find class android/net/DhcpInfoInternal");
+ dhcpInfoInternalFieldIds.constructorId = env->GetMethodID(dhcpInfoInternalClass, "<init>", "()V");
+ dhcpInfoInternalFieldIds.ipaddress = env->GetFieldID(dhcpInfoInternalClass, "ipAddress", "Ljava/lang/String;");
+ dhcpInfoInternalFieldIds.prefixLength = env->GetFieldID(dhcpInfoInternalClass, "prefixLength", "I");
+ dhcpInfoInternalFieldIds.dns1 = env->GetFieldID(dhcpInfoInternalClass, "dns1", "Ljava/lang/String;");
+ dhcpInfoInternalFieldIds.dns2 = env->GetFieldID(dhcpInfoInternalClass, "dns2", "Ljava/lang/String;");
+ dhcpInfoInternalFieldIds.serverAddress = env->GetFieldID(dhcpInfoInternalClass, "serverAddress", "Ljava/lang/String;");
+ dhcpInfoInternalFieldIds.leaseDuration = env->GetFieldID(dhcpInfoInternalClass, "leaseDuration", "I");
return AndroidRuntime::registerNativeMethods(env,
NETUTILS_PKG_NAME, gNetworkUtilMethods, NELEM(gNetworkUtilMethods));
diff --git a/core/jni/android_net_TrafficStats.cpp b/core/jni/android_net_TrafficStats.cpp
index 0c84f11..203b5ef 100644
--- a/core/jni/android_net_TrafficStats.cpp
+++ b/core/jni/android_net_TrafficStats.cpp
@@ -25,6 +25,7 @@
#include <android_runtime/AndroidRuntime.h>
#include <cutils/logger.h>
#include <jni.h>
+#include <ScopedUtfChars.h>
#include <utils/misc.h>
#include <utils/Log.h>
@@ -130,13 +131,14 @@ static jlong getMobileRxBytes(JNIEnv* env, jobject clazz) {
"/sys/class/net/ppp0/statistics/rx_bytes");
}
-static jlong getData(JNIEnv* env, char *what, jstring interface) {
- char filename[80];
- jboolean isCopy;
-
- const char *interfaceStr = env->GetStringUTFChars(interface, &isCopy);
- snprintf(filename, sizeof(filename), "/sys/class/net/%s/statistics/%s", interfaceStr, what);
+static jlong getData(JNIEnv* env, const char* what, jstring javaInterface) {
+ ScopedUtfChars interface(env, javaInterface);
+ if (interface.c_str() == NULL) {
+ return -1;
+ }
+ char filename[80];
+ snprintf(filename, sizeof(filename), "/sys/class/net/%s/statistics/%s", interface.c_str(), what);
return readNumber(filename);
}
diff --git a/core/jni/android_net_wifi_Wifi.cpp b/core/jni/android_net_wifi_Wifi.cpp
index 4e363bf..e930c5c 100644
--- a/core/jni/android_net_wifi_Wifi.cpp
+++ b/core/jni/android_net_wifi_Wifi.cpp
@@ -17,6 +17,7 @@
#define LOG_TAG "wifi"
#include "jni.h"
+#include <ScopedUtfChars.h>
#include <utils/misc.h>
#include <android_runtime/AndroidRuntime.h>
#include <utils/Log.h>
@@ -47,82 +48,100 @@ static int doCommand(const char *cmd, char *replybuf, int replybuflen)
}
}
-static jint doIntCommand(const char *cmd)
+static jint doIntCommand(const char* fmt, ...)
{
+ char buf[BUF_SIZE];
+ va_list args;
+ va_start(args, fmt);
+ int byteCount = vsnprintf(buf, sizeof(buf), fmt, args);
+ va_end(args);
+ if (byteCount < 0 || byteCount >= BUF_SIZE) {
+ return -1;
+ }
char reply[BUF_SIZE];
-
- if (doCommand(cmd, reply, sizeof(reply)) != 0) {
- return (jint)-1;
- } else {
- return (jint)atoi(reply);
+ if (doCommand(buf, reply, sizeof(reply)) != 0) {
+ return -1;
}
+ return static_cast<jint>(atoi(reply));
}
-static jboolean doBooleanCommand(const char *cmd, const char *expect)
+static jboolean doBooleanCommand(const char* expect, const char* fmt, ...)
{
+ char buf[BUF_SIZE];
+ va_list args;
+ va_start(args, fmt);
+ int byteCount = vsnprintf(buf, sizeof(buf), fmt, args);
+ va_end(args);
+ if (byteCount < 0 || byteCount >= BUF_SIZE) {
+ return JNI_FALSE;
+ }
char reply[BUF_SIZE];
-
- if (doCommand(cmd, reply, sizeof(reply)) != 0) {
- return (jboolean)JNI_FALSE;
- } else {
- return (jboolean)(strcmp(reply, expect) == 0);
+ if (doCommand(buf, reply, sizeof(reply)) != 0) {
+ return JNI_FALSE;
}
+ return (strcmp(reply, expect) == 0);
}
// Send a command to the supplicant, and return the reply as a String
-static jstring doStringCommand(JNIEnv *env, const char *cmd)
-{
+static jstring doStringCommand(JNIEnv* env, const char* fmt, ...) {
+ char buf[BUF_SIZE];
+ va_list args;
+ va_start(args, fmt);
+ int byteCount = vsnprintf(buf, sizeof(buf), fmt, args);
+ va_end(args);
+ if (byteCount < 0 || byteCount >= BUF_SIZE) {
+ return NULL;
+ }
char reply[4096];
-
- if (doCommand(cmd, reply, sizeof(reply)) != 0) {
- return env->NewStringUTF(NULL);
- } else {
- String16 str((char *)reply);
- return env->NewString((const jchar *)str.string(), str.size());
+ if (doCommand(buf, reply, sizeof(reply)) != 0) {
+ return NULL;
}
+ // TODO: why not just NewStringUTF?
+ String16 str((char *)reply);
+ return env->NewString((const jchar *)str.string(), str.size());
}
-static jboolean android_net_wifi_isDriverLoaded(JNIEnv* env, jobject clazz)
+static jboolean android_net_wifi_isDriverLoaded(JNIEnv* env, jobject)
{
return (jboolean)(::is_wifi_driver_loaded() == 1);
}
-static jboolean android_net_wifi_loadDriver(JNIEnv* env, jobject clazz)
+static jboolean android_net_wifi_loadDriver(JNIEnv* env, jobject)
{
return (jboolean)(::wifi_load_driver() == 0);
}
-static jboolean android_net_wifi_unloadDriver(JNIEnv* env, jobject clazz)
+static jboolean android_net_wifi_unloadDriver(JNIEnv* env, jobject)
{
return (jboolean)(::wifi_unload_driver() == 0);
}
-static jboolean android_net_wifi_startSupplicant(JNIEnv* env, jobject clazz)
+static jboolean android_net_wifi_startSupplicant(JNIEnv* env, jobject)
{
return (jboolean)(::wifi_start_supplicant() == 0);
}
-static jboolean android_net_wifi_stopSupplicant(JNIEnv* env, jobject clazz)
+static jboolean android_net_wifi_stopSupplicant(JNIEnv* env, jobject)
{
- return doBooleanCommand("TERMINATE", "OK");
+ return doBooleanCommand("OK", "TERMINATE");
}
-static jboolean android_net_wifi_killSupplicant(JNIEnv* env, jobject clazz)
+static jboolean android_net_wifi_killSupplicant(JNIEnv* env, jobject)
{
return (jboolean)(::wifi_stop_supplicant() == 0);
}
-static jboolean android_net_wifi_connectToSupplicant(JNIEnv* env, jobject clazz)
+static jboolean android_net_wifi_connectToSupplicant(JNIEnv* env, jobject)
{
return (jboolean)(::wifi_connect_to_supplicant() == 0);
}
-static void android_net_wifi_closeSupplicantConnection(JNIEnv* env, jobject clazz)
+static void android_net_wifi_closeSupplicantConnection(JNIEnv* env, jobject)
{
::wifi_close_supplicant_connection();
}
-static jstring android_net_wifi_waitForEvent(JNIEnv* env, jobject clazz)
+static jstring android_net_wifi_waitForEvent(JNIEnv* env, jobject)
{
char buf[BUF_SIZE];
@@ -130,197 +149,143 @@ static jstring android_net_wifi_waitForEvent(JNIEnv* env, jobject clazz)
if (nread > 0) {
return env->NewStringUTF(buf);
} else {
- return env->NewStringUTF(NULL);
+ return NULL;
}
}
-static jstring android_net_wifi_listNetworksCommand(JNIEnv* env, jobject clazz)
+static jstring android_net_wifi_listNetworksCommand(JNIEnv* env, jobject)
{
return doStringCommand(env, "LIST_NETWORKS");
}
-static jint android_net_wifi_addNetworkCommand(JNIEnv* env, jobject clazz)
+static jint android_net_wifi_addNetworkCommand(JNIEnv* env, jobject)
{
return doIntCommand("ADD_NETWORK");
}
-static jboolean android_net_wifi_wpsPbcCommand(JNIEnv* env, jobject clazz, jstring bssid)
+static jboolean android_net_wifi_wpsPbcCommand(JNIEnv* env, jobject, jstring javaBssid)
{
- char cmdstr[BUF_SIZE];
- jboolean isCopy;
-
- const char *bssidStr = env->GetStringUTFChars(bssid, &isCopy);
- int numWritten = snprintf(cmdstr, sizeof(cmdstr), "WPS_PBC %s", bssidStr);
- env->ReleaseStringUTFChars(bssid, bssidStr);
-
- if ((numWritten == -1) || (numWritten >= sizeof(cmdstr))) {
- return false;
+ ScopedUtfChars bssid(env, javaBssid);
+ if (bssid.c_str() == NULL) {
+ return JNI_FALSE;
}
- return doBooleanCommand(cmdstr, "OK");
+ return doBooleanCommand("OK", "WPS_PBC %s", bssid.c_str());
}
-static jboolean android_net_wifi_wpsPinFromAccessPointCommand(JNIEnv* env, jobject clazz,
- jstring bssid, jstring apPin)
+static jboolean android_net_wifi_wpsPinFromAccessPointCommand(JNIEnv* env, jobject,
+ jstring javaBssid, jstring javaApPin)
{
- char cmdstr[BUF_SIZE];
- jboolean isCopy;
-
- const char *bssidStr = env->GetStringUTFChars(bssid, &isCopy);
- const char *apPinStr = env->GetStringUTFChars(apPin, &isCopy);
- int numWritten = snprintf(cmdstr, sizeof(cmdstr), "WPS_REG %s %s", bssidStr, apPinStr);
- env->ReleaseStringUTFChars(bssid, bssidStr);
- env->ReleaseStringUTFChars(apPin, apPinStr);
-
- if ((numWritten == -1) || (numWritten >= (int)sizeof(cmdstr))) {
- return false;
+ ScopedUtfChars bssid(env, javaBssid);
+ if (bssid.c_str() == NULL) {
+ return JNI_FALSE;
}
- return doBooleanCommand(cmdstr, "OK");
+ ScopedUtfChars apPin(env, javaApPin);
+ if (apPin.c_str() == NULL) {
+ return JNI_FALSE;
+ }
+ return doBooleanCommand("OK", "WPS_REG %s %s", bssid.c_str(), apPin.c_str());
}
-static jstring android_net_wifi_wpsPinFromDeviceCommand(JNIEnv* env, jobject clazz, jstring bssid)
+static jstring android_net_wifi_wpsPinFromDeviceCommand(JNIEnv* env, jobject, jstring javaBssid)
{
- char cmdstr[BUF_SIZE];
- jboolean isCopy;
-
- const char *bssidStr = env->GetStringUTFChars(bssid, &isCopy);
- int numWritten = snprintf(cmdstr, sizeof(cmdstr), "WPS_PIN %s", bssidStr);
- env->ReleaseStringUTFChars(bssid, bssidStr);
-
- if ((numWritten == -1) || (numWritten >= (int)sizeof(cmdstr))) {
+ ScopedUtfChars bssid(env, javaBssid);
+ if (bssid.c_str() == NULL) {
return NULL;
}
- return doStringCommand(env, cmdstr);
+ return doStringCommand(env, "WPS_PIN %s", bssid.c_str());
}
-static jboolean android_net_wifi_setCountryCodeCommand(JNIEnv* env, jobject clazz, jstring country)
+static jboolean android_net_wifi_setCountryCodeCommand(JNIEnv* env, jobject, jstring javaCountry)
{
- char cmdstr[BUF_SIZE];
- jboolean isCopy;
-
- const char *countryStr = env->GetStringUTFChars(country, &isCopy);
- int numWritten = snprintf(cmdstr, sizeof(cmdstr), "DRIVER COUNTRY %s", countryStr);
- env->ReleaseStringUTFChars(country, countryStr);
-
- if ((numWritten == -1) || (numWritten >= (int)sizeof(cmdstr))) {
- return false;
+ ScopedUtfChars country(env, javaCountry);
+ if (country.c_str() == NULL) {
+ return JNI_FALSE;
}
- return doBooleanCommand(cmdstr, "OK");
+ return doBooleanCommand("OK", "DRIVER COUNTRY %s", country.c_str());
}
static jboolean android_net_wifi_setNetworkVariableCommand(JNIEnv* env,
- jobject clazz,
+ jobject,
jint netId,
- jstring name,
- jstring value)
+ jstring javaName,
+ jstring javaValue)
{
- char cmdstr[BUF_SIZE];
- jboolean isCopy;
-
- const char *nameStr = env->GetStringUTFChars(name, &isCopy);
- const char *valueStr = env->GetStringUTFChars(value, &isCopy);
-
- if (nameStr == NULL || valueStr == NULL)
+ ScopedUtfChars name(env, javaName);
+ if (name.c_str() == NULL) {
return JNI_FALSE;
-
- int cmdTooLong = snprintf(cmdstr, sizeof(cmdstr), "SET_NETWORK %d %s %s",
- netId, nameStr, valueStr) >= (int)sizeof(cmdstr);
-
- env->ReleaseStringUTFChars(name, nameStr);
- env->ReleaseStringUTFChars(value, valueStr);
-
- return (jboolean)!cmdTooLong && doBooleanCommand(cmdstr, "OK");
+ }
+ ScopedUtfChars value(env, javaValue);
+ if (value.c_str() == NULL) {
+ return JNI_FALSE;
+ }
+ return doBooleanCommand("OK", "SET_NETWORK %d %s %s", netId, name.c_str(), value.c_str());
}
static jstring android_net_wifi_getNetworkVariableCommand(JNIEnv* env,
- jobject clazz,
+ jobject,
jint netId,
- jstring name)
+ jstring javaName)
{
- char cmdstr[BUF_SIZE];
- jboolean isCopy;
-
- const char *nameStr = env->GetStringUTFChars(name, &isCopy);
-
- if (nameStr == NULL)
- return env->NewStringUTF(NULL);
-
- int cmdTooLong = snprintf(cmdstr, sizeof(cmdstr), "GET_NETWORK %d %s",
- netId, nameStr) >= (int)sizeof(cmdstr);
-
- env->ReleaseStringUTFChars(name, nameStr);
-
- return cmdTooLong ? env->NewStringUTF(NULL) : doStringCommand(env, cmdstr);
+ ScopedUtfChars name(env, javaName);
+ if (name.c_str() == NULL) {
+ return NULL;
+ }
+ return doStringCommand(env, "GET_NETWORK %d %s", netId, name.c_str());
}
-static jboolean android_net_wifi_removeNetworkCommand(JNIEnv* env, jobject clazz, jint netId)
+static jboolean android_net_wifi_removeNetworkCommand(JNIEnv* env, jobject, jint netId)
{
- char cmdstr[BUF_SIZE];
-
- int numWritten = snprintf(cmdstr, sizeof(cmdstr), "REMOVE_NETWORK %d", netId);
- int cmdTooLong = numWritten >= (int)sizeof(cmdstr);
-
- return (jboolean)!cmdTooLong && doBooleanCommand(cmdstr, "OK");
+ return doBooleanCommand("OK", "REMOVE_NETWORK %d", netId);
}
static jboolean android_net_wifi_enableNetworkCommand(JNIEnv* env,
- jobject clazz,
+ jobject,
jint netId,
jboolean disableOthers)
{
- char cmdstr[BUF_SIZE];
- const char *cmd = disableOthers ? "SELECT_NETWORK" : "ENABLE_NETWORK";
-
- int numWritten = snprintf(cmdstr, sizeof(cmdstr), "%s %d", cmd, netId);
- int cmdTooLong = numWritten >= (int)sizeof(cmdstr);
-
- return (jboolean)!cmdTooLong && doBooleanCommand(cmdstr, "OK");
+ return doBooleanCommand("OK", "%s_NETWORK %d", disableOthers ? "SELECT" : "ENABLE", netId);
}
-static jboolean android_net_wifi_disableNetworkCommand(JNIEnv* env, jobject clazz, jint netId)
+static jboolean android_net_wifi_disableNetworkCommand(JNIEnv* env, jobject, jint netId)
{
- char cmdstr[BUF_SIZE];
-
- int numWritten = snprintf(cmdstr, sizeof(cmdstr), "DISABLE_NETWORK %d", netId);
- int cmdTooLong = numWritten >= (int)sizeof(cmdstr);
-
- return (jboolean)!cmdTooLong && doBooleanCommand(cmdstr, "OK");
+ return doBooleanCommand("OK", "DISABLE_NETWORK %d", netId);
}
-static jstring android_net_wifi_statusCommand(JNIEnv* env, jobject clazz)
+static jstring android_net_wifi_statusCommand(JNIEnv* env, jobject)
{
return doStringCommand(env, "STATUS");
}
-static jboolean android_net_wifi_pingCommand(JNIEnv* env, jobject clazz)
+static jboolean android_net_wifi_pingCommand(JNIEnv* env, jobject)
{
- return doBooleanCommand("PING", "PONG");
+ return doBooleanCommand("PONG", "PING");
}
-static jstring android_net_wifi_scanResultsCommand(JNIEnv* env, jobject clazz)
+static jstring android_net_wifi_scanResultsCommand(JNIEnv* env, jobject)
{
return doStringCommand(env, "SCAN_RESULTS");
}
-static jboolean android_net_wifi_disconnectCommand(JNIEnv* env, jobject clazz)
+static jboolean android_net_wifi_disconnectCommand(JNIEnv* env, jobject)
{
- return doBooleanCommand("DISCONNECT", "OK");
+ return doBooleanCommand("OK", "DISCONNECT");
}
-static jboolean android_net_wifi_reconnectCommand(JNIEnv* env, jobject clazz)
+static jboolean android_net_wifi_reconnectCommand(JNIEnv* env, jobject)
{
- return doBooleanCommand("RECONNECT", "OK");
+ return doBooleanCommand("OK", "RECONNECT");
}
-static jboolean android_net_wifi_reassociateCommand(JNIEnv* env, jobject clazz)
+static jboolean android_net_wifi_reassociateCommand(JNIEnv* env, jobject)
{
- return doBooleanCommand("REASSOCIATE", "OK");
+ return doBooleanCommand("OK", "REASSOCIATE");
}
static jboolean doSetScanMode(jboolean setActive)
{
- return doBooleanCommand((setActive ? "DRIVER SCAN-ACTIVE" : "DRIVER SCAN-PASSIVE"), "OK");
+ return doBooleanCommand("OK", (setActive ? "DRIVER SCAN-ACTIVE" : "DRIVER SCAN-PASSIVE"));
}
-static jboolean android_net_wifi_scanCommand(JNIEnv* env, jobject clazz, jboolean forceActive)
+static jboolean android_net_wifi_scanCommand(JNIEnv* env, jobject, jboolean forceActive)
{
jboolean result;
@@ -328,43 +293,43 @@ static jboolean android_net_wifi_scanCommand(JNIEnv* env, jobject clazz, jboolea
// The scan will still work.
if (forceActive && !sScanModeActive)
doSetScanMode(true);
- result = doBooleanCommand("SCAN", "OK");
+ result = doBooleanCommand("OK", "SCAN");
if (forceActive && !sScanModeActive)
doSetScanMode(sScanModeActive);
return result;
}
-static jboolean android_net_wifi_setScanModeCommand(JNIEnv* env, jobject clazz, jboolean setActive)
+static jboolean android_net_wifi_setScanModeCommand(JNIEnv* env, jobject, jboolean setActive)
{
sScanModeActive = setActive;
return doSetScanMode(setActive);
}
-static jboolean android_net_wifi_startDriverCommand(JNIEnv* env, jobject clazz)
+static jboolean android_net_wifi_startDriverCommand(JNIEnv* env, jobject)
{
- return doBooleanCommand("DRIVER START", "OK");
+ return doBooleanCommand("OK", "DRIVER START");
}
-static jboolean android_net_wifi_stopDriverCommand(JNIEnv* env, jobject clazz)
+static jboolean android_net_wifi_stopDriverCommand(JNIEnv* env, jobject)
{
- return doBooleanCommand("DRIVER STOP", "OK");
+ return doBooleanCommand("OK", "DRIVER STOP");
}
-static jboolean android_net_wifi_startPacketFiltering(JNIEnv* env, jobject clazz)
+static jboolean android_net_wifi_startPacketFiltering(JNIEnv* env, jobject)
{
- return doBooleanCommand("DRIVER RXFILTER-ADD 0", "OK")
- && doBooleanCommand("DRIVER RXFILTER-ADD 1", "OK")
- && doBooleanCommand("DRIVER RXFILTER-ADD 3", "OK")
- && doBooleanCommand("DRIVER RXFILTER-START", "OK");
+ return doBooleanCommand("OK", "DRIVER RXFILTER-ADD 0")
+ && doBooleanCommand("OK", "DRIVER RXFILTER-ADD 1")
+ && doBooleanCommand("OK", "DRIVER RXFILTER-ADD 3")
+ && doBooleanCommand("OK", "DRIVER RXFILTER-START");
}
-static jboolean android_net_wifi_stopPacketFiltering(JNIEnv* env, jobject clazz)
+static jboolean android_net_wifi_stopPacketFiltering(JNIEnv* env, jobject)
{
- jboolean result = doBooleanCommand("DRIVER RXFILTER-STOP", "OK");
+ jboolean result = doBooleanCommand("OK", "DRIVER RXFILTER-STOP");
if (result) {
- (void)doBooleanCommand("DRIVER RXFILTER-REMOVE 3", "OK");
- (void)doBooleanCommand("DRIVER RXFILTER-REMOVE 1", "OK");
- (void)doBooleanCommand("DRIVER RXFILTER-REMOVE 0", "OK");
+ (void)doBooleanCommand("OK", "DRIVER RXFILTER-REMOVE 3");
+ (void)doBooleanCommand("OK", "DRIVER RXFILTER-REMOVE 1");
+ (void)doBooleanCommand("OK", "DRIVER RXFILTER-REMOVE 0");
}
return result;
@@ -399,17 +364,17 @@ static jint android_net_wifi_getRssiHelper(const char *cmd)
return (jint)rssi;
}
-static jint android_net_wifi_getRssiCommand(JNIEnv* env, jobject clazz)
+static jint android_net_wifi_getRssiCommand(JNIEnv* env, jobject)
{
return android_net_wifi_getRssiHelper("DRIVER RSSI");
}
-static jint android_net_wifi_getRssiApproxCommand(JNIEnv* env, jobject clazz)
+static jint android_net_wifi_getRssiApproxCommand(JNIEnv* env, jobject)
{
return android_net_wifi_getRssiHelper("DRIVER RSSI-APPROX");
}
-static jint android_net_wifi_getLinkSpeedCommand(JNIEnv* env, jobject clazz)
+static jint android_net_wifi_getLinkSpeedCommand(JNIEnv* env, jobject)
{
char reply[BUF_SIZE];
int linkspeed;
@@ -423,33 +388,28 @@ static jint android_net_wifi_getLinkSpeedCommand(JNIEnv* env, jobject clazz)
return (jint)linkspeed;
}
-static jstring android_net_wifi_getMacAddressCommand(JNIEnv* env, jobject clazz)
+static jstring android_net_wifi_getMacAddressCommand(JNIEnv* env, jobject)
{
char reply[BUF_SIZE];
char buf[BUF_SIZE];
if (doCommand("DRIVER MACADDR", reply, sizeof(reply)) != 0) {
- return env->NewStringUTF(NULL);
+ return NULL;
}
// reply comes back in the form "Macaddr = XX.XX.XX.XX.XX.XX" where XX
// is the part of the string we're interested in.
- if (sscanf(reply, "%*s = %255s", buf) == 1)
+ if (sscanf(reply, "%*s = %255s", buf) == 1) {
return env->NewStringUTF(buf);
- else
- return env->NewStringUTF(NULL);
+ }
+ return NULL;
}
-static jboolean android_net_wifi_setPowerModeCommand(JNIEnv* env, jobject clazz, jint mode)
+static jboolean android_net_wifi_setPowerModeCommand(JNIEnv* env, jobject, jint mode)
{
- char cmdstr[BUF_SIZE];
-
- int numWritten = snprintf(cmdstr, sizeof(cmdstr), "DRIVER POWERMODE %d", mode);
- int cmdTooLong = numWritten >= (int)sizeof(cmdstr);
-
- return (jboolean)!cmdTooLong && doBooleanCommand(cmdstr, "OK");
+ return doBooleanCommand("OK", "DRIVER POWERMODE %d", mode);
}
-static jint android_net_wifi_getPowerModeCommand(JNIEnv* env, jobject clazz)
+static jint android_net_wifi_getPowerModeCommand(JNIEnv* env, jobject)
{
char reply[BUF_SIZE];
int power;
@@ -465,17 +425,12 @@ static jint android_net_wifi_getPowerModeCommand(JNIEnv* env, jobject clazz)
return (jint)power;
}
-static jboolean android_net_wifi_setBandCommand(JNIEnv* env, jobject clazz, jint band)
+static jboolean android_net_wifi_setBandCommand(JNIEnv* env, jobject, jint band)
{
- char cmdstr[25];
-
- int numWritten = snprintf(cmdstr, sizeof(cmdstr), "DRIVER SETBAND %d", band);
- int cmdTooLong = numWritten >= (int)sizeof(cmdstr);
-
- return (jboolean)!cmdTooLong && doBooleanCommand(cmdstr, "OK");
+ return doBooleanCommand("OK", "DRIVER SETBAND %d", band);
}
-static jint android_net_wifi_getBandCommand(JNIEnv* env, jobject clazz)
+static jint android_net_wifi_getBandCommand(JNIEnv* env, jobject)
{
char reply[25];
int band;
@@ -489,87 +444,69 @@ static jint android_net_wifi_getBandCommand(JNIEnv* env, jobject clazz)
return (jint)band;
}
-static jboolean android_net_wifi_setBluetoothCoexistenceModeCommand(JNIEnv* env, jobject clazz, jint mode)
+static jboolean android_net_wifi_setBluetoothCoexistenceModeCommand(JNIEnv* env, jobject, jint mode)
{
- char cmdstr[BUF_SIZE];
-
- int numWritten = snprintf(cmdstr, sizeof(cmdstr), "DRIVER BTCOEXMODE %d", mode);
- int cmdTooLong = numWritten >= (int)sizeof(cmdstr);
-
- return (jboolean)!cmdTooLong && doBooleanCommand(cmdstr, "OK");
+ return doBooleanCommand("OK", "DRIVER BTCOEXMODE %d", mode);
}
-static jboolean android_net_wifi_setBluetoothCoexistenceScanModeCommand(JNIEnv* env, jobject clazz, jboolean setCoexScanMode)
+static jboolean android_net_wifi_setBluetoothCoexistenceScanModeCommand(JNIEnv* env, jobject, jboolean setCoexScanMode)
{
- char cmdstr[BUF_SIZE];
-
- int numWritten = snprintf(cmdstr, sizeof(cmdstr), "DRIVER BTCOEXSCAN-%s", setCoexScanMode ? "START" : "STOP");
- int cmdTooLong = numWritten >= (int)sizeof(cmdstr);
-
- return (jboolean)!cmdTooLong && doBooleanCommand(cmdstr, "OK");
+ return doBooleanCommand("OK", "DRIVER BTCOEXSCAN-%s", setCoexScanMode ? "START" : "STOP");
}
-static jboolean android_net_wifi_saveConfigCommand(JNIEnv* env, jobject clazz)
+static jboolean android_net_wifi_saveConfigCommand(JNIEnv* env, jobject)
{
// Make sure we never write out a value for AP_SCAN other than 1
- (void)doBooleanCommand("AP_SCAN 1", "OK");
- return doBooleanCommand("SAVE_CONFIG", "OK");
+ (void)doBooleanCommand("OK", "AP_SCAN 1");
+ return doBooleanCommand("OK", "SAVE_CONFIG");
}
-static jboolean android_net_wifi_reloadConfigCommand(JNIEnv* env, jobject clazz)
+static jboolean android_net_wifi_reloadConfigCommand(JNIEnv* env, jobject)
{
- return doBooleanCommand("RECONFIGURE", "OK");
+ return doBooleanCommand("OK", "RECONFIGURE");
}
-static jboolean android_net_wifi_setScanResultHandlingCommand(JNIEnv* env, jobject clazz, jint mode)
+static jboolean android_net_wifi_setScanResultHandlingCommand(JNIEnv* env, jobject, jint mode)
{
- char cmdstr[BUF_SIZE];
-
- int numWritten = snprintf(cmdstr, sizeof(cmdstr), "AP_SCAN %d", mode);
- int cmdTooLong = numWritten >= (int)sizeof(cmdstr);
-
- return (jboolean)!cmdTooLong && doBooleanCommand(cmdstr, "OK");
+ return doBooleanCommand("OK", "AP_SCAN %d", mode);
}
-static jboolean android_net_wifi_addToBlacklistCommand(JNIEnv* env, jobject clazz, jstring bssid)
+static jboolean android_net_wifi_addToBlacklistCommand(JNIEnv* env, jobject, jstring javaBssid)
{
- char cmdstr[BUF_SIZE];
- jboolean isCopy;
-
- const char *bssidStr = env->GetStringUTFChars(bssid, &isCopy);
-
- int cmdTooLong = snprintf(cmdstr, sizeof(cmdstr), "BLACKLIST %s", bssidStr) >= (int)sizeof(cmdstr);
-
- env->ReleaseStringUTFChars(bssid, bssidStr);
-
- return (jboolean)!cmdTooLong && doBooleanCommand(cmdstr, "OK");
+ ScopedUtfChars bssid(env, javaBssid);
+ if (bssid.c_str() == NULL) {
+ return JNI_FALSE;
+ }
+ return doBooleanCommand("OK", "BLACKLIST %s", bssid.c_str());
}
-static jboolean android_net_wifi_clearBlacklistCommand(JNIEnv* env, jobject clazz)
+static jboolean android_net_wifi_clearBlacklistCommand(JNIEnv* env, jobject)
{
- return doBooleanCommand("BLACKLIST clear", "OK");
+ return doBooleanCommand("OK", "BLACKLIST clear");
}
-static jboolean android_net_wifi_setSuspendOptimizationsCommand(JNIEnv* env, jobject clazz, jboolean enabled)
+static jboolean android_net_wifi_setSuspendOptimizationsCommand(JNIEnv* env, jobject, jboolean enabled)
{
- char cmdstr[BUF_SIZE];
-
- snprintf(cmdstr, sizeof(cmdstr), "DRIVER SETSUSPENDOPT %d", enabled ? 0 : 1);
- return doBooleanCommand(cmdstr, "OK");
+ return doBooleanCommand("OK", "DRIVER SETSUSPENDOPT %d", enabled ? 0 : 1);
}
-static void android_net_wifi_enableBackgroundScan(JNIEnv* env, jobject clazz, jboolean enable)
+static void android_net_wifi_enableBackgroundScanCommand(JNIEnv* env, jobject, jboolean enable)
{
//Note: BGSCAN-START and BGSCAN-STOP are documented in core/res/res/values/config.xml
//and will need an update if the names are changed
if (enable) {
- doBooleanCommand("DRIVER BGSCAN-START", "OK");
- }
- else {
- doBooleanCommand("DRIVER BGSCAN-STOP", "OK");
+ doBooleanCommand("OK", "DRIVER BGSCAN-START");
+ } else {
+ doBooleanCommand("OK", "DRIVER BGSCAN-STOP");
}
}
+static void android_net_wifi_setScanIntervalCommand(JNIEnv* env, jobject, jint scanInterval)
+{
+ doBooleanCommand("OK", "SCAN_INTERVAL %d", scanInterval);
+}
+
+
// ----------------------------------------------------------------------------
/*
@@ -637,14 +574,12 @@ static JNINativeMethod gWifiMethods[] = {
(void*) android_net_wifi_setSuspendOptimizationsCommand},
{ "setCountryCodeCommand", "(Ljava/lang/String;)Z",
(void*) android_net_wifi_setCountryCodeCommand},
- { "enableBackgroundScan", "(Z)V", (void*) android_net_wifi_enableBackgroundScan},
+ { "enableBackgroundScanCommand", "(Z)V", (void*) android_net_wifi_enableBackgroundScanCommand},
+ { "setScanIntervalCommand", "(I)V", (void*) android_net_wifi_setScanIntervalCommand},
};
int register_android_net_wifi_WifiManager(JNIEnv* env)
{
- jclass wifi = env->FindClass(WIFI_PKG_NAME);
- LOG_FATAL_IF(wifi == NULL, "Unable to find class " WIFI_PKG_NAME);
-
return AndroidRuntime::registerNativeMethods(env,
WIFI_PKG_NAME, gWifiMethods, NELEM(gWifiMethods));
}
diff --git a/core/jni/android_nio_utils.cpp b/core/jni/android_nio_utils.cpp
index 584e7a4..7cbbe12 100644
--- a/core/jni/android_nio_utils.cpp
+++ b/core/jni/android_nio_utils.cpp
@@ -32,20 +32,20 @@ void* android::nio_getPointer(JNIEnv *_env, jobject buffer, jarray *array) {
jlong pointer;
jint offset;
void *data;
-
+
pointer = _env->CallStaticLongMethod(gNioJNI.nioAccessClass,
gNioJNI.getBasePointerID, buffer);
if (pointer != 0L) {
*array = NULL;
return (void *) (jint) pointer;
}
-
+
*array = (jarray) _env->CallStaticObjectMethod(gNioJNI.nioAccessClass,
gNioJNI.getBaseArrayID, buffer);
offset = _env->CallStaticIntMethod(gNioJNI.nioAccessClass,
gNioJNI.getBaseArrayOffsetID, buffer);
data = _env->GetPrimitiveArrayCritical(*array, (jboolean *) 0);
-
+
return (void *) ((char *) data + offset);
}
@@ -94,8 +94,7 @@ static jfieldID getFieldID(JNIEnv* env, jclass c, const char name[],
}
namespace android {
-
-int register_android_nio_utils(JNIEnv* env);
+
int register_android_nio_utils(JNIEnv* env) {
jclass localClass = findClass(env, "java/nio/NIOAccess");
gNioJNI.getBasePointerID = findStaticMethod(env, localClass,
diff --git a/core/jni/android_opengl_GLES10.cpp b/core/jni/android_opengl_GLES10.cpp
index 2685d75..6c29d6c 100644
--- a/core/jni/android_opengl_GLES10.cpp
+++ b/core/jni/android_opengl_GLES10.cpp
@@ -2,21 +2,23 @@
**
** 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
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
**
-** http://www.apache.org/licenses/LICENSE-2.0
+** http://www.apache.org/licenses/LICENSE-2.0
**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
** limitations under the License.
*/
// This source file is automatically generated
+#include "jni.h"
+#include "JNIHelp.h"
#include <android_runtime/AndroidRuntime.h>
#include <utils/misc.h>
@@ -41,10 +43,6 @@ static int initialized = 0;
static jclass nioAccessClass;
static jclass bufferClass;
-static jclass OOMEClass;
-static jclass UOEClass;
-static jclass IAEClass;
-static jclass AIOOBEClass;
static jmethodID getBasePointerID;
static jmethodID getBaseArrayID;
static jmethodID getBaseArrayOffsetID;
@@ -55,7 +53,7 @@ static jfieldID elementSizeShiftID;
/* Cache method IDs each time the class is loaded. */
static void
-nativeClassInitBuffer(JNIEnv *_env)
+nativeClassInit(JNIEnv *_env, jclass glImplClass)
{
jclass nioAccessClassLocal = _env->FindClass("java/nio/NIOAccess");
nioAccessClass = (jclass) _env->NewGlobalRef(nioAccessClassLocal);
@@ -76,26 +74,6 @@ nativeClassInitBuffer(JNIEnv *_env)
_env->GetFieldID(bufferClass, "_elementSizeShift", "I");
}
-static void
-nativeClassInit(JNIEnv *_env, jclass glImplClass)
-{
- nativeClassInitBuffer(_env);
-
- jclass IAEClassLocal =
- _env->FindClass("java/lang/IllegalArgumentException");
- jclass OOMEClassLocal =
- _env->FindClass("java/lang/OutOfMemoryError");
- jclass UOEClassLocal =
- _env->FindClass("java/lang/UnsupportedOperationException");
- jclass AIOOBEClassLocal =
- _env->FindClass("java/lang/ArrayIndexOutOfBoundsException");
-
- IAEClass = (jclass) _env->NewGlobalRef(IAEClassLocal);
- OOMEClass = (jclass) _env->NewGlobalRef(OOMEClassLocal);
- UOEClass = (jclass) _env->NewGlobalRef(UOEClassLocal);
- AIOOBEClass = (jclass) _env->NewGlobalRef(AIOOBEClassLocal);
-}
-
static void *
getPointer(JNIEnv *_env, jobject buffer, jarray *array, jint *remaining)
{
@@ -116,13 +94,13 @@ getPointer(JNIEnv *_env, jobject buffer, jarray *array, jint *remaining)
*array = NULL;
return (void *) (jint) pointer;
}
-
+
*array = (jarray) _env->CallStaticObjectMethod(nioAccessClass,
getBaseArrayID, buffer);
offset = _env->CallStaticIntMethod(nioAccessClass,
getBaseArrayOffsetID, buffer);
data = _env->GetPrimitiveArrayCritical(*array, (jboolean *) 0);
-
+
return (void *) ((char *) data + offset);
}
@@ -141,7 +119,8 @@ getDirectBufferPointer(JNIEnv *_env, jobject buffer) {
jint elementSizeShift = _env->GetIntField(buffer, elementSizeShiftID);
buf += position << elementSizeShift;
} else {
- _env->ThrowNew(IAEClass, "Must use a native order direct Buffer");
+ jniThrowException(_env, "java/lang/IllegalArgumentException",
+ "Must use a native order direct Buffer");
}
return (void*) buf;
}
@@ -154,7 +133,6 @@ getNumCompressedTextureFormats() {
}
// --------------------------------------------------------------------------
-
/* void glActiveTexture ( GLenum texture ) */
static void
android_glActiveTexture__I
@@ -431,16 +409,16 @@ android_glDeleteTextures__I_3II
GLuint *textures = (GLuint *) 0;
if (!textures_ref) {
- _env->ThrowNew(IAEClass, "textures == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "textures == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(textures_ref) - offset;
if (_remaining < n) {
- _env->ThrowNew(IAEClass, "length - offset < n");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < n");
goto exit;
}
textures_base = (GLuint *)
@@ -469,7 +447,7 @@ android_glDeleteTextures__ILjava_nio_IntBuffer_2
textures = (GLuint *)getPointer(_env, textures_buf, &_array, &_remaining);
if (_remaining < n) {
- _env->ThrowNew(IAEClass, "remaining() < n");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < n");
goto exit;
}
glDeleteTextures(
@@ -560,7 +538,7 @@ android_glDrawElements__IIILjava_nio_Buffer_2
indices = (GLvoid *)getPointer(_env, indices_buf, &_array, &_remaining);
if (_remaining < count) {
- _env->ThrowNew(AIOOBEClass, "remaining() < count");
+ jniThrowException(_env, "java/lang/ArrayIndexOutOfBoundsException", "remaining() < count");
goto exit;
}
glDrawElements(
@@ -627,11 +605,11 @@ android_glFogfv__I_3FI
GLfloat *params = (GLfloat *) 0;
if (!params_ref) {
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -661,7 +639,7 @@ android_glFogfv__I_3FI
break;
}
if (_remaining < _needed) {
- _env->ThrowNew(IAEClass, "length - offset < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
goto exit;
}
params_base = (GLfloat *)
@@ -715,7 +693,7 @@ android_glFogfv__ILjava_nio_FloatBuffer_2
break;
}
if (_remaining < _needed) {
- _env->ThrowNew(IAEClass, "remaining() < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
goto exit;
}
glFogfv(
@@ -748,11 +726,11 @@ android_glFogxv__I_3II
GLfixed *params = (GLfixed *) 0;
if (!params_ref) {
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -782,7 +760,7 @@ android_glFogxv__I_3II
break;
}
if (_remaining < _needed) {
- _env->ThrowNew(IAEClass, "length - offset < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
goto exit;
}
params_base = (GLfixed *)
@@ -836,7 +814,7 @@ android_glFogxv__ILjava_nio_IntBuffer_2
break;
}
if (_remaining < _needed) {
- _env->ThrowNew(IAEClass, "remaining() < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
goto exit;
}
glFogxv(
@@ -898,18 +876,18 @@ android_glGenTextures__I_3II
if (!textures_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "textures == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "textures == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(textures_ref) - offset;
if (_remaining < n) {
_exception = 1;
- _env->ThrowNew(IAEClass, "length - offset < n");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < n");
goto exit;
}
textures_base = (GLuint *)
@@ -940,7 +918,7 @@ android_glGenTextures__ILjava_nio_IntBuffer_2
textures = (GLuint *)getPointer(_env, textures_buf, &_array, &_remaining);
if (_remaining < n) {
_exception = 1;
- _env->ThrowNew(IAEClass, "remaining() < n");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < n");
goto exit;
}
glGenTextures(
@@ -974,12 +952,12 @@ android_glGetIntegerv__I_3II
if (!params_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -1316,7 +1294,7 @@ android_glGetIntegerv__I_3II
}
if (_remaining < _needed) {
_exception = 1;
- _env->ThrowNew(IAEClass, "length - offset < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
goto exit;
}
params_base = (GLint *)
@@ -1678,7 +1656,7 @@ android_glGetIntegerv__ILjava_nio_IntBuffer_2
}
if (_remaining < _needed) {
_exception = 1;
- _env->ThrowNew(IAEClass, "remaining() < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
goto exit;
}
glGetIntegerv(
@@ -1692,16 +1670,10 @@ exit:
}
}
-#include <string.h>
-
/* const GLubyte * glGetString ( GLenum name ) */
-static
-jstring
-android_glGetString
- (JNIEnv *_env, jobject _this, jint name) {
- const char * chars = (const char *)glGetString((GLenum)name);
- jstring output = _env->NewStringUTF(chars);
- return output;
+static jstring android_glGetString(JNIEnv* _env, jobject, jint name) {
+ const char* chars = (const char*) glGetString((GLenum) name);
+ return _env->NewStringUTF(chars);
}
/* void glHint ( GLenum target, GLenum mode ) */
static void
@@ -1732,11 +1704,11 @@ android_glLightModelfv__I_3FI
GLfloat *params = (GLfloat *) 0;
if (!params_ref) {
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -1757,7 +1729,7 @@ android_glLightModelfv__I_3FI
break;
}
if (_remaining < _needed) {
- _env->ThrowNew(IAEClass, "length - offset < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
goto exit;
}
params_base = (GLfloat *)
@@ -1802,7 +1774,7 @@ android_glLightModelfv__ILjava_nio_FloatBuffer_2
break;
}
if (_remaining < _needed) {
- _env->ThrowNew(IAEClass, "remaining() < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
goto exit;
}
glLightModelfv(
@@ -1835,11 +1807,11 @@ android_glLightModelxv__I_3II
GLfixed *params = (GLfixed *) 0;
if (!params_ref) {
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -1860,7 +1832,7 @@ android_glLightModelxv__I_3II
break;
}
if (_remaining < _needed) {
- _env->ThrowNew(IAEClass, "length - offset < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
goto exit;
}
params_base = (GLfixed *)
@@ -1905,7 +1877,7 @@ android_glLightModelxv__ILjava_nio_IntBuffer_2
break;
}
if (_remaining < _needed) {
- _env->ThrowNew(IAEClass, "remaining() < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
goto exit;
}
glLightModelxv(
@@ -1939,11 +1911,11 @@ android_glLightfv__II_3FI
GLfloat *params = (GLfloat *) 0;
if (!params_ref) {
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -1990,7 +1962,7 @@ android_glLightfv__II_3FI
break;
}
if (_remaining < _needed) {
- _env->ThrowNew(IAEClass, "length - offset < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
goto exit;
}
params_base = (GLfloat *)
@@ -2062,7 +2034,7 @@ android_glLightfv__IILjava_nio_FloatBuffer_2
break;
}
if (_remaining < _needed) {
- _env->ThrowNew(IAEClass, "remaining() < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
goto exit;
}
glLightfv(
@@ -2097,11 +2069,11 @@ android_glLightxv__II_3II
GLfixed *params = (GLfixed *) 0;
if (!params_ref) {
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -2148,7 +2120,7 @@ android_glLightxv__II_3II
break;
}
if (_remaining < _needed) {
- _env->ThrowNew(IAEClass, "length - offset < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
goto exit;
}
params_base = (GLfixed *)
@@ -2220,7 +2192,7 @@ android_glLightxv__IILjava_nio_IntBuffer_2
break;
}
if (_remaining < _needed) {
- _env->ThrowNew(IAEClass, "remaining() < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
goto exit;
}
glLightxv(
@@ -2269,11 +2241,11 @@ android_glLoadMatrixf___3FI
GLfloat *m = (GLfloat *) 0;
if (!m_ref) {
- _env->ThrowNew(IAEClass, "m == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "m == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(m_ref) - offset;
@@ -2318,11 +2290,11 @@ android_glLoadMatrixx___3II
GLfixed *m = (GLfixed *) 0;
if (!m_ref) {
- _env->ThrowNew(IAEClass, "m == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "m == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(m_ref) - offset;
@@ -2387,11 +2359,11 @@ android_glMaterialfv__II_3FI
GLfloat *params = (GLfloat *) 0;
if (!params_ref) {
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -2424,7 +2396,7 @@ android_glMaterialfv__II_3FI
break;
}
if (_remaining < _needed) {
- _env->ThrowNew(IAEClass, "length - offset < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
goto exit;
}
params_base = (GLfloat *)
@@ -2482,7 +2454,7 @@ android_glMaterialfv__IILjava_nio_FloatBuffer_2
break;
}
if (_remaining < _needed) {
- _env->ThrowNew(IAEClass, "remaining() < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
goto exit;
}
glMaterialfv(
@@ -2517,11 +2489,11 @@ android_glMaterialxv__II_3II
GLfixed *params = (GLfixed *) 0;
if (!params_ref) {
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -2554,7 +2526,7 @@ android_glMaterialxv__II_3II
break;
}
if (_remaining < _needed) {
- _env->ThrowNew(IAEClass, "length - offset < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
goto exit;
}
params_base = (GLfixed *)
@@ -2612,7 +2584,7 @@ android_glMaterialxv__IILjava_nio_IntBuffer_2
break;
}
if (_remaining < _needed) {
- _env->ThrowNew(IAEClass, "remaining() < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
goto exit;
}
glMaterialxv(
@@ -2645,11 +2617,11 @@ android_glMultMatrixf___3FI
GLfloat *m = (GLfloat *) 0;
if (!m_ref) {
- _env->ThrowNew(IAEClass, "m == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "m == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(m_ref) - offset;
@@ -2694,11 +2666,11 @@ android_glMultMatrixx___3II
GLfixed *m = (GLfixed *) 0;
if (!m_ref) {
- _env->ThrowNew(IAEClass, "m == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "m == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(m_ref) - offset;
@@ -3079,11 +3051,11 @@ android_glTexEnvfv__II_3FI
GLfloat *params = (GLfloat *) 0;
if (!params_ref) {
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -3110,7 +3082,7 @@ android_glTexEnvfv__II_3FI
break;
}
if (_remaining < _needed) {
- _env->ThrowNew(IAEClass, "length - offset < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
goto exit;
}
params_base = (GLfloat *)
@@ -3162,7 +3134,7 @@ android_glTexEnvfv__IILjava_nio_FloatBuffer_2
break;
}
if (_remaining < _needed) {
- _env->ThrowNew(IAEClass, "remaining() < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
goto exit;
}
glTexEnvfv(
@@ -3197,11 +3169,11 @@ android_glTexEnvxv__II_3II
GLfixed *params = (GLfixed *) 0;
if (!params_ref) {
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -3228,7 +3200,7 @@ android_glTexEnvxv__II_3II
break;
}
if (_remaining < _needed) {
- _env->ThrowNew(IAEClass, "length - offset < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
goto exit;
}
params_base = (GLfixed *)
@@ -3280,7 +3252,7 @@ android_glTexEnvxv__IILjava_nio_IntBuffer_2
break;
}
if (_remaining < _needed) {
- _env->ThrowNew(IAEClass, "remaining() < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
goto exit;
}
glTexEnvxv(
diff --git a/core/jni/android_opengl_GLES10Ext.cpp b/core/jni/android_opengl_GLES10Ext.cpp
index f17ef21..1154cef 100644
--- a/core/jni/android_opengl_GLES10Ext.cpp
+++ b/core/jni/android_opengl_GLES10Ext.cpp
@@ -2,21 +2,23 @@
**
** 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
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
**
-** http://www.apache.org/licenses/LICENSE-2.0
+** http://www.apache.org/licenses/LICENSE-2.0
**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
** limitations under the License.
*/
// This source file is automatically generated
+#include "jni.h"
+#include "JNIHelp.h"
#include <android_runtime/AndroidRuntime.h>
#include <utils/misc.h>
@@ -28,10 +30,6 @@ static int initialized = 0;
static jclass nioAccessClass;
static jclass bufferClass;
-static jclass OOMEClass;
-static jclass UOEClass;
-static jclass IAEClass;
-static jclass AIOOBEClass;
static jmethodID getBasePointerID;
static jmethodID getBaseArrayID;
static jmethodID getBaseArrayOffsetID;
@@ -42,7 +40,7 @@ static jfieldID elementSizeShiftID;
/* Cache method IDs each time the class is loaded. */
static void
-nativeClassInitBuffer(JNIEnv *_env)
+nativeClassInit(JNIEnv *_env, jclass glImplClass)
{
jclass nioAccessClassLocal = _env->FindClass("java/nio/NIOAccess");
nioAccessClass = (jclass) _env->NewGlobalRef(nioAccessClassLocal);
@@ -64,26 +62,6 @@ nativeClassInitBuffer(JNIEnv *_env)
}
-static void
-nativeClassInit(JNIEnv *_env, jclass glImplClass)
-{
- nativeClassInitBuffer(_env);
-
- jclass IAEClassLocal =
- _env->FindClass("java/lang/IllegalArgumentException");
- jclass OOMEClassLocal =
- _env->FindClass("java/lang/OutOfMemoryError");
- jclass UOEClassLocal =
- _env->FindClass("java/lang/UnsupportedOperationException");
- jclass AIOOBEClassLocal =
- _env->FindClass("java/lang/ArrayIndexOutOfBoundsException");
-
- IAEClass = (jclass) _env->NewGlobalRef(IAEClassLocal);
- OOMEClass = (jclass) _env->NewGlobalRef(OOMEClassLocal);
- UOEClass = (jclass) _env->NewGlobalRef(UOEClassLocal);
- AIOOBEClass = (jclass) _env->NewGlobalRef(AIOOBEClassLocal);
-}
-
static void *
getPointer(JNIEnv *_env, jobject buffer, jarray *array, jint *remaining)
{
@@ -104,13 +82,13 @@ getPointer(JNIEnv *_env, jobject buffer, jarray *array, jint *remaining)
*array = NULL;
return (void *) (jint) pointer;
}
-
+
*array = (jarray) _env->CallStaticObjectMethod(nioAccessClass,
getBaseArrayID, buffer);
offset = _env->CallStaticIntMethod(nioAccessClass,
getBaseArrayOffsetID, buffer);
data = _env->GetPrimitiveArrayCritical(*array, (jboolean *) 0);
-
+
return (void *) ((char *) data + offset);
}
@@ -123,7 +101,6 @@ releasePointer(JNIEnv *_env, jarray array, void *data, jboolean commit)
}
// --------------------------------------------------------------------------
-
/* GLbitfield glQueryMatrixxOES ( GLfixed *mantissa, GLint *exponent ) */
static jint
android_glQueryMatrixxOES___3II_3II
@@ -139,18 +116,18 @@ android_glQueryMatrixxOES___3II_3II
if (!mantissa_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "mantissa == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "mantissa == null");
goto exit;
}
if (mantissaOffset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "mantissaOffset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "mantissaOffset < 0");
goto exit;
}
_mantissaRemaining = _env->GetArrayLength(mantissa_ref) - mantissaOffset;
if (_mantissaRemaining < 16) {
_exception = 1;
- _env->ThrowNew(IAEClass, "length - mantissaOffset < 16");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - mantissaOffset < 16");
goto exit;
}
mantissa_base = (GLfixed *)
@@ -159,18 +136,18 @@ android_glQueryMatrixxOES___3II_3II
if (!exponent_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "exponent == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "exponent == null");
goto exit;
}
if (exponentOffset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "exponentOffset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "exponentOffset < 0");
goto exit;
}
_exponentRemaining = _env->GetArrayLength(exponent_ref) - exponentOffset;
if (_exponentRemaining < 16) {
_exception = 1;
- _env->ThrowNew(IAEClass, "length - exponentOffset < 16");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - exponentOffset < 16");
goto exit;
}
exponent_base = (GLint *)
@@ -210,13 +187,13 @@ android_glQueryMatrixxOES__Ljava_nio_IntBuffer_2Ljava_nio_IntBuffer_2
mantissa = (GLfixed *)getPointer(_env, mantissa_buf, &_mantissaArray, &_mantissaRemaining);
if (_mantissaRemaining < 16) {
_exception = 1;
- _env->ThrowNew(IAEClass, "remaining() < 16");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 16");
goto exit;
}
exponent = (GLint *)getPointer(_env, exponent_buf, &_exponentArray, &_exponentRemaining);
if (_exponentRemaining < 16) {
_exception = 1;
- _env->ThrowNew(IAEClass, "remaining() < 16");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 16");
goto exit;
}
_returnValue = glQueryMatrixxOES(
diff --git a/core/jni/android_opengl_GLES11.cpp b/core/jni/android_opengl_GLES11.cpp
index 0f71b9f..d038f20 100644
--- a/core/jni/android_opengl_GLES11.cpp
+++ b/core/jni/android_opengl_GLES11.cpp
@@ -2,21 +2,23 @@
**
** 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
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
**
-** http://www.apache.org/licenses/LICENSE-2.0
+** http://www.apache.org/licenses/LICENSE-2.0
**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
** limitations under the License.
*/
// This source file is automatically generated
+#include "jni.h"
+#include "JNIHelp.h"
#include <android_runtime/AndroidRuntime.h>
#include <utils/misc.h>
@@ -35,10 +37,6 @@ static int initialized = 0;
static jclass nioAccessClass;
static jclass bufferClass;
-static jclass OOMEClass;
-static jclass UOEClass;
-static jclass IAEClass;
-static jclass AIOOBEClass;
static jmethodID getBasePointerID;
static jmethodID getBaseArrayID;
static jmethodID getBaseArrayOffsetID;
@@ -49,7 +47,7 @@ static jfieldID elementSizeShiftID;
/* Cache method IDs each time the class is loaded. */
static void
-nativeClassInitBuffer(JNIEnv *_env)
+nativeClassInit(JNIEnv *_env, jclass glImplClass)
{
jclass nioAccessClassLocal = _env->FindClass("java/nio/NIOAccess");
nioAccessClass = (jclass) _env->NewGlobalRef(nioAccessClassLocal);
@@ -71,26 +69,6 @@ nativeClassInitBuffer(JNIEnv *_env)
}
-static void
-nativeClassInit(JNIEnv *_env, jclass glImplClass)
-{
- nativeClassInitBuffer(_env);
-
- jclass IAEClassLocal =
- _env->FindClass("java/lang/IllegalArgumentException");
- jclass OOMEClassLocal =
- _env->FindClass("java/lang/OutOfMemoryError");
- jclass UOEClassLocal =
- _env->FindClass("java/lang/UnsupportedOperationException");
- jclass AIOOBEClassLocal =
- _env->FindClass("java/lang/ArrayIndexOutOfBoundsException");
-
- IAEClass = (jclass) _env->NewGlobalRef(IAEClassLocal);
- OOMEClass = (jclass) _env->NewGlobalRef(OOMEClassLocal);
- UOEClass = (jclass) _env->NewGlobalRef(UOEClassLocal);
- AIOOBEClass = (jclass) _env->NewGlobalRef(AIOOBEClassLocal);
-}
-
static void *
getPointer(JNIEnv *_env, jobject buffer, jarray *array, jint *remaining)
{
@@ -111,13 +89,13 @@ getPointer(JNIEnv *_env, jobject buffer, jarray *array, jint *remaining)
*array = NULL;
return (void *) (jint) pointer;
}
-
+
*array = (jarray) _env->CallStaticObjectMethod(nioAccessClass,
getBaseArrayID, buffer);
offset = _env->CallStaticIntMethod(nioAccessClass,
getBaseArrayOffsetID, buffer);
data = _env->GetPrimitiveArrayCritical(*array, (jboolean *) 0);
-
+
return (void *) ((char *) data + offset);
}
@@ -137,13 +115,13 @@ getDirectBufferPointer(JNIEnv *_env, jobject buffer) {
jint elementSizeShift = _env->GetIntField(buffer, elementSizeShiftID);
buf += position << elementSizeShift;
} else {
- _env->ThrowNew(IAEClass, "Must use a native order direct Buffer");
+ jniThrowException(_env, "java/lang/IllegalArgumentException",
+ "Must use a native order direct Buffer");
}
return (void*) buf;
}
// --------------------------------------------------------------------------
-
/* void glBindBuffer ( GLenum target, GLuint buffer ) */
static void
android_glBindBuffer__II
@@ -165,7 +143,7 @@ android_glBufferData__IILjava_nio_Buffer_2I
if (data_buf) {
data = (GLvoid *)getPointer(_env, data_buf, &_array, &_remaining);
if (_remaining < size) {
- _env->ThrowNew(IAEClass, "remaining() < size");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < size");
goto exit;
}
}
@@ -192,7 +170,7 @@ android_glBufferSubData__IIILjava_nio_Buffer_2
data = (GLvoid *)getPointer(_env, data_buf, &_array, &_remaining);
if (_remaining < size) {
- _env->ThrowNew(IAEClass, "remaining() < size");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < size");
goto exit;
}
glBufferSubData(
@@ -217,11 +195,11 @@ android_glClipPlanef__I_3FI
GLfloat *equation = (GLfloat *) 0;
if (!equation_ref) {
- _env->ThrowNew(IAEClass, "equation == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "equation == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(equation_ref) - offset;
@@ -268,11 +246,11 @@ android_glClipPlanex__I_3II
GLfixed *equation = (GLfixed *) 0;
if (!equation_ref) {
- _env->ThrowNew(IAEClass, "equation == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "equation == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(equation_ref) - offset;
@@ -343,16 +321,16 @@ android_glDeleteBuffers__I_3II
GLuint *buffers = (GLuint *) 0;
if (!buffers_ref) {
- _env->ThrowNew(IAEClass, "buffers == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "buffers == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(buffers_ref) - offset;
if (_remaining < n) {
- _env->ThrowNew(IAEClass, "length - offset < n");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < n");
goto exit;
}
buffers_base = (GLuint *)
@@ -381,7 +359,7 @@ android_glDeleteBuffers__ILjava_nio_IntBuffer_2
buffers = (GLuint *)getPointer(_env, buffers_buf, &_array, &_remaining);
if (_remaining < n) {
- _env->ThrowNew(IAEClass, "remaining() < n");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < n");
goto exit;
}
glDeleteBuffers(
@@ -418,18 +396,18 @@ android_glGenBuffers__I_3II
if (!buffers_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "buffers == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "buffers == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(buffers_ref) - offset;
if (_remaining < n) {
_exception = 1;
- _env->ThrowNew(IAEClass, "length - offset < n");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < n");
goto exit;
}
buffers_base = (GLuint *)
@@ -460,7 +438,7 @@ android_glGenBuffers__ILjava_nio_IntBuffer_2
buffers = (GLuint *)getPointer(_env, buffers_buf, &_array, &_remaining);
if (_remaining < n) {
_exception = 1;
- _env->ThrowNew(IAEClass, "remaining() < n");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < n");
goto exit;
}
glGenBuffers(
@@ -485,12 +463,12 @@ android_glGetBooleanv__I_3ZI
if (!params_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -533,16 +511,69 @@ android_glGetBooleanv__ILjava_nio_IntBuffer_2
static void
android_glGetBufferParameteriv__II_3II
(JNIEnv *_env, jobject _this, jint target, jint pname, jintArray params_ref, jint offset) {
- _env->ThrowNew(UOEClass,
- "glGetBufferParameteriv");
+ jint _exception = 0;
+ GLint *params_base = (GLint *) 0;
+ jint _remaining;
+ GLint *params = (GLint *) 0;
+
+ if (!params_ref) {
+ _exception = 1;
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
+ goto exit;
+ }
+ if (offset < 0) {
+ _exception = 1;
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
+ goto exit;
+ }
+ _remaining = _env->GetArrayLength(params_ref) - offset;
+ if (_remaining < 1) {
+ _exception = 1;
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < 1");
+ goto exit;
+ }
+ params_base = (GLint *)
+ _env->GetPrimitiveArrayCritical(params_ref, (jboolean *)0);
+ params = params_base + offset;
+
+ glGetBufferParameteriv(
+ (GLenum)target,
+ (GLenum)pname,
+ (GLint *)params
+ );
+
+exit:
+ if (params_base) {
+ _env->ReleasePrimitiveArrayCritical(params_ref, params_base,
+ _exception ? JNI_ABORT: 0);
+ }
}
/* void glGetBufferParameteriv ( GLenum target, GLenum pname, GLint *params ) */
static void
android_glGetBufferParameteriv__IILjava_nio_IntBuffer_2
(JNIEnv *_env, jobject _this, jint target, jint pname, jobject params_buf) {
- _env->ThrowNew(UOEClass,
- "glGetBufferParameteriv");
+ jint _exception = 0;
+ jarray _array = (jarray) 0;
+ jint _remaining;
+ GLint *params = (GLint *) 0;
+
+ params = (GLint *)getPointer(_env, params_buf, &_array, &_remaining);
+ if (_remaining < 1) {
+ _exception = 1;
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 1");
+ goto exit;
+ }
+ glGetBufferParameteriv(
+ (GLenum)target,
+ (GLenum)pname,
+ (GLint *)params
+ );
+
+exit:
+ if (_array) {
+ releasePointer(_env, _array, params, _exception ? JNI_FALSE : JNI_TRUE);
+ }
}
/* void glGetClipPlanef ( GLenum pname, GLfloat *eqn ) */
@@ -556,12 +587,12 @@ android_glGetClipPlanef__I_3FI
if (!eqn_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "eqn == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "eqn == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(eqn_ref) - offset;
@@ -611,12 +642,12 @@ android_glGetClipPlanex__I_3II
if (!eqn_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "eqn == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "eqn == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(eqn_ref) - offset;
@@ -666,12 +697,12 @@ android_glGetFixedv__I_3II
if (!params_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -721,12 +752,12 @@ android_glGetFloatv__I_3FI
if (!params_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -776,12 +807,12 @@ android_glGetLightfv__II_3FI
if (!params_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -829,7 +860,7 @@ android_glGetLightfv__II_3FI
}
if (_remaining < _needed) {
_exception = 1;
- _env->ThrowNew(IAEClass, "length - offset < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
goto exit;
}
params_base = (GLfloat *)
@@ -903,7 +934,7 @@ android_glGetLightfv__IILjava_nio_FloatBuffer_2
}
if (_remaining < _needed) {
_exception = 1;
- _env->ThrowNew(IAEClass, "remaining() < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
goto exit;
}
glGetLightfv(
@@ -929,12 +960,12 @@ android_glGetLightxv__II_3II
if (!params_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -982,7 +1013,7 @@ android_glGetLightxv__II_3II
}
if (_remaining < _needed) {
_exception = 1;
- _env->ThrowNew(IAEClass, "length - offset < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
goto exit;
}
params_base = (GLfixed *)
@@ -1056,7 +1087,7 @@ android_glGetLightxv__IILjava_nio_IntBuffer_2
}
if (_remaining < _needed) {
_exception = 1;
- _env->ThrowNew(IAEClass, "remaining() < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
goto exit;
}
glGetLightxv(
@@ -1082,12 +1113,12 @@ android_glGetMaterialfv__II_3FI
if (!params_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -1121,7 +1152,7 @@ android_glGetMaterialfv__II_3FI
}
if (_remaining < _needed) {
_exception = 1;
- _env->ThrowNew(IAEClass, "length - offset < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
goto exit;
}
params_base = (GLfloat *)
@@ -1181,7 +1212,7 @@ android_glGetMaterialfv__IILjava_nio_FloatBuffer_2
}
if (_remaining < _needed) {
_exception = 1;
- _env->ThrowNew(IAEClass, "remaining() < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
goto exit;
}
glGetMaterialfv(
@@ -1207,12 +1238,12 @@ android_glGetMaterialxv__II_3II
if (!params_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -1246,7 +1277,7 @@ android_glGetMaterialxv__II_3II
}
if (_remaining < _needed) {
_exception = 1;
- _env->ThrowNew(IAEClass, "length - offset < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
goto exit;
}
params_base = (GLfixed *)
@@ -1306,7 +1337,7 @@ android_glGetMaterialxv__IILjava_nio_IntBuffer_2
}
if (_remaining < _needed) {
_exception = 1;
- _env->ThrowNew(IAEClass, "remaining() < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
goto exit;
}
glGetMaterialxv(
@@ -1332,12 +1363,12 @@ android_glGetTexEnvfv__II_3FI
if (!params_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -1365,7 +1396,7 @@ android_glGetTexEnvfv__II_3FI
}
if (_remaining < _needed) {
_exception = 1;
- _env->ThrowNew(IAEClass, "length - offset < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
goto exit;
}
params_base = (GLfloat *)
@@ -1419,7 +1450,7 @@ android_glGetTexEnvfv__IILjava_nio_FloatBuffer_2
}
if (_remaining < _needed) {
_exception = 1;
- _env->ThrowNew(IAEClass, "remaining() < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
goto exit;
}
glGetTexEnvfv(
@@ -1445,12 +1476,12 @@ android_glGetTexEnviv__II_3II
if (!params_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -1478,7 +1509,7 @@ android_glGetTexEnviv__II_3II
}
if (_remaining < _needed) {
_exception = 1;
- _env->ThrowNew(IAEClass, "length - offset < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
goto exit;
}
params_base = (GLint *)
@@ -1532,7 +1563,7 @@ android_glGetTexEnviv__IILjava_nio_IntBuffer_2
}
if (_remaining < _needed) {
_exception = 1;
- _env->ThrowNew(IAEClass, "remaining() < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
goto exit;
}
glGetTexEnviv(
@@ -1558,12 +1589,12 @@ android_glGetTexEnvxv__II_3II
if (!params_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -1591,7 +1622,7 @@ android_glGetTexEnvxv__II_3II
}
if (_remaining < _needed) {
_exception = 1;
- _env->ThrowNew(IAEClass, "length - offset < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
goto exit;
}
params_base = (GLfixed *)
@@ -1645,7 +1676,7 @@ android_glGetTexEnvxv__IILjava_nio_IntBuffer_2
}
if (_remaining < _needed) {
_exception = 1;
- _env->ThrowNew(IAEClass, "remaining() < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
goto exit;
}
glGetTexEnvxv(
@@ -1671,18 +1702,18 @@ android_glGetTexParameterfv__II_3FI
if (!params_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
if (_remaining < 1) {
_exception = 1;
- _env->ThrowNew(IAEClass, "length - offset < 1");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < 1");
goto exit;
}
params_base = (GLfloat *)
@@ -1714,7 +1745,7 @@ android_glGetTexParameterfv__IILjava_nio_FloatBuffer_2
params = (GLfloat *)getPointer(_env, params_buf, &_array, &_remaining);
if (_remaining < 1) {
_exception = 1;
- _env->ThrowNew(IAEClass, "remaining() < 1");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 1");
goto exit;
}
glGetTexParameterfv(
@@ -1740,18 +1771,18 @@ android_glGetTexParameteriv__II_3II
if (!params_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
if (_remaining < 1) {
_exception = 1;
- _env->ThrowNew(IAEClass, "length - offset < 1");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < 1");
goto exit;
}
params_base = (GLint *)
@@ -1783,7 +1814,7 @@ android_glGetTexParameteriv__IILjava_nio_IntBuffer_2
params = (GLint *)getPointer(_env, params_buf, &_array, &_remaining);
if (_remaining < 1) {
_exception = 1;
- _env->ThrowNew(IAEClass, "remaining() < 1");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 1");
goto exit;
}
glGetTexParameteriv(
@@ -1809,18 +1840,18 @@ android_glGetTexParameterxv__II_3II
if (!params_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
if (_remaining < 1) {
_exception = 1;
- _env->ThrowNew(IAEClass, "length - offset < 1");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < 1");
goto exit;
}
params_base = (GLfixed *)
@@ -1852,7 +1883,7 @@ android_glGetTexParameterxv__IILjava_nio_IntBuffer_2
params = (GLfixed *)getPointer(_env, params_buf, &_array, &_remaining);
if (_remaining < 1) {
_exception = 1;
- _env->ThrowNew(IAEClass, "remaining() < 1");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 1");
goto exit;
}
glGetTexParameterxv(
@@ -1930,16 +1961,16 @@ android_glPointParameterfv__I_3FI
GLfloat *params = (GLfloat *) 0;
if (!params_ref) {
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
if (_remaining < 1) {
- _env->ThrowNew(IAEClass, "length - offset < 1");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < 1");
goto exit;
}
params_base = (GLfloat *)
@@ -1968,7 +1999,7 @@ android_glPointParameterfv__ILjava_nio_FloatBuffer_2
params = (GLfloat *)getPointer(_env, params_buf, &_array, &_remaining);
if (_remaining < 1) {
- _env->ThrowNew(IAEClass, "remaining() < 1");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 1");
goto exit;
}
glPointParameterfv(
@@ -2001,16 +2032,16 @@ android_glPointParameterxv__I_3II
GLfixed *params = (GLfixed *) 0;
if (!params_ref) {
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
if (_remaining < 1) {
- _env->ThrowNew(IAEClass, "length - offset < 1");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < 1");
goto exit;
}
params_base = (GLfixed *)
@@ -2039,7 +2070,7 @@ android_glPointParameterxv__ILjava_nio_IntBuffer_2
params = (GLfixed *)getPointer(_env, params_buf, &_array, &_remaining);
if (_remaining < 1) {
- _env->ThrowNew(IAEClass, "remaining() < 1");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 1");
goto exit;
}
glPointParameterxv(
@@ -2107,11 +2138,11 @@ android_glTexEnviv__II_3II
GLint *params = (GLint *) 0;
if (!params_ref) {
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -2138,7 +2169,7 @@ android_glTexEnviv__II_3II
break;
}
if (_remaining < _needed) {
- _env->ThrowNew(IAEClass, "length - offset < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
goto exit;
}
params_base = (GLint *)
@@ -2190,7 +2221,7 @@ android_glTexEnviv__IILjava_nio_IntBuffer_2
break;
}
if (_remaining < _needed) {
- _env->ThrowNew(IAEClass, "remaining() < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
goto exit;
}
glTexEnviv(
@@ -2214,16 +2245,16 @@ android_glTexParameterfv__II_3FI
GLfloat *params = (GLfloat *) 0;
if (!params_ref) {
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
if (_remaining < 1) {
- _env->ThrowNew(IAEClass, "length - offset < 1");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < 1");
goto exit;
}
params_base = (GLfloat *)
@@ -2253,7 +2284,7 @@ android_glTexParameterfv__IILjava_nio_FloatBuffer_2
params = (GLfloat *)getPointer(_env, params_buf, &_array, &_remaining);
if (_remaining < 1) {
- _env->ThrowNew(IAEClass, "remaining() < 1");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 1");
goto exit;
}
glTexParameterfv(
@@ -2288,16 +2319,16 @@ android_glTexParameteriv__II_3II
GLint *params = (GLint *) 0;
if (!params_ref) {
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
if (_remaining < 1) {
- _env->ThrowNew(IAEClass, "length - offset < 1");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < 1");
goto exit;
}
params_base = (GLint *)
@@ -2327,7 +2358,7 @@ android_glTexParameteriv__IILjava_nio_IntBuffer_2
params = (GLint *)getPointer(_env, params_buf, &_array, &_remaining);
if (_remaining < 1) {
- _env->ThrowNew(IAEClass, "remaining() < 1");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 1");
goto exit;
}
glTexParameteriv(
@@ -2351,16 +2382,16 @@ android_glTexParameterxv__II_3II
GLfixed *params = (GLfixed *) 0;
if (!params_ref) {
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
if (_remaining < 1) {
- _env->ThrowNew(IAEClass, "length - offset < 1");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < 1");
goto exit;
}
params_base = (GLfixed *)
@@ -2390,7 +2421,7 @@ android_glTexParameterxv__IILjava_nio_IntBuffer_2
params = (GLfixed *)getPointer(_env, params_buf, &_array, &_remaining);
if (_remaining < 1) {
- _env->ThrowNew(IAEClass, "remaining() < 1");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 1");
goto exit;
}
glTexParameterxv(
diff --git a/core/jni/android_opengl_GLES11Ext.cpp b/core/jni/android_opengl_GLES11Ext.cpp
index 942a0d9..d6dc0fe 100644
--- a/core/jni/android_opengl_GLES11Ext.cpp
+++ b/core/jni/android_opengl_GLES11Ext.cpp
@@ -2,21 +2,23 @@
**
** 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
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
**
-** http://www.apache.org/licenses/LICENSE-2.0
+** http://www.apache.org/licenses/LICENSE-2.0
**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
** limitations under the License.
*/
// This source file is automatically generated
+#include "jni.h"
+#include "JNIHelp.h"
#include <android_runtime/AndroidRuntime.h>
#include <utils/misc.h>
@@ -37,10 +39,6 @@ static int initialized = 0;
static jclass nioAccessClass;
static jclass bufferClass;
-static jclass OOMEClass;
-static jclass UOEClass;
-static jclass IAEClass;
-static jclass AIOOBEClass;
static jmethodID getBasePointerID;
static jmethodID getBaseArrayID;
static jmethodID getBaseArrayOffsetID;
@@ -51,7 +49,7 @@ static jfieldID elementSizeShiftID;
/* Cache method IDs each time the class is loaded. */
static void
-nativeClassInitBuffer(JNIEnv *_env)
+nativeClassInit(JNIEnv *_env, jclass glImplClass)
{
jclass nioAccessClassLocal = _env->FindClass("java/nio/NIOAccess");
nioAccessClass = (jclass) _env->NewGlobalRef(nioAccessClassLocal);
@@ -73,26 +71,6 @@ nativeClassInitBuffer(JNIEnv *_env)
}
-static void
-nativeClassInit(JNIEnv *_env, jclass glImplClass)
-{
- nativeClassInitBuffer(_env);
-
- jclass IAEClassLocal =
- _env->FindClass("java/lang/IllegalArgumentException");
- jclass OOMEClassLocal =
- _env->FindClass("java/lang/OutOfMemoryError");
- jclass UOEClassLocal =
- _env->FindClass("java/lang/UnsupportedOperationException");
- jclass AIOOBEClassLocal =
- _env->FindClass("java/lang/ArrayIndexOutOfBoundsException");
-
- IAEClass = (jclass) _env->NewGlobalRef(IAEClassLocal);
- OOMEClass = (jclass) _env->NewGlobalRef(OOMEClassLocal);
- UOEClass = (jclass) _env->NewGlobalRef(UOEClassLocal);
- AIOOBEClass = (jclass) _env->NewGlobalRef(AIOOBEClassLocal);
-}
-
static void *
getPointer(JNIEnv *_env, jobject buffer, jarray *array, jint *remaining)
{
@@ -113,13 +91,13 @@ getPointer(JNIEnv *_env, jobject buffer, jarray *array, jint *remaining)
*array = NULL;
return (void *) (jint) pointer;
}
-
+
*array = (jarray) _env->CallStaticObjectMethod(nioAccessClass,
getBaseArrayID, buffer);
offset = _env->CallStaticIntMethod(nioAccessClass,
getBaseArrayOffsetID, buffer);
data = _env->GetPrimitiveArrayCritical(*array, (jboolean *) 0);
-
+
return (void *) ((char *) data + offset);
}
@@ -139,12 +117,12 @@ getDirectBufferPointer(JNIEnv *_env, jobject buffer) {
jint elementSizeShift = _env->GetIntField(buffer, elementSizeShiftID);
buf += position << elementSizeShift;
} else {
- _env->ThrowNew(IAEClass, "Must use a native order direct Buffer");
+ jniThrowException(_env, "java/lang/IllegalArgumentException",
+ "Must use a native order direct Buffer");
}
return (void*) buf;
}
// --------------------------------------------------------------------------
-
/* void glBlendEquationSeparateOES ( GLenum modeRGB, GLenum modeAlpha ) */
static void
android_glBlendEquationSeparateOES__II
@@ -224,16 +202,16 @@ android_glDrawTexsvOES___3SI
GLshort *coords = (GLshort *) 0;
if (!coords_ref) {
- _env->ThrowNew(IAEClass, "coords == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "coords == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(coords_ref) - offset;
if (_remaining < 5) {
- _env->ThrowNew(IAEClass, "length - offset < 5");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < 5");
goto exit;
}
coords_base = (GLshort *)
@@ -261,7 +239,7 @@ android_glDrawTexsvOES__Ljava_nio_ShortBuffer_2
coords = (GLshort *)getPointer(_env, coords_buf, &_array, &_remaining);
if (_remaining < 5) {
- _env->ThrowNew(IAEClass, "remaining() < 5");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 5");
goto exit;
}
glDrawTexsvOES(
@@ -283,16 +261,16 @@ android_glDrawTexivOES___3II
GLint *coords = (GLint *) 0;
if (!coords_ref) {
- _env->ThrowNew(IAEClass, "coords == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "coords == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(coords_ref) - offset;
if (_remaining < 5) {
- _env->ThrowNew(IAEClass, "length - offset < 5");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < 5");
goto exit;
}
coords_base = (GLint *)
@@ -320,7 +298,7 @@ android_glDrawTexivOES__Ljava_nio_IntBuffer_2
coords = (GLint *)getPointer(_env, coords_buf, &_array, &_remaining);
if (_remaining < 5) {
- _env->ThrowNew(IAEClass, "remaining() < 5");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 5");
goto exit;
}
glDrawTexivOES(
@@ -342,16 +320,16 @@ android_glDrawTexxvOES___3II
GLfixed *coords = (GLfixed *) 0;
if (!coords_ref) {
- _env->ThrowNew(IAEClass, "coords == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "coords == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(coords_ref) - offset;
if (_remaining < 5) {
- _env->ThrowNew(IAEClass, "length - offset < 5");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < 5");
goto exit;
}
coords_base = (GLfixed *)
@@ -379,7 +357,7 @@ android_glDrawTexxvOES__Ljava_nio_IntBuffer_2
coords = (GLfixed *)getPointer(_env, coords_buf, &_array, &_remaining);
if (_remaining < 5) {
- _env->ThrowNew(IAEClass, "remaining() < 5");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 5");
goto exit;
}
glDrawTexxvOES(
@@ -414,16 +392,16 @@ android_glDrawTexfvOES___3FI
GLfloat *coords = (GLfloat *) 0;
if (!coords_ref) {
- _env->ThrowNew(IAEClass, "coords == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "coords == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(coords_ref) - offset;
if (_remaining < 5) {
- _env->ThrowNew(IAEClass, "length - offset < 5");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < 5");
goto exit;
}
coords_base = (GLfloat *)
@@ -451,7 +429,7 @@ android_glDrawTexfvOES__Ljava_nio_FloatBuffer_2
coords = (GLfloat *)getPointer(_env, coords_buf, &_array, &_remaining);
if (_remaining < 5) {
- _env->ThrowNew(IAEClass, "remaining() < 5");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 5");
goto exit;
}
glDrawTexfvOES(
@@ -542,11 +520,11 @@ android_glClipPlanexOES__I_3II
GLfixed *equation = (GLfixed *) 0;
if (!equation_ref) {
- _env->ThrowNew(IAEClass, "equation == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "equation == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(equation_ref) - offset;
@@ -625,11 +603,11 @@ android_glFogxvOES__I_3II
GLfixed *params = (GLfixed *) 0;
if (!params_ref) {
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -692,18 +670,18 @@ android_glGetClipPlanexOES__I_3II
if (!eqn_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "eqn == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "eqn == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(eqn_ref) - offset;
if (_remaining < 4) {
_exception = 1;
- _env->ThrowNew(IAEClass, "length - offset < 4");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < 4");
goto exit;
}
eqn_base = (GLfixed *)
@@ -734,7 +712,7 @@ android_glGetClipPlanexOES__ILjava_nio_IntBuffer_2
eqn = (GLfixed *)getPointer(_env, eqn_buf, &_array, &_remaining);
if (_remaining < 4) {
_exception = 1;
- _env->ThrowNew(IAEClass, "remaining() < 4");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 4");
goto exit;
}
glGetClipPlanexOES(
@@ -759,12 +737,12 @@ android_glGetFixedvOES__I_3II
if (!params_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -814,12 +792,12 @@ android_glGetLightxvOES__II_3II
if (!params_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -871,12 +849,12 @@ android_glGetMaterialxvOES__II_3II
if (!params_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -928,12 +906,12 @@ android_glGetTexEnvxvOES__II_3II
if (!params_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -985,12 +963,12 @@ android_glGetTexParameterxvOES__II_3II
if (!params_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -1050,11 +1028,11 @@ android_glLightModelxvOES__I_3II
GLfixed *params = (GLfixed *) 0;
if (!params_ref) {
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -1112,11 +1090,11 @@ android_glLightxvOES__II_3II
GLfixed *params = (GLfixed *) 0;
if (!params_ref) {
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -1174,11 +1152,11 @@ android_glLoadMatrixxOES___3II
GLfixed *m = (GLfixed *) 0;
if (!m_ref) {
- _env->ThrowNew(IAEClass, "m == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "m == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(m_ref) - offset;
@@ -1234,11 +1212,11 @@ android_glMaterialxvOES__II_3II
GLfixed *params = (GLfixed *) 0;
if (!params_ref) {
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -1287,11 +1265,11 @@ android_glMultMatrixxOES___3II
GLfixed *m = (GLfixed *) 0;
if (!m_ref) {
- _env->ThrowNew(IAEClass, "m == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "m == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(m_ref) - offset;
@@ -1384,11 +1362,11 @@ android_glPointParameterxvOES__I_3II
GLfixed *params = (GLfixed *) 0;
if (!params_ref) {
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -1498,11 +1476,11 @@ android_glTexEnvxvOES__II_3II
GLfixed *params = (GLfixed *) 0;
if (!params_ref) {
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -1562,11 +1540,11 @@ android_glTexParameterxvOES__II_3II
GLfixed *params = (GLfixed *) 0;
if (!params_ref) {
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -1621,171 +1599,495 @@ android_glTranslatexOES__III
static jboolean
android_glIsRenderbufferOES__I
(JNIEnv *_env, jobject _this, jint renderbuffer) {
- _env->ThrowNew(UOEClass,
- "glIsRenderbufferOES");
- return JNI_FALSE;
+ GLboolean _returnValue;
+ _returnValue = glIsRenderbufferOES(
+ (GLuint)renderbuffer
+ );
+ return _returnValue;
}
/* void glBindRenderbufferOES ( GLenum target, GLuint renderbuffer ) */
static void
android_glBindRenderbufferOES__II
(JNIEnv *_env, jobject _this, jint target, jint renderbuffer) {
- _env->ThrowNew(UOEClass,
- "glBindRenderbufferOES");
+ glBindRenderbufferOES(
+ (GLenum)target,
+ (GLuint)renderbuffer
+ );
}
/* void glDeleteRenderbuffersOES ( GLsizei n, const GLuint *renderbuffers ) */
static void
android_glDeleteRenderbuffersOES__I_3II
(JNIEnv *_env, jobject _this, jint n, jintArray renderbuffers_ref, jint offset) {
- _env->ThrowNew(UOEClass,
- "glDeleteRenderbuffersOES");
+ GLuint *renderbuffers_base = (GLuint *) 0;
+ jint _remaining;
+ GLuint *renderbuffers = (GLuint *) 0;
+
+ if (!renderbuffers_ref) {
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "renderbuffers == null");
+ goto exit;
+ }
+ if (offset < 0) {
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
+ goto exit;
+ }
+ _remaining = _env->GetArrayLength(renderbuffers_ref) - offset;
+ if (_remaining < n) {
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < n");
+ goto exit;
+ }
+ renderbuffers_base = (GLuint *)
+ _env->GetPrimitiveArrayCritical(renderbuffers_ref, (jboolean *)0);
+ renderbuffers = renderbuffers_base + offset;
+
+ glDeleteRenderbuffersOES(
+ (GLsizei)n,
+ (GLuint *)renderbuffers
+ );
+
+exit:
+ if (renderbuffers_base) {
+ _env->ReleasePrimitiveArrayCritical(renderbuffers_ref, renderbuffers_base,
+ JNI_ABORT);
+ }
}
/* void glDeleteRenderbuffersOES ( GLsizei n, const GLuint *renderbuffers ) */
static void
android_glDeleteRenderbuffersOES__ILjava_nio_IntBuffer_2
(JNIEnv *_env, jobject _this, jint n, jobject renderbuffers_buf) {
- _env->ThrowNew(UOEClass,
- "glDeleteRenderbuffersOES");
+ jarray _array = (jarray) 0;
+ jint _remaining;
+ GLuint *renderbuffers = (GLuint *) 0;
+
+ renderbuffers = (GLuint *)getPointer(_env, renderbuffers_buf, &_array, &_remaining);
+ if (_remaining < n) {
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < n");
+ goto exit;
+ }
+ glDeleteRenderbuffersOES(
+ (GLsizei)n,
+ (GLuint *)renderbuffers
+ );
+
+exit:
+ if (_array) {
+ releasePointer(_env, _array, renderbuffers, JNI_FALSE);
+ }
}
/* void glGenRenderbuffersOES ( GLsizei n, GLuint *renderbuffers ) */
static void
android_glGenRenderbuffersOES__I_3II
(JNIEnv *_env, jobject _this, jint n, jintArray renderbuffers_ref, jint offset) {
- _env->ThrowNew(UOEClass,
- "glGenRenderbuffersOES");
+ jint _exception = 0;
+ GLuint *renderbuffers_base = (GLuint *) 0;
+ jint _remaining;
+ GLuint *renderbuffers = (GLuint *) 0;
+
+ if (!renderbuffers_ref) {
+ _exception = 1;
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "renderbuffers == null");
+ goto exit;
+ }
+ if (offset < 0) {
+ _exception = 1;
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
+ goto exit;
+ }
+ _remaining = _env->GetArrayLength(renderbuffers_ref) - offset;
+ if (_remaining < n) {
+ _exception = 1;
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < n");
+ goto exit;
+ }
+ renderbuffers_base = (GLuint *)
+ _env->GetPrimitiveArrayCritical(renderbuffers_ref, (jboolean *)0);
+ renderbuffers = renderbuffers_base + offset;
+
+ glGenRenderbuffersOES(
+ (GLsizei)n,
+ (GLuint *)renderbuffers
+ );
+
+exit:
+ if (renderbuffers_base) {
+ _env->ReleasePrimitiveArrayCritical(renderbuffers_ref, renderbuffers_base,
+ _exception ? JNI_ABORT: 0);
+ }
}
/* void glGenRenderbuffersOES ( GLsizei n, GLuint *renderbuffers ) */
static void
android_glGenRenderbuffersOES__ILjava_nio_IntBuffer_2
(JNIEnv *_env, jobject _this, jint n, jobject renderbuffers_buf) {
- _env->ThrowNew(UOEClass,
- "glGenRenderbuffersOES");
+ jint _exception = 0;
+ jarray _array = (jarray) 0;
+ jint _remaining;
+ GLuint *renderbuffers = (GLuint *) 0;
+
+ renderbuffers = (GLuint *)getPointer(_env, renderbuffers_buf, &_array, &_remaining);
+ if (_remaining < n) {
+ _exception = 1;
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < n");
+ goto exit;
+ }
+ glGenRenderbuffersOES(
+ (GLsizei)n,
+ (GLuint *)renderbuffers
+ );
+
+exit:
+ if (_array) {
+ releasePointer(_env, _array, renderbuffers, _exception ? JNI_FALSE : JNI_TRUE);
+ }
}
/* void glRenderbufferStorageOES ( GLenum target, GLenum internalformat, GLsizei width, GLsizei height ) */
static void
android_glRenderbufferStorageOES__IIII
(JNIEnv *_env, jobject _this, jint target, jint internalformat, jint width, jint height) {
- _env->ThrowNew(UOEClass,
- "glRenderbufferStorageOES");
+ glRenderbufferStorageOES(
+ (GLenum)target,
+ (GLenum)internalformat,
+ (GLsizei)width,
+ (GLsizei)height
+ );
}
/* void glGetRenderbufferParameterivOES ( GLenum target, GLenum pname, GLint *params ) */
static void
android_glGetRenderbufferParameterivOES__II_3II
(JNIEnv *_env, jobject _this, jint target, jint pname, jintArray params_ref, jint offset) {
- _env->ThrowNew(UOEClass,
- "glGetRenderbufferParameterivOES");
+ jint _exception = 0;
+ GLint *params_base = (GLint *) 0;
+ jint _remaining;
+ GLint *params = (GLint *) 0;
+
+ if (!params_ref) {
+ _exception = 1;
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
+ goto exit;
+ }
+ if (offset < 0) {
+ _exception = 1;
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
+ goto exit;
+ }
+ _remaining = _env->GetArrayLength(params_ref) - offset;
+ if (_remaining < 1) {
+ _exception = 1;
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < 1");
+ goto exit;
+ }
+ params_base = (GLint *)
+ _env->GetPrimitiveArrayCritical(params_ref, (jboolean *)0);
+ params = params_base + offset;
+
+ glGetRenderbufferParameterivOES(
+ (GLenum)target,
+ (GLenum)pname,
+ (GLint *)params
+ );
+
+exit:
+ if (params_base) {
+ _env->ReleasePrimitiveArrayCritical(params_ref, params_base,
+ _exception ? JNI_ABORT: 0);
+ }
}
/* void glGetRenderbufferParameterivOES ( GLenum target, GLenum pname, GLint *params ) */
static void
android_glGetRenderbufferParameterivOES__IILjava_nio_IntBuffer_2
(JNIEnv *_env, jobject _this, jint target, jint pname, jobject params_buf) {
- _env->ThrowNew(UOEClass,
- "glGetRenderbufferParameterivOES");
+ jint _exception = 0;
+ jarray _array = (jarray) 0;
+ jint _remaining;
+ GLint *params = (GLint *) 0;
+
+ params = (GLint *)getPointer(_env, params_buf, &_array, &_remaining);
+ if (_remaining < 1) {
+ _exception = 1;
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 1");
+ goto exit;
+ }
+ glGetRenderbufferParameterivOES(
+ (GLenum)target,
+ (GLenum)pname,
+ (GLint *)params
+ );
+
+exit:
+ if (_array) {
+ releasePointer(_env, _array, params, _exception ? JNI_FALSE : JNI_TRUE);
+ }
}
/* GLboolean glIsFramebufferOES ( GLuint framebuffer ) */
static jboolean
android_glIsFramebufferOES__I
(JNIEnv *_env, jobject _this, jint framebuffer) {
- _env->ThrowNew(UOEClass,
- "glIsFramebufferOES");
- return JNI_FALSE;
+ GLboolean _returnValue;
+ _returnValue = glIsFramebufferOES(
+ (GLuint)framebuffer
+ );
+ return _returnValue;
}
/* void glBindFramebufferOES ( GLenum target, GLuint framebuffer ) */
static void
android_glBindFramebufferOES__II
(JNIEnv *_env, jobject _this, jint target, jint framebuffer) {
- _env->ThrowNew(UOEClass,
- "glBindFramebufferOES");
+ glBindFramebufferOES(
+ (GLenum)target,
+ (GLuint)framebuffer
+ );
}
/* void glDeleteFramebuffersOES ( GLsizei n, const GLuint *framebuffers ) */
static void
android_glDeleteFramebuffersOES__I_3II
(JNIEnv *_env, jobject _this, jint n, jintArray framebuffers_ref, jint offset) {
- _env->ThrowNew(UOEClass,
- "glDeleteFramebuffersOES");
+ GLuint *framebuffers_base = (GLuint *) 0;
+ jint _remaining;
+ GLuint *framebuffers = (GLuint *) 0;
+
+ if (!framebuffers_ref) {
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "framebuffers == null");
+ goto exit;
+ }
+ if (offset < 0) {
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
+ goto exit;
+ }
+ _remaining = _env->GetArrayLength(framebuffers_ref) - offset;
+ if (_remaining < n) {
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < n");
+ goto exit;
+ }
+ framebuffers_base = (GLuint *)
+ _env->GetPrimitiveArrayCritical(framebuffers_ref, (jboolean *)0);
+ framebuffers = framebuffers_base + offset;
+
+ glDeleteFramebuffersOES(
+ (GLsizei)n,
+ (GLuint *)framebuffers
+ );
+
+exit:
+ if (framebuffers_base) {
+ _env->ReleasePrimitiveArrayCritical(framebuffers_ref, framebuffers_base,
+ JNI_ABORT);
+ }
}
/* void glDeleteFramebuffersOES ( GLsizei n, const GLuint *framebuffers ) */
static void
android_glDeleteFramebuffersOES__ILjava_nio_IntBuffer_2
(JNIEnv *_env, jobject _this, jint n, jobject framebuffers_buf) {
- _env->ThrowNew(UOEClass,
- "glDeleteFramebuffersOES");
+ jarray _array = (jarray) 0;
+ jint _remaining;
+ GLuint *framebuffers = (GLuint *) 0;
+
+ framebuffers = (GLuint *)getPointer(_env, framebuffers_buf, &_array, &_remaining);
+ if (_remaining < n) {
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < n");
+ goto exit;
+ }
+ glDeleteFramebuffersOES(
+ (GLsizei)n,
+ (GLuint *)framebuffers
+ );
+
+exit:
+ if (_array) {
+ releasePointer(_env, _array, framebuffers, JNI_FALSE);
+ }
}
/* void glGenFramebuffersOES ( GLsizei n, GLuint *framebuffers ) */
static void
android_glGenFramebuffersOES__I_3II
(JNIEnv *_env, jobject _this, jint n, jintArray framebuffers_ref, jint offset) {
- _env->ThrowNew(UOEClass,
- "glGenFramebuffersOES");
+ jint _exception = 0;
+ GLuint *framebuffers_base = (GLuint *) 0;
+ jint _remaining;
+ GLuint *framebuffers = (GLuint *) 0;
+
+ if (!framebuffers_ref) {
+ _exception = 1;
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "framebuffers == null");
+ goto exit;
+ }
+ if (offset < 0) {
+ _exception = 1;
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
+ goto exit;
+ }
+ _remaining = _env->GetArrayLength(framebuffers_ref) - offset;
+ if (_remaining < n) {
+ _exception = 1;
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < n");
+ goto exit;
+ }
+ framebuffers_base = (GLuint *)
+ _env->GetPrimitiveArrayCritical(framebuffers_ref, (jboolean *)0);
+ framebuffers = framebuffers_base + offset;
+
+ glGenFramebuffersOES(
+ (GLsizei)n,
+ (GLuint *)framebuffers
+ );
+
+exit:
+ if (framebuffers_base) {
+ _env->ReleasePrimitiveArrayCritical(framebuffers_ref, framebuffers_base,
+ _exception ? JNI_ABORT: 0);
+ }
}
/* void glGenFramebuffersOES ( GLsizei n, GLuint *framebuffers ) */
static void
android_glGenFramebuffersOES__ILjava_nio_IntBuffer_2
(JNIEnv *_env, jobject _this, jint n, jobject framebuffers_buf) {
- _env->ThrowNew(UOEClass,
- "glGenFramebuffersOES");
+ jint _exception = 0;
+ jarray _array = (jarray) 0;
+ jint _remaining;
+ GLuint *framebuffers = (GLuint *) 0;
+
+ framebuffers = (GLuint *)getPointer(_env, framebuffers_buf, &_array, &_remaining);
+ if (_remaining < n) {
+ _exception = 1;
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < n");
+ goto exit;
+ }
+ glGenFramebuffersOES(
+ (GLsizei)n,
+ (GLuint *)framebuffers
+ );
+
+exit:
+ if (_array) {
+ releasePointer(_env, _array, framebuffers, _exception ? JNI_FALSE : JNI_TRUE);
+ }
}
/* GLenum glCheckFramebufferStatusOES ( GLenum target ) */
static jint
android_glCheckFramebufferStatusOES__I
(JNIEnv *_env, jobject _this, jint target) {
- _env->ThrowNew(UOEClass,
- "glCheckFramebufferStatusOES");
- return 0;
+ GLenum _returnValue;
+ _returnValue = glCheckFramebufferStatusOES(
+ (GLenum)target
+ );
+ return _returnValue;
}
/* void glFramebufferRenderbufferOES ( GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer ) */
static void
android_glFramebufferRenderbufferOES__IIII
(JNIEnv *_env, jobject _this, jint target, jint attachment, jint renderbuffertarget, jint renderbuffer) {
- _env->ThrowNew(UOEClass,
- "glFramebufferRenderbufferOES");
+ glFramebufferRenderbufferOES(
+ (GLenum)target,
+ (GLenum)attachment,
+ (GLenum)renderbuffertarget,
+ (GLuint)renderbuffer
+ );
}
/* void glFramebufferTexture2DOES ( GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level ) */
static void
android_glFramebufferTexture2DOES__IIIII
(JNIEnv *_env, jobject _this, jint target, jint attachment, jint textarget, jint texture, jint level) {
- _env->ThrowNew(UOEClass,
- "glFramebufferTexture2DOES");
+ glFramebufferTexture2DOES(
+ (GLenum)target,
+ (GLenum)attachment,
+ (GLenum)textarget,
+ (GLuint)texture,
+ (GLint)level
+ );
}
/* void glGetFramebufferAttachmentParameterivOES ( GLenum target, GLenum attachment, GLenum pname, GLint *params ) */
static void
android_glGetFramebufferAttachmentParameterivOES__III_3II
(JNIEnv *_env, jobject _this, jint target, jint attachment, jint pname, jintArray params_ref, jint offset) {
- _env->ThrowNew(UOEClass,
- "glGetFramebufferAttachmentParameterivOES");
+ jint _exception = 0;
+ GLint *params_base = (GLint *) 0;
+ jint _remaining;
+ GLint *params = (GLint *) 0;
+
+ if (!params_ref) {
+ _exception = 1;
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
+ goto exit;
+ }
+ if (offset < 0) {
+ _exception = 1;
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
+ goto exit;
+ }
+ _remaining = _env->GetArrayLength(params_ref) - offset;
+ if (_remaining < 1) {
+ _exception = 1;
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < 1");
+ goto exit;
+ }
+ params_base = (GLint *)
+ _env->GetPrimitiveArrayCritical(params_ref, (jboolean *)0);
+ params = params_base + offset;
+
+ glGetFramebufferAttachmentParameterivOES(
+ (GLenum)target,
+ (GLenum)attachment,
+ (GLenum)pname,
+ (GLint *)params
+ );
+
+exit:
+ if (params_base) {
+ _env->ReleasePrimitiveArrayCritical(params_ref, params_base,
+ _exception ? JNI_ABORT: 0);
+ }
}
/* void glGetFramebufferAttachmentParameterivOES ( GLenum target, GLenum attachment, GLenum pname, GLint *params ) */
static void
android_glGetFramebufferAttachmentParameterivOES__IIILjava_nio_IntBuffer_2
(JNIEnv *_env, jobject _this, jint target, jint attachment, jint pname, jobject params_buf) {
- _env->ThrowNew(UOEClass,
- "glGetFramebufferAttachmentParameterivOES");
+ jint _exception = 0;
+ jarray _array = (jarray) 0;
+ jint _remaining;
+ GLint *params = (GLint *) 0;
+
+ params = (GLint *)getPointer(_env, params_buf, &_array, &_remaining);
+ if (_remaining < 1) {
+ _exception = 1;
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 1");
+ goto exit;
+ }
+ glGetFramebufferAttachmentParameterivOES(
+ (GLenum)target,
+ (GLenum)attachment,
+ (GLenum)pname,
+ (GLint *)params
+ );
+
+exit:
+ if (_array) {
+ releasePointer(_env, _array, params, _exception ? JNI_FALSE : JNI_TRUE);
+ }
}
/* void glGenerateMipmapOES ( GLenum target ) */
static void
android_glGenerateMipmapOES__I
(JNIEnv *_env, jobject _this, jint target) {
- _env->ThrowNew(UOEClass,
- "glGenerateMipmapOES");
+ glGenerateMipmapOES(
+ (GLenum)target
+ );
}
/* void glCurrentPaletteMatrixOES ( GLuint matrixpaletteindex ) */
@@ -1897,11 +2199,11 @@ android_glClipPlanefOES__I_3FI
GLfloat *equation = (GLfloat *) 0;
if (!equation_ref) {
- _env->ThrowNew(IAEClass, "equation == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "equation == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(equation_ref) - offset;
@@ -1950,18 +2252,18 @@ android_glGetClipPlanefOES__I_3FI
if (!eqn_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "eqn == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "eqn == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(eqn_ref) - offset;
if (_remaining < 4) {
_exception = 1;
- _env->ThrowNew(IAEClass, "length - offset < 4");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < 4");
goto exit;
}
eqn_base = (GLfloat *)
@@ -1992,7 +2294,7 @@ android_glGetClipPlanefOES__ILjava_nio_FloatBuffer_2
eqn = (GLfloat *)getPointer(_env, eqn_buf, &_array, &_remaining);
if (_remaining < 4) {
_exception = 1;
- _env->ThrowNew(IAEClass, "remaining() < 4");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 4");
goto exit;
}
glGetClipPlanefOES(
@@ -2035,11 +2337,11 @@ android_glTexGenfvOES__II_3FI
GLfloat *params = (GLfloat *) 0;
if (!params_ref) {
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -2099,11 +2401,11 @@ android_glTexGenivOES__II_3II
GLint *params = (GLint *) 0;
if (!params_ref) {
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -2163,11 +2465,11 @@ android_glTexGenxvOES__II_3II
GLfixed *params = (GLfixed *) 0;
if (!params_ref) {
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -2218,12 +2520,12 @@ android_glGetTexGenfvOES__II_3FI
if (!params_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -2275,12 +2577,12 @@ android_glGetTexGenivOES__II_3II
if (!params_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -2332,12 +2634,12 @@ android_glGetTexGenxvOES__II_3II
if (!params_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
diff --git a/core/jni/android_opengl_GLES20.cpp b/core/jni/android_opengl_GLES20.cpp
index ef25319..a53e4d7 100644
--- a/core/jni/android_opengl_GLES20.cpp
+++ b/core/jni/android_opengl_GLES20.cpp
@@ -2,21 +2,23 @@
**
** 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
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
**
-** http://www.apache.org/licenses/LICENSE-2.0
+** http://www.apache.org/licenses/LICENSE-2.0
**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
** limitations under the License.
*/
// This source file is automatically generated
+#include "jni.h"
+#include "JNIHelp.h"
#include <android_runtime/AndroidRuntime.h>
#include <utils/misc.h>
@@ -28,10 +30,6 @@ static int initialized = 0;
static jclass nioAccessClass;
static jclass bufferClass;
-static jclass OOMEClass;
-static jclass UOEClass;
-static jclass IAEClass;
-static jclass AIOOBEClass;
static jmethodID getBasePointerID;
static jmethodID getBaseArrayID;
static jmethodID getBaseArrayOffsetID;
@@ -42,7 +40,7 @@ static jfieldID elementSizeShiftID;
/* Cache method IDs each time the class is loaded. */
static void
-nativeClassInitBuffer(JNIEnv *_env)
+nativeClassInit(JNIEnv *_env, jclass glImplClass)
{
jclass nioAccessClassLocal = _env->FindClass("java/nio/NIOAccess");
nioAccessClass = (jclass) _env->NewGlobalRef(nioAccessClassLocal);
@@ -64,26 +62,6 @@ nativeClassInitBuffer(JNIEnv *_env)
}
-static void
-nativeClassInit(JNIEnv *_env, jclass glImplClass)
-{
- nativeClassInitBuffer(_env);
-
- jclass IAEClassLocal =
- _env->FindClass("java/lang/IllegalArgumentException");
- jclass OOMEClassLocal =
- _env->FindClass("java/lang/OutOfMemoryError");
- jclass UOEClassLocal =
- _env->FindClass("java/lang/UnsupportedOperationException");
- jclass AIOOBEClassLocal =
- _env->FindClass("java/lang/ArrayIndexOutOfBoundsException");
-
- IAEClass = (jclass) _env->NewGlobalRef(IAEClassLocal);
- OOMEClass = (jclass) _env->NewGlobalRef(OOMEClassLocal);
- UOEClass = (jclass) _env->NewGlobalRef(UOEClassLocal);
- AIOOBEClass = (jclass) _env->NewGlobalRef(AIOOBEClassLocal);
-}
-
static void *
getPointer(JNIEnv *_env, jobject buffer, jarray *array, jint *remaining)
{
@@ -104,13 +82,13 @@ getPointer(JNIEnv *_env, jobject buffer, jarray *array, jint *remaining)
*array = NULL;
return (void *) (jint) pointer;
}
-
+
*array = (jarray) _env->CallStaticObjectMethod(nioAccessClass,
getBaseArrayID, buffer);
offset = _env->CallStaticIntMethod(nioAccessClass,
getBaseArrayOffsetID, buffer);
data = _env->GetPrimitiveArrayCritical(*array, (jboolean *) 0);
-
+
return (void *) ((char *) data + offset);
}
@@ -130,7 +108,8 @@ getDirectBufferPointer(JNIEnv *_env, jobject buffer) {
jint elementSizeShift = _env->GetIntField(buffer, elementSizeShiftID);
buf += position << elementSizeShift;
} else {
- _env->ThrowNew(IAEClass, "Must use a native order direct Buffer");
+ jniThrowException(_env, "java/lang/IllegalArgumentException",
+ "Must use a native order direct Buffer");
}
return (void*) buf;
}
@@ -148,7 +127,6 @@ static void glVertexAttribPointerBounds(GLuint indx, GLint size, GLenum type,
}
// --------------------------------------------------------------------------
-
/* void glActiveTexture ( GLenum texture ) */
static void
android_glActiveTexture__I
@@ -175,7 +153,7 @@ android_glBindAttribLocation__IILjava_lang_String_2
const char* _nativename = 0;
if (!name) {
- _env->ThrowNew(IAEClass, "name == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "name == null");
goto exit;
}
_nativename = _env->GetStringUTFChars(name, 0);
@@ -249,16 +227,19 @@ android_glBlendColor__FFFF
static void
android_glBlendEquation__I
(JNIEnv *_env, jobject _this, jint mode) {
- _env->ThrowNew(UOEClass,
- "glBlendEquation");
+ glBlendEquation(
+ (GLenum)mode
+ );
}
/* void glBlendEquationSeparate ( GLenum modeRGB, GLenum modeAlpha ) */
static void
android_glBlendEquationSeparate__II
(JNIEnv *_env, jobject _this, jint modeRGB, jint modeAlpha) {
- _env->ThrowNew(UOEClass,
- "glBlendEquationSeparate");
+ glBlendEquationSeparate(
+ (GLenum)modeRGB,
+ (GLenum)modeAlpha
+ );
}
/* void glBlendFunc ( GLenum sfactor, GLenum dfactor ) */
@@ -275,8 +256,12 @@ android_glBlendFunc__II
static void
android_glBlendFuncSeparate__IIII
(JNIEnv *_env, jobject _this, jint srcRGB, jint dstRGB, jint srcAlpha, jint dstAlpha) {
- _env->ThrowNew(UOEClass,
- "glBlendFuncSeparate");
+ glBlendFuncSeparate(
+ (GLenum)srcRGB,
+ (GLenum)dstRGB,
+ (GLenum)srcAlpha,
+ (GLenum)dstAlpha
+ );
}
/* void glBufferData ( GLenum target, GLsizeiptr size, const GLvoid *data, GLenum usage ) */
@@ -290,7 +275,7 @@ android_glBufferData__IILjava_nio_Buffer_2I
if (data_buf) {
data = (GLvoid *)getPointer(_env, data_buf, &_array, &_remaining);
if (_remaining < size) {
- _env->ThrowNew(IAEClass, "remaining() < size");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < size");
goto exit;
}
}
@@ -317,7 +302,7 @@ android_glBufferSubData__IIILjava_nio_Buffer_2
data = (GLvoid *)getPointer(_env, data_buf, &_array, &_remaining);
if (_remaining < size) {
- _env->ThrowNew(IAEClass, "remaining() < size");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < size");
goto exit;
}
glBufferSubData(
@@ -523,16 +508,16 @@ android_glDeleteBuffers__I_3II
GLuint *buffers = (GLuint *) 0;
if (!buffers_ref) {
- _env->ThrowNew(IAEClass, "buffers == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "buffers == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(buffers_ref) - offset;
if (_remaining < n) {
- _env->ThrowNew(IAEClass, "length - offset < n");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < n");
goto exit;
}
buffers_base = (GLuint *)
@@ -561,7 +546,7 @@ android_glDeleteBuffers__ILjava_nio_IntBuffer_2
buffers = (GLuint *)getPointer(_env, buffers_buf, &_array, &_remaining);
if (_remaining < n) {
- _env->ThrowNew(IAEClass, "remaining() < n");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < n");
goto exit;
}
glDeleteBuffers(
@@ -584,11 +569,11 @@ android_glDeleteFramebuffers__I_3II
GLuint *framebuffers = (GLuint *) 0;
if (!framebuffers_ref) {
- _env->ThrowNew(IAEClass, "framebuffers == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "framebuffers == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(framebuffers_ref) - offset;
@@ -644,11 +629,11 @@ android_glDeleteRenderbuffers__I_3II
GLuint *renderbuffers = (GLuint *) 0;
if (!renderbuffers_ref) {
- _env->ThrowNew(IAEClass, "renderbuffers == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "renderbuffers == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(renderbuffers_ref) - offset;
@@ -704,16 +689,16 @@ android_glDeleteTextures__I_3II
GLuint *textures = (GLuint *) 0;
if (!textures_ref) {
- _env->ThrowNew(IAEClass, "textures == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "textures == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(textures_ref) - offset;
if (_remaining < n) {
- _env->ThrowNew(IAEClass, "length - offset < n");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < n");
goto exit;
}
textures_base = (GLuint *)
@@ -742,7 +727,7 @@ android_glDeleteTextures__ILjava_nio_IntBuffer_2
textures = (GLuint *)getPointer(_env, textures_buf, &_array, &_remaining);
if (_remaining < n) {
- _env->ThrowNew(IAEClass, "remaining() < n");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < n");
goto exit;
}
glDeleteTextures(
@@ -845,7 +830,7 @@ android_glDrawElements__IIILjava_nio_Buffer_2
indices = (GLvoid *)getPointer(_env, indices_buf, &_array, &_remaining);
if (_remaining < count) {
- _env->ThrowNew(AIOOBEClass, "remaining() < count");
+ jniThrowException(_env, "java/lang/ArrayIndexOutOfBoundsException", "remaining() < count");
goto exit;
}
glDrawElements(
@@ -938,18 +923,18 @@ android_glGenBuffers__I_3II
if (!buffers_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "buffers == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "buffers == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(buffers_ref) - offset;
if (_remaining < n) {
_exception = 1;
- _env->ThrowNew(IAEClass, "length - offset < n");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < n");
goto exit;
}
buffers_base = (GLuint *)
@@ -980,7 +965,7 @@ android_glGenBuffers__ILjava_nio_IntBuffer_2
buffers = (GLuint *)getPointer(_env, buffers_buf, &_array, &_remaining);
if (_remaining < n) {
_exception = 1;
- _env->ThrowNew(IAEClass, "remaining() < n");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < n");
goto exit;
}
glGenBuffers(
@@ -1014,12 +999,12 @@ android_glGenFramebuffers__I_3II
if (!framebuffers_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "framebuffers == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "framebuffers == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(framebuffers_ref) - offset;
@@ -1069,12 +1054,12 @@ android_glGenRenderbuffers__I_3II
if (!renderbuffers_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "renderbuffers == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "renderbuffers == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(renderbuffers_ref) - offset;
@@ -1124,18 +1109,18 @@ android_glGenTextures__I_3II
if (!textures_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "textures == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "textures == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(textures_ref) - offset;
if (_remaining < n) {
_exception = 1;
- _env->ThrowNew(IAEClass, "length - offset < n");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < n");
goto exit;
}
textures_base = (GLuint *)
@@ -1166,7 +1151,7 @@ android_glGenTextures__ILjava_nio_IntBuffer_2
textures = (GLuint *)getPointer(_env, textures_buf, &_array, &_remaining);
if (_remaining < n) {
_exception = 1;
- _env->ThrowNew(IAEClass, "remaining() < n");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < n");
goto exit;
}
glGenTextures(
@@ -1200,12 +1185,12 @@ android_glGetActiveAttrib__III_3II_3II_3II_3BI
if (!length_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "length == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length == null");
goto exit;
}
if (lengthOffset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "lengthOffset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "lengthOffset < 0");
goto exit;
}
_lengthRemaining = _env->GetArrayLength(length_ref) - lengthOffset;
@@ -1215,12 +1200,12 @@ android_glGetActiveAttrib__III_3II_3II_3II_3BI
if (!size_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "size == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "size == null");
goto exit;
}
if (sizeOffset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "sizeOffset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "sizeOffset < 0");
goto exit;
}
_sizeRemaining = _env->GetArrayLength(size_ref) - sizeOffset;
@@ -1230,12 +1215,12 @@ android_glGetActiveAttrib__III_3II_3II_3II_3BI
if (!type_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "type == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "type == null");
goto exit;
}
if (typeOffset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "typeOffset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "typeOffset < 0");
goto exit;
}
_typeRemaining = _env->GetArrayLength(type_ref) - typeOffset;
@@ -1245,12 +1230,12 @@ android_glGetActiveAttrib__III_3II_3II_3II_3BI
if (!name_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "name == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "name == null");
goto exit;
}
if (nameOffset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "nameOffset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "nameOffset < 0");
goto exit;
}
_nameRemaining = _env->GetArrayLength(name_ref) - nameOffset;
@@ -1345,12 +1330,12 @@ android_glGetActiveUniform__III_3II_3II_3II_3BI
if (!length_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "length == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length == null");
goto exit;
}
if (lengthOffset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "lengthOffset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "lengthOffset < 0");
goto exit;
}
_lengthRemaining = _env->GetArrayLength(length_ref) - lengthOffset;
@@ -1360,12 +1345,12 @@ android_glGetActiveUniform__III_3II_3II_3II_3BI
if (!size_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "size == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "size == null");
goto exit;
}
if (sizeOffset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "sizeOffset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "sizeOffset < 0");
goto exit;
}
_sizeRemaining = _env->GetArrayLength(size_ref) - sizeOffset;
@@ -1375,12 +1360,12 @@ android_glGetActiveUniform__III_3II_3II_3II_3BI
if (!type_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "type == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "type == null");
goto exit;
}
if (typeOffset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "typeOffset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "typeOffset < 0");
goto exit;
}
_typeRemaining = _env->GetArrayLength(type_ref) - typeOffset;
@@ -1390,12 +1375,12 @@ android_glGetActiveUniform__III_3II_3II_3II_3BI
if (!name_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "name == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "name == null");
goto exit;
}
if (nameOffset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "nameOffset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "nameOffset < 0");
goto exit;
}
_nameRemaining = _env->GetArrayLength(name_ref) - nameOffset;
@@ -1484,12 +1469,12 @@ android_glGetAttachedShaders__II_3II_3II
if (!count_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "count == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "count == null");
goto exit;
}
if (countOffset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "countOffset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "countOffset < 0");
goto exit;
}
_countRemaining = _env->GetArrayLength(count_ref) - countOffset;
@@ -1499,12 +1484,12 @@ android_glGetAttachedShaders__II_3II_3II
if (!shaders_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "shaders == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "shaders == null");
goto exit;
}
if (shadersOffset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "shadersOffset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "shadersOffset < 0");
goto exit;
}
_shadersRemaining = _env->GetArrayLength(shaders_ref) - shadersOffset;
@@ -1566,7 +1551,7 @@ android_glGetAttribLocation__ILjava_lang_String_2
const char* _nativename = 0;
if (!name) {
- _env->ThrowNew(IAEClass, "name == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "name == null");
goto exit;
}
_nativename = _env->GetStringUTFChars(name, 0);
@@ -1595,12 +1580,12 @@ android_glGetBooleanv__I_3ZI
if (!params_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -1643,16 +1628,69 @@ android_glGetBooleanv__ILjava_nio_IntBuffer_2
static void
android_glGetBufferParameteriv__II_3II
(JNIEnv *_env, jobject _this, jint target, jint pname, jintArray params_ref, jint offset) {
- _env->ThrowNew(UOEClass,
- "glGetBufferParameteriv");
+ jint _exception = 0;
+ GLint *params_base = (GLint *) 0;
+ jint _remaining;
+ GLint *params = (GLint *) 0;
+
+ if (!params_ref) {
+ _exception = 1;
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
+ goto exit;
+ }
+ if (offset < 0) {
+ _exception = 1;
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
+ goto exit;
+ }
+ _remaining = _env->GetArrayLength(params_ref) - offset;
+ if (_remaining < 1) {
+ _exception = 1;
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < 1");
+ goto exit;
+ }
+ params_base = (GLint *)
+ _env->GetPrimitiveArrayCritical(params_ref, (jboolean *)0);
+ params = params_base + offset;
+
+ glGetBufferParameteriv(
+ (GLenum)target,
+ (GLenum)pname,
+ (GLint *)params
+ );
+
+exit:
+ if (params_base) {
+ _env->ReleasePrimitiveArrayCritical(params_ref, params_base,
+ _exception ? JNI_ABORT: 0);
+ }
}
/* void glGetBufferParameteriv ( GLenum target, GLenum pname, GLint *params ) */
static void
android_glGetBufferParameteriv__IILjava_nio_IntBuffer_2
(JNIEnv *_env, jobject _this, jint target, jint pname, jobject params_buf) {
- _env->ThrowNew(UOEClass,
- "glGetBufferParameteriv");
+ jint _exception = 0;
+ jarray _array = (jarray) 0;
+ jint _remaining;
+ GLint *params = (GLint *) 0;
+
+ params = (GLint *)getPointer(_env, params_buf, &_array, &_remaining);
+ if (_remaining < 1) {
+ _exception = 1;
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 1");
+ goto exit;
+ }
+ glGetBufferParameteriv(
+ (GLenum)target,
+ (GLenum)pname,
+ (GLint *)params
+ );
+
+exit:
+ if (_array) {
+ releasePointer(_env, _array, params, _exception ? JNI_FALSE : JNI_TRUE);
+ }
}
/* GLenum glGetError ( void ) */
@@ -1675,12 +1713,12 @@ android_glGetFloatv__I_3FI
if (!params_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -1730,12 +1768,12 @@ android_glGetFramebufferAttachmentParameteriv__III_3II
if (!params_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -1789,12 +1827,12 @@ android_glGetIntegerv__I_3II
if (!params_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -2131,7 +2169,7 @@ android_glGetIntegerv__I_3II
}
if (_remaining < _needed) {
_exception = 1;
- _env->ThrowNew(IAEClass, "length - offset < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
goto exit;
}
params_base = (GLint *)
@@ -2493,7 +2531,7 @@ android_glGetIntegerv__ILjava_nio_IntBuffer_2
}
if (_remaining < _needed) {
_exception = 1;
- _env->ThrowNew(IAEClass, "remaining() < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
goto exit;
}
glGetIntegerv(
@@ -2518,12 +2556,12 @@ android_glGetProgramiv__II_3II
if (!params_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -2564,32 +2602,24 @@ android_glGetProgramiv__IILjava_nio_IntBuffer_2
}
}
-#include <string.h>
+#include <stdlib.h>
/* void glGetProgramInfoLog ( GLuint shader, GLsizei maxLength, GLsizei* length, GLchar* infoLog ) */
-static
-jstring
-android_glGetProgramInfoLog (JNIEnv *_env, jobject _this, jint shader) {
+static jstring android_glGetProgramInfoLog(JNIEnv *_env, jobject, jint shader) {
GLint infoLen = 0;
- jstring _result = 0;
- char* buf = 0;
glGetProgramiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
- if (infoLen) {
- char* buf = (char*) malloc(infoLen);
- if (buf == 0) {
- _env->ThrowNew(IAEClass, "out of memory");
- goto exit;
- }
- glGetProgramInfoLog(shader, infoLen, NULL, buf);
- _result = _env->NewStringUTF(buf);
- } else {
- _result = _env->NewStringUTF("");
+ if (!infoLen) {
+ return _env->NewStringUTF("");
}
-exit:
- if (buf) {
- free(buf);
+ char* buf = (char*) malloc(infoLen);
+ if (buf == NULL) {
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "out of memory");
+ return NULL;
}
- return _result;
+ glGetProgramInfoLog(shader, infoLen, NULL, buf);
+ jstring result = _env->NewStringUTF(buf);
+ free(buf);
+ return result;
}
/* void glGetRenderbufferParameteriv ( GLenum target, GLenum pname, GLint *params ) */
static void
@@ -2602,12 +2632,12 @@ android_glGetRenderbufferParameteriv__II_3II
if (!params_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -2659,12 +2689,12 @@ android_glGetShaderiv__II_3II
if (!params_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -2705,32 +2735,24 @@ android_glGetShaderiv__IILjava_nio_IntBuffer_2
}
}
-#include <string.h>
+#include <stdlib.h>
/* void glGetShaderInfoLog ( GLuint shader, GLsizei maxLength, GLsizei* length, GLchar* infoLog ) */
-static
-jstring
-android_glGetShaderInfoLog (JNIEnv *_env, jobject _this, jint shader) {
+static jstring android_glGetShaderInfoLog(JNIEnv *_env, jobject, jint shader) {
GLint infoLen = 0;
- jstring _result = 0;
- char* buf = 0;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
- if (infoLen) {
- char* buf = (char*) malloc(infoLen);
- if (buf == 0) {
- _env->ThrowNew(IAEClass, "out of memory");
- goto exit;
- }
- glGetShaderInfoLog(shader, infoLen, NULL, buf);
- _result = _env->NewStringUTF(buf);
- } else {
- _result = _env->NewStringUTF("");
+ if (!infoLen) {
+ return _env->NewStringUTF("");
}
-exit:
- if (buf) {
- free(buf);
+ char* buf = (char*) malloc(infoLen);
+ if (buf == NULL) {
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "out of memory");
+ return NULL;
}
- return _result;
+ glGetShaderInfoLog(shader, infoLen, NULL, buf);
+ jstring result = _env->NewStringUTF(buf);
+ free(buf);
+ return result;
}
/* void glGetShaderPrecisionFormat ( GLenum shadertype, GLenum precisiontype, GLint *range, GLint *precision ) */
static void
@@ -2746,12 +2768,12 @@ android_glGetShaderPrecisionFormat__II_3II_3II
if (!range_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "range == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "range == null");
goto exit;
}
if (rangeOffset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "rangeOffset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "rangeOffset < 0");
goto exit;
}
_rangeRemaining = _env->GetArrayLength(range_ref) - rangeOffset;
@@ -2761,12 +2783,12 @@ android_glGetShaderPrecisionFormat__II_3II_3II
if (!precision_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "precision == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "precision == null");
goto exit;
}
if (precisionOffset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "precisionOffset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "precisionOffset < 0");
goto exit;
}
_precisionRemaining = _env->GetArrayLength(precision_ref) - precisionOffset;
@@ -2834,12 +2856,12 @@ android_glGetShaderSource__II_3II_3BI
if (!length_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "length == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length == null");
goto exit;
}
if (lengthOffset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "lengthOffset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "lengthOffset < 0");
goto exit;
}
_lengthRemaining = _env->GetArrayLength(length_ref) - lengthOffset;
@@ -2849,12 +2871,12 @@ android_glGetShaderSource__II_3II_3BI
if (!source_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "source == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "source == null");
goto exit;
}
if (sourceOffset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "sourceOffset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "sourceOffset < 0");
goto exit;
}
_sourceRemaining = _env->GetArrayLength(source_ref) - sourceOffset;
@@ -2901,16 +2923,10 @@ android_glGetShaderSource__IILjava_nio_IntBuffer_2B
}
}
-#include <string.h>
-
/* const GLubyte * glGetString ( GLenum name ) */
-static
-jstring
-android_glGetString
- (JNIEnv *_env, jobject _this, jint name) {
- const char * chars = (const char *)glGetString((GLenum)name);
- jstring output = _env->NewStringUTF(chars);
- return output;
+static jstring android_glGetString(JNIEnv* _env, jobject, jint name) {
+ const char* chars = (const char*) glGetString((GLenum) name);
+ return _env->NewStringUTF(chars);
}
/* void glGetTexParameterfv ( GLenum target, GLenum pname, GLfloat *params ) */
static void
@@ -2923,18 +2939,18 @@ android_glGetTexParameterfv__II_3FI
if (!params_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
if (_remaining < 1) {
_exception = 1;
- _env->ThrowNew(IAEClass, "length - offset < 1");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < 1");
goto exit;
}
params_base = (GLfloat *)
@@ -2966,7 +2982,7 @@ android_glGetTexParameterfv__IILjava_nio_FloatBuffer_2
params = (GLfloat *)getPointer(_env, params_buf, &_array, &_remaining);
if (_remaining < 1) {
_exception = 1;
- _env->ThrowNew(IAEClass, "remaining() < 1");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 1");
goto exit;
}
glGetTexParameterfv(
@@ -2992,18 +3008,18 @@ android_glGetTexParameteriv__II_3II
if (!params_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
if (_remaining < 1) {
_exception = 1;
- _env->ThrowNew(IAEClass, "length - offset < 1");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < 1");
goto exit;
}
params_base = (GLint *)
@@ -3035,7 +3051,7 @@ android_glGetTexParameteriv__IILjava_nio_IntBuffer_2
params = (GLint *)getPointer(_env, params_buf, &_array, &_remaining);
if (_remaining < 1) {
_exception = 1;
- _env->ThrowNew(IAEClass, "remaining() < 1");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 1");
goto exit;
}
glGetTexParameteriv(
@@ -3061,12 +3077,12 @@ android_glGetUniformfv__II_3FI
if (!params_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -3118,12 +3134,12 @@ android_glGetUniformiv__II_3II
if (!params_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -3172,7 +3188,7 @@ android_glGetUniformLocation__ILjava_lang_String_2
const char* _nativename = 0;
if (!name) {
- _env->ThrowNew(IAEClass, "name == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "name == null");
goto exit;
}
_nativename = _env->GetStringUTFChars(name, 0);
@@ -3201,12 +3217,12 @@ android_glGetVertexAttribfv__II_3FI
if (!params_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -3258,12 +3274,12 @@ android_glGetVertexAttribiv__II_3II
if (!params_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -3506,11 +3522,11 @@ android_glShaderBinary__I_3IIILjava_nio_Buffer_2I
GLvoid *binary = (GLvoid *) 0;
if (!shaders_ref) {
- _env->ThrowNew(IAEClass, "shaders == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "shaders == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_shadersRemaining = _env->GetArrayLength(shaders_ref) - offset;
@@ -3573,7 +3589,7 @@ android_glShaderSource
(JNIEnv *_env, jobject _this, jint shader, jstring string) {
if (!string) {
- _env->ThrowNew(IAEClass, "string == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "string == null");
return;
}
@@ -3694,16 +3710,16 @@ android_glTexParameterfv__II_3FI
GLfloat *params = (GLfloat *) 0;
if (!params_ref) {
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
if (_remaining < 1) {
- _env->ThrowNew(IAEClass, "length - offset < 1");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < 1");
goto exit;
}
params_base = (GLfloat *)
@@ -3733,7 +3749,7 @@ android_glTexParameterfv__IILjava_nio_FloatBuffer_2
params = (GLfloat *)getPointer(_env, params_buf, &_array, &_remaining);
if (_remaining < 1) {
- _env->ThrowNew(IAEClass, "remaining() < 1");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 1");
goto exit;
}
glTexParameterfv(
@@ -3768,16 +3784,16 @@ android_glTexParameteriv__II_3II
GLint *params = (GLint *) 0;
if (!params_ref) {
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
if (_remaining < 1) {
- _env->ThrowNew(IAEClass, "length - offset < 1");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < 1");
goto exit;
}
params_base = (GLint *)
@@ -3807,7 +3823,7 @@ android_glTexParameteriv__IILjava_nio_IntBuffer_2
params = (GLint *)getPointer(_env, params_buf, &_array, &_remaining);
if (_remaining < 1) {
- _env->ThrowNew(IAEClass, "remaining() < 1");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 1");
goto exit;
}
glTexParameteriv(
@@ -3868,11 +3884,11 @@ android_glUniform1fv__II_3FI
GLfloat *v = (GLfloat *) 0;
if (!v_ref) {
- _env->ThrowNew(IAEClass, "v == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "v == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(v_ref) - offset;
@@ -3931,11 +3947,11 @@ android_glUniform1iv__II_3II
GLint *v = (GLint *) 0;
if (!v_ref) {
- _env->ThrowNew(IAEClass, "v == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "v == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(v_ref) - offset;
@@ -3995,11 +4011,11 @@ android_glUniform2fv__II_3FI
GLfloat *v = (GLfloat *) 0;
if (!v_ref) {
- _env->ThrowNew(IAEClass, "v == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "v == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(v_ref) - offset;
@@ -4059,11 +4075,11 @@ android_glUniform2iv__II_3II
GLint *v = (GLint *) 0;
if (!v_ref) {
- _env->ThrowNew(IAEClass, "v == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "v == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(v_ref) - offset;
@@ -4124,11 +4140,11 @@ android_glUniform3fv__II_3FI
GLfloat *v = (GLfloat *) 0;
if (!v_ref) {
- _env->ThrowNew(IAEClass, "v == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "v == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(v_ref) - offset;
@@ -4189,11 +4205,11 @@ android_glUniform3iv__II_3II
GLint *v = (GLint *) 0;
if (!v_ref) {
- _env->ThrowNew(IAEClass, "v == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "v == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(v_ref) - offset;
@@ -4255,11 +4271,11 @@ android_glUniform4fv__II_3FI
GLfloat *v = (GLfloat *) 0;
if (!v_ref) {
- _env->ThrowNew(IAEClass, "v == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "v == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(v_ref) - offset;
@@ -4321,11 +4337,11 @@ android_glUniform4iv__II_3II
GLint *v = (GLint *) 0;
if (!v_ref) {
- _env->ThrowNew(IAEClass, "v == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "v == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(v_ref) - offset;
@@ -4374,11 +4390,11 @@ android_glUniformMatrix2fv__IIZ_3FI
GLfloat *value = (GLfloat *) 0;
if (!value_ref) {
- _env->ThrowNew(IAEClass, "value == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "value == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(value_ref) - offset;
@@ -4429,11 +4445,11 @@ android_glUniformMatrix3fv__IIZ_3FI
GLfloat *value = (GLfloat *) 0;
if (!value_ref) {
- _env->ThrowNew(IAEClass, "value == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "value == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(value_ref) - offset;
@@ -4484,11 +4500,11 @@ android_glUniformMatrix4fv__IIZ_3FI
GLfloat *value = (GLfloat *) 0;
if (!value_ref) {
- _env->ThrowNew(IAEClass, "value == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "value == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(value_ref) - offset;
@@ -4567,11 +4583,11 @@ android_glVertexAttrib1fv__I_3FI
GLfloat *values = (GLfloat *) 0;
if (!values_ref) {
- _env->ThrowNew(IAEClass, "values == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "values == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(values_ref) - offset;
@@ -4629,11 +4645,11 @@ android_glVertexAttrib2fv__I_3FI
GLfloat *values = (GLfloat *) 0;
if (!values_ref) {
- _env->ThrowNew(IAEClass, "values == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "values == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(values_ref) - offset;
@@ -4692,11 +4708,11 @@ android_glVertexAttrib3fv__I_3FI
GLfloat *values = (GLfloat *) 0;
if (!values_ref) {
- _env->ThrowNew(IAEClass, "values == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "values == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(values_ref) - offset;
@@ -4756,11 +4772,11 @@ android_glVertexAttrib4fv__I_3FI
GLfloat *values = (GLfloat *) 0;
if (!values_ref) {
- _env->ThrowNew(IAEClass, "values == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "values == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(values_ref) - offset;
diff --git a/core/jni/android_os_FileUtils.cpp b/core/jni/android_os_FileUtils.cpp
index d8a3db3..89dce89 100644
--- a/core/jni/android_os_FileUtils.cpp
+++ b/core/jni/android_os_FileUtils.cpp
@@ -2,16 +2,16 @@
**
** Copyright 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
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
**
-** http://www.apache.org/licenses/LICENSE-2.0
+** http://www.apache.org/licenses/LICENSE-2.0
**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
** limitations under the License.
*/
@@ -36,7 +36,6 @@
namespace android {
-static jclass gFileStatusClass;
static jfieldID gFileStatusDevFieldID;
static jfieldID gFileStatusInoFieldID;
static jfieldID gFileStatusModeFieldID;
@@ -146,7 +145,7 @@ jint android_os_FileUtils_getFatVolumeId(JNIEnv* env, jobject clazz, jstring pat
jboolean android_os_FileUtils_getFileStatus(JNIEnv* env, jobject clazz, jstring path, jobject fileStatus) {
const char* pathStr = env->GetStringUTFChars(path, NULL);
jboolean ret = false;
-
+
struct stat s;
int res = stat(pathStr, &s);
if (res == 0) {
@@ -166,9 +165,9 @@ jboolean android_os_FileUtils_getFileStatus(JNIEnv* env, jobject clazz, jstring
env->SetLongField(fileStatus, gFileStatusCtimeFieldID, s.st_ctime);
}
}
-
+
env->ReleaseStringUTFChars(path, pathStr);
-
+
return ret;
}
@@ -184,26 +183,21 @@ static const char* const kFileUtilsPathName = "android/os/FileUtils";
int register_android_os_FileUtils(JNIEnv* env)
{
- jclass clazz;
-
- clazz = env->FindClass(kFileUtilsPathName);
- LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.FileUtils");
-
- gFileStatusClass = env->FindClass("android/os/FileUtils$FileStatus");
- LOG_FATAL_IF(gFileStatusClass == NULL, "Unable to find class android.os.FileUtils$FileStatus");
-
- gFileStatusDevFieldID = env->GetFieldID(gFileStatusClass, "dev", "I");
- gFileStatusInoFieldID = env->GetFieldID(gFileStatusClass, "ino", "I");
- gFileStatusModeFieldID = env->GetFieldID(gFileStatusClass, "mode", "I");
- gFileStatusNlinkFieldID = env->GetFieldID(gFileStatusClass, "nlink", "I");
- gFileStatusUidFieldID = env->GetFieldID(gFileStatusClass, "uid", "I");
- gFileStatusGidFieldID = env->GetFieldID(gFileStatusClass, "gid", "I");
- gFileStatusSizeFieldID = env->GetFieldID(gFileStatusClass, "size", "J");
- gFileStatusBlksizeFieldID = env->GetFieldID(gFileStatusClass, "blksize", "I");
- gFileStatusBlocksFieldID = env->GetFieldID(gFileStatusClass, "blocks", "J");
- gFileStatusAtimeFieldID = env->GetFieldID(gFileStatusClass, "atime", "J");
- gFileStatusMtimeFieldID = env->GetFieldID(gFileStatusClass, "mtime", "J");
- gFileStatusCtimeFieldID = env->GetFieldID(gFileStatusClass, "ctime", "J");
+ jclass fileStatusClass = env->FindClass("android/os/FileUtils$FileStatus");
+ LOG_FATAL_IF(fileStatusClass == NULL, "Unable to find class android.os.FileUtils$FileStatus");
+
+ gFileStatusDevFieldID = env->GetFieldID(fileStatusClass, "dev", "I");
+ gFileStatusInoFieldID = env->GetFieldID(fileStatusClass, "ino", "I");
+ gFileStatusModeFieldID = env->GetFieldID(fileStatusClass, "mode", "I");
+ gFileStatusNlinkFieldID = env->GetFieldID(fileStatusClass, "nlink", "I");
+ gFileStatusUidFieldID = env->GetFieldID(fileStatusClass, "uid", "I");
+ gFileStatusGidFieldID = env->GetFieldID(fileStatusClass, "gid", "I");
+ gFileStatusSizeFieldID = env->GetFieldID(fileStatusClass, "size", "J");
+ gFileStatusBlksizeFieldID = env->GetFieldID(fileStatusClass, "blksize", "I");
+ gFileStatusBlocksFieldID = env->GetFieldID(fileStatusClass, "blocks", "J");
+ gFileStatusAtimeFieldID = env->GetFieldID(fileStatusClass, "atime", "J");
+ gFileStatusMtimeFieldID = env->GetFieldID(fileStatusClass, "mtime", "J");
+ gFileStatusCtimeFieldID = env->GetFieldID(fileStatusClass, "ctime", "J");
return AndroidRuntime::registerNativeMethods(
env, kFileUtilsPathName,
diff --git a/core/jni/android_os_MemoryFile.cpp b/core/jni/android_os_MemoryFile.cpp
index ee8d836..7134191 100644
--- a/core/jni/android_os_MemoryFile.cpp
+++ b/core/jni/android_os_MemoryFile.cpp
@@ -148,17 +148,10 @@ static const JNINativeMethod methods[] = {
(void*)android_os_MemoryFile_get_size}
};
-static const char* const kClassPathName = "android/os/MemoryFile";
-
int register_android_os_MemoryFile(JNIEnv* env)
{
- jclass clazz;
-
- clazz = env->FindClass(kClassPathName);
- LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.FileUtils");
-
return AndroidRuntime::registerNativeMethods(
- env, kClassPathName,
+ env, "android/os/MemoryFile",
methods, NELEM(methods));
}
diff --git a/core/jni/android_os_MessageQueue.cpp b/core/jni/android_os_MessageQueue.cpp
index d2e5462..12a77d5 100644
--- a/core/jni/android_os_MessageQueue.cpp
+++ b/core/jni/android_os_MessageQueue.cpp
@@ -27,8 +27,6 @@ namespace android {
// ----------------------------------------------------------------------------
static struct {
- jclass clazz;
-
jfieldID mPtr; // native object attached to the DVM MessageQueue
} gMessageQueueClassInfo;
@@ -135,8 +133,7 @@ static JNINativeMethod gMessageQueueMethods[] = {
#define FIND_CLASS(var, className) \
var = env->FindClass(className); \
- LOG_FATAL_IF(! var, "Unable to find class " className); \
- var = jclass(env->NewGlobalRef(var));
+ LOG_FATAL_IF(! var, "Unable to find class " className);
#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \
var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \
@@ -147,9 +144,10 @@ int register_android_os_MessageQueue(JNIEnv* env) {
gMessageQueueMethods, NELEM(gMessageQueueMethods));
LOG_FATAL_IF(res < 0, "Unable to register native methods.");
- FIND_CLASS(gMessageQueueClassInfo.clazz, "android/os/MessageQueue");
+ jclass clazz;
+ FIND_CLASS(clazz, "android/os/MessageQueue");
- GET_FIELD_ID(gMessageQueueClassInfo.mPtr, gMessageQueueClassInfo.clazz,
+ GET_FIELD_ID(gMessageQueueClassInfo.mPtr, clazz,
"mPtr", "I");
return 0;
diff --git a/core/jni/android_os_ParcelFileDescriptor.cpp b/core/jni/android_os_ParcelFileDescriptor.cpp
index e73d30b..99a2d04 100644
--- a/core/jni/android_os_ParcelFileDescriptor.cpp
+++ b/core/jni/android_os_ParcelFileDescriptor.cpp
@@ -29,26 +29,8 @@
namespace android
{
-static struct file_descriptor_offsets_t
-{
- jclass mClass;
- jmethodID mConstructor;
- jfieldID mDescriptor;
-} gFileDescriptorOffsets;
-
-static struct socket_offsets_t
-{
- jfieldID mSocketImpl;
-} gSocketOffsets;
-
-static struct socket_impl_offsets_t
-{
- jfieldID mFileDescriptor;
-} gSocketImplOffsets;
-
static struct parcel_file_descriptor_offsets_t
{
- jclass mClass;
jfieldID mFileDescriptor;
} gParcelFileDescriptorOffsets;
@@ -60,38 +42,13 @@ static jobject android_os_ParcelFileDescriptor_getFileDescriptorFromFd(JNIEnv* e
jniThrowException(env, "java/io/IOException", strerror(errno));
return NULL;
}
- jobject fileDescriptorClone = env->NewObject(gFileDescriptorOffsets.mClass,
- gFileDescriptorOffsets.mConstructor);
- if (fileDescriptorClone != NULL) {
- env->SetIntField(fileDescriptorClone, gFileDescriptorOffsets.mDescriptor, fd);
- }
- return fileDescriptorClone;
+ return jniCreateFileDescriptor(env, fd);
}
static jobject android_os_ParcelFileDescriptor_getFileDescriptorFromFdNoDup(JNIEnv* env,
jobject clazz, jint fd)
{
- jobject fileDescriptorClone = env->NewObject(gFileDescriptorOffsets.mClass,
- gFileDescriptorOffsets.mConstructor);
- if (fileDescriptorClone != NULL) {
- env->SetIntField(fileDescriptorClone, gFileDescriptorOffsets.mDescriptor, fd);
- }
- return fileDescriptorClone;
-}
-
-static jobject android_os_ParcelFileDescriptor_getFileDescriptorFromSocket(JNIEnv* env,
- jobject clazz, jobject object)
-{
- jobject socketImpl = env->GetObjectField(object, gSocketOffsets.mSocketImpl);
- jobject fileDescriptor = env->GetObjectField(socketImpl, gSocketImplOffsets.mFileDescriptor);
- jint fd = env->GetIntField(fileDescriptor, gFileDescriptorOffsets.mDescriptor);
- jobject fileDescriptorClone = env->NewObject(gFileDescriptorOffsets.mClass,
- gFileDescriptorOffsets.mConstructor);
- if (fileDescriptorClone != NULL) {
- // XXXX need to throw an exception if the dup fails!
- env->SetIntField(fileDescriptorClone, gFileDescriptorOffsets.mDescriptor, dup(fd));
- }
- return fileDescriptorClone;
+ return jniCreateFileDescriptor(env, fd);
}
static void android_os_ParcelFileDescriptor_createPipeNative(JNIEnv* env,
@@ -105,11 +62,7 @@ static void android_os_ParcelFileDescriptor_createPipeNative(JNIEnv* env,
}
for (int i=0; i<2; i++) {
- jobject fdObj = env->NewObject(gFileDescriptorOffsets.mClass,
- gFileDescriptorOffsets.mConstructor);
- if (fdObj != NULL) {
- env->SetIntField(fdObj, gFileDescriptorOffsets.mDescriptor, fds[i]);
- }
+ jobject fdObj = jniCreateFileDescriptor(env, fds[i]);
env->SetObjectArrayElement(outFds, i, fdObj);
}
}
@@ -118,7 +71,7 @@ static jint getFd(JNIEnv* env, jobject clazz)
{
jobject descriptor = env->GetObjectField(clazz, gParcelFileDescriptorOffsets.mFileDescriptor);
if (descriptor == NULL) return -1;
- return env->GetIntField(descriptor, gFileDescriptorOffsets.mDescriptor);
+ return jniGetFDFromFileDescriptor(env, descriptor);
}
static jlong android_os_ParcelFileDescriptor_getStatSize(JNIEnv* env,
@@ -129,16 +82,16 @@ static jlong android_os_ParcelFileDescriptor_getStatSize(JNIEnv* env,
jniThrowException(env, "java/lang/IllegalArgumentException", "bad file descriptor");
return -1;
}
-
+
struct stat st;
if (fstat(fd, &st) != 0) {
return -1;
}
-
+
if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) {
return st.st_size;
}
-
+
return -1;
}
@@ -150,7 +103,7 @@ static jlong android_os_ParcelFileDescriptor_seekTo(JNIEnv* env,
jniThrowException(env, "java/lang/IllegalArgumentException", "bad file descriptor");
return -1;
}
-
+
return lseek(fd, pos, SEEK_SET);
}
@@ -170,8 +123,6 @@ static const JNINativeMethod gParcelFileDescriptorMethods[] = {
(void*)android_os_ParcelFileDescriptor_getFileDescriptorFromFd},
{"getFileDescriptorFromFdNoDup", "(I)Ljava/io/FileDescriptor;",
(void*)android_os_ParcelFileDescriptor_getFileDescriptorFromFdNoDup},
- {"getFileDescriptorFromSocket", "(Ljava/net/Socket;)Ljava/io/FileDescriptor;",
- (void*)android_os_ParcelFileDescriptor_getFileDescriptorFromSocket},
{"createPipeNative", "([Ljava/io/FileDescriptor;)V",
(void*)android_os_ParcelFileDescriptor_createPipeNative},
{"getStatSize", "()J",
@@ -186,31 +137,8 @@ const char* const kParcelFileDescriptorPathName = "android/os/ParcelFileDescript
int register_android_os_ParcelFileDescriptor(JNIEnv* env)
{
- jclass clazz;
-
- clazz = env->FindClass("java/net/Socket");
- LOG_FATAL_IF(clazz == NULL, "Unable to find class java.net.Socket");
- gSocketOffsets.mSocketImpl = env->GetFieldID(clazz, "impl", "Ljava/net/SocketImpl;");
- LOG_FATAL_IF(gSocketOffsets.mSocketImpl == NULL,
- "Unable to find impl field in java.net.Socket");
-
- clazz = env->FindClass("java/net/SocketImpl");
- LOG_FATAL_IF(clazz == NULL, "Unable to find class java.net.SocketImpl");
- gSocketImplOffsets.mFileDescriptor = env->GetFieldID(clazz, "fd", "Ljava/io/FileDescriptor;");
- LOG_FATAL_IF(gSocketImplOffsets.mFileDescriptor == NULL,
- "Unable to find fd field in java.net.SocketImpl");
-
- clazz = env->FindClass("java/io/FileDescriptor");
- LOG_FATAL_IF(clazz == NULL, "Unable to find class java.io.FileDescriptor");
- gFileDescriptorOffsets.mClass = (jclass) env->NewGlobalRef(clazz);
- gFileDescriptorOffsets.mConstructor = env->GetMethodID(clazz, "<init>", "()V");
- gFileDescriptorOffsets.mDescriptor = env->GetFieldID(clazz, "descriptor", "I");
- LOG_FATAL_IF(gFileDescriptorOffsets.mDescriptor == NULL,
- "Unable to find descriptor field in java.io.FileDescriptor");
-
- clazz = env->FindClass(kParcelFileDescriptorPathName);
+ jclass clazz = env->FindClass(kParcelFileDescriptorPathName);
LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.ParcelFileDescriptor");
- gParcelFileDescriptorOffsets.mClass = (jclass) env->NewGlobalRef(clazz);
gParcelFileDescriptorOffsets.mFileDescriptor = env->GetFieldID(clazz, "mFileDescriptor", "Ljava/io/FileDescriptor;");
LOG_FATAL_IF(gParcelFileDescriptorOffsets.mFileDescriptor == NULL,
"Unable to find mFileDescriptor field in android.os.ParcelFileDescriptor");
diff --git a/core/jni/android_os_Power.cpp b/core/jni/android_os_Power.cpp
index 5cfb9b1..9ae4a63 100644
--- a/core/jni/android_os_Power.cpp
+++ b/core/jni/android_os_Power.cpp
@@ -25,18 +25,11 @@
namespace android
{
-static void throw_NullPointerException(JNIEnv *env, const char* msg)
-{
- jclass clazz;
- clazz = env->FindClass("java/lang/NullPointerException");
- env->ThrowNew(clazz, msg);
-}
-
static void
acquireWakeLock(JNIEnv *env, jobject clazz, jint lock, jstring idObj)
{
if (idObj == NULL) {
- throw_NullPointerException(env, "id is null");
+ jniThrowNullPointerException(env, "id is null");
return ;
}
@@ -51,7 +44,7 @@ static void
releaseWakeLock(JNIEnv *env, jobject clazz, jstring idObj)
{
if (idObj == NULL) {
- throw_NullPointerException(env, "id is null");
+ jniThrowNullPointerException(env, "id is null");
return ;
}
diff --git a/core/jni/android_os_SystemProperties.cpp b/core/jni/android_os_SystemProperties.cpp
index 406884b..66af965 100644
--- a/core/jni/android_os_SystemProperties.cpp
+++ b/core/jni/android_os_SystemProperties.cpp
@@ -2,16 +2,16 @@
**
** Copyright 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
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
**
-** http://www.apache.org/licenses/LICENSE-2.0
+** http://www.apache.org/licenses/LICENSE-2.0
**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
** limitations under the License.
*/
@@ -32,8 +32,7 @@ static jstring SystemProperties_getSS(JNIEnv *env, jobject clazz,
jstring rvJ = NULL;
if (keyJ == NULL) {
- jniThrowException(env, "java/lang/NullPointerException",
- "key must not be null.");
+ jniThrowNullPointerException(env, "key must not be null.");
goto error;
}
@@ -69,8 +68,7 @@ static jint SystemProperties_get_int(JNIEnv *env, jobject clazz,
jint result = defJ;
if (keyJ == NULL) {
- jniThrowException(env, "java/lang/NullPointerException",
- "key must not be null.");
+ jniThrowNullPointerException(env, "key must not be null.");
goto error;
}
@@ -98,8 +96,7 @@ static jlong SystemProperties_get_long(JNIEnv *env, jobject clazz,
jlong result = defJ;
if (keyJ == NULL) {
- jniThrowException(env, "java/lang/NullPointerException",
- "key must not be null.");
+ jniThrowNullPointerException(env, "key must not be null.");
goto error;
}
@@ -127,8 +124,7 @@ static jboolean SystemProperties_get_boolean(JNIEnv *env, jobject clazz,
jboolean result = defJ;
if (keyJ == NULL) {
- jniThrowException(env, "java/lang/NullPointerException",
- "key must not be null.");
+ jniThrowNullPointerException(env, "key must not be null.");
goto error;
}
@@ -163,8 +159,7 @@ static void SystemProperties_set(JNIEnv *env, jobject clazz,
const char* val;
if (keyJ == NULL) {
- jniThrowException(env, "java/lang/NullPointerException",
- "key must not be null.");
+ jniThrowNullPointerException(env, "key must not be null.");
return ;
}
key = env->GetStringUTFChars(keyJ, NULL);
@@ -174,15 +169,20 @@ static void SystemProperties_set(JNIEnv *env, jobject clazz,
} else {
val = env->GetStringUTFChars(valJ, NULL);
}
-
+
err = property_set(key, val);
-
+
env->ReleaseStringUTFChars(keyJ, key);
-
+
if (valJ != NULL) {
- env->ReleaseStringUTFChars(valJ, val);
+ env->ReleaseStringUTFChars(valJ, val);
}
-}
+
+ if (err < 0) {
+ jniThrowException(env, "java/lang/RuntimeException",
+ "failed to set system property");
+ }
+}
static JNINativeMethod method_table[] = {
{ "native_get", "(Ljava/lang/String;)Ljava/lang/String;",
diff --git a/core/jni/android_pim_EventRecurrence.cpp b/core/jni/android_pim_EventRecurrence.cpp
index cbe99bc..44e898d 100644
--- a/core/jni/android_pim_EventRecurrence.cpp
+++ b/core/jni/android_pim_EventRecurrence.cpp
@@ -1,17 +1,17 @@
/* //device/libs/android_runtime/android_pim_EventRecurrence.cpp
**
-** Copyright 2006, The Android Open Source Project
+** Copyright 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
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
**
-** http://www.apache.org/licenses/LICENSE-2.0
+** http://www.apache.org/licenses/LICENSE-2.0
**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
** limitations under the License.
*/
@@ -28,7 +28,6 @@ struct cached_array_fields_t
jfieldID count;
};
-static jclass clazz;
static jfieldID freq_field;
static jfieldID until_field;
static jfieldID count_field;
@@ -84,12 +83,10 @@ static void
EventRecurrence_parse(JNIEnv* env, jobject This, jstring jstr)
{
if (jstr == NULL) {
- jniThrowException(env, "java/lang/NullPointerException",
- "EventRecurrence.parse str parameter null");
+ jniThrowNullPointerException(env, "EventRecurrence.parse str parameter null");
return ;
}
- jboolean isCopy;
- const jchar* jchars = env->GetStringChars(jstr, &isCopy);
+ const jchar* jchars = env->GetStringChars(jstr, NULL);
jsize len = env->GetStringLength(jstr);
String16 str(jchars, len);
env->ReleaseStringChars(jstr, jchars);
@@ -112,8 +109,8 @@ EventRecurrence_parse(JNIEnv* env, jobject This, jstring jstr)
if (er.until.size() > 0) {
untilStr = env->NewString(er.until.string(), er.until.size());
if (untilStr == NULL) {
- jniThrowException(env, "java/lang/RuntimeException",
- "EventRecurrence.parse error setting field 'until'");
+ jniThrowException(env, "java/lang/RuntimeException",
+ "EventRecurrence.parse error setting field 'until'");
return ;
}
} else {
@@ -132,7 +129,7 @@ EventRecurrence_parse(JNIEnv* env, jobject This, jstring jstr)
SET_ARRAY_AND_CHECK(byday)
// we'll just set the bydayCount field twice, it'll be less code total
if (set_array(env, er.bydayCount, er.bydayNum, This, bydayNum_fields)
- != NO_ERROR) {
+ != NO_ERROR) {
jniThrowException(env, "java/lang/RuntimeException",
"EventRecurrence.parse error setting field bydayNum or "
"bydayCount.");
@@ -157,7 +154,7 @@ static const char*const CLASS_NAME = "android/pim/EventRecurrence";
int register_android_pim_EventRecurrence(JNIEnv* env)
{
- clazz = env->FindClass(CLASS_NAME);
+ jclass clazz = env->FindClass(CLASS_NAME);
if (clazz == NULL) {
LOGE("Field lookup unable to find class '%s'\n", CLASS_NAME);
return -1;
@@ -196,4 +193,3 @@ int register_android_pim_EventRecurrence(JNIEnv* env)
}
}; // namespace android
-
diff --git a/core/jni/android_server_BluetoothEventLoop.cpp b/core/jni/android_server_BluetoothEventLoop.cpp
index a6bde88..59b97c2 100644
--- a/core/jni/android_server_BluetoothEventLoop.cpp
+++ b/core/jni/android_server_BluetoothEventLoop.cpp
@@ -373,7 +373,6 @@ static int register_agent(native_data_t *nat,
}
dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &agent_path,
DBUS_TYPE_STRING, &capabilities,
- DBUS_TYPE_BOOLEAN, &oob,
DBUS_TYPE_INVALID);
dbus_error_init(&err);
diff --git a/core/jni/android_server_BluetoothService.cpp b/core/jni/android_server_BluetoothService.cpp
index 158e475..5c6958a 100644
--- a/core/jni/android_server_BluetoothService.cpp
+++ b/core/jni/android_server_BluetoothService.cpp
@@ -695,9 +695,7 @@ static jobjectArray getDevicePropertiesNative(JNIEnv *env, jobject object,
str_array = parse_remote_device_properties(env, &iter);
dbus_message_unref(reply);
- env->PopLocalFrame(NULL);
-
- return str_array;
+ return (jobjectArray) env->PopLocalFrame(str_array);
}
#endif
return NULL;
@@ -731,8 +729,7 @@ static jobjectArray getAdapterPropertiesNative(JNIEnv *env, jobject object) {
str_array = parse_adapter_properties(env, &iter);
dbus_message_unref(reply);
- env->PopLocalFrame(NULL);
- return str_array;
+ return (jobjectArray) env->PopLocalFrame(str_array);
}
#endif
return NULL;
diff --git a/core/jni/android_server_Watchdog.cpp b/core/jni/android_server_Watchdog.cpp
index 2a90db7..9e0ed47 100644
--- a/core/jni/android_server_Watchdog.cpp
+++ b/core/jni/android_server_Watchdog.cpp
@@ -17,6 +17,7 @@
#define LOG_TAG "Watchdog_N"
#include <utils/Log.h>
+#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <dirent.h>
@@ -63,7 +64,8 @@ static void dumpKernelStacks(JNIEnv* env, jobject clazz, jstring pathStr) {
const char *path = env->GetStringUTFChars(pathStr, NULL);
- int outFd = open(path, O_WRONLY | O_APPEND | O_CREAT);
+ int outFd = open(path, O_WRONLY | O_APPEND | O_CREAT,
+ S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
if (outFd < 0) {
LOGE("Unable to open stack dump file: %d (%s)", errno, strerror(errno));
goto done;
diff --git a/core/jni/android_text_AndroidBidi.cpp b/core/jni/android_text_AndroidBidi.cpp
index 53028c3..d50a69f 100644
--- a/core/jni/android_text_AndroidBidi.cpp
+++ b/core/jni/android_text_AndroidBidi.cpp
@@ -2,16 +2,16 @@
**
** Copyright 2010, The Android Open Source Project
**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
**
-** http://www.apache.org/licenses/LICENSE-2.0
+** http://www.apache.org/licenses/LICENSE-2.0
**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
** limitations under the License.
*/
@@ -24,8 +24,8 @@
#include "unicode/ubidi.h"
namespace android {
-
-static jint runBidi(JNIEnv* env, jobject obj, jint dir, jcharArray chsArray,
+
+static jint runBidi(JNIEnv* env, jobject obj, jint dir, jcharArray chsArray,
jbyteArray infoArray, int n, jboolean haveInfo)
{
// Parameters are checked on java side
@@ -63,9 +63,6 @@ static JNINativeMethod gMethods[] = {
int register_android_text_AndroidBidi(JNIEnv* env)
{
- jclass clazz = env->FindClass("android/text/AndroidBidi");
- LOG_ASSERT(clazz, "Cannot find android/text/AndroidBidi");
-
return AndroidRuntime::registerNativeMethods(env, "android/text/AndroidBidi",
gMethods, NELEM(gMethods));
}
diff --git a/core/jni/android_text_AndroidCharacter.cpp b/core/jni/android_text_AndroidCharacter.cpp
index 6b90541..dacbe41 100644
--- a/core/jni/android_text_AndroidCharacter.cpp
+++ b/core/jni/android_text_AndroidCharacter.cpp
@@ -2,22 +2,23 @@
**
** Copyright 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
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
**
-** http://www.apache.org/licenses/LICENSE-2.0
+** http://www.apache.org/licenses/LICENSE-2.0
**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
** limitations under the License.
*/
#define LOG_TAG "AndroidUnicode"
#include "JNIHelp.h"
+#include "ScopedPrimitiveArray.h"
#include <android_runtime/AndroidRuntime.h>
#include "utils/misc.h"
#include "utils/Log.h"
@@ -49,19 +50,21 @@ static int directionality_map[U_CHAR_DIRECTION_COUNT] = {
};
namespace android {
-
+
static void getDirectionalities(JNIEnv* env, jobject obj, jcharArray srcArray, jbyteArray destArray, int count)
{
- jchar* src = env->GetCharArrayElements(srcArray, NULL);
- jbyte* dest = env->GetByteArrayElements(destArray, NULL);
- if (src == NULL || dest == NULL) {
- jniThrowException(env, "java/lang/NullPointerException", NULL);
- goto DIRECTION_END;
+ ScopedCharArrayRO src(env, srcArray);
+ if (src.get() == NULL) {
+ return;
+ }
+ ScopedByteArrayRW dest(env, destArray);
+ if (dest.get() == NULL) {
+ return;
}
if (env->GetArrayLength(srcArray) < count || env->GetArrayLength(destArray) < count) {
jniThrowException(env, "java/lang/ArrayIndexOutOfBoundsException", NULL);
- goto DIRECTION_END;
+ return;
}
for (int i = 0; i < count; i++) {
@@ -87,10 +90,6 @@ static void getDirectionalities(JNIEnv* env, jobject obj, jcharArray srcArray, j
dest[i] = directionality_map[dir];
}
}
-
-DIRECTION_END:
- env->ReleaseCharArrayElements(srcArray, src, JNI_ABORT);
- env->ReleaseByteArrayElements(destArray, dest, JNI_ABORT);
}
static jint getEastAsianWidth(JNIEnv* env, jobject obj, jchar input)
@@ -105,18 +104,20 @@ static jint getEastAsianWidth(JNIEnv* env, jobject obj, jchar input)
static void getEastAsianWidths(JNIEnv* env, jobject obj, jcharArray srcArray,
int start, int count, jbyteArray destArray)
{
- jchar* src = env->GetCharArrayElements(srcArray, NULL);
- jbyte* dest = env->GetByteArrayElements(destArray, NULL);
- if (src == NULL || dest == NULL) {
- jniThrowException(env, "java/lang/NullPointerException", NULL);
- goto EA_END;
+ ScopedCharArrayRO src(env, srcArray);
+ if (src.get() == NULL) {
+ return;
+ }
+ ScopedByteArrayRW dest(env, destArray);
+ if (dest.get() == NULL) {
+ return;
}
if (start < 0 || start > start + count
|| env->GetArrayLength(srcArray) < (start + count)
|| env->GetArrayLength(destArray) < count) {
jniThrowException(env, "java/lang/ArrayIndexOutOfBoundsException", NULL);
- goto EA_END;
+ return;
}
for (int i = 0; i < count; i++) {
@@ -141,26 +142,19 @@ static void getEastAsianWidths(JNIEnv* env, jobject obj, jcharArray srcArray,
dest[i] = width;
}
}
-
-EA_END:
- env->ReleaseCharArrayElements(srcArray, src, JNI_ABORT);
- env->ReleaseByteArrayElements(destArray, dest, JNI_ABORT);
}
static jboolean mirror(JNIEnv* env, jobject obj, jcharArray charArray, int start, int count)
{
- jchar* data = env->GetCharArrayElements(charArray, NULL);
- bool ret = false;
-
- if (data == NULL) {
- jniThrowException(env, "java/lang/NullPointerException", NULL);
- goto MIRROR_END;
+ ScopedCharArrayRW data(env, charArray);
+ if (data.get() == NULL) {
+ return false;
}
if (start < 0 || start > start + count
|| env->GetArrayLength(charArray) < start + count) {
jniThrowException(env, "java/lang/ArrayIndexOutOfBoundsException", NULL);
- goto MIRROR_END;
+ return false;
}
for (int i = start; i < start + count; i++) {
@@ -171,17 +165,14 @@ static jboolean mirror(JNIEnv* env, jobject obj, jcharArray charArray, int start
if (c1 != c2) {
data[i] = c2;
- ret = true;
+ return true;
}
}
-
-MIRROR_END:
- env->ReleaseCharArrayElements(charArray, data, JNI_ABORT);
- return ret;
+ return false;
}
static jchar getMirror(JNIEnv* env, jobject obj, jchar c)
-{
+{
return u_charMirror(c);
}
@@ -200,9 +191,6 @@ static JNINativeMethod gMethods[] = {
int register_android_text_AndroidCharacter(JNIEnv* env)
{
- jclass clazz = env->FindClass("android/text/AndroidCharacter");
- LOG_ASSERT(clazz, "Cannot find android/text/AndroidCharacter");
-
return AndroidRuntime::registerNativeMethods(env, "android/text/AndroidCharacter",
gMethods, NELEM(gMethods));
}
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index 636d257..4f8f1af 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -2,16 +2,16 @@
**
** Copyright 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
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
**
-** http://www.apache.org/licenses/LICENSE-2.0
+** http://www.apache.org/licenses/LICENSE-2.0
**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
** limitations under the License.
*/
@@ -24,6 +24,8 @@
#include "jni.h"
#include "JNIHelp.h"
+#include "ScopedStringChars.h"
+#include "ScopedUtfChars.h"
#include "android_util_Binder.h"
#include <utils/misc.h>
#include <android_runtime/AndroidRuntime.h>
@@ -121,8 +123,8 @@ static jint android_content_AssetManager_openAsset(JNIEnv* env, jobject clazz,
LOGV("openAsset in %p (Java object %p)\n", am, clazz);
- if (fileName == NULL) {
- jniThrowException(env, "java/lang/NullPointerException", "fileName");
+ ScopedUtfChars fileName8(env, fileName);
+ if (fileName8.c_str() == NULL) {
return -1;
}
@@ -132,15 +134,12 @@ static jint android_content_AssetManager_openAsset(JNIEnv* env, jobject clazz,
return -1;
}
- const char* fileName8 = env->GetStringUTFChars(fileName, NULL);
- Asset* a = am->open(fileName8, (Asset::AccessMode)mode);
+ Asset* a = am->open(fileName8.c_str(), (Asset::AccessMode)mode);
if (a == NULL) {
- jniThrowException(env, "java/io/FileNotFoundException", fileName8);
- env->ReleaseStringUTFChars(fileName, fileName8);
+ jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str());
return -1;
}
- env->ReleaseStringUTFChars(fileName, fileName8);
//printf("Created Asset Stream: %p\n", a);
@@ -152,30 +151,30 @@ static jobject returnParcelFileDescriptor(JNIEnv* env, Asset* a, jlongArray outO
off64_t startOffset, length;
int fd = a->openFileDescriptor(&startOffset, &length);
delete a;
-
+
if (fd < 0) {
jniThrowException(env, "java/io/FileNotFoundException",
"This file can not be opened as a file descriptor; it is probably compressed");
return NULL;
}
-
+
jlong* offsets = (jlong*)env->GetPrimitiveArrayCritical(outOffsets, 0);
if (offsets == NULL) {
close(fd);
return NULL;
}
-
+
offsets[0] = startOffset;
offsets[1] = length;
-
+
env->ReleasePrimitiveArrayCritical(outOffsets, offsets, 0);
-
- jobject fileDesc = newFileDescriptor(env, fd);
+
+ jobject fileDesc = jniCreateFileDescriptor(env, fd);
if (fileDesc == NULL) {
close(fd);
return NULL;
}
-
+
return newParcelFileDescriptor(env, fileDesc);
}
@@ -189,20 +188,17 @@ static jobject android_content_AssetManager_openAssetFd(JNIEnv* env, jobject cla
LOGV("openAssetFd in %p (Java object %p)\n", am, clazz);
- if (fileName == NULL) {
- jniThrowException(env, "java/lang/NullPointerException", "fileName");
+ ScopedUtfChars fileName8(env, fileName);
+ if (fileName8.c_str() == NULL) {
return NULL;
}
- const char* fileName8 = env->GetStringUTFChars(fileName, NULL);
- Asset* a = am->open(fileName8, Asset::ACCESS_RANDOM);
+ Asset* a = am->open(fileName8.c_str(), Asset::ACCESS_RANDOM);
if (a == NULL) {
- jniThrowException(env, "java/io/FileNotFoundException", fileName8);
- env->ReleaseStringUTFChars(fileName, fileName8);
+ jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str());
return NULL;
}
- env->ReleaseStringUTFChars(fileName, fileName8);
//printf("Created Asset Stream: %p\n", a);
@@ -221,8 +217,8 @@ static jint android_content_AssetManager_openNonAssetNative(JNIEnv* env, jobject
LOGV("openNonAssetNative in %p (Java object %p)\n", am, clazz);
- if (fileName == NULL) {
- jniThrowException(env, "java/lang/NullPointerException", "fileName");
+ ScopedUtfChars fileName8(env, fileName);
+ if (fileName8.c_str() == NULL) {
return -1;
}
@@ -232,17 +228,14 @@ static jint android_content_AssetManager_openNonAssetNative(JNIEnv* env, jobject
return -1;
}
- const char* fileName8 = env->GetStringUTFChars(fileName, NULL);
Asset* a = cookie
- ? am->openNonAsset((void*)cookie, fileName8, (Asset::AccessMode)mode)
- : am->openNonAsset(fileName8, (Asset::AccessMode)mode);
+ ? am->openNonAsset((void*)cookie, fileName8.c_str(), (Asset::AccessMode)mode)
+ : am->openNonAsset(fileName8.c_str(), (Asset::AccessMode)mode);
if (a == NULL) {
- jniThrowException(env, "java/io/FileNotFoundException", fileName8);
- env->ReleaseStringUTFChars(fileName, fileName8);
+ jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str());
return -1;
}
- env->ReleaseStringUTFChars(fileName, fileName8);
//printf("Created Asset Stream: %p\n", a);
@@ -261,22 +254,19 @@ static jobject android_content_AssetManager_openNonAssetFdNative(JNIEnv* env, jo
LOGV("openNonAssetFd in %p (Java object %p)\n", am, clazz);
- if (fileName == NULL ) {
- jniThrowException(env, "java/lang/NullPointerException", "fileName");
+ ScopedUtfChars fileName8(env, fileName);
+ if (fileName8.c_str() == NULL) {
return NULL;
}
- const char* fileName8 = env->GetStringUTFChars(fileName, NULL);
Asset* a = cookie
- ? am->openNonAsset((void*)cookie, fileName8, Asset::ACCESS_RANDOM)
- : am->openNonAsset(fileName8, Asset::ACCESS_RANDOM);
+ ? am->openNonAsset((void*)cookie, fileName8.c_str(), Asset::ACCESS_RANDOM)
+ : am->openNonAsset(fileName8.c_str(), Asset::ACCESS_RANDOM);
if (a == NULL) {
- jniThrowException(env, "java/io/FileNotFoundException", fileName8);
- env->ReleaseStringUTFChars(fileName, fileName8);
+ jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str());
return NULL;
}
- env->ReleaseStringUTFChars(fileName, fileName8);
//printf("Created Asset Stream: %p\n", a);
@@ -291,19 +281,15 @@ static jobjectArray android_content_AssetManager_list(JNIEnv* env, jobject clazz
return NULL;
}
- if (fileName == NULL) {
- jniThrowException(env, "java/lang/NullPointerException", "fileName");
+ ScopedUtfChars fileName8(env, fileName);
+ if (fileName8.c_str() == NULL) {
return NULL;
}
- const char* fileName8 = env->GetStringUTFChars(fileName, NULL);
-
- AssetDir* dir = am->openDir(fileName8);
-
- env->ReleaseStringUTFChars(fileName, fileName8);
+ AssetDir* dir = am->openDir(fileName8.c_str());
if (dir == NULL) {
- jniThrowException(env, "java/io/FileNotFoundException", fileName8);
+ jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str());
return NULL;
}
@@ -347,7 +333,7 @@ static void android_content_AssetManager_destroyAsset(JNIEnv* env, jobject clazz
//printf("Destroying Asset Stream: %p\n", a);
if (a == NULL) {
- jniThrowException(env, "java/lang/NullPointerException", "asset");
+ jniThrowNullPointerException(env, "asset");
return;
}
@@ -360,7 +346,7 @@ static jint android_content_AssetManager_readAssetChar(JNIEnv* env, jobject claz
Asset* a = (Asset*)asset;
if (a == NULL) {
- jniThrowException(env, "java/lang/NullPointerException", "asset");
+ jniThrowNullPointerException(env, "asset");
return -1;
}
@@ -376,14 +362,14 @@ static jint android_content_AssetManager_readAsset(JNIEnv* env, jobject clazz,
Asset* a = (Asset*)asset;
if (a == NULL || bArray == NULL) {
- jniThrowException(env, "java/lang/NullPointerException", "asset");
+ jniThrowNullPointerException(env, "asset");
return -1;
}
if (len == 0) {
return 0;
}
-
+
jsize bLen = env->GetArrayLength(bArray);
if (off < 0 || off >= bLen || len < 0 || len > bLen || (off+len) > bLen) {
jniThrowException(env, "java/lang/IndexOutOfBoundsException", "");
@@ -409,7 +395,7 @@ static jlong android_content_AssetManager_seekAsset(JNIEnv* env, jobject clazz,
Asset* a = (Asset*)asset;
if (a == NULL) {
- jniThrowException(env, "java/lang/NullPointerException", "asset");
+ jniThrowNullPointerException(env, "asset");
return -1;
}
@@ -423,7 +409,7 @@ static jlong android_content_AssetManager_getAssetLength(JNIEnv* env, jobject cl
Asset* a = (Asset*)asset;
if (a == NULL) {
- jniThrowException(env, "java/lang/NullPointerException", "asset");
+ jniThrowNullPointerException(env, "asset");
return -1;
}
@@ -436,7 +422,7 @@ static jlong android_content_AssetManager_getAssetRemainingLength(JNIEnv* env, j
Asset* a = (Asset*)asset;
if (a == NULL) {
- jniThrowException(env, "java/lang/NullPointerException", "asset");
+ jniThrowNullPointerException(env, "asset");
return -1;
}
@@ -446,9 +432,9 @@ static jlong android_content_AssetManager_getAssetRemainingLength(JNIEnv* env, j
static jint android_content_AssetManager_addAssetPath(JNIEnv* env, jobject clazz,
jstring path)
{
- if (path == NULL) {
- jniThrowException(env, "java/lang/NullPointerException", "path");
- return JNI_FALSE;
+ ScopedUtfChars path8(env, path);
+ if (path8.c_str() == NULL) {
+ return NULL;
}
AssetManager* am = assetManagerForJavaObject(env, clazz);
@@ -456,12 +442,8 @@ static jint android_content_AssetManager_addAssetPath(JNIEnv* env, jobject clazz
return JNI_FALSE;
}
- const char* path8 = env->GetStringUTFChars(path, NULL);
-
void* cookie;
- bool res = am->addAssetPath(String8(path8), &cookie);
-
- env->ReleaseStringUTFChars(path, path8);
+ bool res = am->addAssetPath(String8(path8.c_str()), &cookie);
return (res) ? (jint)cookie : 0;
}
@@ -478,21 +460,17 @@ static jboolean android_content_AssetManager_isUpToDate(JNIEnv* env, jobject cla
static void android_content_AssetManager_setLocale(JNIEnv* env, jobject clazz,
jstring locale)
{
- if (locale == NULL) {
- jniThrowException(env, "java/lang/NullPointerException", "locale");
+ ScopedUtfChars locale8(env, locale);
+ if (locale8.c_str() == NULL) {
return;
}
- const char* locale8 = env->GetStringUTFChars(locale, NULL);
-
AssetManager* am = assetManagerForJavaObject(env, clazz);
if (am == NULL) {
return;
}
- am->setLocale(locale8);
-
- env->ReleaseStringUTFChars(locale, locale8);
+ am->setLocale(locale8.c_str());
}
static jobjectArray android_content_AssetManager_getLocales(JNIEnv* env, jobject clazz)
@@ -544,9 +522,9 @@ static void android_content_AssetManager_setConfiguration(JNIEnv* env, jobject c
ResTable_config config;
memset(&config, 0, sizeof(config));
-
+
const char* locale8 = locale != NULL ? env->GetStringUTFChars(locale, NULL) : NULL;
-
+
config.mcc = (uint16_t)mcc;
config.mnc = (uint16_t)mnc;
config.orientation = (uint8_t)orientation;
@@ -565,7 +543,7 @@ static void android_content_AssetManager_setConfiguration(JNIEnv* env, jobject c
config.sdkVersion = (uint16_t)sdkVersion;
config.minorVersion = 0;
am->setConfiguration(config, locale8);
-
+
if (locale != NULL) env->ReleaseStringUTFChars(locale, locale8);
}
@@ -574,8 +552,8 @@ static jint android_content_AssetManager_getResourceIdentifier(JNIEnv* env, jobj
jstring defType,
jstring defPackage)
{
- if (name == NULL) {
- jniThrowException(env, "java/lang/NullPointerException", "name");
+ ScopedStringChars name16(env, name);
+ if (name16.get() == NULL) {
return 0;
}
@@ -584,8 +562,6 @@ static jint android_content_AssetManager_getResourceIdentifier(JNIEnv* env, jobj
return 0;
}
- const char16_t* name16 = env->GetStringChars(name, NULL);
- jsize nameLen = env->GetStringLength(name);
const char16_t* defType16 = defType
? env->GetStringChars(defType, NULL) : NULL;
jsize defTypeLen = defType
@@ -596,7 +572,7 @@ static jint android_content_AssetManager_getResourceIdentifier(JNIEnv* env, jobj
? env->GetStringLength(defPackage) : 0;
jint ident = am->getResources().identifierForName(
- name16, nameLen, defType16, defTypeLen, defPackage16, defPackageLen);
+ name16.get(), name16.size(), defType16, defTypeLen, defPackage16, defPackageLen);
if (defPackage16) {
env->ReleaseStringChars(defPackage, defPackage16);
@@ -604,7 +580,6 @@ static jint android_content_AssetManager_getResourceIdentifier(JNIEnv* env, jobj
if (defType16) {
env->ReleaseStringChars(defType, defType16);
}
- env->ReleaseStringChars(name, name16);
return ident;
}
@@ -616,12 +591,12 @@ static jstring android_content_AssetManager_getResourceName(JNIEnv* env, jobject
if (am == NULL) {
return NULL;
}
-
+
ResTable::resource_name name;
if (!am->getResources().getResourceName(resid, &name)) {
return NULL;
}
-
+
String16 str;
if (name.package != NULL) {
str.setTo(name.package, name.packageLen);
@@ -640,7 +615,7 @@ static jstring android_content_AssetManager_getResourceName(JNIEnv* env, jobject
}
str.append(name.name, name.nameLen);
}
-
+
return env->NewString((const jchar*)str.string(), str.size());
}
@@ -651,16 +626,16 @@ static jstring android_content_AssetManager_getResourcePackageName(JNIEnv* env,
if (am == NULL) {
return NULL;
}
-
+
ResTable::resource_name name;
if (!am->getResources().getResourceName(resid, &name)) {
return NULL;
}
-
+
if (name.package != NULL) {
return env->NewString((const jchar*)name.package, name.packageLen);
}
-
+
return NULL;
}
@@ -671,16 +646,16 @@ static jstring android_content_AssetManager_getResourceTypeName(JNIEnv* env, job
if (am == NULL) {
return NULL;
}
-
+
ResTable::resource_name name;
if (!am->getResources().getResourceName(resid, &name)) {
return NULL;
}
-
+
if (name.type != NULL) {
return env->NewString((const jchar*)name.type, name.typeLen);
}
-
+
return NULL;
}
@@ -691,16 +666,16 @@ static jstring android_content_AssetManager_getResourceEntryName(JNIEnv* env, jo
if (am == NULL) {
return NULL;
}
-
+
ResTable::resource_name name;
if (!am->getResources().getResourceName(resid, &name)) {
return NULL;
}
-
+
if (name.name != NULL) {
return env->NewString((const jchar*)name.name, name.nameLen);
}
-
+
return NULL;
}
@@ -748,10 +723,10 @@ static jint android_content_AssetManager_loadResourceBagValue(JNIEnv* env, jobje
return 0;
}
const ResTable& res(am->getResources());
-
+
// Now lock down the resource object and start pulling stuff from it.
res.lock();
-
+
ssize_t block = -1;
Res_value value;
@@ -772,7 +747,7 @@ static jint android_content_AssetManager_loadResourceBagValue(JNIEnv* env, jobje
if (block < 0) {
return block;
}
-
+
uint32_t ref = ident;
if (resolve) {
block = res.resolveReference(&value, block, &ref, &typeSpecFlags);
@@ -883,25 +858,9 @@ static void android_content_AssetManager_dumpTheme(JNIEnv* env, jobject clazz,
{
ResTable::Theme* theme = (ResTable::Theme*)themeInt;
const ResTable& res(theme->getResTable());
-
- if (tag == NULL) {
- jniThrowException(env, "java/lang/NullPointerException", "tag");
- return;
- }
-
- const char* tag8 = env->GetStringUTFChars(tag, NULL);
- const char* prefix8 = NULL;
- if (prefix != NULL) {
- prefix8 = env->GetStringUTFChars(prefix, NULL);
- }
-
+
// XXX Need to use params.
theme->dumpToLog();
-
- if (prefix8 != NULL) {
- env->ReleaseStringUTFChars(prefix, prefix8);
- }
- env->ReleaseStringUTFChars(tag, tag8);
}
static jboolean android_content_AssetManager_applyStyle(JNIEnv* env, jobject clazz,
@@ -914,21 +873,21 @@ static jboolean android_content_AssetManager_applyStyle(JNIEnv* env, jobject cla
jintArray outIndices)
{
if (themeToken == 0) {
- jniThrowException(env, "java/lang/NullPointerException", "theme token");
+ jniThrowNullPointerException(env, "theme token");
return JNI_FALSE;
}
if (attrs == NULL) {
- jniThrowException(env, "java/lang/NullPointerException", "attrs");
+ jniThrowNullPointerException(env, "attrs");
return JNI_FALSE;
}
if (outValues == NULL) {
- jniThrowException(env, "java/lang/NullPointerException", "out values");
+ jniThrowNullPointerException(env, "out values");
return JNI_FALSE;
}
DEBUG_STYLES(LOGI("APPLY STYLE: theme=0x%x defStyleAttr=0x%x defStyleRes=0x%x xml=0x%x",
themeToken, defStyleAttr, defStyleRes, xmlParserToken));
-
+
ResTable::Theme* theme = (ResTable::Theme*)themeToken;
const ResTable& res = theme->getResTable();
ResXMLParser* xmlParser = (ResXMLParser*)xmlParserToken;
@@ -944,7 +903,6 @@ static jboolean android_content_AssetManager_applyStyle(JNIEnv* env, jobject cla
jint* src = (jint*)env->GetPrimitiveArrayCritical(attrs, 0);
if (src == NULL) {
- jniThrowException(env, "java/lang/OutOfMemoryError", "");
return JNI_FALSE;
}
@@ -952,7 +910,6 @@ static jboolean android_content_AssetManager_applyStyle(JNIEnv* env, jobject cla
jint* dest = baseDest;
if (dest == NULL) {
env->ReleasePrimitiveArrayCritical(attrs, src, 0);
- jniThrowException(env, "java/lang/OutOfMemoryError", "");
return JNI_FALSE;
}
@@ -1027,7 +984,7 @@ static jboolean android_content_AssetManager_applyStyle(JNIEnv* env, jobject cla
const uint32_t curIdent = (uint32_t)src[ii];
DEBUG_STYLES(LOGI("RETRIEVING ATTR 0x%08x...", curIdent));
-
+
// Try to find a value for this attribute... we prioritize values
// coming from, first XML attributes, then XML style, then default
// style, and finally the theme.
@@ -1130,12 +1087,12 @@ static jboolean android_content_AssetManager_applyStyle(JNIEnv* env, jobject cla
dest[STYLE_RESOURCE_ID] = resid;
dest[STYLE_CHANGING_CONFIGURATIONS] = typeSetFlags;
dest[STYLE_DENSITY] = config.density;
-
+
if (indices != NULL && value.dataType != Res_value::TYPE_NULL) {
indicesIdx++;
indices[indicesIdx] = ii;
}
-
+
dest += STYLE_NUM_ENTRIES;
}
@@ -1158,18 +1115,18 @@ static jboolean android_content_AssetManager_retrieveAttributes(JNIEnv* env, job
jintArray outIndices)
{
if (xmlParserToken == 0) {
- jniThrowException(env, "java/lang/NullPointerException", "xmlParserToken");
+ jniThrowNullPointerException(env, "xmlParserToken");
return JNI_FALSE;
}
if (attrs == NULL) {
- jniThrowException(env, "java/lang/NullPointerException", "attrs");
+ jniThrowNullPointerException(env, "attrs");
return JNI_FALSE;
}
if (outValues == NULL) {
- jniThrowException(env, "java/lang/NullPointerException", "out values");
+ jniThrowNullPointerException(env, "out values");
return JNI_FALSE;
}
-
+
AssetManager* am = assetManagerForJavaObject(env, clazz);
if (am == NULL) {
return JNI_FALSE;
@@ -1178,28 +1135,26 @@ static jboolean android_content_AssetManager_retrieveAttributes(JNIEnv* env, job
ResXMLParser* xmlParser = (ResXMLParser*)xmlParserToken;
ResTable_config config;
Res_value value;
-
+
const jsize NI = env->GetArrayLength(attrs);
const jsize NV = env->GetArrayLength(outValues);
if (NV < (NI*STYLE_NUM_ENTRIES)) {
jniThrowException(env, "java/lang/IndexOutOfBoundsException", "out values too small");
return JNI_FALSE;
}
-
+
jint* src = (jint*)env->GetPrimitiveArrayCritical(attrs, 0);
if (src == NULL) {
- jniThrowException(env, "java/lang/OutOfMemoryError", "");
return JNI_FALSE;
}
-
+
jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0);
jint* dest = baseDest;
if (dest == NULL) {
env->ReleasePrimitiveArrayCritical(attrs, src, 0);
- jniThrowException(env, "java/lang/OutOfMemoryError", "");
return JNI_FALSE;
}
-
+
jint* indices = NULL;
int indicesIdx = 0;
if (outIndices != NULL) {
@@ -1210,27 +1165,27 @@ static jboolean android_content_AssetManager_retrieveAttributes(JNIEnv* env, job
// Now lock down the resource object and start pulling stuff from it.
res.lock();
-
+
// Retrieve the XML attributes, if requested.
const jsize NX = xmlParser->getAttributeCount();
jsize ix=0;
uint32_t curXmlAttr = xmlParser->getAttributeNameResID(ix);
-
+
static const ssize_t kXmlBlock = 0x10000000;
-
+
// Now iterate through all of the attributes that the client has requested,
// filling in each with whatever data we can find.
ssize_t block = 0;
uint32_t typeSetFlags;
for (jsize ii=0; ii<NI; ii++) {
const uint32_t curIdent = (uint32_t)src[ii];
-
+
// Try to find a value for this attribute...
value.dataType = Res_value::TYPE_NULL;
value.data = 0;
typeSetFlags = 0;
config.density = 0;
-
+
// Skip through XML attributes until the end or the next possible match.
while (ix < NX && curIdent > curXmlAttr) {
ix++;
@@ -1243,7 +1198,7 @@ static jboolean android_content_AssetManager_retrieveAttributes(JNIEnv* env, job
ix++;
curXmlAttr = xmlParser->getAttributeNameResID(ix);
}
-
+
//printf("Attribute 0x%08x: type=0x%x, data=0x%08x\n", curIdent, value.dataType, value.data);
uint32_t resid = 0;
if (value.dataType != Res_value::TYPE_NULL) {
@@ -1259,14 +1214,14 @@ static jboolean android_content_AssetManager_retrieveAttributes(JNIEnv* env, job
#endif
if (newBlock >= 0) block = newBlock;
}
-
+
// Deal with the special @null value -- it turns back to TYPE_NULL.
if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) {
value.dataType = Res_value::TYPE_NULL;
}
-
+
//printf("Attribute 0x%08x: final type=0x%x, data=0x%08x\n", curIdent, value.dataType, value.data);
-
+
// Write the final value back to Java.
dest[STYLE_TYPE] = value.dataType;
dest[STYLE_DATA] = value.data;
@@ -1275,25 +1230,25 @@ static jboolean android_content_AssetManager_retrieveAttributes(JNIEnv* env, job
dest[STYLE_RESOURCE_ID] = resid;
dest[STYLE_CHANGING_CONFIGURATIONS] = typeSetFlags;
dest[STYLE_DENSITY] = config.density;
-
+
if (indices != NULL && value.dataType != Res_value::TYPE_NULL) {
indicesIdx++;
indices[indicesIdx] = ii;
}
-
+
dest += STYLE_NUM_ENTRIES;
}
-
+
res.unlock();
-
+
if (indices != NULL) {
indices[0] = indicesIdx;
env->ReleasePrimitiveArrayCritical(outIndices, indices, 0);
}
-
+
env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0);
env->ReleasePrimitiveArrayCritical(attrs, src, 0);
-
+
return JNI_TRUE;
}
@@ -1305,12 +1260,12 @@ static jint android_content_AssetManager_getArraySize(JNIEnv* env, jobject clazz
return 0;
}
const ResTable& res(am->getResources());
-
+
res.lock();
const ResTable::bag_entry* defStyleEnt = NULL;
ssize_t bagOff = res.getBagLocked(id, &defStyleEnt);
res.unlock();
-
+
return bagOff;
}
@@ -1319,10 +1274,10 @@ static jint android_content_AssetManager_retrieveArray(JNIEnv* env, jobject claz
jintArray outValues)
{
if (outValues == NULL) {
- jniThrowException(env, "java/lang/NullPointerException", "out values");
+ jniThrowNullPointerException(env, "out values");
return JNI_FALSE;
}
-
+
AssetManager* am = assetManagerForJavaObject(env, clazz);
if (am == NULL) {
return JNI_FALSE;
@@ -1331,25 +1286,25 @@ static jint android_content_AssetManager_retrieveArray(JNIEnv* env, jobject claz
ResTable_config config;
Res_value value;
ssize_t block;
-
+
const jsize NV = env->GetArrayLength(outValues);
-
+
jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0);
jint* dest = baseDest;
if (dest == NULL) {
jniThrowException(env, "java/lang/OutOfMemoryError", "");
return JNI_FALSE;
}
-
+
// Now lock down the resource object and start pulling stuff from it.
res.lock();
-
+
const ResTable::bag_entry* arrayEnt = NULL;
uint32_t arrayTypeSetFlags = 0;
ssize_t bagOff = res.getBagLocked(id, &arrayEnt, &arrayTypeSetFlags);
const ResTable::bag_entry* endArrayEnt = arrayEnt +
(bagOff >= 0 ? bagOff : 0);
-
+
int i = 0;
uint32_t typeSetFlags;
while (i < NV && arrayEnt < endArrayEnt) {
@@ -1357,7 +1312,7 @@ static jint android_content_AssetManager_retrieveArray(JNIEnv* env, jobject claz
typeSetFlags = arrayTypeSetFlags;
config.density = 0;
value = arrayEnt->map.value;
-
+
uint32_t resid = 0;
if (value.dataType != Res_value::TYPE_NULL) {
// Take care of resolving the found resource to its final value.
@@ -1391,13 +1346,13 @@ static jint android_content_AssetManager_retrieveArray(JNIEnv* env, jobject claz
i+= STYLE_NUM_ENTRIES;
arrayEnt++;
}
-
+
i /= STYLE_NUM_ENTRIES;
-
+
res.unlock();
-
+
env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0);
-
+
return i;
}
@@ -1412,22 +1367,19 @@ static jint android_content_AssetManager_openXmlAssetNative(JNIEnv* env, jobject
LOGV("openXmlAsset in %p (Java object %p)\n", am, clazz);
- if (fileName == NULL) {
- jniThrowException(env, "java/lang/NullPointerException", "fileName");
+ ScopedUtfChars fileName8(env, fileName);
+ if (fileName8.c_str() == NULL) {
return 0;
}
- const char* fileName8 = env->GetStringUTFChars(fileName, NULL);
Asset* a = cookie
- ? am->openNonAsset((void*)cookie, fileName8, Asset::ACCESS_BUFFER)
- : am->openNonAsset(fileName8, Asset::ACCESS_BUFFER);
+ ? am->openNonAsset((void*)cookie, fileName8.c_str(), Asset::ACCESS_BUFFER)
+ : am->openNonAsset(fileName8.c_str(), Asset::ACCESS_BUFFER);
if (a == NULL) {
- jniThrowException(env, "java/io/FileNotFoundException", fileName8);
- env->ReleaseStringUTFChars(fileName, fileName8);
+ jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str());
return 0;
}
- env->ReleaseStringUTFChars(fileName, fileName8);
ResXMLTree* block = new ResXMLTree();
status_t err = block->setTo(a->getBuffer(true), a->getLength(), true);
@@ -1459,7 +1411,6 @@ static jintArray android_content_AssetManager_getArrayStringInfo(JNIEnv* env, jo
jintArray array = env->NewIntArray(N * 2);
if (array == NULL) {
- jniThrowException(env, "java/lang/OutOfMemoryError", "");
res.unlockBag(startOfBag);
return NULL;
}
@@ -1470,20 +1421,20 @@ static jintArray android_content_AssetManager_getArrayStringInfo(JNIEnv* env, jo
jint stringIndex = -1;
jint stringBlock = 0;
value = bag->map.value;
-
+
// Take care of resolving the found resource to its final value.
stringBlock = res.resolveReference(&value, bag->stringBlock, NULL);
if (value.dataType == Res_value::TYPE_STRING) {
stringIndex = value.data;
}
-
+
#if THROW_ON_BAD_ID
if (stringBlock == BAD_INDEX) {
jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
return array;
}
#endif
-
+
//todo: It might be faster to allocate a C array to contain
// the blocknums and indices, put them in there and then
// do just one SetIntArrayRegion()
@@ -1583,7 +1534,6 @@ static jintArray android_content_AssetManager_getArrayIntResource(JNIEnv* env, j
jintArray array = env->NewIntArray(N);
if (array == NULL) {
- jniThrowException(env, "java/lang/OutOfMemoryError", "");
res.unlockBag(startOfBag);
return NULL;
}
@@ -1592,7 +1542,7 @@ static jintArray android_content_AssetManager_getArrayIntResource(JNIEnv* env, j
const ResTable::bag_entry* bag = startOfBag;
for (size_t i=0; ((ssize_t)i)<N; i++, bag++) {
value = bag->map.value;
-
+
// Take care of resolving the found resource to its final value.
ssize_t block = res.resolveReference(&value, bag->stringBlock, NULL);
#if THROW_ON_BAD_ID
@@ -1647,7 +1597,7 @@ static jobject android_content_AssetManager_getAssetAllocations(JNIEnv* env, job
if (alloc.length() <= 0) {
return NULL;
}
-
+
jstring str = env->NewStringUTF(alloc.string());
return str;
}
@@ -1811,7 +1761,9 @@ int register_android_content_AssetManager(JNIEnv* env)
= env->GetFieldID(assetManager, "mObject", "I");
LOG_FATAL_IF(gAssetManagerOffsets.mObject == NULL, "Unable to find AssetManager.mObject");
- g_stringClass = env->FindClass("java/lang/String");
+ jclass stringClass = env->FindClass("java/lang/String");
+ LOG_FATAL_IF(stringClass == NULL, "Unable to find class java/lang/String");
+ g_stringClass = (jclass)env->NewGlobalRef(stringClass);
return AndroidRuntime::registerNativeMethods(env,
"android/content/res/AssetManager", gAssetManagerMethods, NELEM(gAssetManagerMethods));
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index eb1c437..b432d65 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -131,13 +131,6 @@ static struct log_offsets_t
jmethodID mLogE;
} gLogOffsets;
-static struct file_descriptor_offsets_t
-{
- jclass mClass;
- jmethodID mConstructor;
- jfieldID mDescriptor;
-} gFileDescriptorOffsets;
-
static struct parcel_file_descriptor_offsets_t
{
jclass mClass;
@@ -289,6 +282,16 @@ protected:
code, (int32_t)&data, (int32_t)reply, flags);
jthrowable excep = env->ExceptionOccurred();
+ if (excep) {
+ report_exception(env, excep,
+ "*** Uncaught remote exception! "
+ "(Exceptions are not yet supported across processes.)");
+ res = JNI_FALSE;
+
+ /* clean up JNI local ref -- we don't return to Java code */
+ env->DeleteLocalRef(excep);
+ }
+
// Restore the Java binder thread's state if it changed while
// processing a call (as it would if the Parcel's header had a
// new policy mask and Parcel.enforceInterface() changed
@@ -301,14 +304,12 @@ protected:
set_dalvik_blockguard_policy(env, strict_policy_before);
}
- if (excep) {
- report_exception(env, excep,
- "*** Uncaught remote exception! "
- "(Exceptions are not yet supported across processes.)");
- res = JNI_FALSE;
-
+ jthrowable excep2 = env->ExceptionOccurred();
+ if (excep2) {
+ report_exception(env, excep2,
+ "*** Uncaught exception in onBinderStrictModePolicyChange");
/* clean up JNI local ref -- we don't return to Java code */
- env->DeleteLocalRef(excep);
+ env->DeleteLocalRef(excep2);
}
//aout << "onTransact to Java code; result=" << res << endl
@@ -591,17 +592,6 @@ Parcel* parcelForJavaObject(JNIEnv* env, jobject obj)
return NULL;
}
-jobject newFileDescriptor(JNIEnv* env, int fd)
-{
- jobject object = env->NewObject(
- gFileDescriptorOffsets.mClass, gFileDescriptorOffsets.mConstructor);
- if (object != NULL) {
- //LOGI("Created new FileDescriptor %p with fd %d\n", object, fd);
- env->SetIntField(object, gFileDescriptorOffsets.mDescriptor, fd);
- }
- return object;
-}
-
jobject newParcelFileDescriptor(JNIEnv* env, jobject fileDesc)
{
return env->NewObject(
@@ -993,7 +983,7 @@ static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj,
jobject replyObj, jint flags)
{
if (dataObj == NULL) {
- jniThrowException(env, "java/lang/NullPointerException", NULL);
+ jniThrowNullPointerException(env, NULL);
return JNI_FALSE;
}
@@ -1045,7 +1035,7 @@ static void android_os_BinderProxy_linkToDeath(JNIEnv* env, jobject obj,
jobject recipient, jint flags)
{
if (recipient == NULL) {
- jniThrowException(env, "java/lang/NullPointerException", NULL);
+ jniThrowNullPointerException(env, NULL);
return;
}
@@ -1077,7 +1067,7 @@ static jboolean android_os_BinderProxy_unlinkToDeath(JNIEnv* env, jobject obj,
{
jboolean res = JNI_FALSE;
if (recipient == NULL) {
- jniThrowException(env, "java/lang/NullPointerException", NULL);
+ jniThrowNullPointerException(env, NULL);
return res;
}
@@ -1166,7 +1156,7 @@ static int int_register_android_os_BinderProxy(JNIEnv* env)
clazz = env->FindClass("java/lang/Error");
LOG_FATAL_IF(clazz == NULL, "Unable to find class java.lang.Error");
gErrorOffsets.mClass = (jclass) env->NewGlobalRef(clazz);
-
+
clazz = env->FindClass(kBinderProxyPathName);
LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.BinderProxy");
@@ -1358,8 +1348,8 @@ static void android_os_Parcel_writeFileDescriptor(JNIEnv* env, jobject clazz, jo
{
Parcel* parcel = parcelForJavaObject(env, clazz);
if (parcel != NULL) {
- const status_t err = parcel->writeDupFileDescriptor(
- env->GetIntField(object, gFileDescriptorOffsets.mDescriptor));
+ const status_t err =
+ parcel->writeDupFileDescriptor(jniGetFDFromFileDescriptor(env, object));
if (err != NO_ERROR) {
jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
}
@@ -1459,13 +1449,7 @@ static jobject android_os_Parcel_readFileDescriptor(JNIEnv* env, jobject clazz)
if (fd < 0) return NULL;
fd = dup(fd);
if (fd < 0) return NULL;
- jobject object = env->NewObject(
- gFileDescriptorOffsets.mClass, gFileDescriptorOffsets.mConstructor);
- if (object != NULL) {
- //LOGI("Created new FileDescriptor %p with fd %d\n", object, fd);
- env->SetIntField(object, gFileDescriptorOffsets.mDescriptor, fd);
- }
- return object;
+ return jniCreateFileDescriptor(env, fd);
}
return NULL;
}
@@ -1474,7 +1458,7 @@ static jobject android_os_Parcel_openFileDescriptor(JNIEnv* env, jobject clazz,
jstring name, jint mode)
{
if (name == NULL) {
- jniThrowException(env, "java/lang/NullPointerException", NULL);
+ jniThrowNullPointerException(env, NULL);
return NULL;
}
const jchar* str = env->GetStringCritical(name, 0);
@@ -1512,7 +1496,7 @@ static jobject android_os_Parcel_openFileDescriptor(JNIEnv* env, jobject clazz,
jniThrowException(env, "java/io/FileNotFoundException", strerror(errno));
return NULL;
}
- jobject object = newFileDescriptor(env, fd);
+ jobject object = jniCreateFileDescriptor(env, fd);
if (object == NULL) {
close(fd);
}
@@ -1522,10 +1506,10 @@ static jobject android_os_Parcel_openFileDescriptor(JNIEnv* env, jobject clazz,
static jobject android_os_Parcel_dupFileDescriptor(JNIEnv* env, jobject clazz, jobject orig)
{
if (orig == NULL) {
- jniThrowException(env, "java/lang/NullPointerException", NULL);
+ jniThrowNullPointerException(env, NULL);
return NULL;
}
- int origfd = env->GetIntField(orig, gFileDescriptorOffsets.mDescriptor);
+ int origfd = jniGetFDFromFileDescriptor(env, orig);
if (origfd < 0) {
jniThrowException(env, "java/lang/IllegalArgumentException", "bad FileDescriptor");
return NULL;
@@ -1533,10 +1517,10 @@ static jobject android_os_Parcel_dupFileDescriptor(JNIEnv* env, jobject clazz, j
int fd = dup(origfd);
if (fd < 0) {
- jniThrowException(env, "java/io/IOException", strerror(errno));
+ jniThrowIOException(env, errno);
return NULL;
}
- jobject object = newFileDescriptor(env, fd);
+ jobject object = jniCreateFileDescriptor(env, fd);
if (object == NULL) {
close(fd);
}
@@ -1546,12 +1530,12 @@ static jobject android_os_Parcel_dupFileDescriptor(JNIEnv* env, jobject clazz, j
static void android_os_Parcel_closeFileDescriptor(JNIEnv* env, jobject clazz, jobject object)
{
if (object == NULL) {
- jniThrowException(env, "java/lang/NullPointerException", NULL);
+ jniThrowNullPointerException(env, NULL);
return;
}
- int fd = env->GetIntField(object, gFileDescriptorOffsets.mDescriptor);
+ int fd = jniGetFDFromFileDescriptor(env, object);
if (fd >= 0) {
- env->SetIntField(object, gFileDescriptorOffsets.mDescriptor, -1);
+ jniSetFileDescriptorOfFD(env, object, -1);
//LOGI("Closing ParcelFileDescriptor %d\n", fd);
close(fd);
}
@@ -1560,12 +1544,12 @@ static void android_os_Parcel_closeFileDescriptor(JNIEnv* env, jobject clazz, jo
static void android_os_Parcel_clearFileDescriptor(JNIEnv* env, jobject clazz, jobject object)
{
if (object == NULL) {
- jniThrowException(env, "java/lang/NullPointerException", NULL);
+ jniThrowNullPointerException(env, NULL);
return;
}
- int fd = env->GetIntField(object, gFileDescriptorOffsets.mDescriptor);
+ int fd = jniGetFDFromFileDescriptor(env, object);
if (fd >= 0) {
- env->SetIntField(object, gFileDescriptorOffsets.mDescriptor, -1);
+ jniSetFileDescriptorOfFD(env, object, -1);
}
}
@@ -1794,15 +1778,6 @@ static int int_register_android_os_Parcel(JNIEnv* env)
clazz, "e", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;)I");
assert(gLogOffsets.mLogE);
- clazz = env->FindClass("java/io/FileDescriptor");
- LOG_FATAL_IF(clazz == NULL, "Unable to find class java.io.FileDescriptor");
- gFileDescriptorOffsets.mClass = (jclass) env->NewGlobalRef(clazz);
- gFileDescriptorOffsets.mConstructor
- = env->GetMethodID(clazz, "<init>", "()V");
- gFileDescriptorOffsets.mDescriptor = env->GetFieldID(clazz, "descriptor", "I");
- LOG_FATAL_IF(gFileDescriptorOffsets.mDescriptor == NULL,
- "Unable to find descriptor field in java.io.FileDescriptor");
-
clazz = env->FindClass("android/os/ParcelFileDescriptor");
LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.ParcelFileDescriptor");
gParcelFileDescriptorOffsets.mClass = (jclass) env->NewGlobalRef(clazz);
@@ -1842,13 +1817,3 @@ int register_android_os_Binder(JNIEnv* env)
return -1;
return 0;
}
-
-namespace android {
-
-// Returns the Unix file descriptor for a ParcelFileDescriptor object
-int getParcelFileDescriptorFD(JNIEnv* env, jobject object)
-{
- return env->GetIntField(object, gFileDescriptorOffsets.mDescriptor);
-}
-
-}
diff --git a/core/jni/android_util_Binder.h b/core/jni/android_util_Binder.h
index 495e76a..0122691 100644
--- a/core/jni/android_util_Binder.h
+++ b/core/jni/android_util_Binder.h
@@ -2,16 +2,16 @@
**
** Copyright 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
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
**
-** http://www.apache.org/licenses/LICENSE-2.0
+** http://www.apache.org/licenses/LICENSE-2.0
**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
** limitations under the License.
*/
@@ -29,7 +29,6 @@ extern sp<IBinder> ibinderForJavaObject(JNIEnv* env, jobject obj);
// Note: does not type checking; must guarantee jobject is a Java Parcel
extern Parcel* parcelForJavaObject(JNIEnv* env, jobject obj);
-extern jobject newFileDescriptor(JNIEnv* env, int fd);
extern jobject newParcelFileDescriptor(JNIEnv* env, jobject fileDesc);
}
diff --git a/core/jni/android_util_EventLog.cpp b/core/jni/android_util_EventLog.cpp
index e43478c..5d51110 100644
--- a/core/jni/android_util_EventLog.cpp
+++ b/core/jni/android_util_EventLog.cpp
@@ -35,9 +35,6 @@ static jmethodID gEventInitID;
static jclass gIntegerClass;
static jfieldID gIntegerValueID;
-static jclass gListClass;
-static jfieldID gListItemsID;
-
static jclass gLongClass;
static jfieldID gLongValueID;
@@ -151,7 +148,7 @@ static void android_util_EventLog_readEvents(JNIEnv* env, jobject clazz,
jintArray tags,
jobject out) {
if (tags == NULL || out == NULL) {
- jniThrowException(env, "java/lang/NullPointerException", NULL);
+ jniThrowNullPointerException(env, NULL);
return;
}
diff --git a/core/jni/android_util_Log.cpp b/core/jni/android_util_Log.cpp
index 6b97951..0fbe0e7 100644
--- a/core/jni/android_util_Log.cpp
+++ b/core/jni/android_util_Log.cpp
@@ -2,16 +2,16 @@
**
** Copyright 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
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
**
-** http://www.apache.org/licenses/LICENSE-2.0
+** http://www.apache.org/licenses/LICENSE-2.0
**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
** limitations under the License.
*/
@@ -24,6 +24,7 @@
#include <utils/String8.h>
#include "jni.h"
+#include "JNIHelp.h"
#include "utils/misc.h"
#include "android_runtime/AndroidRuntime.h"
@@ -41,7 +42,7 @@ struct levels_t {
};
static levels_t levels;
-static int toLevel(const char* value)
+static int toLevel(const char* value)
{
switch (value[0]) {
case 'V': return levels.verbose;
@@ -67,13 +68,12 @@ static jboolean android_util_Log_isLoggable(JNIEnv* env, jobject clazz, jstring
if (tag == NULL) {
return false;
}
-
+
jboolean result = false;
-
+
const char* chars = env->GetStringUTFChars(tag, NULL);
if ((strlen(chars)+sizeof(LOG_NAMESPACE)) > PROPERTY_KEY_MAX) {
- jclass clazz = env->FindClass("java/lang/IllegalArgumentException");
char buf2[200];
snprintf(buf2, sizeof(buf2), "Log tag \"%s\" exceeds limit of %d characters\n",
chars, PROPERTY_KEY_MAX - sizeof(LOG_NAMESPACE));
@@ -81,13 +81,13 @@ static jboolean android_util_Log_isLoggable(JNIEnv* env, jobject clazz, jstring
// release the chars!
env->ReleaseStringUTFChars(tag, chars);
- env->ThrowNew(clazz, buf2);
+ jniThrowException(env, "java/lang/IllegalArgumentException", buf2);
return false;
} else {
strncpy(key, LOG_NAMESPACE, sizeof(LOG_NAMESPACE)-1);
strcpy(key + sizeof(LOG_NAMESPACE) - 1, chars);
}
-
+
env->ReleaseStringUTFChars(tag, chars);
len = property_get(key, buf, "");
@@ -107,22 +107,12 @@ static jint android_util_Log_println_native(JNIEnv* env, jobject clazz,
const char* msg = NULL;
if (msgObj == NULL) {
- jclass npeClazz;
-
- npeClazz = env->FindClass("java/lang/NullPointerException");
- assert(npeClazz != NULL);
-
- env->ThrowNew(npeClazz, "println needs a message");
+ jniThrowNullPointerException(env, "println needs a message");
return -1;
}
if (bufID < 0 || bufID >= LOG_ID_MAX) {
- jclass npeClazz;
-
- npeClazz = env->FindClass("java/lang/NullPointerException");
- assert(npeClazz != NULL);
-
- env->ThrowNew(npeClazz, "bad bufID");
+ jniThrowNullPointerException(env, "bad bufID");
return -1;
}
@@ -156,16 +146,15 @@ int register_android_util_Log(JNIEnv* env)
LOGE("Can't find android/util/Log");
return -1;
}
-
+
levels.verbose = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "VERBOSE", "I"));
levels.debug = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "DEBUG", "I"));
levels.info = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "INFO", "I"));
levels.warn = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "WARN", "I"));
levels.error = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "ERROR", "I"));
levels.assert = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "ASSERT", "I"));
-
+
return AndroidRuntime::registerNativeMethods(env, "android/util/Log", gMethods, NELEM(gMethods));
}
}; // namespace android
-
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index 7dfb716..e5c2848 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -2,16 +2,16 @@
**
** Copyright 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
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
**
-** http://www.apache.org/licenses/LICENSE-2.0
+** http://www.apache.org/licenses/LICENSE-2.0
**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
** limitations under the License.
*/
@@ -103,17 +103,6 @@ static void signalExceptionForGroupError(JNIEnv* env, jobject obj, int err)
}
}
-
-static void fakeProcessEntry(void* arg)
-{
- String8* cls = (String8*)arg;
-
- AndroidRuntime* jr = AndroidRuntime::getRuntime();
- jr->callMain(cls->string(), 0, NULL);
-
- delete cls;
-}
-
jint android_os_Process_myPid(JNIEnv* env, jobject clazz)
{
return getpid();
@@ -132,10 +121,10 @@ jint android_os_Process_myTid(JNIEnv* env, jobject clazz)
jint android_os_Process_getUidForName(JNIEnv* env, jobject clazz, jstring name)
{
if (name == NULL) {
- jniThrowException(env, "java/lang/NullPointerException", NULL);
+ jniThrowNullPointerException(env, NULL);
return -1;
}
-
+
const jchar* str16 = env->GetStringCritical(name, 0);
String8 name8;
if (str16) {
@@ -163,10 +152,10 @@ jint android_os_Process_getUidForName(JNIEnv* env, jobject clazz, jstring name)
jint android_os_Process_getGidForName(JNIEnv* env, jobject clazz, jstring name)
{
if (name == NULL) {
- jniThrowException(env, "java/lang/NullPointerException", NULL);
+ jniThrowNullPointerException(env, NULL);
return -1;
}
-
+
const jchar* str16 = env->GetStringCritical(name, 0);
String8 name8;
if (str16) {
@@ -200,14 +189,14 @@ void android_os_Process_setThreadGroup(JNIEnv* env, jobject clazz, int pid, jint
}
}
-void android_os_Process_setProcessGroup(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) {
+ if (grp > ANDROID_TGROUP_MAX || grp < 0) {
signalExceptionForGroupError(env, clazz, EINVAL);
return;
}
@@ -225,7 +214,7 @@ void android_os_Process_setProcessGroup(JNIEnv* env, jobject clazz, int pid, jin
cmdline[rc] = 0;
close(fd);
}
-
+
if (grp == ANDROID_TGROUP_BG_NONINTERACT) {
LOGD("setProcessGroup: vvv pid %d (%s)", pid, cmdline);
} else {
@@ -260,7 +249,7 @@ void android_os_Process_setProcessGroup(JNIEnv* env, jobject clazz, int pid, jin
// This task wants to stay at background
continue;
}
-
+
if (androidSetThreadSchedulingGroup(t_pid, grp) != NO_ERROR) {
signalExceptionForGroupError(env, clazz, errno);
break;
@@ -312,7 +301,7 @@ void android_os_Process_setThreadPriority(JNIEnv* env, jobject clazz,
signalExceptionForGroupError(env, clazz, errno);
}
}
-
+
//LOGI("Setting priority of %d: %d, getpriority returns %d\n",
// pid, pri, getpriority(PRIO_PROCESS, pid));
}
@@ -358,10 +347,10 @@ jboolean android_os_Process_setOomAdj(JNIEnv* env, jobject clazz,
void android_os_Process_setArgV0(JNIEnv* env, jobject clazz, jstring name)
{
if (name == NULL) {
- jniThrowException(env, "java/lang/NullPointerException", NULL);
+ jniThrowNullPointerException(env, NULL);
return;
}
-
+
const jchar* str = env->GetStringCritical(name, 0);
String8 name8;
if (str) {
@@ -406,16 +395,16 @@ static int pid_compare(const void* v1, const void* v2)
static jlong android_os_Process_getFreeMemory(JNIEnv* env, jobject clazz)
{
int fd = open("/proc/meminfo", O_RDONLY);
-
+
if (fd < 0) {
LOGW("Unable to open /proc/meminfo");
return -1;
}
-
+
char buffer[256];
const int len = read(fd, buffer, sizeof(buffer)-1);
close(fd);
-
+
if (len < 0) {
LOGW("Unable to read /proc/meminfo");
return -1;
@@ -424,10 +413,10 @@ static jlong android_os_Process_getFreeMemory(JNIEnv* env, jobject clazz)
int numFound = 0;
jlong mem = 0;
-
+
static const char* const sums[] = { "MemFree:", "Cached:", NULL };
static const int sumsLen[] = { strlen("MemFree:"), strlen("Cached:"), NULL };
-
+
char* p = buffer;
while (*p && numFound < 2) {
int i = 0;
@@ -450,7 +439,7 @@ static jlong android_os_Process_getFreeMemory(JNIEnv* env, jobject clazz)
}
p++;
}
-
+
return numFound > 0 ? mem : -1;
}
@@ -458,71 +447,71 @@ void android_os_Process_readProcLines(JNIEnv* env, jobject clazz, jstring fileSt
jobjectArray reqFields, jlongArray outFields)
{
//LOGI("getMemInfo: %p %p", reqFields, outFields);
-
+
if (fileStr == NULL || reqFields == NULL || outFields == NULL) {
- jniThrowException(env, "java/lang/NullPointerException", NULL);
+ jniThrowNullPointerException(env, NULL);
return;
}
-
+
const char* file8 = env->GetStringUTFChars(fileStr, NULL);
if (file8 == NULL) {
return;
}
String8 file(file8);
env->ReleaseStringUTFChars(fileStr, file8);
-
+
jsize count = env->GetArrayLength(reqFields);
if (count > env->GetArrayLength(outFields)) {
jniThrowException(env, "java/lang/IllegalArgumentException", "Array lengths differ");
return;
}
-
+
Vector<String8> fields;
int i;
-
+
for (i=0; i<count; i++) {
jobject obj = env->GetObjectArrayElement(reqFields, i);
if (obj != NULL) {
const char* str8 = env->GetStringUTFChars((jstring)obj, NULL);
//LOGI("String at %d: %p = %s", i, obj, str8);
if (str8 == NULL) {
- jniThrowException(env, "java/lang/NullPointerException", "Element in reqFields");
+ jniThrowNullPointerException(env, "Element in reqFields");
return;
}
fields.add(String8(str8));
env->ReleaseStringUTFChars((jstring)obj, str8);
} else {
- jniThrowException(env, "java/lang/NullPointerException", "Element in reqFields");
+ jniThrowNullPointerException(env, "Element in reqFields");
return;
}
}
-
+
jlong* sizesArray = env->GetLongArrayElements(outFields, 0);
if (sizesArray == NULL) {
return;
}
-
+
//LOGI("Clearing %d sizes", count);
for (i=0; i<count; i++) {
sizesArray[i] = 0;
}
-
+
int fd = open(file.string(), O_RDONLY);
-
+
if (fd >= 0) {
const size_t BUFFER_SIZE = 2048;
char* buffer = (char*)malloc(BUFFER_SIZE);
int len = read(fd, buffer, BUFFER_SIZE-1);
close(fd);
-
+
if (len < 0) {
LOGW("Unable to read %s", file.string());
len = 0;
}
buffer[len] = 0;
-
+
int foundCount = 0;
-
+
char* p = buffer;
while (*p && foundCount < count) {
bool skipToEol = true;
@@ -555,12 +544,12 @@ void android_os_Process_readProcLines(JNIEnv* env, jobject clazz, jstring fileSt
}
}
}
-
+
free(buffer);
} else {
LOGW("Unable to open %s", file.string());
}
-
+
//LOGI("Done!");
env->ReleaseLongArrayElements(outFields, sizesArray, 0);
}
@@ -569,33 +558,33 @@ jintArray android_os_Process_getPids(JNIEnv* env, jobject clazz,
jstring file, jintArray lastArray)
{
if (file == NULL) {
- jniThrowException(env, "java/lang/NullPointerException", NULL);
+ jniThrowNullPointerException(env, NULL);
return NULL;
}
-
+
const char* file8 = env->GetStringUTFChars(file, NULL);
if (file8 == NULL) {
jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
return NULL;
}
-
+
DIR* dirp = opendir(file8);
-
+
env->ReleaseStringUTFChars(file, file8);
-
+
if(dirp == NULL) {
return NULL;
}
-
+
jsize curCount = 0;
jint* curData = NULL;
if (lastArray != NULL) {
curCount = env->GetArrayLength(lastArray);
curData = env->GetIntArrayElements(lastArray, 0);
}
-
+
jint curPos = 0;
-
+
struct dirent* entry;
while ((entry=readdir(dirp)) != NULL) {
const char* p = entry->d_name;
@@ -604,7 +593,7 @@ jintArray android_os_Process_getPids(JNIEnv* env, jobject clazz,
p++;
}
if (*p != 0) continue;
-
+
char* end;
int pid = strtol(entry->d_name, &end, 10);
//LOGI("File %s pid=%d\n", entry->d_name, pid);
@@ -625,26 +614,26 @@ jintArray android_os_Process_getPids(JNIEnv* env, jobject clazz,
curCount = newCount;
curData = newData;
}
-
+
curData[curPos] = pid;
curPos++;
}
-
+
closedir(dirp);
-
+
if (curData != NULL && curPos > 0) {
qsort(curData, curPos, sizeof(jint), pid_compare);
}
-
+
while (curPos < curCount) {
curData[curPos] = -1;
curPos++;
}
-
+
if (curData != NULL) {
env->ReleaseIntArrayElements(lastArray, curData, 0);
}
-
+
return lastArray;
}
@@ -660,15 +649,15 @@ enum {
};
jboolean android_os_Process_parseProcLineArray(JNIEnv* env, jobject clazz,
- char* buffer, jint startIndex, jint endIndex, jintArray format,
+ char* buffer, jint startIndex, jint endIndex, jintArray format,
jobjectArray outStrings, jlongArray outLongs, jfloatArray outFloats)
{
-
+
const jsize NF = env->GetArrayLength(format);
const jsize NS = outStrings ? env->GetArrayLength(outStrings) : 0;
const jsize NL = outLongs ? env->GetArrayLength(outLongs) : 0;
const jsize NR = outFloats ? env->GetArrayLength(outFloats) : 0;
-
+
jint* formatData = env->GetIntArrayElements(format, 0);
jlong* longsData = outLongs ?
env->GetLongArrayElements(outLongs, 0) : NULL;
@@ -691,9 +680,9 @@ jboolean android_os_Process_parseProcLineArray(JNIEnv* env, jobject clazz,
jsize i = startIndex;
jsize di = 0;
-
+
jboolean res = JNI_TRUE;
-
+
for (jsize fi=0; fi<NF; fi++) {
const jint mode = formatData[fi];
if ((mode&PROC_PARENS) != 0) {
@@ -705,7 +694,7 @@ jboolean android_os_Process_parseProcLineArray(JNIEnv* env, jobject clazz,
res = JNI_FALSE;
break;
}
-
+
jsize end = -1;
if ((mode&PROC_PARENS) != 0) {
while (buffer[i] != ')' && i < endIndex) {
@@ -720,7 +709,7 @@ jboolean android_os_Process_parseProcLineArray(JNIEnv* env, jobject clazz,
if (end < 0) {
end = i;
}
-
+
if (i < endIndex) {
i++;
if ((mode&PROC_COMBINE) != 0) {
@@ -729,9 +718,9 @@ jboolean android_os_Process_parseProcLineArray(JNIEnv* env, jobject clazz,
}
}
}
-
+
//LOGI("Field %d: %d-%d dest=%d mode=0x%x\n", i, start, end, di, mode);
-
+
if ((mode&(PROC_OUT_FLOAT|PROC_OUT_LONG|PROC_OUT_STRING)) != 0) {
char c = buffer[end];
buffer[end] = 0;
@@ -751,7 +740,7 @@ jboolean android_os_Process_parseProcLineArray(JNIEnv* env, jobject clazz,
di++;
}
}
-
+
env->ReleaseIntArrayElements(format, formatData, 0);
if (longsData != NULL) {
env->ReleaseLongArrayElements(outLongs, longsData, 0);
@@ -759,22 +748,22 @@ jboolean android_os_Process_parseProcLineArray(JNIEnv* env, jobject clazz,
if (floatsData != NULL) {
env->ReleaseFloatArrayElements(outFloats, floatsData, 0);
}
-
+
return res;
}
jboolean android_os_Process_parseProcLine(JNIEnv* env, jobject clazz,
- jbyteArray buffer, jint startIndex, jint endIndex, jintArray format,
+ jbyteArray buffer, jint startIndex, jint endIndex, jintArray format,
jobjectArray outStrings, jlongArray outLongs, jfloatArray outFloats)
{
jbyte* bufferArray = env->GetByteArrayElements(buffer, NULL);
- jboolean result = android_os_Process_parseProcLineArray(env, clazz,
- (char*) bufferArray, startIndex, endIndex, format, outStrings,
+ jboolean result = android_os_Process_parseProcLineArray(env, clazz,
+ (char*) bufferArray, startIndex, endIndex, format, outStrings,
outLongs, outFloats);
-
+
env->ReleaseByteArrayElements(buffer, bufferArray, 0);
-
+
return result;
}
@@ -783,7 +772,7 @@ jboolean android_os_Process_readProcFile(JNIEnv* env, jobject clazz,
jlongArray outLongs, jfloatArray outFloats)
{
if (file == NULL || format == NULL) {
- jniThrowException(env, "java/lang/NullPointerException", NULL);
+ jniThrowNullPointerException(env, NULL);
return JNI_FALSE;
}
@@ -794,32 +783,32 @@ jboolean android_os_Process_readProcFile(JNIEnv* env, jobject clazz,
}
int fd = open(file8, O_RDONLY);
env->ReleaseStringUTFChars(file, file8);
-
+
if (fd < 0) {
//LOGW("Unable to open process file: %s\n", file8);
return JNI_FALSE;
}
-
+
char buffer[256];
const int len = read(fd, buffer, sizeof(buffer)-1);
close(fd);
-
+
if (len < 0) {
//LOGW("Unable to open process file: %s fd=%d\n", file8, fd);
return JNI_FALSE;
}
buffer[len] = 0;
-
- return android_os_Process_parseProcLineArray(env, clazz, buffer, 0, len,
+
+ return android_os_Process_parseProcLineArray(env, clazz, buffer, 0, len,
format, outStrings, outLongs, outFloats);
-
+
}
void android_os_Process_setApplicationObject(JNIEnv* env, jobject clazz,
jobject binderObject)
{
if (binderObject == NULL) {
- jniThrowException(env, "java/lang/NullPointerException", NULL);
+ jniThrowNullPointerException(env, NULL);
return;
}
@@ -846,11 +835,11 @@ static jlong android_os_Process_getElapsedCpuTime(JNIEnv* env, jobject clazz)
struct timespec ts;
int res = clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts);
-
+
if (res != 0) {
return (jlong) 0;
- }
-
+ }
+
nsecs_t when = seconds_to_nanoseconds(ts.tv_sec) + ts.tv_nsec;
return (jlong) nanoseconds_to_milliseconds(when);
}
@@ -915,11 +904,6 @@ const char* const kProcessPathName = "android/os/Process";
int register_android_os_Process(JNIEnv* env)
{
- jclass clazz;
-
- clazz = env->FindClass(kProcessPathName);
- LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.Process");
-
return AndroidRuntime::registerNativeMethods(
env, kProcessPathName,
methods, NELEM(methods));
diff --git a/core/jni/android_util_StringBlock.cpp b/core/jni/android_util_StringBlock.cpp
index a021efd..958ddb2 100644
--- a/core/jni/android_util_StringBlock.cpp
+++ b/core/jni/android_util_StringBlock.cpp
@@ -2,22 +2,23 @@
**
** Copyright 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
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
**
-** http://www.apache.org/licenses/LICENSE-2.0
+** http://www.apache.org/licenses/LICENSE-2.0
**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
** limitations under the License.
*/
#define LOG_TAG "StringBlock"
#include "jni.h"
+#include "JNIHelp.h"
#include <utils/misc.h>
#include <android_runtime/AndroidRuntime.h>
#include <utils/Log.h>
@@ -30,28 +31,18 @@ namespace android {
// ----------------------------------------------------------------------------
-static void doThrow(JNIEnv* env, const char* exc, const char* msg = NULL)
-{
- jclass npeClazz;
-
- npeClazz = env->FindClass(exc);
- LOG_FATAL_IF(npeClazz == NULL, "Unable to find class %s", exc);
-
- env->ThrowNew(npeClazz, msg);
-}
-
static jint android_content_StringBlock_nativeCreate(JNIEnv* env, jobject clazz,
jbyteArray bArray,
jint off, jint len)
{
if (bArray == NULL) {
- doThrow(env, "java/lang/NullPointerException");
+ jniThrowNullPointerException(env, NULL);
return 0;
}
jsize bLen = env->GetArrayLength(bArray);
if (off < 0 || off >= bLen || len < 0 || len > bLen || (off+len) > bLen) {
- doThrow(env, "java/lang/IndexOutOfBoundsException");
+ jniThrowException(env, "java/lang/IndexOutOfBoundsException", NULL);
return 0;
}
@@ -60,7 +51,7 @@ static jint android_content_StringBlock_nativeCreate(JNIEnv* env, jobject clazz,
env->ReleaseByteArrayElements(bArray, b, 0);
if (osb == NULL || osb->getError() != NO_ERROR) {
- doThrow(env, "java/lang/IllegalArgumentException");
+ jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
return 0;
}
@@ -72,7 +63,7 @@ static jint android_content_StringBlock_nativeGetSize(JNIEnv* env, jobject clazz
{
ResStringPool* osb = (ResStringPool*)token;
if (osb == NULL) {
- doThrow(env, "java/lang/NullPointerException");
+ jniThrowNullPointerException(env, NULL);
return 0;
}
@@ -84,7 +75,7 @@ static jstring android_content_StringBlock_nativeGetString(JNIEnv* env, jobject
{
ResStringPool* osb = (ResStringPool*)token;
if (osb == NULL) {
- doThrow(env, "java/lang/NullPointerException");
+ jniThrowNullPointerException(env, NULL);
return 0;
}
@@ -96,7 +87,7 @@ static jstring android_content_StringBlock_nativeGetString(JNIEnv* env, jobject
const char16_t* str = osb->stringAt(idx, &len);
if (str == NULL) {
- doThrow(env, "java/lang/IndexOutOfBoundsException");
+ jniThrowException(env, "java/lang/IndexOutOfBoundsException", NULL);
return 0;
}
@@ -108,7 +99,7 @@ static jintArray android_content_StringBlock_nativeGetStyle(JNIEnv* env, jobject
{
ResStringPool* osb = (ResStringPool*)token;
if (osb == NULL) {
- doThrow(env, "java/lang/NullPointerException");
+ jniThrowNullPointerException(env, NULL);
return NULL;
}
@@ -129,8 +120,7 @@ static jintArray android_content_StringBlock_nativeGetStyle(JNIEnv* env, jobject
}
jintArray array = env->NewIntArray((num*sizeof(ResStringPool_span))/sizeof(jint));
- if (array == NULL) {
- doThrow(env, "java/lang/OutOfMemoryError");
+ if (array == NULL) { // NewIntArray already threw OutOfMemoryError.
return NULL;
}
@@ -152,7 +142,7 @@ static void android_content_StringBlock_nativeDestroy(JNIEnv* env, jobject clazz
{
ResStringPool* osb = (ResStringPool*)token;
if (osb == NULL) {
- doThrow(env, "java/lang/NullPointerException");
+ jniThrowNullPointerException(env, NULL);
return;
}
@@ -185,4 +175,3 @@ int register_android_content_StringBlock(JNIEnv* env)
}
}; // namespace android
-
diff --git a/core/jni/android_util_XmlBlock.cpp b/core/jni/android_util_XmlBlock.cpp
index 8887fdc..45728db 100644
--- a/core/jni/android_util_XmlBlock.cpp
+++ b/core/jni/android_util_XmlBlock.cpp
@@ -2,22 +2,23 @@
**
** Copyright 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
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
**
-** http://www.apache.org/licenses/LICENSE-2.0
+** http://www.apache.org/licenses/LICENSE-2.0
**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
** limitations under the License.
*/
#define LOG_TAG "XmlBlock"
#include "jni.h"
+#include "JNIHelp.h"
#include <utils/misc.h>
#include <android_runtime/AndroidRuntime.h>
#include <utils/AssetManager.h>
@@ -31,28 +32,18 @@ namespace android {
// ----------------------------------------------------------------------------
-static void doThrow(JNIEnv* env, const char* exc, const char* msg = NULL)
-{
- jclass npeClazz;
-
- npeClazz = env->FindClass(exc);
- LOG_FATAL_IF(npeClazz == NULL, "Unable to find class %s", exc);
-
- env->ThrowNew(npeClazz, msg);
-}
-
static jint android_content_XmlBlock_nativeCreate(JNIEnv* env, jobject clazz,
jbyteArray bArray,
jint off, jint len)
{
if (bArray == NULL) {
- doThrow(env, "java/lang/NullPointerException");
+ jniThrowNullPointerException(env, NULL);
return 0;
}
jsize bLen = env->GetArrayLength(bArray);
if (off < 0 || off >= bLen || len < 0 || len > bLen || (off+len) > bLen) {
- doThrow(env, "java/lang/IndexOutOfBoundsException");
+ jniThrowException(env, "java/lang/IndexOutOfBoundsException", NULL);
return 0;
}
@@ -61,7 +52,7 @@ static jint android_content_XmlBlock_nativeCreate(JNIEnv* env, jobject clazz,
env->ReleaseByteArrayElements(bArray, b, 0);
if (osb == NULL || osb->getError() != NO_ERROR) {
- doThrow(env, "java/lang/IllegalArgumentException");
+ jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
return 0;
}
@@ -73,7 +64,7 @@ static jint android_content_XmlBlock_nativeGetStringBlock(JNIEnv* env, jobject c
{
ResXMLTree* osb = (ResXMLTree*)token;
if (osb == NULL) {
- doThrow(env, "java/lang/NullPointerException");
+ jniThrowNullPointerException(env, NULL);
return 0;
}
@@ -85,13 +76,13 @@ static jint android_content_XmlBlock_nativeCreateParseState(JNIEnv* env, jobject
{
ResXMLTree* osb = (ResXMLTree*)token;
if (osb == NULL) {
- doThrow(env, "java/lang/NullPointerException");
+ jniThrowNullPointerException(env, NULL);
return 0;
}
ResXMLParser* st = new ResXMLParser(*osb);
if (st == NULL) {
- doThrow(env, "java/lang/OutOfMemoryError");
+ jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
return 0;
}
@@ -125,9 +116,9 @@ static jint android_content_XmlBlock_nativeNext(JNIEnv* env, jobject clazz,
goto bad;
}
} while (true);
-
+
bad:
- doThrow(env, "org/xmlpull/v1/XmlPullParserException",
+ jniThrowException(env, "org/xmlpull/v1/XmlPullParserException",
"Corrupt XML binary file");
return ResXMLParser::BAD_DOCUMENT;
}
@@ -139,7 +130,7 @@ static jint android_content_XmlBlock_nativeGetNamespace(JNIEnv* env, jobject cla
if (st == NULL) {
return -1;
}
-
+
return (jint)st->getElementNamespaceID();
}
@@ -170,7 +161,7 @@ static jint android_content_XmlBlock_nativeGetLineNumber(JNIEnv* env, jobject cl
{
ResXMLParser* st = (ResXMLParser*)token;
if (st == NULL) {
- doThrow(env, "java/lang/NullPointerException");
+ jniThrowNullPointerException(env, NULL);
return 0;
}
@@ -182,7 +173,7 @@ static jint android_content_XmlBlock_nativeGetAttributeCount(JNIEnv* env, jobjec
{
ResXMLParser* st = (ResXMLParser*)token;
if (st == NULL) {
- doThrow(env, "java/lang/NullPointerException");
+ jniThrowNullPointerException(env, NULL);
return 0;
}
@@ -194,10 +185,10 @@ static jint android_content_XmlBlock_nativeGetAttributeNamespace(JNIEnv* env, jo
{
ResXMLParser* st = (ResXMLParser*)token;
if (st == NULL) {
- doThrow(env, "java/lang/NullPointerException");
+ jniThrowNullPointerException(env, NULL);
return 0;
}
-
+
return (jint)st->getAttributeNamespaceID(idx);
}
@@ -206,7 +197,7 @@ static jint android_content_XmlBlock_nativeGetAttributeName(JNIEnv* env, jobject
{
ResXMLParser* st = (ResXMLParser*)token;
if (st == NULL) {
- doThrow(env, "java/lang/NullPointerException");
+ jniThrowNullPointerException(env, NULL);
return 0;
}
@@ -218,7 +209,7 @@ static jint android_content_XmlBlock_nativeGetAttributeResource(JNIEnv* env, job
{
ResXMLParser* st = (ResXMLParser*)token;
if (st == NULL) {
- doThrow(env, "java/lang/NullPointerException");
+ jniThrowNullPointerException(env, NULL);
return 0;
}
@@ -230,7 +221,7 @@ static jint android_content_XmlBlock_nativeGetAttributeDataType(JNIEnv* env, job
{
ResXMLParser* st = (ResXMLParser*)token;
if (st == NULL) {
- doThrow(env, "java/lang/NullPointerException");
+ jniThrowNullPointerException(env, NULL);
return 0;
}
@@ -242,7 +233,7 @@ static jint android_content_XmlBlock_nativeGetAttributeData(JNIEnv* env, jobject
{
ResXMLParser* st = (ResXMLParser*)token;
if (st == NULL) {
- doThrow(env, "java/lang/NullPointerException");
+ jniThrowNullPointerException(env, NULL);
return 0;
}
@@ -254,7 +245,7 @@ static jint android_content_XmlBlock_nativeGetAttributeStringValue(JNIEnv* env,
{
ResXMLParser* st = (ResXMLParser*)token;
if (st == NULL) {
- doThrow(env, "java/lang/NullPointerException");
+ jniThrowNullPointerException(env, NULL);
return 0;
}
@@ -267,7 +258,7 @@ static jint android_content_XmlBlock_nativeGetAttributeIndex(JNIEnv* env, jobjec
{
ResXMLParser* st = (ResXMLParser*)token;
if (st == NULL || name == NULL) {
- doThrow(env, "java/lang/NullPointerException");
+ jniThrowNullPointerException(env, NULL);
return 0;
}
@@ -277,7 +268,7 @@ static jint android_content_XmlBlock_nativeGetAttributeIndex(JNIEnv* env, jobjec
ns16 = env->GetStringChars(ns, NULL);
nsLen = env->GetStringLength(ns);
}
-
+
const char16_t* name16 = env->GetStringChars(name, NULL);
jsize nameLen = env->GetStringLength(name);
@@ -296,7 +287,7 @@ static jint android_content_XmlBlock_nativeGetIdAttribute(JNIEnv* env, jobject c
{
ResXMLParser* st = (ResXMLParser*)token;
if (st == NULL) {
- doThrow(env, "java/lang/NullPointerException");
+ jniThrowNullPointerException(env, NULL);
return 0;
}
@@ -309,10 +300,10 @@ static jint android_content_XmlBlock_nativeGetClassAttribute(JNIEnv* env, jobjec
{
ResXMLParser* st = (ResXMLParser*)token;
if (st == NULL) {
- doThrow(env, "java/lang/NullPointerException");
+ jniThrowNullPointerException(env, NULL);
return 0;
}
-
+
ssize_t idx = st->indexOfClass();
return idx >= 0 ? (jint)st->getAttributeValueStringID(idx) : -1;
}
@@ -322,7 +313,7 @@ static jint android_content_XmlBlock_nativeGetStyleAttribute(JNIEnv* env, jobjec
{
ResXMLParser* st = (ResXMLParser*)token;
if (st == NULL) {
- doThrow(env, "java/lang/NullPointerException");
+ jniThrowNullPointerException(env, NULL);
return 0;
}
@@ -336,7 +327,7 @@ static jint android_content_XmlBlock_nativeGetStyleAttribute(JNIEnv* env, jobjec
return 0;
}
- return value.dataType == value.TYPE_REFERENCE
+ return value.dataType == value.TYPE_REFERENCE
|| value.dataType == value.TYPE_ATTRIBUTE
? value.data : 0;
}
@@ -346,7 +337,7 @@ static void android_content_XmlBlock_nativeDestroyParseState(JNIEnv* env, jobjec
{
ResXMLParser* st = (ResXMLParser*)token;
if (st == NULL) {
- doThrow(env, "java/lang/NullPointerException");
+ jniThrowNullPointerException(env, NULL);
return;
}
@@ -358,7 +349,7 @@ static void android_content_XmlBlock_nativeDestroy(JNIEnv* env, jobject clazz,
{
ResXMLTree* osb = (ResXMLTree*)token;
if (osb == NULL) {
- doThrow(env, "java/lang/NullPointerException");
+ jniThrowNullPointerException(env, NULL);
return;
}
@@ -423,4 +414,3 @@ int register_android_content_XmlBlock(JNIEnv* env)
}
}; // namespace android
-
diff --git a/core/jni/android_view_Display.cpp b/core/jni/android_view_Display.cpp
index c6aad2d..97f9fc3 100644
--- a/core/jni/android_view_Display.cpp
+++ b/core/jni/android_view_Display.cpp
@@ -24,6 +24,7 @@
#include <ui/DisplayInfo.h>
#include "jni.h"
+#include "JNIHelp.h"
#include <android_runtime/AndroidRuntime.h>
#include <utils/misc.h>
#include <utils/Log.h>
@@ -49,12 +50,6 @@ static int gLongSize = -1;
static int gOldSize = -1;
static int gNewSize = -1;
-static void doThrow(JNIEnv* env, const char* exc, const char* msg = NULL)
-{
- jclass npeClazz = env->FindClass(exc);
- env->ThrowNew(npeClazz, msg);
-}
-
// ----------------------------------------------------------------------------
static void android_view_Display_init(
@@ -63,7 +58,7 @@ static void android_view_Display_init(
DisplayInfo info;
status_t err = SurfaceComposerClient::getDisplayInfo(DisplayID(dpy), &info);
if (err < 0) {
- doThrow(env, "java/lang/IllegalArgumentException");
+ jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
return;
}
env->SetIntField(clazz, offsets.pixelFormat,info.pixelFormatInfo.format);
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index 5116f09..e539cd2 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -24,6 +24,8 @@
#include <android_runtime/AndroidRuntime.h>
#include <utils/ResourceTypes.h>
+#include <gui/SurfaceTexture.h>
+
#include <SkBitmap.h>
#include <SkCanvas.h>
#include <SkMatrix.h>
@@ -67,10 +69,13 @@ using namespace uirenderer;
#define RENDERER_LOGD(...)
#endif
+#define MODIFIER_SHADOW 1
+#define MODIFIER_SHADER 2
+#define MODIFIER_COLOR_FILTER 4
+
// ----------------------------------------------------------------------------
static struct {
- jclass clazz;
jmethodID set;
} gRectClassInfo;
@@ -363,6 +368,13 @@ static void android_view_GLES20Canvas_drawRects(JNIEnv* env, jobject clazz,
}
}
+static void android_view_GLES20Canvas_drawPoints(JNIEnv* env, jobject clazz,
+ OpenGLRenderer* renderer, jfloatArray points, jint offset, jint count, SkPaint* paint) {
+ jfloat* storage = env->GetFloatArrayElements(points, NULL);
+ renderer->drawPoints(storage + offset, count, paint);
+ env->ReleaseFloatArrayElements(points, storage, 0);
+}
+
static void android_view_GLES20Canvas_drawPath(JNIEnv* env, jobject clazz,
OpenGLRenderer* renderer, SkPath* path, SkPaint* paint) {
renderer->drawPath(path, paint);
@@ -371,9 +383,7 @@ static void android_view_GLES20Canvas_drawPath(JNIEnv* env, jobject clazz,
static void android_view_GLES20Canvas_drawLines(JNIEnv* env, jobject clazz,
OpenGLRenderer* renderer, jfloatArray points, jint offset, jint count, SkPaint* paint) {
jfloat* storage = env->GetFloatArrayElements(points, NULL);
-
renderer->drawLines(storage + offset, count, paint);
-
env->ReleaseFloatArrayElements(points, storage, 0);
}
@@ -382,10 +392,10 @@ static void android_view_GLES20Canvas_drawLines(JNIEnv* env, jobject clazz,
// ----------------------------------------------------------------------------
static void android_view_GLES20Canvas_resetModifiers(JNIEnv* env, jobject clazz,
- OpenGLRenderer* renderer) {
- renderer->resetShader();
- renderer->resetColorFilter();
- renderer->resetShadow();
+ OpenGLRenderer* renderer, jint modifiers) {
+ if (modifiers & MODIFIER_SHADOW) renderer->resetShadow();
+ if (modifiers & MODIFIER_SHADER) renderer->resetShader();
+ if (modifiers & MODIFIER_COLOR_FILTER) renderer->resetColorFilter();
}
static void android_view_GLES20Canvas_setupShader(JNIEnv* env, jobject clazz,
@@ -409,6 +419,20 @@ static void android_view_GLES20Canvas_setupShadow(JNIEnv* env, jobject clazz,
static void renderText(OpenGLRenderer* renderer, const jchar* text, int count,
jfloat x, jfloat y, int flags, SkPaint* paint) {
+#if 0 // TODO: replace "0" by "RTL_USE_HARFBUZZ" when renderer->drawGlyphs() is implemented
+ sp<TextLayoutCacheValue> value = gTextLayoutCache.getValue(
+ paint, text, 0, count, count, flags);
+ if (value == NULL) {
+ LOGE("Cannot get TextLayoutCache value");
+ return ;
+ }
+#if DEBUG_GLYPHS
+ logGlyphs(value);
+#endif
+ const jchar* glyphArray = value->getGlyphs();
+ int glyphCount = value->getGlyphsCount();
+ renderer->drawGlyphs((const char*) glyphArray, 0, glyphCount << 1, x, y, paint);
+#else
const jchar *workText;
jchar* buffer = NULL;
int32_t workBytes;
@@ -416,11 +440,26 @@ static void renderText(OpenGLRenderer* renderer, const jchar* text, int count,
renderer->drawText((const char*) workText, workBytes, count, x, y, paint);
free(buffer);
}
+#endif
}
static void renderTextRun(OpenGLRenderer* renderer, const jchar* text,
jint start, jint count, jint contextCount, jfloat x, jfloat y,
int flags, SkPaint* paint) {
+#if 0 // TODO: replace "0" by "RTL_USE_HARFBUZZ" when renderer->drawGlyphs() is implemented
+ sp<TextLayoutCacheValue> value = gTextLayoutCache.getValue(
+ paint, text, start, count, contextCount, flags);
+ if (value == NULL) {
+ LOGE("Cannot get TextLayoutCache value");
+ return ;
+ }
+#if DEBUG_GLYPHS
+ logGlyphs(value);
+#endif
+ const jchar* glyphArray = value->getGlyphs();
+ int glyphCount = value->getGlyphsCount();
+ renderer->drawGlyphs((const char*) glyphArray, 0, glyphCount << 1, x, y, paint);
+#else
uint8_t rtl = flags & 0x1;
if (rtl) {
SkAutoSTMalloc<80, jchar> buffer(contextCount);
@@ -433,6 +472,7 @@ static void renderTextRun(OpenGLRenderer* renderer, const jchar* text,
} else {
renderer->drawText((const char*) (text + start), count << 1, count, x, y, paint);
}
+#endif
}
static void android_view_GLES20Canvas_drawTextArray(JNIEnv* env, jobject clazz,
@@ -509,6 +549,11 @@ static bool android_view_GLES20Canvas_drawDisplayList(JNIEnv* env,
return redraw;
}
+static void android_view_GLES20Canvas_outputDisplayList(JNIEnv* env,
+ jobject clazz, OpenGLRenderer* renderer, DisplayList* displayList) {
+ renderer->outputDisplayList(displayList);
+}
+
// ----------------------------------------------------------------------------
// Layers
// ----------------------------------------------------------------------------
@@ -531,6 +576,19 @@ static OpenGLRenderer* android_view_GLES20Canvas_createLayerRenderer(JNIEnv* env
return NULL;
}
+static Layer* android_view_GLES20Canvas_createTextureLayer(JNIEnv* env, jobject clazz,
+ jintArray layerInfo) {
+ Layer* layer = LayerRenderer::createTextureLayer();
+
+ if (layer) {
+ jint* storage = env->GetIntArrayElements(layerInfo, NULL);
+ storage[0] = layer->texture;
+ env->ReleaseIntArrayElements(layerInfo, storage, 0);
+ }
+
+ return layer;
+}
+
static Layer* android_view_GLES20Canvas_createLayer(JNIEnv* env, jobject clazz,
jint width, jint height, jboolean isOpaque, jintArray layerInfo) {
Layer* layer = LayerRenderer::createLayer(width, height, isOpaque);
@@ -555,6 +613,16 @@ static void android_view_GLES20Canvas_resizeLayer(JNIEnv* env, jobject clazz,
env->ReleaseIntArrayElements(layerInfo, storage, 0);
}
+static void android_view_GLES20Canvas_updateTextureLayer(JNIEnv* env, jobject clazz,
+ Layer* layer, jint width, jint height, SurfaceTexture* surface) {
+ float transform[16];
+ surface->updateTexImage();
+ surface->getTransformMatrix(transform);
+ GLenum renderTarget = surface->getCurrentTextureTarget();
+
+ LayerRenderer::updateTextureLayer(layer, width, height, renderTarget, transform);
+}
+
static void android_view_GLES20Canvas_destroyLayer(JNIEnv* env, jobject clazz, Layer* layer) {
LayerRenderer::destroyLayer(layer);
}
@@ -584,6 +652,19 @@ static jboolean android_view_GLES20Canvas_isAvailable(JNIEnv* env, jobject clazz
}
// ----------------------------------------------------------------------------
+// Logging
+// ----------------------------------------------------------------------------
+
+static void
+android_app_ActivityThread_dumpGraphics(JNIEnv* env, jobject clazz, jobject javaFileDescriptor)
+{
+#ifdef USE_OPENGL_RENDERER
+ int fd = jniGetFDFromFileDescriptor(env, javaFileDescriptor);
+ android::uirenderer::DisplayList::outputLogBuffer(fd);
+#endif // USE_OPENGL_RENDERER
+}
+
+// ----------------------------------------------------------------------------
// JNI Glue
// ----------------------------------------------------------------------------
@@ -642,11 +723,12 @@ static JNINativeMethod gMethods[] = {
{ "nDrawCircle", "(IFFFI)V", (void*) android_view_GLES20Canvas_drawCircle },
{ "nDrawOval", "(IFFFFI)V", (void*) android_view_GLES20Canvas_drawOval },
{ "nDrawArc", "(IFFFFFFZI)V", (void*) android_view_GLES20Canvas_drawArc },
+ { "nDrawPoints", "(I[FIII)V", (void*) android_view_GLES20Canvas_drawPoints },
{ "nDrawPath", "(III)V", (void*) android_view_GLES20Canvas_drawPath },
{ "nDrawLines", "(I[FIII)V", (void*) android_view_GLES20Canvas_drawLines },
- { "nResetModifiers", "(I)V", (void*) android_view_GLES20Canvas_resetModifiers },
+ { "nResetModifiers", "(II)V", (void*) android_view_GLES20Canvas_resetModifiers },
{ "nSetupShader", "(II)V", (void*) android_view_GLES20Canvas_setupShader },
{ "nSetupColorFilter", "(II)V", (void*) android_view_GLES20Canvas_setupColorFilter },
{ "nSetupShadow", "(IFFFI)V", (void*) android_view_GLES20Canvas_setupShadow },
@@ -667,13 +749,15 @@ static JNINativeMethod gMethods[] = {
{ "nGetDisplayListRenderer", "(I)I", (void*) android_view_GLES20Canvas_getDisplayListRenderer },
{ "nDrawDisplayList", "(IIIILandroid/graphics/Rect;)Z",
(void*) android_view_GLES20Canvas_drawDisplayList },
-
+ { "nOutputDisplayList", "(II)V", (void*) android_view_GLES20Canvas_outputDisplayList },
{ "nInterrupt", "(I)V", (void*) android_view_GLES20Canvas_interrupt },
{ "nResume", "(I)V", (void*) android_view_GLES20Canvas_resume },
{ "nCreateLayerRenderer", "(I)I", (void*) android_view_GLES20Canvas_createLayerRenderer },
{ "nCreateLayer", "(IIZ[I)I", (void*) android_view_GLES20Canvas_createLayer },
{ "nResizeLayer", "(III[I)V" , (void*) android_view_GLES20Canvas_resizeLayer },
+ { "nCreateTextureLayer", "([I)I", (void*) android_view_GLES20Canvas_createTextureLayer },
+ { "nUpdateTextureLayer", "(IIII)V", (void*) android_view_GLES20Canvas_updateTextureLayer },
{ "nDestroyLayer", "(I)V", (void*) android_view_GLES20Canvas_destroyLayer },
{ "nDestroyLayerDeferred", "(I)V", (void*) android_view_GLES20Canvas_destroyLayerDeferred },
{ "nDrawLayer", "(IIFFI)V", (void*) android_view_GLES20Canvas_drawLayer },
@@ -681,12 +765,17 @@ static JNINativeMethod gMethods[] = {
#endif
};
+static JNINativeMethod gActivityThreadMethods[] = {
+ { "dumpGraphicsInfo", "(Ljava/io/FileDescriptor;)V",
+ (void*) android_app_ActivityThread_dumpGraphics }
+};
+
+
#ifdef USE_OPENGL_RENDERER
#define FIND_CLASS(var, className) \
var = env->FindClass(className); \
- LOG_FATAL_IF(! var, "Unable to find class " className); \
- var = jclass(env->NewGlobalRef(var));
-
+ LOG_FATAL_IF(! var, "Unable to find class " className);
+
#define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \
var = env->GetMethodID(clazz, methodName, methodDescriptor); \
LOG_FATAL_IF(! var, "Unable to find method " methodName);
@@ -696,10 +785,20 @@ static JNINativeMethod gMethods[] = {
#endif
int register_android_view_GLES20Canvas(JNIEnv* env) {
- FIND_CLASS(gRectClassInfo.clazz, "android/graphics/Rect");
- GET_METHOD_ID(gRectClassInfo.set, gRectClassInfo.clazz, "set", "(IIII)V");
+ jclass clazz;
+ FIND_CLASS(clazz, "android/graphics/Rect");
+ GET_METHOD_ID(gRectClassInfo.set, clazz, "set", "(IIII)V");
return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods));
}
+const char* const kActivityThreadPathName = "android/app/ActivityThread";
+
+int register_android_app_ActivityThread(JNIEnv* env)
+{
+ return AndroidRuntime::registerNativeMethods(
+ env, kActivityThreadPathName,
+ gActivityThreadMethods, NELEM(gActivityThreadMethods));
+}
+
};
diff --git a/core/jni/android_view_InputQueue.cpp b/core/jni/android_view_InputQueue.cpp
index b5a5d2e..80c4871 100644
--- a/core/jni/android_view_InputQueue.cpp
+++ b/core/jni/android_view_InputQueue.cpp
@@ -380,7 +380,7 @@ int NativeInputQueue::handleReceiveCallback(int receiveFd, int events, void* dat
#if DEBUG_DISPATCH_CYCLE
LOGD("channel '%s' ~ Received motion event.", connection->getInputChannelName());
#endif
- inputEventObj = android_view_MotionEvent_fromNative(env,
+ inputEventObj = android_view_MotionEvent_obtainAsCopy(env,
static_cast<MotionEvent*>(inputEvent));
dispatchMethodId = gInputQueueClassInfo.dispatchMotionEvent;
break;
diff --git a/core/jni/android_view_KeyCharacterMap.cpp b/core/jni/android_view_KeyCharacterMap.cpp
index bfeec4f..aba3a72 100644
--- a/core/jni/android_view_KeyCharacterMap.cpp
+++ b/core/jni/android_view_KeyCharacterMap.cpp
@@ -30,8 +30,6 @@ static struct {
} gKeyEventClassInfo;
static struct {
- jclass clazz;
-
jfieldID keyCode;
jfieldID metaState;
} gFallbackActionClassInfo;
@@ -165,8 +163,7 @@ static JNINativeMethod g_methods[] = {
#define FIND_CLASS(var, className) \
var = env->FindClass(className); \
- LOG_FATAL_IF(! var, "Unable to find class " className); \
- var = jclass(env->NewGlobalRef(var));
+ LOG_FATAL_IF(! var, "Unable to find class " className);
#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \
var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \
@@ -175,13 +172,15 @@ static JNINativeMethod g_methods[] = {
int register_android_text_KeyCharacterMap(JNIEnv* env)
{
FIND_CLASS(gKeyEventClassInfo.clazz, "android/view/KeyEvent");
+ gKeyEventClassInfo.clazz = jclass(env->NewGlobalRef(gKeyEventClassInfo.clazz));
- FIND_CLASS(gFallbackActionClassInfo.clazz, "android/view/KeyCharacterMap$FallbackAction");
+ jclass clazz;
+ FIND_CLASS(clazz, "android/view/KeyCharacterMap$FallbackAction");
- GET_FIELD_ID(gFallbackActionClassInfo.keyCode, gFallbackActionClassInfo.clazz,
+ GET_FIELD_ID(gFallbackActionClassInfo.keyCode, clazz,
"keyCode", "I");
- GET_FIELD_ID(gFallbackActionClassInfo.metaState, gFallbackActionClassInfo.clazz,
+ GET_FIELD_ID(gFallbackActionClassInfo.metaState, clazz,
"metaState", "I");
return AndroidRuntime::registerNativeMethods(env,
diff --git a/core/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp
index 97cba23..fef06b2 100644
--- a/core/jni/android_view_MotionEvent.cpp
+++ b/core/jni/android_view_MotionEvent.cpp
@@ -42,8 +42,6 @@ static struct {
} gMotionEventClassInfo;
static struct {
- jclass clazz;
-
jfieldID mPackedAxisBits;
jfieldID mPackedAxisValues;
jfieldID x;
@@ -57,9 +55,17 @@ static struct {
jfieldID orientation;
} gPointerCoordsClassInfo;
+static struct {
+ jfieldID id;
+ jfieldID toolType;
+} gPointerPropertiesClassInfo;
+
// ----------------------------------------------------------------------------
-static MotionEvent* android_view_MotionEvent_getNativePtr(JNIEnv* env, jobject eventObj) {
+MotionEvent* android_view_MotionEvent_getNativePtr(JNIEnv* env, jobject eventObj) {
+ if (!eventObj) {
+ return NULL;
+ }
return reinterpret_cast<MotionEvent*>(
env->GetIntField(eventObj, gMotionEventClassInfo.mNativePtr));
}
@@ -70,10 +76,10 @@ static void android_view_MotionEvent_setNativePtr(JNIEnv* env, jobject eventObj,
reinterpret_cast<int>(event));
}
-jobject android_view_MotionEvent_fromNative(JNIEnv* env, const MotionEvent* event) {
+jobject android_view_MotionEvent_obtainAsCopy(JNIEnv* env, const MotionEvent* event) {
jobject eventObj = env->CallStaticObjectMethod(gMotionEventClassInfo.clazz,
gMotionEventClassInfo.obtain);
- if (env->ExceptionCheck()) {
+ if (env->ExceptionCheck() || !eventObj) {
LOGE("An exception occurred while obtaining a motion event.");
LOGE_EX(env);
env->ExceptionClear();
@@ -90,18 +96,6 @@ jobject android_view_MotionEvent_fromNative(JNIEnv* env, const MotionEvent* even
return eventObj;
}
-status_t android_view_MotionEvent_toNative(JNIEnv* env, jobject eventObj,
- MotionEvent* event) {
- MotionEvent* srcEvent = android_view_MotionEvent_getNativePtr(env, eventObj);
- if (!srcEvent) {
- LOGE("MotionEvent was finalized");
- return BAD_VALUE;
- }
-
- event->copyFrom(srcEvent, true);
- return OK;
-}
-
status_t android_view_MotionEvent_recycle(JNIEnv* env, jobject eventObj) {
env->CallVoidMethod(eventObj, gMotionEventClassInfo.recycle);
if (env->ExceptionCheck()) {
@@ -126,17 +120,17 @@ static bool validatePointerCount(JNIEnv* env, jint pointerCount) {
return true;
}
-static bool validatePointerIdsArray(JNIEnv* env, jintArray pointerIdsArray,
+static bool validatePointerPropertiesArray(JNIEnv* env, jobjectArray pointerPropertiesObjArray,
size_t pointerCount) {
- if (!pointerIdsArray) {
+ if (!pointerPropertiesObjArray) {
jniThrowException(env, "java/lang/IllegalArgumentException",
- "pointerIds array must not be null");
+ "pointerProperties array must not be null");
return false;
}
- size_t length = size_t(env->GetArrayLength(pointerIdsArray));
+ size_t length = size_t(env->GetArrayLength(pointerPropertiesObjArray));
if (length < pointerCount) {
jniThrowException(env, "java/lang/IllegalArgumentException",
- "pointerIds array must be large enough to hold all pointers");
+ "pointerProperties array must be large enough to hold all pointers");
return false;
}
return true;
@@ -185,6 +179,15 @@ static bool validatePointerCoords(JNIEnv* env, jobject pointerCoordsObj) {
return true;
}
+static bool validatePointerProperties(JNIEnv* env, jobject pointerPropertiesObj) {
+ if (!pointerPropertiesObj) {
+ jniThrowException(env, "java/lang/IllegalArgumentException",
+ "pointerProperties must not be null");
+ return false;
+ }
+ return true;
+}
+
static void pointerCoordsToNative(JNIEnv* env, jobject pointerCoordsObj,
float xOffset, float yOffset, PointerCoords* outRawPointerCoords) {
outRawPointerCoords->clear();
@@ -311,17 +314,36 @@ static void pointerCoordsFromNative(JNIEnv* env, const PointerCoords* rawPointer
env->SetLongField(outPointerCoordsObj, gPointerCoordsClassInfo.mPackedAxisBits, outBits);
}
+static void pointerPropertiesToNative(JNIEnv* env, jobject pointerPropertiesObj,
+ PointerProperties* outPointerProperties) {
+ outPointerProperties->clear();
+ outPointerProperties->id = env->GetIntField(pointerPropertiesObj,
+ gPointerPropertiesClassInfo.id);
+ outPointerProperties->toolType = env->GetIntField(pointerPropertiesObj,
+ gPointerPropertiesClassInfo.toolType);
+}
+
+static void pointerPropertiesFromNative(JNIEnv* env, const PointerProperties* pointerProperties,
+ jobject outPointerPropertiesObj) {
+ env->SetIntField(outPointerPropertiesObj, gPointerPropertiesClassInfo.id,
+ pointerProperties->id);
+ env->SetIntField(outPointerPropertiesObj, gPointerPropertiesClassInfo.toolType,
+ pointerProperties->toolType);
+}
+
// ----------------------------------------------------------------------------
static jint android_view_MotionEvent_nativeInitialize(JNIEnv* env, jclass clazz,
jint nativePtr,
- jint deviceId, jint source, jint action, jint flags, jint edgeFlags, jint metaState,
+ jint deviceId, jint source, jint action, jint flags, jint edgeFlags,
+ jint metaState, jint buttonState,
jfloat xOffset, jfloat yOffset, jfloat xPrecision, jfloat yPrecision,
jlong downTimeNanos, jlong eventTimeNanos,
- jint pointerCount, jintArray pointerIdsArray, jobjectArray pointerCoordsObjArray) {
+ jint pointerCount, jobjectArray pointerPropertiesObjArray,
+ jobjectArray pointerCoordsObjArray) {
if (!validatePointerCount(env, pointerCount)
- || !validatePointerIdsArray(env, pointerIdsArray, pointerCount)
+ || !validatePointerPropertiesArray(env, pointerPropertiesObjArray, pointerCount)
|| !validatePointerCoordsObjArray(env, pointerCoordsObjArray, pointerCount)) {
return 0;
}
@@ -331,29 +353,37 @@ static jint android_view_MotionEvent_nativeInitialize(JNIEnv* env, jclass clazz,
event = new MotionEvent();
}
+ PointerProperties pointerProperties[pointerCount];
PointerCoords rawPointerCoords[pointerCount];
for (jint i = 0; i < pointerCount; i++) {
+ jobject pointerPropertiesObj = env->GetObjectArrayElement(pointerPropertiesObjArray, i);
+ if (!pointerPropertiesObj) {
+ goto Error;
+ }
+ pointerPropertiesToNative(env, pointerPropertiesObj, &pointerProperties[i]);
+ env->DeleteLocalRef(pointerPropertiesObj);
+
jobject pointerCoordsObj = env->GetObjectArrayElement(pointerCoordsObjArray, i);
if (!pointerCoordsObj) {
jniThrowNullPointerException(env, "pointerCoords");
- if (!nativePtr) {
- delete event;
- }
- return 0;
+ goto Error;
}
pointerCoordsToNative(env, pointerCoordsObj, xOffset, yOffset, &rawPointerCoords[i]);
env->DeleteLocalRef(pointerCoordsObj);
}
- int* pointerIds = static_cast<int*>(env->GetPrimitiveArrayCritical(pointerIdsArray, NULL));
-
- event->initialize(deviceId, source, action, flags, edgeFlags, metaState,
+ event->initialize(deviceId, source, action, flags, edgeFlags, metaState, buttonState,
xOffset, yOffset, xPrecision, yPrecision,
- downTimeNanos, eventTimeNanos, pointerCount, pointerIds, rawPointerCoords);
+ downTimeNanos, eventTimeNanos, pointerCount, pointerProperties, rawPointerCoords);
- env->ReleasePrimitiveArrayCritical(pointerIdsArray, pointerIds, JNI_ABORT);
return reinterpret_cast<jint>(event);
+
+Error:
+ if (!nativePtr) {
+ delete event;
+ }
+ return 0;
}
static jint android_view_MotionEvent_nativeCopy(JNIEnv* env, jclass clazz,
@@ -441,6 +471,12 @@ static jint android_view_MotionEvent_nativeGetFlags(JNIEnv* env, jclass clazz,
return event->getFlags();
}
+static void android_view_MotionEvent_nativeSetFlags(JNIEnv* env, jclass clazz,
+ jint nativePtr, jint flags) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ event->setFlags(flags);
+}
+
static jint android_view_MotionEvent_nativeGetEdgeFlags(JNIEnv* env, jclass clazz,
jint nativePtr) {
MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
@@ -459,12 +495,30 @@ static jint android_view_MotionEvent_nativeGetMetaState(JNIEnv* env, jclass claz
return event->getMetaState();
}
+static jint android_view_MotionEvent_nativeGetButtonState(JNIEnv* env, jclass clazz,
+ jint nativePtr) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ return event->getButtonState();
+}
+
static void android_view_MotionEvent_nativeOffsetLocation(JNIEnv* env, jclass clazz,
jint nativePtr, jfloat deltaX, jfloat deltaY) {
MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
return event->offsetLocation(deltaX, deltaY);
}
+static jfloat android_view_MotionEvent_nativeGetXOffset(JNIEnv* env, jclass clazz,
+ jint nativePtr) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ return event->getXOffset();
+}
+
+static jfloat android_view_MotionEvent_nativeGetYOffset(JNIEnv* env, jclass clazz,
+ jint nativePtr) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ return event->getYOffset();
+}
+
static jfloat android_view_MotionEvent_nativeGetXPrecision(JNIEnv* env, jclass clazz,
jint nativePtr) {
MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
@@ -483,6 +537,12 @@ static jlong android_view_MotionEvent_nativeGetDownTimeNanos(JNIEnv* env, jclass
return event->getDownTime();
}
+static void android_view_MotionEvent_nativeSetDownTimeNanos(JNIEnv* env, jclass clazz,
+ jint nativePtr, jlong downTimeNanos) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ event->setDownTime(downTimeNanos);
+}
+
static jint android_view_MotionEvent_nativeGetPointerCount(JNIEnv* env, jclass clazz,
jint nativePtr) {
MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
@@ -499,16 +559,20 @@ static jint android_view_MotionEvent_nativeGetPointerId(JNIEnv* env, jclass claz
return event->getPointerId(pointerIndex);
}
-static jint android_view_MotionEvent_nativeFindPointerIndex(JNIEnv* env, jclass clazz,
- jint nativePtr, jint pointerId) {
+static jint android_view_MotionEvent_nativeGetToolType(JNIEnv* env, jclass clazz,
+ jint nativePtr, jint pointerIndex) {
MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
size_t pointerCount = event->getPointerCount();
- for (size_t i = 0; i < pointerCount; i++) {
- if (event->getPointerId(i) == pointerId) {
- return i;
- }
+ if (!validatePointerIndex(env, pointerIndex, pointerCount)) {
+ return -1;
}
- return -1;
+ return event->getToolType(pointerIndex);
+}
+
+static jint android_view_MotionEvent_nativeFindPointerIndex(JNIEnv* env, jclass clazz,
+ jint nativePtr, jint pointerId) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ return jint(event->findPointerIndex(pointerId));
}
static jint android_view_MotionEvent_nativeGetHistorySize(JNIEnv* env, jclass clazz,
@@ -592,6 +656,19 @@ static void android_view_MotionEvent_nativeGetPointerCoords(JNIEnv* env, jclass
outPointerCoordsObj);
}
+static void android_view_MotionEvent_nativeGetPointerProperties(JNIEnv* env, jclass clazz,
+ jint nativePtr, jint pointerIndex, jobject outPointerPropertiesObj) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ size_t pointerCount = event->getPointerCount();
+ if (!validatePointerIndex(env, pointerIndex, pointerCount)
+ || !validatePointerProperties(env, outPointerPropertiesObj)) {
+ return;
+ }
+
+ const PointerProperties* pointerProperties = event->getPointerProperties(pointerIndex);
+ pointerPropertiesFromNative(env, pointerProperties, outPointerPropertiesObj);
+}
+
static void android_view_MotionEvent_nativeScale(JNIEnv* env, jclass clazz,
jint nativePtr, jfloat scale) {
MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
@@ -641,7 +718,8 @@ static void android_view_MotionEvent_nativeWriteToParcel(JNIEnv* env, jclass cla
static JNINativeMethod gMotionEventMethods[] = {
/* name, signature, funcPtr */
{ "nativeInitialize",
- "(IIIIIIIFFFFJJI[I[Landroid/view/MotionEvent$PointerCoords;)I",
+ "(IIIIIIIIFFFFJJI[Landroid/view/MotionEvent$PointerProperties;"
+ "[Landroid/view/MotionEvent$PointerCoords;)I",
(void*)android_view_MotionEvent_nativeInitialize },
{ "nativeCopy",
"(IIZ)I",
@@ -673,6 +751,9 @@ static JNINativeMethod gMotionEventMethods[] = {
{ "nativeGetFlags",
"(I)I",
(void*)android_view_MotionEvent_nativeGetFlags },
+ { "nativeSetFlags",
+ "(II)V",
+ (void*)android_view_MotionEvent_nativeSetFlags },
{ "nativeGetEdgeFlags",
"(I)I",
(void*)android_view_MotionEvent_nativeGetEdgeFlags },
@@ -682,9 +763,18 @@ static JNINativeMethod gMotionEventMethods[] = {
{ "nativeGetMetaState",
"(I)I",
(void*)android_view_MotionEvent_nativeGetMetaState },
+ { "nativeGetButtonState",
+ "(I)I",
+ (void*)android_view_MotionEvent_nativeGetButtonState },
{ "nativeOffsetLocation",
"(IFF)V",
(void*)android_view_MotionEvent_nativeOffsetLocation },
+ { "nativeGetXOffset",
+ "(I)F",
+ (void*)android_view_MotionEvent_nativeGetXOffset },
+ { "nativeGetYOffset",
+ "(I)F",
+ (void*)android_view_MotionEvent_nativeGetYOffset },
{ "nativeGetXPrecision",
"(I)F",
(void*)android_view_MotionEvent_nativeGetXPrecision },
@@ -694,12 +784,18 @@ static JNINativeMethod gMotionEventMethods[] = {
{ "nativeGetDownTimeNanos",
"(I)J",
(void*)android_view_MotionEvent_nativeGetDownTimeNanos },
+ { "nativeSetDownTimeNanos",
+ "(IJ)V",
+ (void*)android_view_MotionEvent_nativeSetDownTimeNanos },
{ "nativeGetPointerCount",
"(I)I",
(void*)android_view_MotionEvent_nativeGetPointerCount },
{ "nativeGetPointerId",
"(II)I",
(void*)android_view_MotionEvent_nativeGetPointerId },
+ { "nativeGetToolType",
+ "(II)I",
+ (void*)android_view_MotionEvent_nativeGetToolType },
{ "nativeFindPointerIndex",
"(II)I",
(void*)android_view_MotionEvent_nativeFindPointerIndex },
@@ -718,6 +814,9 @@ static JNINativeMethod gMotionEventMethods[] = {
{ "nativeGetPointerCoords",
"(IIILandroid/view/MotionEvent$PointerCoords;)V",
(void*)android_view_MotionEvent_nativeGetPointerCoords },
+ { "nativeGetPointerProperties",
+ "(IILandroid/view/MotionEvent$PointerProperties;)V",
+ (void*)android_view_MotionEvent_nativeGetPointerProperties },
{ "nativeScale",
"(IF)V",
(void*)android_view_MotionEvent_nativeScale },
@@ -734,8 +833,7 @@ static JNINativeMethod gMotionEventMethods[] = {
#define FIND_CLASS(var, className) \
var = env->FindClass(className); \
- LOG_FATAL_IF(! var, "Unable to find class " className); \
- var = jclass(env->NewGlobalRef(var));
+ LOG_FATAL_IF(! var, "Unable to find class " className);
#define GET_STATIC_METHOD_ID(var, clazz, methodName, fieldDescriptor) \
var = env->GetStaticMethodID(clazz, methodName, fieldDescriptor); \
@@ -755,6 +853,7 @@ int register_android_view_MotionEvent(JNIEnv* env) {
LOG_FATAL_IF(res < 0, "Unable to register native methods.");
FIND_CLASS(gMotionEventClassInfo.clazz, "android/view/MotionEvent");
+ gMotionEventClassInfo.clazz = jclass(env->NewGlobalRef(gMotionEventClassInfo.clazz));
GET_STATIC_METHOD_ID(gMotionEventClassInfo.obtain, gMotionEventClassInfo.clazz,
"obtain", "()Landroid/view/MotionEvent;");
@@ -763,31 +862,39 @@ int register_android_view_MotionEvent(JNIEnv* env) {
GET_FIELD_ID(gMotionEventClassInfo.mNativePtr, gMotionEventClassInfo.clazz,
"mNativePtr", "I");
- FIND_CLASS(gPointerCoordsClassInfo.clazz, "android/view/MotionEvent$PointerCoords");
+ jclass clazz;
+ FIND_CLASS(clazz, "android/view/MotionEvent$PointerCoords");
- GET_FIELD_ID(gPointerCoordsClassInfo.mPackedAxisBits, gPointerCoordsClassInfo.clazz,
+ GET_FIELD_ID(gPointerCoordsClassInfo.mPackedAxisBits, clazz,
"mPackedAxisBits", "J");
- GET_FIELD_ID(gPointerCoordsClassInfo.mPackedAxisValues, gPointerCoordsClassInfo.clazz,
+ GET_FIELD_ID(gPointerCoordsClassInfo.mPackedAxisValues, clazz,
"mPackedAxisValues", "[F");
- GET_FIELD_ID(gPointerCoordsClassInfo.x, gPointerCoordsClassInfo.clazz,
+ GET_FIELD_ID(gPointerCoordsClassInfo.x, clazz,
"x", "F");
- GET_FIELD_ID(gPointerCoordsClassInfo.y, gPointerCoordsClassInfo.clazz,
+ GET_FIELD_ID(gPointerCoordsClassInfo.y, clazz,
"y", "F");
- GET_FIELD_ID(gPointerCoordsClassInfo.pressure, gPointerCoordsClassInfo.clazz,
+ GET_FIELD_ID(gPointerCoordsClassInfo.pressure, clazz,
"pressure", "F");
- GET_FIELD_ID(gPointerCoordsClassInfo.size, gPointerCoordsClassInfo.clazz,
+ GET_FIELD_ID(gPointerCoordsClassInfo.size, clazz,
"size", "F");
- GET_FIELD_ID(gPointerCoordsClassInfo.touchMajor, gPointerCoordsClassInfo.clazz,
+ GET_FIELD_ID(gPointerCoordsClassInfo.touchMajor, clazz,
"touchMajor", "F");
- GET_FIELD_ID(gPointerCoordsClassInfo.touchMinor, gPointerCoordsClassInfo.clazz,
+ GET_FIELD_ID(gPointerCoordsClassInfo.touchMinor, clazz,
"touchMinor", "F");
- GET_FIELD_ID(gPointerCoordsClassInfo.toolMajor, gPointerCoordsClassInfo.clazz,
+ GET_FIELD_ID(gPointerCoordsClassInfo.toolMajor, clazz,
"toolMajor", "F");
- GET_FIELD_ID(gPointerCoordsClassInfo.toolMinor, gPointerCoordsClassInfo.clazz,
+ GET_FIELD_ID(gPointerCoordsClassInfo.toolMinor, clazz,
"toolMinor", "F");
- GET_FIELD_ID(gPointerCoordsClassInfo.orientation, gPointerCoordsClassInfo.clazz,
+ GET_FIELD_ID(gPointerCoordsClassInfo.orientation, clazz,
"orientation", "F");
+ FIND_CLASS(clazz, "android/view/MotionEvent$PointerProperties");
+
+ GET_FIELD_ID(gPointerPropertiesClassInfo.id, clazz,
+ "id", "I");
+ GET_FIELD_ID(gPointerPropertiesClassInfo.toolType, clazz,
+ "toolType", "I");
+
return 0;
}
diff --git a/core/jni/android_view_MotionEvent.h b/core/jni/android_view_MotionEvent.h
index 80dc861..0cf1fb2 100644
--- a/core/jni/android_view_MotionEvent.h
+++ b/core/jni/android_view_MotionEvent.h
@@ -26,12 +26,11 @@ class MotionEvent;
/* Obtains an instance of a DVM MotionEvent object as a copy of a native MotionEvent instance.
* Returns NULL on error. */
-extern jobject android_view_MotionEvent_fromNative(JNIEnv* env, const MotionEvent* event);
+extern jobject android_view_MotionEvent_obtainAsCopy(JNIEnv* env, const MotionEvent* event);
-/* Copies the contents of a DVM MotionEvent object to a native MotionEvent instance.
- * Returns non-zero on error. */
-extern status_t android_view_MotionEvent_toNative(JNIEnv* env, jobject eventObj,
- MotionEvent* event);
+/* Gets the underlying native MotionEvent instance within a DVM MotionEvent object.
+ * Returns NULL if the event is NULL or if it is uninitialized. */
+extern MotionEvent* android_view_MotionEvent_getNativePtr(JNIEnv* env, jobject eventObj);
/* Recycles a DVM MotionEvent object.
* Returns non-zero on error. */
diff --git a/core/jni/android_view_PointerIcon.cpp b/core/jni/android_view_PointerIcon.cpp
new file mode 100644
index 0000000..091341a
--- /dev/null
+++ b/core/jni/android_view_PointerIcon.cpp
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2011 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 "PointerIcon-JNI"
+
+#include "JNIHelp.h"
+
+#include "android_view_PointerIcon.h"
+
+#include <android_runtime/AndroidRuntime.h>
+#include <utils/Log.h>
+#include <android/graphics/GraphicsJNI.h>
+
+namespace android {
+
+static struct {
+ jclass clazz;
+ jfieldID mStyle;
+ jfieldID mBitmap;
+ jfieldID mHotSpotX;
+ jfieldID mHotSpotY;
+ jmethodID getSystemIcon;
+ jmethodID load;
+} gPointerIconClassInfo;
+
+
+// --- Global Functions ---
+
+jobject android_view_PointerIcon_getSystemIcon(JNIEnv* env, jobject contextObj, int32_t style) {
+ jobject pointerIconObj = env->CallStaticObjectMethod(gPointerIconClassInfo.clazz,
+ gPointerIconClassInfo.getSystemIcon, contextObj, style);
+ if (env->ExceptionCheck()) {
+ LOGW("An exception occurred while getting a pointer icon with style %d.", style);
+ LOGW_EX(env);
+ env->ExceptionClear();
+ return NULL;
+ }
+ return pointerIconObj;
+}
+
+status_t android_view_PointerIcon_load(JNIEnv* env, jobject pointerIconObj, jobject contextObj,
+ PointerIcon* outPointerIcon) {
+ outPointerIcon->reset();
+
+ if (!pointerIconObj) {
+ return OK;
+ }
+
+ jobject loadedPointerIconObj = env->CallObjectMethod(pointerIconObj,
+ gPointerIconClassInfo.load, contextObj);
+ if (env->ExceptionCheck() || !loadedPointerIconObj) {
+ LOGW("An exception occurred while loading a pointer icon.");
+ LOGW_EX(env);
+ env->ExceptionClear();
+ return UNKNOWN_ERROR;
+ }
+
+ outPointerIcon->style = env->GetIntField(loadedPointerIconObj,
+ gPointerIconClassInfo.mStyle);
+ outPointerIcon->hotSpotX = env->GetFloatField(loadedPointerIconObj,
+ gPointerIconClassInfo.mHotSpotX);
+ outPointerIcon->hotSpotY = env->GetFloatField(loadedPointerIconObj,
+ gPointerIconClassInfo.mHotSpotY);
+
+ jobject bitmapObj = env->GetObjectField(loadedPointerIconObj, gPointerIconClassInfo.mBitmap);
+ if (bitmapObj) {
+ SkBitmap* bitmap = GraphicsJNI::getNativeBitmap(env, bitmapObj);
+ if (bitmap) {
+ outPointerIcon->bitmap = *bitmap; // use a shared pixel ref
+ }
+ env->DeleteLocalRef(bitmapObj);
+ }
+
+ env->DeleteLocalRef(loadedPointerIconObj);
+ return OK;
+}
+
+status_t android_view_PointerIcon_loadSystemIcon(JNIEnv* env, jobject contextObj,
+ int32_t style, PointerIcon* outPointerIcon) {
+ jobject pointerIconObj = android_view_PointerIcon_getSystemIcon(env, contextObj, style);
+ if (!pointerIconObj) {
+ outPointerIcon->reset();
+ return UNKNOWN_ERROR;
+ }
+
+ status_t status = android_view_PointerIcon_load(env, pointerIconObj,
+ contextObj, outPointerIcon);
+ env->DeleteLocalRef(pointerIconObj);
+ return status;
+}
+
+
+// --- JNI Registration ---
+
+#define FIND_CLASS(var, className) \
+ var = env->FindClass(className); \
+ LOG_FATAL_IF(! var, "Unable to find class " className); \
+ var = jclass(env->NewGlobalRef(var));
+
+#define GET_STATIC_METHOD_ID(var, clazz, methodName, methodDescriptor) \
+ var = env->GetStaticMethodID(clazz, methodName, methodDescriptor); \
+ LOG_FATAL_IF(! var, "Unable to find method " methodName);
+
+#define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \
+ var = env->GetMethodID(clazz, methodName, methodDescriptor); \
+ LOG_FATAL_IF(! var, "Unable to find method " methodName);
+
+#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \
+ var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \
+ LOG_FATAL_IF(! var, "Unable to find field " fieldName);
+
+int register_android_view_PointerIcon(JNIEnv* env) {
+ FIND_CLASS(gPointerIconClassInfo.clazz, "android/view/PointerIcon");
+
+ GET_FIELD_ID(gPointerIconClassInfo.mBitmap, gPointerIconClassInfo.clazz,
+ "mBitmap", "Landroid/graphics/Bitmap;");
+
+ GET_FIELD_ID(gPointerIconClassInfo.mStyle, gPointerIconClassInfo.clazz,
+ "mStyle", "I");
+
+ GET_FIELD_ID(gPointerIconClassInfo.mHotSpotX, gPointerIconClassInfo.clazz,
+ "mHotSpotX", "F");
+
+ GET_FIELD_ID(gPointerIconClassInfo.mHotSpotY, gPointerIconClassInfo.clazz,
+ "mHotSpotY", "F");
+
+ GET_STATIC_METHOD_ID(gPointerIconClassInfo.getSystemIcon, gPointerIconClassInfo.clazz,
+ "getSystemIcon", "(Landroid/content/Context;I)Landroid/view/PointerIcon;");
+
+ GET_METHOD_ID(gPointerIconClassInfo.load, gPointerIconClassInfo.clazz,
+ "load", "(Landroid/content/Context;)Landroid/view/PointerIcon;");
+
+ return 0;
+}
+
+} // namespace android
diff --git a/core/jni/android_view_PointerIcon.h b/core/jni/android_view_PointerIcon.h
new file mode 100644
index 0000000..3bfd645
--- /dev/null
+++ b/core/jni/android_view_PointerIcon.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _ANDROID_VIEW_POINTER_ICON_H
+#define _ANDROID_VIEW_POINTER_ICON_H
+
+#include "jni.h"
+
+#include <utils/Errors.h>
+#include <SkBitmap.h>
+
+namespace android {
+
+/* Pointer icon styles.
+ * Must match the definition in android.view.PointerIcon.
+ */
+enum {
+ POINTER_ICON_STYLE_CUSTOM = -1,
+ POINTER_ICON_STYLE_NULL = 0,
+ POINTER_ICON_STYLE_ARROW = 1000,
+ POINTER_ICON_STYLE_SPOT_HOVER = 2000,
+ POINTER_ICON_STYLE_SPOT_TOUCH = 2001,
+ POINTER_ICON_STYLE_SPOT_ANCHOR = 2002,
+};
+
+/*
+ * Describes a pointer icon.
+ */
+struct PointerIcon {
+ inline PointerIcon() {
+ reset();
+ }
+
+ int32_t style;
+ SkBitmap bitmap;
+ float hotSpotX;
+ float hotSpotY;
+
+ inline bool isNullIcon() {
+ return style == POINTER_ICON_STYLE_NULL;
+ }
+
+ inline void reset() {
+ style = POINTER_ICON_STYLE_NULL;
+ bitmap.reset();
+ hotSpotX = 0;
+ hotSpotY = 0;
+ }
+};
+
+/* Gets a system pointer icon with the specified style. */
+extern jobject android_view_PointerIcon_getSystemIcon(JNIEnv* env,
+ jobject contextObj, int32_t style);
+
+/* Loads the bitmap associated with a pointer icon.
+ * If pointerIconObj is NULL, returns OK and a pointer icon with POINTER_ICON_STYLE_NULL. */
+extern status_t android_view_PointerIcon_load(JNIEnv* env,
+ jobject pointerIconObj, jobject contextObj, PointerIcon* outPointerIcon);
+
+/* Loads the bitmap associated with a pointer icon by style.
+ * If pointerIconObj is NULL, returns OK and a pointer icon with POINTER_ICON_STYLE_NULL. */
+extern status_t android_view_PointerIcon_loadSystemIcon(JNIEnv* env,
+ jobject contextObj, int32_t style, PointerIcon* outPointerIcon);
+
+} // namespace android
+
+#endif // _ANDROID_OS_POINTER_ICON_H
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index bd2e669..ec8b6e0 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -35,6 +35,7 @@
#include <SkPixelRef.h>
#include "jni.h"
+#include "JNIHelp.h"
#include <android_runtime/AndroidRuntime.h>
#include <android_runtime/android_view_Surface.h>
#include <utils/misc.h>
@@ -131,15 +132,15 @@ static void SurfaceSession_kill(JNIEnv* env, jobject clazz)
static sp<SurfaceControl> getSurfaceControl(JNIEnv* env, jobject clazz)
{
- SurfaceControl* const p =
+ SurfaceControl* const p =
(SurfaceControl*)env->GetIntField(clazz, so.surfaceControl);
return sp<SurfaceControl>(p);
}
-static void setSurfaceControl(JNIEnv* env, jobject clazz,
+static void setSurfaceControl(JNIEnv* env, jobject clazz,
const sp<SurfaceControl>& surface)
{
- SurfaceControl* const p =
+ SurfaceControl* const p =
(SurfaceControl*)env->GetIntField(clazz, so.surfaceControl);
if (surface.get()) {
surface->incStrong(clazz);
@@ -157,12 +158,12 @@ static sp<Surface> getSurface(JNIEnv* env, jobject clazz)
/*
* if this method is called from the WindowManager's process, it means
* the client is is not remote, and therefore is allowed to have
- * a Surface (data), so we create it here.
+ * a Surface (data), so we create it here.
* If we don't have a SurfaceControl, it means we're in a different
* process.
*/
-
- SurfaceControl* const control =
+
+ SurfaceControl* const control =
(SurfaceControl*)env->GetIntField(clazz, so.surfaceControl);
if (control) {
result = control->getSurface();
@@ -201,30 +202,30 @@ static void setSurface(JNIEnv* env, jobject clazz, const sp<Surface>& surface)
// ----------------------------------------------------------------------------
static void Surface_init(
- JNIEnv* env, jobject clazz,
+ JNIEnv* env, jobject clazz,
jobject session,
- jint pid, jstring jname, jint dpy, jint w, jint h, jint format, jint flags)
+ jint, jstring jname, jint dpy, jint w, jint h, jint format, jint flags)
{
if (session == NULL) {
- doThrow(env, "java/lang/NullPointerException");
+ doThrowNPE(env);
return;
}
-
+
SurfaceComposerClient* client =
(SurfaceComposerClient*)env->GetIntField(session, sso.client);
sp<SurfaceControl> surface;
if (jname == NULL) {
- surface = client->createSurface(pid, dpy, w, h, format, flags);
+ surface = client->createSurface(dpy, w, h, format, flags);
} else {
const jchar* str = env->GetStringCritical(jname, 0);
const String8 name(str, env->GetStringLength(jname));
env->ReleaseStringCritical(jname, str);
- surface = client->createSurface(pid, name, dpy, w, h, format, flags);
+ surface = client->createSurface(name, dpy, w, h, format, flags);
}
if (surface == 0) {
- doThrow(env, OutOfResourcesException);
+ jniThrowException(env, OutOfResourcesException, NULL);
return;
}
setSurfaceControl(env, clazz, surface);
@@ -234,7 +235,7 @@ static void Surface_initParcel(JNIEnv* env, jobject clazz, jobject argParcel)
{
Parcel* parcel = (Parcel*)env->GetIntField(argParcel, no.native_parcel);
if (parcel == NULL) {
- doThrow(env, "java/lang/NullPointerException", NULL);
+ doThrowNPE(env);
return;
}
@@ -297,7 +298,7 @@ static jobject Surface_lockCanvas(JNIEnv* env, jobject clazz, jobject dirtyRect)
{
const sp<Surface>& surface(getSurface(env, clazz));
if (!Surface::isValid(surface)) {
- doThrow(env, "java/lang/IllegalArgumentException", NULL);
+ doThrowIAE(env);
return 0;
}
@@ -310,7 +311,7 @@ static jobject Surface_lockCanvas(JNIEnv* env, jobject clazz, jobject dirtyRect)
dirty.right = env->GetIntField(dirtyRect, ro.r);
dirty.bottom= env->GetIntField(dirtyRect, ro.b);
if (!dirty.isEmpty()) {
- dirtyRegion.set(dirty);
+ dirtyRegion.set(dirty);
}
} else {
dirtyRegion.set(Rect(0x3FFF,0x3FFF));
@@ -322,7 +323,7 @@ static jobject Surface_lockCanvas(JNIEnv* env, jobject clazz, jobject dirtyRect)
const char* const exception = (err == NO_MEMORY) ?
OutOfResourcesException :
"java/lang/IllegalArgumentException";
- doThrow(env, exception, NULL);
+ jniThrowException(env, exception, NULL);
return 0;
}
@@ -344,7 +345,7 @@ static jobject Surface_lockCanvas(JNIEnv* env, jobject clazz, jobject dirtyRect)
bitmap.setPixels(NULL);
}
nativeCanvas->setBitmapDevice(bitmap);
-
+
SkRegion clipReg;
if (dirtyRegion.isRect()) { // very common case
const Rect b(dirtyRegion.getBounds());
@@ -359,7 +360,7 @@ static jobject Surface_lockCanvas(JNIEnv* env, jobject clazz, jobject dirtyRect)
}
nativeCanvas->clipRegion(clipReg);
-
+
int saveCount = nativeCanvas->save();
env->SetIntField(clazz, so.saveCount, saveCount);
@@ -370,7 +371,7 @@ static jobject Surface_lockCanvas(JNIEnv* env, jobject clazz, jobject dirtyRect)
env->SetIntField(dirtyRect, ro.r, bounds.right);
env->SetIntField(dirtyRect, ro.b, bounds.bottom);
}
-
+
return canvas;
}
@@ -378,11 +379,11 @@ static void Surface_unlockCanvasAndPost(
JNIEnv* env, jobject clazz, jobject argCanvas)
{
jobject canvas = env->GetObjectField(clazz, so.canvas);
- if (canvas != argCanvas) {
- doThrow(env, "java/lang/IllegalArgumentException", NULL);
+ if (env->IsSameObject(canvas, argCanvas) == JNI_FALSE) {
+ doThrowIAE(env);
return;
}
-
+
const sp<Surface>& surface(getSurface(env, clazz));
if (!Surface::isValid(surface))
return;
@@ -397,7 +398,7 @@ static void Surface_unlockCanvasAndPost(
// unlock surface
status_t err = surface->unlockAndPost();
if (err < 0) {
- doThrow(env, "java/lang/IllegalArgumentException", NULL);
+ doThrowIAE(env);
}
}
@@ -405,7 +406,7 @@ static void Surface_unlockCanvas(
JNIEnv* env, jobject clazz, jobject argCanvas)
{
// XXX: this API has been removed
- doThrow(env, "java/lang/IllegalArgumentException", NULL);
+ doThrowIAE(env);
}
static void Surface_openTransaction(
@@ -425,7 +426,7 @@ static void Surface_setOrientation(
{
int err = SurfaceComposerClient::setOrientation(display, orientation, flags);
if (err < 0) {
- doThrow(env, "java/lang/IllegalArgumentException", NULL);
+ doThrowIAE(env);
}
}
@@ -434,7 +435,7 @@ static void Surface_freezeDisplay(
{
int err = SurfaceComposerClient::freezeDisplay(display, 0);
if (err < 0) {
- doThrow(env, "java/lang/IllegalArgumentException", NULL);
+ doThrowIAE(env);
}
}
@@ -443,7 +444,7 @@ static void Surface_unfreezeDisplay(
{
int err = SurfaceComposerClient::unfreezeDisplay(display, 0);
if (err < 0) {
- doThrow(env, "java/lang/IllegalArgumentException", NULL);
+ doThrowIAE(env);
}
}
@@ -554,8 +555,9 @@ static void Surface_setLayer(
const sp<SurfaceControl>& surface(getSurfaceControl(env, clazz));
if (surface == 0) return;
status_t err = surface->setLayer(zorder);
- if (err<0 && err!=NO_INIT)
- doThrow(env, "java/lang/IllegalArgumentException", NULL);
+ if (err<0 && err!=NO_INIT) {
+ doThrowIAE(env);
+ }
}
static void Surface_setPosition(
@@ -564,8 +566,9 @@ static void Surface_setPosition(
const sp<SurfaceControl>& surface(getSurfaceControl(env, clazz));
if (surface == 0) return;
status_t err = surface->setPosition(x, y);
- if (err<0 && err!=NO_INIT)
- doThrow(env, "java/lang/IllegalArgumentException", NULL);
+ if (err<0 && err!=NO_INIT) {
+ doThrowIAE(env);
+ }
}
static void Surface_setSize(
@@ -574,8 +577,9 @@ static void Surface_setSize(
const sp<SurfaceControl>& surface(getSurfaceControl(env, clazz));
if (surface == 0) return;
status_t err = surface->setSize(w, h);
- if (err<0 && err!=NO_INIT)
- doThrow(env, "java/lang/IllegalArgumentException", NULL);
+ if (err<0 && err!=NO_INIT) {
+ doThrowIAE(env);
+ }
}
static void Surface_hide(
@@ -584,8 +588,9 @@ static void Surface_hide(
const sp<SurfaceControl>& surface(getSurfaceControl(env, clazz));
if (surface == 0) return;
status_t err = surface->hide();
- if (err<0 && err!=NO_INIT)
- doThrow(env, "java/lang/IllegalArgumentException", NULL);
+ if (err<0 && err!=NO_INIT) {
+ doThrowIAE(env);
+ }
}
static void Surface_show(
@@ -594,8 +599,9 @@ static void Surface_show(
const sp<SurfaceControl>& surface(getSurfaceControl(env, clazz));
if (surface == 0) return;
status_t err = surface->show();
- if (err<0 && err!=NO_INIT)
- doThrow(env, "java/lang/IllegalArgumentException", NULL);
+ if (err<0 && err!=NO_INIT) {
+ doThrowIAE(env);
+ }
}
static void Surface_freeze(
@@ -604,8 +610,9 @@ static void Surface_freeze(
const sp<SurfaceControl>& surface(getSurfaceControl(env, clazz));
if (surface == 0) return;
status_t err = surface->freeze();
- if (err<0 && err!=NO_INIT)
- doThrow(env, "java/lang/IllegalArgumentException", NULL);
+ if (err<0 && err!=NO_INIT) {
+ doThrowIAE(env);
+ }
}
static void Surface_unfreeze(
@@ -614,8 +621,9 @@ static void Surface_unfreeze(
const sp<SurfaceControl>& surface(getSurfaceControl(env, clazz));
if (surface == 0) return;
status_t err = surface->unfreeze();
- if (err<0 && err!=NO_INIT)
- doThrow(env, "java/lang/IllegalArgumentException", NULL);
+ if (err<0 && err!=NO_INIT) {
+ doThrowIAE(env);
+ }
}
static void Surface_setFlags(
@@ -624,8 +632,9 @@ static void Surface_setFlags(
const sp<SurfaceControl>& surface(getSurfaceControl(env, clazz));
if (surface == 0) return;
status_t err = surface->setFlags(flags, mask);
- if (err<0 && err!=NO_INIT)
- doThrow(env, "java/lang/IllegalArgumentException", NULL);
+ if (err<0 && err!=NO_INIT) {
+ doThrowIAE(env);
+ }
}
static void Surface_setTransparentRegion(
@@ -634,7 +643,7 @@ static void Surface_setTransparentRegion(
const sp<SurfaceControl>& surface(getSurfaceControl(env, clazz));
if (surface == 0) return;
SkRegion* nativeRegion = (SkRegion*)env->GetIntField(argRegion, no.native_region);
-
+
const SkIRect& b(nativeRegion->getBounds());
Region reg(Rect(b.fLeft, b.fTop, b.fRight, b.fBottom));
if (nativeRegion->isComplex()) {
@@ -645,10 +654,11 @@ static void Surface_setTransparentRegion(
it.next();
}
}
-
+
status_t err = surface->setTransparentRegionHint(reg);
- if (err<0 && err!=NO_INIT)
- doThrow(env, "java/lang/IllegalArgumentException", NULL);
+ if (err<0 && err!=NO_INIT) {
+ doThrowIAE(env);
+ }
}
static void Surface_setAlpha(
@@ -657,8 +667,9 @@ static void Surface_setAlpha(
const sp<SurfaceControl>& surface(getSurfaceControl(env, clazz));
if (surface == 0) return;
status_t err = surface->setAlpha(alpha);
- if (err<0 && err!=NO_INIT)
- doThrow(env, "java/lang/IllegalArgumentException", NULL);
+ if (err<0 && err!=NO_INIT) {
+ doThrowIAE(env);
+ }
}
static void Surface_setMatrix(
@@ -668,8 +679,9 @@ static void Surface_setMatrix(
const sp<SurfaceControl>& surface(getSurfaceControl(env, clazz));
if (surface == 0) return;
status_t err = surface->setMatrix(dsdx, dtdx, dsdy, dtdy);
- if (err<0 && err!=NO_INIT)
- doThrow(env, "java/lang/IllegalArgumentException", NULL);
+ if (err<0 && err!=NO_INIT) {
+ doThrowIAE(env);
+ }
}
static void Surface_setFreezeTint(
@@ -679,8 +691,9 @@ static void Surface_setFreezeTint(
const sp<SurfaceControl>& surface(getSurfaceControl(env, clazz));
if (surface == 0) return;
status_t err = surface->setFreezeTint(tint);
- if (err<0 && err!=NO_INIT)
- doThrow(env, "java/lang/IllegalArgumentException", NULL);
+ if (err<0 && err!=NO_INIT) {
+ doThrowIAE(env);
+ }
}
// ----------------------------------------------------------------------------
@@ -692,7 +705,7 @@ static void Surface_copyFrom(
return;
if (other == NULL) {
- doThrow(env, "java/lang/NullPointerException", NULL);
+ doThrowNPE(env);
return;
}
@@ -716,7 +729,7 @@ static void Surface_readFromParcel(
{
Parcel* parcel = (Parcel*)env->GetIntField( argParcel, no.native_parcel);
if (parcel == NULL) {
- doThrow(env, "java/lang/NullPointerException", NULL);
+ doThrowNPE(env);
return;
}
@@ -731,7 +744,7 @@ static void Surface_writeToParcel(
argParcel, no.native_parcel);
if (parcel == NULL) {
- doThrow(env, "java/lang/NullPointerException", NULL);
+ doThrowNPE(env);
return;
}
diff --git a/core/jni/android_view_TextureView.cpp b/core/jni/android_view_TextureView.cpp
new file mode 100644
index 0000000..c5d86c8
--- /dev/null
+++ b/core/jni/android_view_TextureView.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2011 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 "jni.h"
+#include <nativehelper/JNIHelp.h>
+#include <android_runtime/AndroidRuntime.h>
+
+#include <gui/SurfaceTexture.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+// Native layer
+// ----------------------------------------------------------------------------
+
+static void android_view_TextureView_setDefaultBufferSize(JNIEnv* env, jobject,
+ jint surfaceTexture, jint width, jint height) {
+
+ sp<SurfaceTexture> surface = reinterpret_cast<SurfaceTexture*>(surfaceTexture);
+ surface->setDefaultBufferSize(width, height);
+}
+
+// ----------------------------------------------------------------------------
+// JNI Glue
+// ----------------------------------------------------------------------------
+
+const char* const kClassPathName = "android/view/TextureView";
+
+static JNINativeMethod gMethods[] = {
+ { "nSetDefaultBufferSize", "(III)V", (void*) android_view_TextureView_setDefaultBufferSize }
+};
+
+int register_android_view_TextureView(JNIEnv* env) {
+ return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods));
+}
+
+};
diff --git a/core/jni/android_view_VelocityTracker.cpp b/core/jni/android_view_VelocityTracker.cpp
new file mode 100644
index 0000000..daa0adc
--- /dev/null
+++ b/core/jni/android_view_VelocityTracker.cpp
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2011 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 "VelocityTracker-JNI"
+
+#include "JNIHelp.h"
+
+#include <android_runtime/AndroidRuntime.h>
+#include <utils/Log.h>
+#include <ui/Input.h>
+#include "android_view_MotionEvent.h"
+
+
+namespace android {
+
+// Special constant to request the velocity of the active pointer.
+static const int ACTIVE_POINTER_ID = -1;
+
+// --- VelocityTrackerState ---
+
+class VelocityTrackerState {
+public:
+ VelocityTrackerState();
+
+ void clear();
+ void addMovement(const MotionEvent* event);
+ void computeCurrentVelocity(int32_t units, float maxVelocity);
+ void getVelocity(int32_t id, float* outVx, float* outVy);
+
+private:
+ struct Velocity {
+ float vx, vy;
+ };
+
+ VelocityTracker mVelocityTracker;
+ int32_t mActivePointerId;
+ BitSet32 mCalculatedIdBits;
+ Velocity mCalculatedVelocity[MAX_POINTERS];
+};
+
+VelocityTrackerState::VelocityTrackerState() : mActivePointerId(-1) {
+}
+
+void VelocityTrackerState::clear() {
+ mVelocityTracker.clear();
+ mActivePointerId = -1;
+ mCalculatedIdBits.clear();
+}
+
+void VelocityTrackerState::addMovement(const MotionEvent* event) {
+ mVelocityTracker.addMovement(event);
+}
+
+void VelocityTrackerState::computeCurrentVelocity(int32_t units, float maxVelocity) {
+ BitSet32 idBits(mVelocityTracker.getCurrentPointerIdBits());
+ mCalculatedIdBits = idBits;
+
+ for (uint32_t index = 0; !idBits.isEmpty(); index++) {
+ uint32_t id = idBits.firstMarkedBit();
+ idBits.clearBit(id);
+
+ float vx, vy;
+ mVelocityTracker.getVelocity(id, &vx, &vy);
+
+ vx = vx * units / 1000;
+ vy = vy * units / 1000;
+
+ if (vx > maxVelocity) {
+ vx = maxVelocity;
+ } else if (vx < -maxVelocity) {
+ vx = -maxVelocity;
+ }
+ if (vy > maxVelocity) {
+ vy = maxVelocity;
+ } else if (vy < -maxVelocity) {
+ vy = -maxVelocity;
+ }
+
+ Velocity& velocity = mCalculatedVelocity[index];
+ velocity.vx = vx;
+ velocity.vy = vy;
+ }
+}
+
+void VelocityTrackerState::getVelocity(int32_t id, float* outVx, float* outVy) {
+ if (id == ACTIVE_POINTER_ID) {
+ id = mVelocityTracker.getActivePointerId();
+ }
+
+ float vx, vy;
+ if (id >= 0 && id <= MAX_POINTER_ID && mCalculatedIdBits.hasBit(id)) {
+ uint32_t index = mCalculatedIdBits.getIndexOfBit(id);
+ const Velocity& velocity = mCalculatedVelocity[index];
+ vx = velocity.vx;
+ vy = velocity.vy;
+ } else {
+ vx = 0;
+ vy = 0;
+ }
+
+ if (outVx) {
+ *outVx = vx;
+ }
+ if (outVy) {
+ *outVy = vy;
+ }
+}
+
+
+// --- JNI Methods ---
+
+static jint android_view_VelocityTracker_nativeInitialize(JNIEnv* env, jclass clazz) {
+ return reinterpret_cast<jint>(new VelocityTrackerState());
+}
+
+static void android_view_VelocityTracker_nativeDispose(JNIEnv* env, jclass clazz, jint ptr) {
+ VelocityTrackerState* state = reinterpret_cast<VelocityTrackerState*>(ptr);
+ delete state;
+}
+
+static void android_view_VelocityTracker_nativeClear(JNIEnv* env, jclass clazz, jint ptr) {
+ VelocityTrackerState* state = reinterpret_cast<VelocityTrackerState*>(ptr);
+ state->clear();
+}
+
+static void android_view_VelocityTracker_nativeAddMovement(JNIEnv* env, jclass clazz, jint ptr,
+ jobject eventObj) {
+ const MotionEvent* event = android_view_MotionEvent_getNativePtr(env, eventObj);
+ if (!event) {
+ LOGW("nativeAddMovement failed because MotionEvent was finalized.");
+ return;
+ }
+
+ VelocityTrackerState* state = reinterpret_cast<VelocityTrackerState*>(ptr);
+ state->addMovement(event);
+}
+
+static void android_view_VelocityTracker_nativeComputeCurrentVelocity(JNIEnv* env, jclass clazz,
+ jint ptr, jint units, jfloat maxVelocity) {
+ VelocityTrackerState* state = reinterpret_cast<VelocityTrackerState*>(ptr);
+ state->computeCurrentVelocity(units, maxVelocity);
+}
+
+static jfloat android_view_VelocityTracker_nativeGetXVelocity(JNIEnv* env, jclass clazz,
+ jint ptr, jint id) {
+ VelocityTrackerState* state = reinterpret_cast<VelocityTrackerState*>(ptr);
+ float vx;
+ state->getVelocity(id, &vx, NULL);
+ return vx;
+}
+
+static jfloat android_view_VelocityTracker_nativeGetYVelocity(JNIEnv* env, jclass clazz,
+ jint ptr, jint id) {
+ VelocityTrackerState* state = reinterpret_cast<VelocityTrackerState*>(ptr);
+ float vy;
+ state->getVelocity(id, NULL, &vy);
+ return vy;
+}
+
+
+// --- JNI Registration ---
+
+static JNINativeMethod gVelocityTrackerMethods[] = {
+ /* name, signature, funcPtr */
+ { "nativeInitialize",
+ "()I",
+ (void*)android_view_VelocityTracker_nativeInitialize },
+ { "nativeDispose",
+ "(I)V",
+ (void*)android_view_VelocityTracker_nativeDispose },
+ { "nativeClear",
+ "(I)V",
+ (void*)android_view_VelocityTracker_nativeClear },
+ { "nativeAddMovement",
+ "(ILandroid/view/MotionEvent;)V",
+ (void*)android_view_VelocityTracker_nativeAddMovement },
+ { "nativeComputeCurrentVelocity",
+ "(IIF)V",
+ (void*)android_view_VelocityTracker_nativeComputeCurrentVelocity },
+ { "nativeGetXVelocity",
+ "(II)F",
+ (void*)android_view_VelocityTracker_nativeGetXVelocity },
+ { "nativeGetYVelocity",
+ "(II)F",
+ (void*)android_view_VelocityTracker_nativeGetYVelocity },
+};
+
+int register_android_view_VelocityTracker(JNIEnv* env) {
+ int res = jniRegisterNativeMethods(env, "android/view/VelocityTracker",
+ gVelocityTrackerMethods, NELEM(gVelocityTrackerMethods));
+ LOG_FATAL_IF(res < 0, "Unable to register native methods.");
+ return 0;
+}
+
+} // namespace android
diff --git a/core/jni/android_view_ViewRoot.cpp b/core/jni/android_view_ViewAncestor.cpp
index 2988ae8..d8e1124 100644
--- a/core/jni/android_view_ViewRoot.cpp
+++ b/core/jni/android_view_ViewAncestor.cpp
@@ -35,7 +35,7 @@ namespace android {
static int gPrevDur;
-static void android_view_ViewRoot_showFPS(JNIEnv* env, jobject, jobject jcanvas,
+static void android_view_ViewAncestor_showFPS(JNIEnv* env, jobject, jobject jcanvas,
jint dur) {
NPE_CHECK_RETURN_VOID(env, jcanvas);
SkCanvas* canvas = GraphicsJNI::getNativeCanvas(env, jcanvas);
@@ -79,14 +79,14 @@ static void android_view_ViewRoot_showFPS(JNIEnv* env, jobject, jobject jcanvas,
// ----------------------------------------------------------------------------
-const char* const kClassPathName = "android/view/ViewRoot";
+const char* const kClassPathName = "android/view/ViewAncestor";
static JNINativeMethod gMethods[] = {
{ "nativeShowFPS", "(Landroid/graphics/Canvas;I)V",
- (void*)android_view_ViewRoot_showFPS }
+ (void*)android_view_ViewAncestor_showFPS }
};
-int register_android_view_ViewRoot(JNIEnv* env) {
+int register_android_view_ViewAncestor(JNIEnv* env) {
return AndroidRuntime::registerNativeMethods(env,
kClassPathName, gMethods, NELEM(gMethods));
}
diff --git a/core/jni/com_android_internal_graphics_NativeUtils.cpp b/core/jni/com_android_internal_graphics_NativeUtils.cpp
index 0829532..9cc43606 100644
--- a/core/jni/com_android_internal_graphics_NativeUtils.cpp
+++ b/core/jni/com_android_internal_graphics_NativeUtils.cpp
@@ -29,17 +29,12 @@
namespace android
{
-static jclass class_fileDescriptor;
-static jfieldID field_fileDescriptor_descriptor;
-static jmethodID method_fileDescriptor_init;
-
-
static jboolean scrollRect(JNIEnv* env, jobject graphics2D, jobject canvas, jobject rect, int dx, int dy) {
if (canvas == NULL) {
- jniThrowException(env, "java/lang/NullPointerException", NULL);
+ jniThrowNullPointerException(env, NULL);
return false;
}
-
+
SkIRect src, *srcPtr = NULL;
if (NULL != rect) {
GraphicsJNI::jrect_to_irect(env, rect, &src);
diff --git a/core/jni/com_android_internal_os_ZygoteInit.cpp b/core/jni/com_android_internal_os_ZygoteInit.cpp
index 52e8f42..e627e4a 100644
--- a/core/jni/com_android_internal_os_ZygoteInit.cpp
+++ b/core/jni/com_android_internal_os_ZygoteInit.cpp
@@ -94,7 +94,7 @@ static jint com_android_internal_os_ZygoteInit_getpgid(
return ret;
}
-static void com_android_internal_os_ZygoteInit_reopenStdio(JNIEnv* env,
+static void com_android_internal_os_ZygoteInit_reopenStdio(JNIEnv* env,
jobject clazz, jobject in, jobject out, jobject errfd)
{
int fd;
@@ -131,7 +131,7 @@ static void com_android_internal_os_ZygoteInit_reopenStdio(JNIEnv* env,
} while (err < 0 && errno == EINTR);
}
-static void com_android_internal_os_ZygoteInit_closeDescriptor(JNIEnv* env,
+static void com_android_internal_os_ZygoteInit_closeDescriptor(JNIEnv* env,
jobject clazz, jobject descriptor)
{
int fd;
@@ -206,7 +206,7 @@ static void com_android_internal_os_ZygoteInit_setCapabilities (JNIEnv *env,
capdata.effective = effective;
capdata.permitted = permitted;
- err = capset (&capheader, &capdata);
+ err = capset (&capheader, &capdata);
if (err < 0) {
jniThrowIOException(env, errno);
@@ -231,7 +231,7 @@ static jlong com_android_internal_os_ZygoteInit_capgetPermitted (JNIEnv *env,
capheader.version = _LINUX_CAPABILITY_VERSION;
capheader.pid = pid;
- err = capget (&capheader, &capdata);
+ err = capget (&capheader, &capdata);
if (err < 0) {
jniThrowIOException(env, errno);
@@ -243,11 +243,10 @@ static jlong com_android_internal_os_ZygoteInit_capgetPermitted (JNIEnv *env,
}
static jint com_android_internal_os_ZygoteInit_selectReadable (
- JNIEnv *env, jobject clazz, jobjectArray fds)
+ JNIEnv *env, jobject clazz, jobjectArray fds)
{
if (fds == NULL) {
- jniThrowException(env, "java/lang/NullPointerException",
- "fds == null");
+ jniThrowNullPointerException(env, "fds == null");
return -1;
}
@@ -311,7 +310,7 @@ static jint com_android_internal_os_ZygoteInit_selectReadable (
}
static jobject com_android_internal_os_ZygoteInit_createFileDescriptor (
- JNIEnv *env, jobject clazz, jint fd)
+ JNIEnv *env, jobject clazz, jint fd)
{
return jniCreateFileDescriptor(env, fd);
}
@@ -329,17 +328,17 @@ static JNINativeMethod gMethods[] = {
(void *) com_android_internal_os_ZygoteInit_setpgid },
{ "getpgid", "(I)I",
(void *) com_android_internal_os_ZygoteInit_getpgid },
- { "reopenStdio",
+ { "reopenStdio",
"(Ljava/io/FileDescriptor;Ljava/io/FileDescriptor;"
- "Ljava/io/FileDescriptor;)V",
+ "Ljava/io/FileDescriptor;)V",
(void *) com_android_internal_os_ZygoteInit_reopenStdio},
- { "closeDescriptor", "(Ljava/io/FileDescriptor;)V",
+ { "closeDescriptor", "(Ljava/io/FileDescriptor;)V",
(void *) com_android_internal_os_ZygoteInit_closeDescriptor},
- { "setCloseOnExec", "(Ljava/io/FileDescriptor;Z)V",
+ { "setCloseOnExec", "(Ljava/io/FileDescriptor;Z)V",
(void *) com_android_internal_os_ZygoteInit_setCloseOnExec},
- { "setCapabilities", "(JJ)V",
+ { "setCapabilities", "(JJ)V",
(void *) com_android_internal_os_ZygoteInit_setCapabilities },
- { "capgetPermitted", "(I)J",
+ { "capgetPermitted", "(I)J",
(void *) com_android_internal_os_ZygoteInit_capgetPermitted },
{ "selectReadable", "([Ljava/io/FileDescriptor;)I",
(void *) com_android_internal_os_ZygoteInit_selectReadable },
@@ -353,4 +352,3 @@ int register_com_android_internal_os_ZygoteInit(JNIEnv* env)
}
}; // namespace android
-
diff --git a/core/jni/com_google_android_gles_jni_EGLImpl.cpp b/core/jni/com_google_android_gles_jni_EGLImpl.cpp
index 3d24bee..f777527 100644
--- a/core/jni/com_google_android_gles_jni_EGLImpl.cpp
+++ b/core/jni/com_google_android_gles_jni_EGLImpl.cpp
@@ -15,6 +15,7 @@
** limitations under the License.
*/
+#include "JNIHelp.h"
#include <android_runtime/AndroidRuntime.h>
#include <android_runtime/android_view_Surface.h>
#include <utils/misc.h>
@@ -26,11 +27,11 @@
#include <SkBitmap.h>
#include <SkPixelRef.h>
+#include <gui/SurfaceTexture.h>
+#include <gui/SurfaceTextureClient.h>
+
namespace android {
-static jclass gDisplay_class;
-static jclass gContext_class;
-static jclass gSurface_class;
static jclass gConfig_class;
static jmethodID gConfig_ctorID;
@@ -43,28 +44,6 @@ static jfieldID gConfig_EGLConfigFieldID;
static jfieldID gSurface_SurfaceFieldID;
static jfieldID gBitmap_NativeBitmapFieldID;
-static __attribute__((noinline))
-void doThrow(JNIEnv* env, const char* exc, const char* msg = NULL)
-{
- jclass npeClazz = env->FindClass(exc);
- env->ThrowNew(npeClazz, msg);
-}
-
-static __attribute__((noinline))
-bool hasException(JNIEnv *env) {
- if (env->ExceptionCheck() != 0) {
- env->ExceptionDescribe();
- return true;
- }
- return false;
-}
-
-static __attribute__((noinline))
-jclass make_globalref(JNIEnv* env, const char classname[]) {
- jclass c = env->FindClass(classname);
- return (jclass)env->NewGlobalRef(c);
-}
-
static inline EGLDisplay getDisplay(JNIEnv* env, jobject o) {
if (!o) return EGL_NO_DISPLAY;
return (EGLDisplay)env->GetIntField(o, gDisplay_EGLDisplayFieldID);
@@ -83,18 +62,20 @@ static inline EGLConfig getConfig(JNIEnv* env, jobject o) {
}
static void nativeClassInit(JNIEnv *_env, jclass eglImplClass)
{
- gDisplay_class = make_globalref(_env, "com/google/android/gles_jni/EGLDisplayImpl");
- gContext_class = make_globalref(_env, "com/google/android/gles_jni/EGLContextImpl");
- gSurface_class = make_globalref(_env, "com/google/android/gles_jni/EGLSurfaceImpl");
- gConfig_class = make_globalref(_env, "com/google/android/gles_jni/EGLConfigImpl");
+ jclass config_class = _env->FindClass("com/google/android/gles_jni/EGLConfigImpl");
+ gConfig_class = (jclass) _env->NewGlobalRef(config_class);
+ gConfig_ctorID = _env->GetMethodID(gConfig_class, "<init>", "(I)V");
+ gConfig_EGLConfigFieldID = _env->GetFieldID(gConfig_class, "mEGLConfig", "I");
- gConfig_ctorID = _env->GetMethodID(gConfig_class, "<init>", "(I)V");
+ jclass display_class = _env->FindClass("com/google/android/gles_jni/EGLDisplayImpl");
+ gDisplay_EGLDisplayFieldID = _env->GetFieldID(display_class, "mEGLDisplay", "I");
- gDisplay_EGLDisplayFieldID = _env->GetFieldID(gDisplay_class, "mEGLDisplay", "I");
- gContext_EGLContextFieldID = _env->GetFieldID(gContext_class, "mEGLContext", "I");
- gSurface_EGLSurfaceFieldID = _env->GetFieldID(gSurface_class, "mEGLSurface", "I");
- gSurface_NativePixelRefFieldID = _env->GetFieldID(gSurface_class, "mNativePixelRef", "I");
- gConfig_EGLConfigFieldID = _env->GetFieldID(gConfig_class, "mEGLConfig", "I");
+ jclass context_class = _env->FindClass("com/google/android/gles_jni/EGLContextImpl");
+ gContext_EGLContextFieldID = _env->GetFieldID(context_class, "mEGLContext", "I");
+
+ jclass surface_class = _env->FindClass("com/google/android/gles_jni/EGLSurfaceImpl");
+ gSurface_EGLSurfaceFieldID = _env->GetFieldID(surface_class, "mEGLSurface", "I");
+ gSurface_NativePixelRefFieldID = _env->GetFieldID(surface_class, "mNativePixelRef", "I");
jclass bitmap_class = _env->FindClass("android/graphics/Bitmap");
gBitmap_NativeBitmapFieldID = _env->GetFieldID(bitmap_class, "mNativeBitmap", "I");
@@ -133,7 +114,7 @@ static jboolean jni_eglInitialize(JNIEnv *_env, jobject _this, jobject display,
jintArray major_minor) {
if (display == NULL || (major_minor != NULL &&
_env->GetArrayLength(major_minor) < 2)) {
- doThrow(_env, "java/lang/IllegalArgumentException");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", NULL);
return JNI_FALSE;
}
@@ -156,7 +137,7 @@ static jboolean jni_eglQueryContext(JNIEnv *_env, jobject _this, jobject display
jobject context, jint attribute, jintArray value) {
if (display == NULL || context == NULL || value == NULL
|| _env->GetArrayLength(value) < 1) {
- doThrow(_env, "java/lang/IllegalArgumentException");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", NULL);
return JNI_FALSE;
}
EGLDisplay dpy = getDisplay(_env, display);
@@ -175,7 +156,7 @@ static jboolean jni_eglQuerySurface(JNIEnv *_env, jobject _this, jobject display
jobject surface, jint attribute, jintArray value) {
if (display == NULL || surface == NULL || value == NULL
|| _env->GetArrayLength(value) < 1) {
- doThrow(_env, "java/lang/IllegalArgumentException");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", NULL);
return JNI_FALSE;
}
EGLDisplay dpy = getDisplay(_env, display);
@@ -197,7 +178,7 @@ static jboolean jni_eglChooseConfig(JNIEnv *_env, jobject _this, jobject display
|| !validAttribList(_env, attrib_list)
|| (configs != NULL && _env->GetArrayLength(configs) < config_size)
|| (num_config != NULL && _env->GetArrayLength(num_config) < 1)) {
- doThrow(_env, "java/lang/IllegalArgumentException");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", NULL);
return JNI_FALSE;
}
EGLDisplay dpy = getDisplay(_env, display);
@@ -230,7 +211,7 @@ static jint jni_eglCreateContext(JNIEnv *_env, jobject _this, jobject display,
jobject config, jobject share_context, jintArray attrib_list) {
if (display == NULL || config == NULL || share_context == NULL
|| !validAttribList(_env, attrib_list)) {
- doThrow(_env, "java/lang/IllegalArgumentException");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", NULL);
return JNI_FALSE;
}
EGLDisplay dpy = getDisplay(_env, display);
@@ -246,7 +227,7 @@ static jint jni_eglCreatePbufferSurface(JNIEnv *_env, jobject _this, jobject dis
jobject config, jintArray attrib_list) {
if (display == NULL || config == NULL
|| !validAttribList(_env, attrib_list)) {
- doThrow(_env, "java/lang/IllegalArgumentException");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", NULL);
return JNI_FALSE;
}
EGLDisplay dpy = getDisplay(_env, display);
@@ -274,7 +255,7 @@ static void jni_eglCreatePixmapSurface(JNIEnv *_env, jobject _this, jobject out_
{
if (display == NULL || config == NULL || native_pixmap == NULL
|| !validAttribList(_env, attrib_list)) {
- doThrow(_env, "java/lang/IllegalArgumentException");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", NULL);
return;
}
EGLDisplay dpy = getDisplay(_env, display);
@@ -286,7 +267,7 @@ static void jni_eglCreatePixmapSurface(JNIEnv *_env, jobject _this, jobject out_
gBitmap_NativeBitmapFieldID);
SkPixelRef* ref = nativeBitmap ? nativeBitmap->pixelRef() : 0;
if (ref == NULL) {
- doThrow(_env, "java/lang/IllegalArgumentException", "Bitmap has no PixelRef");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "Bitmap has no PixelRef");
return;
}
@@ -318,7 +299,7 @@ static jint jni_eglCreateWindowSurface(JNIEnv *_env, jobject _this, jobject disp
jobject config, jobject native_window, jintArray attrib_list) {
if (display == NULL || config == NULL
|| !validAttribList(_env, attrib_list)) {
- doThrow(_env, "java/lang/IllegalArgumentException");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", NULL);
return JNI_FALSE;
}
EGLDisplay dpy = getDisplay(_env, display);
@@ -326,7 +307,7 @@ static jint jni_eglCreateWindowSurface(JNIEnv *_env, jobject _this, jobject disp
sp<ANativeWindow> window;
if (native_window == NULL) {
not_valid_surface:
- doThrow(_env, "java/lang/IllegalArgumentException",
+ jniThrowException(_env, "java/lang/IllegalArgumentException",
"Make sure the SurfaceView or associated SurfaceHolder has a valid Surface");
return 0;
}
@@ -341,11 +322,40 @@ not_valid_surface:
return (jint)sur;
}
+static jint jni_eglCreateWindowSurfaceTexture(JNIEnv *_env, jobject _this, jobject display,
+ jobject config, jint native_window, jintArray attrib_list) {
+ if (display == NULL || config == NULL
+ || !validAttribList(_env, attrib_list)) {
+ jniThrowException(_env, "java/lang/IllegalArgumentException", NULL);
+ return JNI_FALSE;
+ }
+ EGLDisplay dpy = getDisplay(_env, display);
+ EGLContext cnf = getConfig(_env, config);
+ sp<ANativeWindow> window;
+ if (native_window == 0) {
+not_valid_surface:
+ jniThrowException(_env, "java/lang/IllegalArgumentException",
+ "Make sure the SurfaceTexture is valid");
+ return 0;
+ }
+
+ sp<SurfaceTexture> surfaceTexture = reinterpret_cast<SurfaceTexture*>(native_window);
+
+ window = new SurfaceTextureClient(surfaceTexture);
+ if (window == NULL)
+ goto not_valid_surface;
+
+ jint* base = beginNativeAttribList(_env, attrib_list);
+ EGLSurface sur = eglCreateWindowSurface(dpy, cnf, window.get(), base);
+ endNativeAttributeList(_env, attrib_list, base);
+ return (jint)sur;
+}
+
static jboolean jni_eglGetConfigAttrib(JNIEnv *_env, jobject _this, jobject display,
jobject config, jint attribute, jintArray value) {
if (display == NULL || config == NULL
|| (value == NULL || _env->GetArrayLength(value) < 1)) {
- doThrow(_env, "java/lang/IllegalArgumentException");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", NULL);
return JNI_FALSE;
}
EGLDisplay dpy = getDisplay(_env, display);
@@ -363,7 +373,7 @@ static jboolean jni_eglGetConfigs(JNIEnv *_env, jobject _this, jobject display,
jobjectArray configs, jint config_size, jintArray num_config) {
if (display == NULL || (configs != NULL && _env->GetArrayLength(configs) < config_size)
|| (num_config != NULL && _env->GetArrayLength(num_config) < 1)) {
- doThrow(_env, "java/lang/IllegalArgumentException");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", NULL);
return JNI_FALSE;
}
EGLDisplay dpy = getDisplay(_env, display);
@@ -401,7 +411,7 @@ static jint jni_eglGetCurrentDisplay(JNIEnv *_env, jobject _this) {
static jint jni_eglGetCurrentSurface(JNIEnv *_env, jobject _this, jint readdraw) {
if ((readdraw != EGL_READ) && (readdraw != EGL_DRAW)) {
- doThrow(_env, "java/lang/IllegalArgumentException");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", NULL);
return 0;
}
return (jint)eglGetCurrentSurface(readdraw);
@@ -409,7 +419,7 @@ static jint jni_eglGetCurrentSurface(JNIEnv *_env, jobject _this, jint readdraw)
static jboolean jni_eglDestroyContext(JNIEnv *_env, jobject _this, jobject display, jobject context) {
if (display == NULL || context == NULL) {
- doThrow(_env, "java/lang/IllegalArgumentException");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", NULL);
return JNI_FALSE;
}
EGLDisplay dpy = getDisplay(_env, display);
@@ -419,7 +429,7 @@ static jboolean jni_eglDestroyContext(JNIEnv *_env, jobject _this, jobject displ
static jboolean jni_eglDestroySurface(JNIEnv *_env, jobject _this, jobject display, jobject surface) {
if (display == NULL || surface == NULL) {
- doThrow(_env, "java/lang/IllegalArgumentException");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", NULL);
return JNI_FALSE;
}
EGLDisplay dpy = getDisplay(_env, display);
@@ -442,7 +452,7 @@ static jint jni_eglGetDisplay(JNIEnv *_env, jobject _this, jobject native_displa
static jboolean jni_eglMakeCurrent(JNIEnv *_env, jobject _this, jobject display, jobject draw, jobject read, jobject context) {
if (display == NULL || draw == NULL || read == NULL || context == NULL) {
- doThrow(_env, "java/lang/IllegalArgumentException");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", NULL);
return JNI_FALSE;
}
EGLDisplay dpy = getDisplay(_env, display);
@@ -454,7 +464,7 @@ static jboolean jni_eglMakeCurrent(JNIEnv *_env, jobject _this, jobject display,
static jstring jni_eglQueryString(JNIEnv *_env, jobject _this, jobject display, jint name) {
if (display == NULL) {
- doThrow(_env, "java/lang/IllegalArgumentException");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", NULL);
return NULL;
}
EGLDisplay dpy = getDisplay(_env, display);
@@ -464,7 +474,7 @@ static jstring jni_eglQueryString(JNIEnv *_env, jobject _this, jobject display,
static jboolean jni_eglSwapBuffers(JNIEnv *_env, jobject _this, jobject display, jobject surface) {
if (display == NULL || surface == NULL) {
- doThrow(_env, "java/lang/IllegalArgumentException");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", NULL);
return JNI_FALSE;
}
EGLDisplay dpy = getDisplay(_env, display);
@@ -474,7 +484,7 @@ static jboolean jni_eglSwapBuffers(JNIEnv *_env, jobject _this, jobject display,
static jboolean jni_eglTerminate(JNIEnv *_env, jobject _this, jobject display) {
if (display == NULL) {
- doThrow(_env, "java/lang/IllegalArgumentException");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", NULL);
return JNI_FALSE;
}
EGLDisplay dpy = getDisplay(_env, display);
@@ -484,7 +494,7 @@ static jboolean jni_eglTerminate(JNIEnv *_env, jobject _this, jobject display) {
static jboolean jni_eglCopyBuffers(JNIEnv *_env, jobject _this, jobject display,
jobject surface, jobject native_pixmap) {
if (display == NULL || surface == NULL || native_pixmap == NULL) {
- doThrow(_env, "java/lang/IllegalArgumentException");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", NULL);
return JNI_FALSE;
}
// TODO: Implement this
@@ -530,6 +540,7 @@ static JNINativeMethod methods[] = {
{"_eglCreatePbufferSurface","(" DISPLAY CONFIG "[I)I", (void*)jni_eglCreatePbufferSurface },
{"_eglCreatePixmapSurface", "(" SURFACE DISPLAY CONFIG OBJECT "[I)V", (void*)jni_eglCreatePixmapSurface },
{"_eglCreateWindowSurface", "(" DISPLAY CONFIG OBJECT "[I)I", (void*)jni_eglCreateWindowSurface },
+{"_eglCreateWindowSurfaceTexture", "(" DISPLAY CONFIG "I[I)I", (void*)jni_eglCreateWindowSurfaceTexture },
{"eglDestroyContext", "(" DISPLAY CONTEXT ")Z", (void*)jni_eglDestroyContext },
{"eglDestroySurface", "(" DISPLAY SURFACE ")Z", (void*)jni_eglDestroySurface },
{"eglMakeCurrent", "(" DISPLAY SURFACE SURFACE CONTEXT")Z", (void*)jni_eglMakeCurrent },
@@ -546,4 +557,3 @@ int register_com_google_android_gles_jni_EGLImpl(JNIEnv *_env)
android::classPathName, android::methods, NELEM(android::methods));
return err;
}
-
diff --git a/core/jni/com_google_android_gles_jni_GLImpl.cpp b/core/jni/com_google_android_gles_jni_GLImpl.cpp
index bf613e1..8777131 100644
--- a/core/jni/com_google_android_gles_jni_GLImpl.cpp
+++ b/core/jni/com_google_android_gles_jni_GLImpl.cpp
@@ -2,21 +2,23 @@
**
** Copyright 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
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
**
-** http://www.apache.org/licenses/LICENSE-2.0
+** http://www.apache.org/licenses/LICENSE-2.0
**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
** limitations under the License.
*/
// This source file is automatically generated
+#include "jni.h"
+#include "JNIHelp.h"
#include <android_runtime/AndroidRuntime.h>
#include <utils/misc.h>
@@ -64,10 +66,6 @@ static int initialized = 0;
static jclass nioAccessClass;
static jclass bufferClass;
-static jclass OOMEClass;
-static jclass UOEClass;
-static jclass IAEClass;
-static jclass AIOOBEClass;
static jclass G11ImplClass;
static jmethodID getBasePointerID;
static jmethodID getBaseArrayID;
@@ -85,7 +83,7 @@ static jfieldID have_OES_texture_cube_mapID;
/* Cache method IDs each time the class is loaded. */
static void
-nativeClassInitBuffer(JNIEnv *_env)
+nativeClassInit(JNIEnv *_env, jclass glImplClass)
{
jclass nioAccessClassLocal = _env->FindClass("java/nio/NIOAccess");
nioAccessClass = (jclass) _env->NewGlobalRef(nioAccessClassLocal);
@@ -115,26 +113,6 @@ nativeClassInitBuffer(JNIEnv *_env)
_env->GetFieldID(bufferClass, "_elementSizeShift", "I");
}
-static void
-nativeClassInit(JNIEnv *_env, jclass glImplClass)
-{
- nativeClassInitBuffer(_env);
-
- jclass IAEClassLocal =
- _env->FindClass("java/lang/IllegalArgumentException");
- jclass OOMEClassLocal =
- _env->FindClass("java/lang/OutOfMemoryError");
- jclass UOEClassLocal =
- _env->FindClass("java/lang/UnsupportedOperationException");
- jclass AIOOBEClassLocal =
- _env->FindClass("java/lang/ArrayIndexOutOfBoundsException");
-
- IAEClass = (jclass) _env->NewGlobalRef(IAEClassLocal);
- OOMEClass = (jclass) _env->NewGlobalRef(OOMEClassLocal);
- UOEClass = (jclass) _env->NewGlobalRef(UOEClassLocal);
- AIOOBEClass = (jclass) _env->NewGlobalRef(AIOOBEClassLocal);
-}
-
static void *
getPointer(JNIEnv *_env, jobject buffer, jarray *array, jint *remaining)
{
@@ -155,7 +133,7 @@ getPointer(JNIEnv *_env, jobject buffer, jarray *array, jint *remaining)
*array = NULL;
return (void *) (jint) pointer;
}
-
+
*array = (jarray) _env->CallStaticObjectMethod(nioAccessClass,
getBaseArrayID, buffer);
if (*array == NULL) {
@@ -164,7 +142,7 @@ getPointer(JNIEnv *_env, jobject buffer, jarray *array, jint *remaining)
offset = _env->CallStaticIntMethod(nioAccessClass,
getBaseArrayOffsetID, buffer);
data = _env->GetPrimitiveArrayCritical(*array, (jboolean *) 0);
-
+
return (void *) ((char *) data + offset);
}
@@ -208,7 +186,8 @@ getDirectBufferPointer(JNIEnv *_env, jobject buffer) {
releasePointer(_env, array, buf, 0);
}
} else {
- _env->ThrowNew(IAEClass, "Must use a native order direct Buffer");
+ jniThrowException(_env, "java/lang/IllegalArgumentException",
+ "Must use a native order direct Buffer");
}
}
return buf;
@@ -251,7 +230,7 @@ nextExtension(const GLubyte* pExtensions) {
}
}
}
-
+
static bool
checkForExtension(const GLubyte* pExtensions, const GLubyte* pExtension) {
for (;*pExtensions != '\0'; pExtensions = nextExtension(pExtensions)) {
@@ -280,7 +259,6 @@ supportsExtension(JNIEnv *_env, jobject impl, jfieldID fieldId) {
}
// --------------------------------------------------------------------------
-
/* void glActiveTexture ( GLenum texture ) */
static void
android_glActiveTexture__I
@@ -557,16 +535,16 @@ android_glDeleteTextures__I_3II
GLuint *textures = (GLuint *) 0;
if (!textures_ref) {
- _env->ThrowNew(IAEClass, "textures == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "textures == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(textures_ref) - offset;
if (_remaining < n) {
- _env->ThrowNew(IAEClass, "length - offset < n");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < n");
goto exit;
}
textures_base = (GLuint *)
@@ -595,7 +573,7 @@ android_glDeleteTextures__ILjava_nio_IntBuffer_2
textures = (GLuint *)getPointer(_env, textures_buf, &_array, &_remaining);
if (_remaining < n) {
- _env->ThrowNew(IAEClass, "remaining() < n");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < n");
goto exit;
}
glDeleteTextures(
@@ -686,7 +664,7 @@ android_glDrawElements__IIILjava_nio_Buffer_2
indices = (GLvoid *)getPointer(_env, indices_buf, &_array, &_remaining);
if (_remaining < count) {
- _env->ThrowNew(AIOOBEClass, "remaining() < count");
+ jniThrowException(_env, "java/lang/ArrayIndexOutOfBoundsException", "remaining() < count");
goto exit;
}
glDrawElements(
@@ -753,11 +731,11 @@ android_glFogfv__I_3FI
GLfloat *params = (GLfloat *) 0;
if (!params_ref) {
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -787,7 +765,7 @@ android_glFogfv__I_3FI
break;
}
if (_remaining < _needed) {
- _env->ThrowNew(IAEClass, "length - offset < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
goto exit;
}
params_base = (GLfloat *)
@@ -841,7 +819,7 @@ android_glFogfv__ILjava_nio_FloatBuffer_2
break;
}
if (_remaining < _needed) {
- _env->ThrowNew(IAEClass, "remaining() < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
goto exit;
}
glFogfv(
@@ -874,11 +852,11 @@ android_glFogxv__I_3II
GLfixed *params = (GLfixed *) 0;
if (!params_ref) {
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -908,7 +886,7 @@ android_glFogxv__I_3II
break;
}
if (_remaining < _needed) {
- _env->ThrowNew(IAEClass, "length - offset < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
goto exit;
}
params_base = (GLfixed *)
@@ -962,7 +940,7 @@ android_glFogxv__ILjava_nio_IntBuffer_2
break;
}
if (_remaining < _needed) {
- _env->ThrowNew(IAEClass, "remaining() < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
goto exit;
}
glFogxv(
@@ -1024,18 +1002,18 @@ android_glGenTextures__I_3II
if (!textures_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "textures == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "textures == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(textures_ref) - offset;
if (_remaining < n) {
_exception = 1;
- _env->ThrowNew(IAEClass, "length - offset < n");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < n");
goto exit;
}
textures_base = (GLuint *)
@@ -1066,7 +1044,7 @@ android_glGenTextures__ILjava_nio_IntBuffer_2
textures = (GLuint *)getPointer(_env, textures_buf, &_array, &_remaining);
if (_remaining < n) {
_exception = 1;
- _env->ThrowNew(IAEClass, "remaining() < n");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < n");
goto exit;
}
glGenTextures(
@@ -1100,12 +1078,12 @@ android_glGetIntegerv__I_3II
if (!params_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -1442,7 +1420,7 @@ android_glGetIntegerv__I_3II
}
if (_remaining < _needed) {
_exception = 1;
- _env->ThrowNew(IAEClass, "length - offset < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
goto exit;
}
params_base = (GLint *)
@@ -1804,7 +1782,7 @@ android_glGetIntegerv__ILjava_nio_IntBuffer_2
}
if (_remaining < _needed) {
_exception = 1;
- _env->ThrowNew(IAEClass, "remaining() < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
goto exit;
}
glGetIntegerv(
@@ -1818,16 +1796,10 @@ exit:
}
}
-#include <string.h>
-
/* const GLubyte * glGetString ( GLenum name ) */
-static
-jstring
-android_glGetString
- (JNIEnv *_env, jobject _this, jint name) {
- const char * chars = (const char *)glGetString((GLenum)name);
- jstring output = _env->NewStringUTF(chars);
- return output;
+static jstring android_glGetString(JNIEnv *_env, jobject, jint name) {
+ const char* chars = (const char*) glGetString((GLenum) name);
+ return _env->NewStringUTF(chars);
}
/* void glHint ( GLenum target, GLenum mode ) */
static void
@@ -1858,11 +1830,11 @@ android_glLightModelfv__I_3FI
GLfloat *params = (GLfloat *) 0;
if (!params_ref) {
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -1883,7 +1855,7 @@ android_glLightModelfv__I_3FI
break;
}
if (_remaining < _needed) {
- _env->ThrowNew(IAEClass, "length - offset < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
goto exit;
}
params_base = (GLfloat *)
@@ -1928,7 +1900,7 @@ android_glLightModelfv__ILjava_nio_FloatBuffer_2
break;
}
if (_remaining < _needed) {
- _env->ThrowNew(IAEClass, "remaining() < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
goto exit;
}
glLightModelfv(
@@ -1961,11 +1933,11 @@ android_glLightModelxv__I_3II
GLfixed *params = (GLfixed *) 0;
if (!params_ref) {
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -1986,7 +1958,7 @@ android_glLightModelxv__I_3II
break;
}
if (_remaining < _needed) {
- _env->ThrowNew(IAEClass, "length - offset < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
goto exit;
}
params_base = (GLfixed *)
@@ -2031,7 +2003,7 @@ android_glLightModelxv__ILjava_nio_IntBuffer_2
break;
}
if (_remaining < _needed) {
- _env->ThrowNew(IAEClass, "remaining() < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
goto exit;
}
glLightModelxv(
@@ -2065,11 +2037,11 @@ android_glLightfv__II_3FI
GLfloat *params = (GLfloat *) 0;
if (!params_ref) {
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -2116,7 +2088,7 @@ android_glLightfv__II_3FI
break;
}
if (_remaining < _needed) {
- _env->ThrowNew(IAEClass, "length - offset < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
goto exit;
}
params_base = (GLfloat *)
@@ -2188,7 +2160,7 @@ android_glLightfv__IILjava_nio_FloatBuffer_2
break;
}
if (_remaining < _needed) {
- _env->ThrowNew(IAEClass, "remaining() < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
goto exit;
}
glLightfv(
@@ -2223,11 +2195,11 @@ android_glLightxv__II_3II
GLfixed *params = (GLfixed *) 0;
if (!params_ref) {
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -2274,7 +2246,7 @@ android_glLightxv__II_3II
break;
}
if (_remaining < _needed) {
- _env->ThrowNew(IAEClass, "length - offset < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
goto exit;
}
params_base = (GLfixed *)
@@ -2346,7 +2318,7 @@ android_glLightxv__IILjava_nio_IntBuffer_2
break;
}
if (_remaining < _needed) {
- _env->ThrowNew(IAEClass, "remaining() < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
goto exit;
}
glLightxv(
@@ -2395,11 +2367,11 @@ android_glLoadMatrixf___3FI
GLfloat *m = (GLfloat *) 0;
if (!m_ref) {
- _env->ThrowNew(IAEClass, "m == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "m == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(m_ref) - offset;
@@ -2444,11 +2416,11 @@ android_glLoadMatrixx___3II
GLfixed *m = (GLfixed *) 0;
if (!m_ref) {
- _env->ThrowNew(IAEClass, "m == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "m == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(m_ref) - offset;
@@ -2513,11 +2485,11 @@ android_glMaterialfv__II_3FI
GLfloat *params = (GLfloat *) 0;
if (!params_ref) {
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -2550,7 +2522,7 @@ android_glMaterialfv__II_3FI
break;
}
if (_remaining < _needed) {
- _env->ThrowNew(IAEClass, "length - offset < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
goto exit;
}
params_base = (GLfloat *)
@@ -2608,7 +2580,7 @@ android_glMaterialfv__IILjava_nio_FloatBuffer_2
break;
}
if (_remaining < _needed) {
- _env->ThrowNew(IAEClass, "remaining() < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
goto exit;
}
glMaterialfv(
@@ -2643,11 +2615,11 @@ android_glMaterialxv__II_3II
GLfixed *params = (GLfixed *) 0;
if (!params_ref) {
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -2680,7 +2652,7 @@ android_glMaterialxv__II_3II
break;
}
if (_remaining < _needed) {
- _env->ThrowNew(IAEClass, "length - offset < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
goto exit;
}
params_base = (GLfixed *)
@@ -2738,7 +2710,7 @@ android_glMaterialxv__IILjava_nio_IntBuffer_2
break;
}
if (_remaining < _needed) {
- _env->ThrowNew(IAEClass, "remaining() < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
goto exit;
}
glMaterialxv(
@@ -2771,11 +2743,11 @@ android_glMultMatrixf___3FI
GLfloat *m = (GLfloat *) 0;
if (!m_ref) {
- _env->ThrowNew(IAEClass, "m == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "m == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(m_ref) - offset;
@@ -2820,11 +2792,11 @@ android_glMultMatrixx___3II
GLfixed *m = (GLfixed *) 0;
if (!m_ref) {
- _env->ThrowNew(IAEClass, "m == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "m == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(m_ref) - offset;
@@ -3205,11 +3177,11 @@ android_glTexEnvfv__II_3FI
GLfloat *params = (GLfloat *) 0;
if (!params_ref) {
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -3236,7 +3208,7 @@ android_glTexEnvfv__II_3FI
break;
}
if (_remaining < _needed) {
- _env->ThrowNew(IAEClass, "length - offset < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
goto exit;
}
params_base = (GLfloat *)
@@ -3288,7 +3260,7 @@ android_glTexEnvfv__IILjava_nio_FloatBuffer_2
break;
}
if (_remaining < _needed) {
- _env->ThrowNew(IAEClass, "remaining() < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
goto exit;
}
glTexEnvfv(
@@ -3323,11 +3295,11 @@ android_glTexEnvxv__II_3II
GLfixed *params = (GLfixed *) 0;
if (!params_ref) {
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -3354,7 +3326,7 @@ android_glTexEnvxv__II_3II
break;
}
if (_remaining < _needed) {
- _env->ThrowNew(IAEClass, "length - offset < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
goto exit;
}
params_base = (GLfixed *)
@@ -3406,7 +3378,7 @@ android_glTexEnvxv__IILjava_nio_IntBuffer_2
break;
}
if (_remaining < _needed) {
- _env->ThrowNew(IAEClass, "remaining() < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
goto exit;
}
glTexEnvxv(
@@ -3569,18 +3541,18 @@ android_glQueryMatrixxOES___3II_3II
if (!mantissa_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "mantissa == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "mantissa == null");
goto exit;
}
if (mantissaOffset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "mantissaOffset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "mantissaOffset < 0");
goto exit;
}
_mantissaRemaining = _env->GetArrayLength(mantissa_ref) - mantissaOffset;
if (_mantissaRemaining < 16) {
_exception = 1;
- _env->ThrowNew(IAEClass, "length - mantissaOffset < 16");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - mantissaOffset < 16");
goto exit;
}
mantissa_base = (GLfixed *)
@@ -3589,18 +3561,18 @@ android_glQueryMatrixxOES___3II_3II
if (!exponent_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "exponent == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "exponent == null");
goto exit;
}
if (exponentOffset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "exponentOffset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "exponentOffset < 0");
goto exit;
}
_exponentRemaining = _env->GetArrayLength(exponent_ref) - exponentOffset;
if (_exponentRemaining < 16) {
_exception = 1;
- _env->ThrowNew(IAEClass, "length - exponentOffset < 16");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - exponentOffset < 16");
goto exit;
}
exponent_base = (GLint *)
@@ -3640,13 +3612,13 @@ android_glQueryMatrixxOES__Ljava_nio_IntBuffer_2Ljava_nio_IntBuffer_2
mantissa = (GLfixed *)getPointer(_env, mantissa_buf, &_mantissaArray, &_mantissaRemaining);
if (_mantissaRemaining < 16) {
_exception = 1;
- _env->ThrowNew(IAEClass, "remaining() < 16");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 16");
goto exit;
}
exponent = (GLint *)getPointer(_env, exponent_buf, &_exponentArray, &_exponentRemaining);
if (_exponentRemaining < 16) {
_exception = 1;
- _env->ThrowNew(IAEClass, "remaining() < 16");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 16");
goto exit;
}
_returnValue = glQueryMatrixxOES(
@@ -3685,7 +3657,7 @@ android_glBufferData__IILjava_nio_Buffer_2I
if (data_buf) {
data = (GLvoid *)getPointer(_env, data_buf, &_array, &_remaining);
if (_remaining < size) {
- _env->ThrowNew(IAEClass, "remaining() < size");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < size");
goto exit;
}
}
@@ -3712,7 +3684,7 @@ android_glBufferSubData__IIILjava_nio_Buffer_2
data = (GLvoid *)getPointer(_env, data_buf, &_array, &_remaining);
if (_remaining < size) {
- _env->ThrowNew(IAEClass, "remaining() < size");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < size");
goto exit;
}
glBufferSubData(
@@ -3737,16 +3709,16 @@ android_glClipPlanef__I_3FI
GLfloat *equation = (GLfloat *) 0;
if (!equation_ref) {
- _env->ThrowNew(IAEClass, "equation == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "equation == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(equation_ref) - offset;
if (_remaining < 4) {
- _env->ThrowNew(IAEClass, "length - offset < 4");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < 4");
goto exit;
}
equation_base = (GLfloat *)
@@ -3775,7 +3747,7 @@ android_glClipPlanef__ILjava_nio_FloatBuffer_2
equation = (GLfloat *)getPointer(_env, equation_buf, &_array, &_remaining);
if (_remaining < 4) {
- _env->ThrowNew(IAEClass, "remaining() < 4");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 4");
goto exit;
}
glClipPlanef(
@@ -3798,16 +3770,16 @@ android_glClipPlanex__I_3II
GLfixed *equation = (GLfixed *) 0;
if (!equation_ref) {
- _env->ThrowNew(IAEClass, "equation == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "equation == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(equation_ref) - offset;
if (_remaining < 4) {
- _env->ThrowNew(IAEClass, "length - offset < 4");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < 4");
goto exit;
}
equation_base = (GLfixed *)
@@ -3836,7 +3808,7 @@ android_glClipPlanex__ILjava_nio_IntBuffer_2
equation = (GLfixed *)getPointer(_env, equation_buf, &_array, &_remaining);
if (_remaining < 4) {
- _env->ThrowNew(IAEClass, "remaining() < 4");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 4");
goto exit;
}
glClipPlanex(
@@ -3883,16 +3855,16 @@ android_glDeleteBuffers__I_3II
GLuint *buffers = (GLuint *) 0;
if (!buffers_ref) {
- _env->ThrowNew(IAEClass, "buffers == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "buffers == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(buffers_ref) - offset;
if (_remaining < n) {
- _env->ThrowNew(IAEClass, "length - offset < n");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < n");
goto exit;
}
buffers_base = (GLuint *)
@@ -3921,7 +3893,7 @@ android_glDeleteBuffers__ILjava_nio_IntBuffer_2
buffers = (GLuint *)getPointer(_env, buffers_buf, &_array, &_remaining);
if (_remaining < n) {
- _env->ThrowNew(IAEClass, "remaining() < n");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < n");
goto exit;
}
glDeleteBuffers(
@@ -3958,18 +3930,18 @@ android_glGenBuffers__I_3II
if (!buffers_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "buffers == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "buffers == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(buffers_ref) - offset;
if (_remaining < n) {
_exception = 1;
- _env->ThrowNew(IAEClass, "length - offset < n");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < n");
goto exit;
}
buffers_base = (GLuint *)
@@ -4000,7 +3972,7 @@ android_glGenBuffers__ILjava_nio_IntBuffer_2
buffers = (GLuint *)getPointer(_env, buffers_buf, &_array, &_remaining);
if (_remaining < n) {
_exception = 1;
- _env->ThrowNew(IAEClass, "remaining() < n");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < n");
goto exit;
}
glGenBuffers(
@@ -4025,12 +3997,12 @@ android_glGetBooleanv__I_3ZI
if (!params_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -4073,7 +4045,7 @@ android_glGetBooleanv__ILjava_nio_IntBuffer_2
static void
android_glGetBufferParameteriv__II_3II
(JNIEnv *_env, jobject _this, jint target, jint pname, jintArray params_ref, jint offset) {
- _env->ThrowNew(UOEClass,
+ jniThrowException(_env, "java/lang/UnsupportedOperationException",
"glGetBufferParameteriv");
}
@@ -4081,7 +4053,7 @@ android_glGetBufferParameteriv__II_3II
static void
android_glGetBufferParameteriv__IILjava_nio_IntBuffer_2
(JNIEnv *_env, jobject _this, jint target, jint pname, jobject params_buf) {
- _env->ThrowNew(UOEClass,
+ jniThrowException(_env, "java/lang/UnsupportedOperationException",
"glGetBufferParameteriv");
}
@@ -4096,12 +4068,12 @@ android_glGetClipPlanef__I_3FI
if (!eqn_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "eqn == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "eqn == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(eqn_ref) - offset;
@@ -4151,12 +4123,12 @@ android_glGetClipPlanex__I_3II
if (!eqn_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "eqn == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "eqn == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(eqn_ref) - offset;
@@ -4206,12 +4178,12 @@ android_glGetFixedv__I_3II
if (!params_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -4261,12 +4233,12 @@ android_glGetFloatv__I_3FI
if (!params_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -4316,12 +4288,12 @@ android_glGetLightfv__II_3FI
if (!params_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -4369,7 +4341,7 @@ android_glGetLightfv__II_3FI
}
if (_remaining < _needed) {
_exception = 1;
- _env->ThrowNew(IAEClass, "length - offset < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
goto exit;
}
params_base = (GLfloat *)
@@ -4443,7 +4415,7 @@ android_glGetLightfv__IILjava_nio_FloatBuffer_2
}
if (_remaining < _needed) {
_exception = 1;
- _env->ThrowNew(IAEClass, "remaining() < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
goto exit;
}
glGetLightfv(
@@ -4469,12 +4441,12 @@ android_glGetLightxv__II_3II
if (!params_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -4522,7 +4494,7 @@ android_glGetLightxv__II_3II
}
if (_remaining < _needed) {
_exception = 1;
- _env->ThrowNew(IAEClass, "length - offset < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
goto exit;
}
params_base = (GLfixed *)
@@ -4596,7 +4568,7 @@ android_glGetLightxv__IILjava_nio_IntBuffer_2
}
if (_remaining < _needed) {
_exception = 1;
- _env->ThrowNew(IAEClass, "remaining() < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
goto exit;
}
glGetLightxv(
@@ -4622,12 +4594,12 @@ android_glGetMaterialfv__II_3FI
if (!params_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -4661,7 +4633,7 @@ android_glGetMaterialfv__II_3FI
}
if (_remaining < _needed) {
_exception = 1;
- _env->ThrowNew(IAEClass, "length - offset < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
goto exit;
}
params_base = (GLfloat *)
@@ -4721,7 +4693,7 @@ android_glGetMaterialfv__IILjava_nio_FloatBuffer_2
}
if (_remaining < _needed) {
_exception = 1;
- _env->ThrowNew(IAEClass, "remaining() < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
goto exit;
}
glGetMaterialfv(
@@ -4747,12 +4719,12 @@ android_glGetMaterialxv__II_3II
if (!params_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -4786,7 +4758,7 @@ android_glGetMaterialxv__II_3II
}
if (_remaining < _needed) {
_exception = 1;
- _env->ThrowNew(IAEClass, "length - offset < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
goto exit;
}
params_base = (GLfixed *)
@@ -4846,7 +4818,7 @@ android_glGetMaterialxv__IILjava_nio_IntBuffer_2
}
if (_remaining < _needed) {
_exception = 1;
- _env->ThrowNew(IAEClass, "remaining() < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
goto exit;
}
glGetMaterialxv(
@@ -4872,12 +4844,12 @@ android_glGetTexEnviv__II_3II
if (!params_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -4905,7 +4877,7 @@ android_glGetTexEnviv__II_3II
}
if (_remaining < _needed) {
_exception = 1;
- _env->ThrowNew(IAEClass, "length - offset < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
goto exit;
}
params_base = (GLint *)
@@ -4959,7 +4931,7 @@ android_glGetTexEnviv__IILjava_nio_IntBuffer_2
}
if (_remaining < _needed) {
_exception = 1;
- _env->ThrowNew(IAEClass, "remaining() < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
goto exit;
}
glGetTexEnviv(
@@ -4985,12 +4957,12 @@ android_glGetTexEnvxv__II_3II
if (!params_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -5018,7 +4990,7 @@ android_glGetTexEnvxv__II_3II
}
if (_remaining < _needed) {
_exception = 1;
- _env->ThrowNew(IAEClass, "length - offset < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
goto exit;
}
params_base = (GLfixed *)
@@ -5072,7 +5044,7 @@ android_glGetTexEnvxv__IILjava_nio_IntBuffer_2
}
if (_remaining < _needed) {
_exception = 1;
- _env->ThrowNew(IAEClass, "remaining() < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
goto exit;
}
glGetTexEnvxv(
@@ -5098,18 +5070,18 @@ android_glGetTexParameterfv__II_3FI
if (!params_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
if (_remaining < 1) {
_exception = 1;
- _env->ThrowNew(IAEClass, "length - offset < 1");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < 1");
goto exit;
}
params_base = (GLfloat *)
@@ -5141,7 +5113,7 @@ android_glGetTexParameterfv__IILjava_nio_FloatBuffer_2
params = (GLfloat *)getPointer(_env, params_buf, &_array, &_remaining);
if (_remaining < 1) {
_exception = 1;
- _env->ThrowNew(IAEClass, "remaining() < 1");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 1");
goto exit;
}
glGetTexParameterfv(
@@ -5167,18 +5139,18 @@ android_glGetTexParameteriv__II_3II
if (!params_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
if (_remaining < 1) {
_exception = 1;
- _env->ThrowNew(IAEClass, "length - offset < 1");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < 1");
goto exit;
}
params_base = (GLint *)
@@ -5210,7 +5182,7 @@ android_glGetTexParameteriv__IILjava_nio_IntBuffer_2
params = (GLint *)getPointer(_env, params_buf, &_array, &_remaining);
if (_remaining < 1) {
_exception = 1;
- _env->ThrowNew(IAEClass, "remaining() < 1");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 1");
goto exit;
}
glGetTexParameteriv(
@@ -5236,18 +5208,18 @@ android_glGetTexParameterxv__II_3II
if (!params_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
if (_remaining < 1) {
_exception = 1;
- _env->ThrowNew(IAEClass, "length - offset < 1");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < 1");
goto exit;
}
params_base = (GLfixed *)
@@ -5279,7 +5251,7 @@ android_glGetTexParameterxv__IILjava_nio_IntBuffer_2
params = (GLfixed *)getPointer(_env, params_buf, &_array, &_remaining);
if (_remaining < 1) {
_exception = 1;
- _env->ThrowNew(IAEClass, "remaining() < 1");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 1");
goto exit;
}
glGetTexParameterxv(
@@ -5357,16 +5329,16 @@ android_glPointParameterfv__I_3FI
GLfloat *params = (GLfloat *) 0;
if (!params_ref) {
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
if (_remaining < 1) {
- _env->ThrowNew(IAEClass, "length - offset < 1");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < 1");
goto exit;
}
params_base = (GLfloat *)
@@ -5395,7 +5367,7 @@ android_glPointParameterfv__ILjava_nio_FloatBuffer_2
params = (GLfloat *)getPointer(_env, params_buf, &_array, &_remaining);
if (_remaining < 1) {
- _env->ThrowNew(IAEClass, "remaining() < 1");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 1");
goto exit;
}
glPointParameterfv(
@@ -5428,16 +5400,16 @@ android_glPointParameterxv__I_3II
GLfixed *params = (GLfixed *) 0;
if (!params_ref) {
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
if (_remaining < 1) {
- _env->ThrowNew(IAEClass, "length - offset < 1");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < 1");
goto exit;
}
params_base = (GLfixed *)
@@ -5466,7 +5438,7 @@ android_glPointParameterxv__ILjava_nio_IntBuffer_2
params = (GLfixed *)getPointer(_env, params_buf, &_array, &_remaining);
if (_remaining < 1) {
- _env->ThrowNew(IAEClass, "remaining() < 1");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 1");
goto exit;
}
glPointParameterxv(
@@ -5534,11 +5506,11 @@ android_glTexEnviv__II_3II
GLint *params = (GLint *) 0;
if (!params_ref) {
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -5565,7 +5537,7 @@ android_glTexEnviv__II_3II
break;
}
if (_remaining < _needed) {
- _env->ThrowNew(IAEClass, "length - offset < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
goto exit;
}
params_base = (GLint *)
@@ -5617,7 +5589,7 @@ android_glTexEnviv__IILjava_nio_IntBuffer_2
break;
}
if (_remaining < _needed) {
- _env->ThrowNew(IAEClass, "remaining() < needed");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
goto exit;
}
glTexEnviv(
@@ -5641,16 +5613,16 @@ android_glTexParameterfv__II_3FI
GLfloat *params = (GLfloat *) 0;
if (!params_ref) {
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
if (_remaining < 1) {
- _env->ThrowNew(IAEClass, "length - offset < 1");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < 1");
goto exit;
}
params_base = (GLfloat *)
@@ -5680,7 +5652,7 @@ android_glTexParameterfv__IILjava_nio_FloatBuffer_2
params = (GLfloat *)getPointer(_env, params_buf, &_array, &_remaining);
if (_remaining < 1) {
- _env->ThrowNew(IAEClass, "remaining() < 1");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 1");
goto exit;
}
glTexParameterfv(
@@ -5715,16 +5687,16 @@ android_glTexParameteriv__II_3II
GLint *params = (GLint *) 0;
if (!params_ref) {
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
if (_remaining < 1) {
- _env->ThrowNew(IAEClass, "length - offset < 1");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < 1");
goto exit;
}
params_base = (GLint *)
@@ -5754,7 +5726,7 @@ android_glTexParameteriv__IILjava_nio_IntBuffer_2
params = (GLint *)getPointer(_env, params_buf, &_array, &_remaining);
if (_remaining < 1) {
- _env->ThrowNew(IAEClass, "remaining() < 1");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 1");
goto exit;
}
glTexParameteriv(
@@ -5778,16 +5750,16 @@ android_glTexParameterxv__II_3II
GLfixed *params = (GLfixed *) 0;
if (!params_ref) {
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
if (_remaining < 1) {
- _env->ThrowNew(IAEClass, "length - offset < 1");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < 1");
goto exit;
}
params_base = (GLfixed *)
@@ -5817,7 +5789,7 @@ android_glTexParameterxv__IILjava_nio_IntBuffer_2
params = (GLfixed *)getPointer(_env, params_buf, &_array, &_remaining);
if (_remaining < 1) {
- _env->ThrowNew(IAEClass, "remaining() < 1");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 1");
goto exit;
}
glTexParameterxv(
@@ -5875,16 +5847,16 @@ android_glDrawTexfvOES___3FI
GLfloat *coords = (GLfloat *) 0;
if (!coords_ref) {
- _env->ThrowNew(IAEClass, "coords == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "coords == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(coords_ref) - offset;
if (_remaining < 5) {
- _env->ThrowNew(IAEClass, "length - offset < 5");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < 5");
goto exit;
}
coords_base = (GLfloat *)
@@ -5912,7 +5884,7 @@ android_glDrawTexfvOES__Ljava_nio_FloatBuffer_2
coords = (GLfloat *)getPointer(_env, coords_buf, &_array, &_remaining);
if (_remaining < 5) {
- _env->ThrowNew(IAEClass, "remaining() < 5");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 5");
goto exit;
}
glDrawTexfvOES(
@@ -5947,16 +5919,16 @@ android_glDrawTexivOES___3II
GLint *coords = (GLint *) 0;
if (!coords_ref) {
- _env->ThrowNew(IAEClass, "coords == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "coords == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(coords_ref) - offset;
if (_remaining < 5) {
- _env->ThrowNew(IAEClass, "length - offset < 5");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < 5");
goto exit;
}
coords_base = (GLint *)
@@ -5984,7 +5956,7 @@ android_glDrawTexivOES__Ljava_nio_IntBuffer_2
coords = (GLint *)getPointer(_env, coords_buf, &_array, &_remaining);
if (_remaining < 5) {
- _env->ThrowNew(IAEClass, "remaining() < 5");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 5");
goto exit;
}
glDrawTexivOES(
@@ -6019,16 +5991,16 @@ android_glDrawTexsvOES___3SI
GLshort *coords = (GLshort *) 0;
if (!coords_ref) {
- _env->ThrowNew(IAEClass, "coords == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "coords == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(coords_ref) - offset;
if (_remaining < 5) {
- _env->ThrowNew(IAEClass, "length - offset < 5");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < 5");
goto exit;
}
coords_base = (GLshort *)
@@ -6056,7 +6028,7 @@ android_glDrawTexsvOES__Ljava_nio_ShortBuffer_2
coords = (GLshort *)getPointer(_env, coords_buf, &_array, &_remaining);
if (_remaining < 5) {
- _env->ThrowNew(IAEClass, "remaining() < 5");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 5");
goto exit;
}
glDrawTexsvOES(
@@ -6091,16 +6063,16 @@ android_glDrawTexxvOES___3II
GLfixed *coords = (GLfixed *) 0;
if (!coords_ref) {
- _env->ThrowNew(IAEClass, "coords == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "coords == null");
goto exit;
}
if (offset < 0) {
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(coords_ref) - offset;
if (_remaining < 5) {
- _env->ThrowNew(IAEClass, "length - offset < 5");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < 5");
goto exit;
}
coords_base = (GLfixed *)
@@ -6128,7 +6100,7 @@ android_glDrawTexxvOES__Ljava_nio_IntBuffer_2
coords = (GLfixed *)getPointer(_env, coords_buf, &_array, &_remaining);
if (_remaining < 5) {
- _env->ThrowNew(IAEClass, "remaining() < 5");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 5");
goto exit;
}
glDrawTexxvOES(
@@ -6223,7 +6195,7 @@ static void
android_glBindFramebufferOES__II
(JNIEnv *_env, jobject _this, jint target, jint framebuffer) {
if (! supportsExtension(_env, _this, have_OES_framebuffer_objectID)) {
- _env->ThrowNew(UOEClass,
+ jniThrowException(_env, "java/lang/UnsupportedOperationException",
"glBindFramebufferOES");
return;
}
@@ -6238,7 +6210,7 @@ static void
android_glBindRenderbufferOES__II
(JNIEnv *_env, jobject _this, jint target, jint renderbuffer) {
if (! supportsExtension(_env, _this, have_OES_framebuffer_objectID)) {
- _env->ThrowNew(UOEClass,
+ jniThrowException(_env, "java/lang/UnsupportedOperationException",
"glBindRenderbufferOES");
return;
}
@@ -6253,7 +6225,7 @@ static void
android_glBlendEquation__I
(JNIEnv *_env, jobject _this, jint mode) {
if (! supportsExtension(_env, _this, have_OES_blend_subtractID)) {
- _env->ThrowNew(UOEClass,
+ jniThrowException(_env, "java/lang/UnsupportedOperationException",
"glBlendEquation");
return;
}
@@ -6267,7 +6239,7 @@ static void
android_glBlendEquationSeparate__II
(JNIEnv *_env, jobject _this, jint modeRGB, jint modeAlpha) {
if (! supportsExtension(_env, _this, have_OES_blend_equation_separateID)) {
- _env->ThrowNew(UOEClass,
+ jniThrowException(_env, "java/lang/UnsupportedOperationException",
"glBlendEquationSeparate");
return;
}
@@ -6282,7 +6254,7 @@ static void
android_glBlendFuncSeparate__IIII
(JNIEnv *_env, jobject _this, jint srcRGB, jint dstRGB, jint srcAlpha, jint dstAlpha) {
if (! supportsExtension(_env, _this, have_OES_blend_equation_separateID)) {
- _env->ThrowNew(UOEClass,
+ jniThrowException(_env, "java/lang/UnsupportedOperationException",
"glBlendFuncSeparate");
return;
}
@@ -6299,7 +6271,7 @@ static jint
android_glCheckFramebufferStatusOES__I
(JNIEnv *_env, jobject _this, jint target) {
if (! supportsExtension(_env, _this, have_OES_framebuffer_objectID)) {
- _env->ThrowNew(UOEClass,
+ jniThrowException(_env, "java/lang/UnsupportedOperationException",
"glCheckFramebufferStatusOES");
return 0;
}
@@ -6315,7 +6287,7 @@ static void
android_glDeleteFramebuffersOES__I_3II
(JNIEnv *_env, jobject _this, jint n, jintArray framebuffers_ref, jint offset) {
if (! supportsExtension(_env, _this, have_OES_framebuffer_objectID)) {
- _env->ThrowNew(UOEClass,
+ jniThrowException(_env, "java/lang/UnsupportedOperationException",
"glDeleteFramebuffersOES");
return;
}
@@ -6326,18 +6298,18 @@ android_glDeleteFramebuffersOES__I_3II
if (!framebuffers_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "framebuffers == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "framebuffers == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(framebuffers_ref) - offset;
if (_remaining < n) {
_exception = 1;
- _env->ThrowNew(IAEClass, "length - offset < n");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < n");
goto exit;
}
framebuffers_base = (GLuint *)
@@ -6361,7 +6333,7 @@ static void
android_glDeleteFramebuffersOES__ILjava_nio_IntBuffer_2
(JNIEnv *_env, jobject _this, jint n, jobject framebuffers_buf) {
if (! supportsExtension(_env, _this, have_OES_framebuffer_objectID)) {
- _env->ThrowNew(UOEClass,
+ jniThrowException(_env, "java/lang/UnsupportedOperationException",
"glDeleteFramebuffersOES");
return;
}
@@ -6373,7 +6345,7 @@ android_glDeleteFramebuffersOES__ILjava_nio_IntBuffer_2
framebuffers = (GLuint *)getPointer(_env, framebuffers_buf, &_array, &_remaining);
if (_remaining < n) {
_exception = 1;
- _env->ThrowNew(IAEClass, "remaining() < n");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < n");
goto exit;
}
glDeleteFramebuffersOES(
@@ -6392,7 +6364,7 @@ static void
android_glDeleteRenderbuffersOES__I_3II
(JNIEnv *_env, jobject _this, jint n, jintArray renderbuffers_ref, jint offset) {
if (! supportsExtension(_env, _this, have_OES_framebuffer_objectID)) {
- _env->ThrowNew(UOEClass,
+ jniThrowException(_env, "java/lang/UnsupportedOperationException",
"glDeleteRenderbuffersOES");
return;
}
@@ -6403,18 +6375,18 @@ android_glDeleteRenderbuffersOES__I_3II
if (!renderbuffers_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "renderbuffers == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "renderbuffers == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(renderbuffers_ref) - offset;
if (_remaining < n) {
_exception = 1;
- _env->ThrowNew(IAEClass, "length - offset < n");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < n");
goto exit;
}
renderbuffers_base = (GLuint *)
@@ -6438,7 +6410,7 @@ static void
android_glDeleteRenderbuffersOES__ILjava_nio_IntBuffer_2
(JNIEnv *_env, jobject _this, jint n, jobject renderbuffers_buf) {
if (! supportsExtension(_env, _this, have_OES_framebuffer_objectID)) {
- _env->ThrowNew(UOEClass,
+ jniThrowException(_env, "java/lang/UnsupportedOperationException",
"glDeleteRenderbuffersOES");
return;
}
@@ -6450,7 +6422,7 @@ android_glDeleteRenderbuffersOES__ILjava_nio_IntBuffer_2
renderbuffers = (GLuint *)getPointer(_env, renderbuffers_buf, &_array, &_remaining);
if (_remaining < n) {
_exception = 1;
- _env->ThrowNew(IAEClass, "remaining() < n");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < n");
goto exit;
}
glDeleteRenderbuffersOES(
@@ -6469,7 +6441,7 @@ static void
android_glFramebufferRenderbufferOES__IIII
(JNIEnv *_env, jobject _this, jint target, jint attachment, jint renderbuffertarget, jint renderbuffer) {
if (! supportsExtension(_env, _this, have_OES_framebuffer_objectID)) {
- _env->ThrowNew(UOEClass,
+ jniThrowException(_env, "java/lang/UnsupportedOperationException",
"glFramebufferRenderbufferOES");
return;
}
@@ -6486,7 +6458,7 @@ static void
android_glFramebufferTexture2DOES__IIIII
(JNIEnv *_env, jobject _this, jint target, jint attachment, jint textarget, jint texture, jint level) {
if (! supportsExtension(_env, _this, have_OES_framebuffer_objectID)) {
- _env->ThrowNew(UOEClass,
+ jniThrowException(_env, "java/lang/UnsupportedOperationException",
"glFramebufferTexture2DOES");
return;
}
@@ -6504,7 +6476,7 @@ static void
android_glGenerateMipmapOES__I
(JNIEnv *_env, jobject _this, jint target) {
if (! supportsExtension(_env, _this, have_OES_framebuffer_objectID)) {
- _env->ThrowNew(UOEClass,
+ jniThrowException(_env, "java/lang/UnsupportedOperationException",
"glGenerateMipmapOES");
return;
}
@@ -6518,7 +6490,7 @@ static void
android_glGenFramebuffersOES__I_3II
(JNIEnv *_env, jobject _this, jint n, jintArray framebuffers_ref, jint offset) {
if (! supportsExtension(_env, _this, have_OES_framebuffer_objectID)) {
- _env->ThrowNew(UOEClass,
+ jniThrowException(_env, "java/lang/UnsupportedOperationException",
"glGenFramebuffersOES");
return;
}
@@ -6529,18 +6501,18 @@ android_glGenFramebuffersOES__I_3II
if (!framebuffers_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "framebuffers == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "framebuffers == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(framebuffers_ref) - offset;
if (_remaining < n) {
_exception = 1;
- _env->ThrowNew(IAEClass, "length - offset < n");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < n");
goto exit;
}
framebuffers_base = (GLuint *)
@@ -6564,7 +6536,7 @@ static void
android_glGenFramebuffersOES__ILjava_nio_IntBuffer_2
(JNIEnv *_env, jobject _this, jint n, jobject framebuffers_buf) {
if (! supportsExtension(_env, _this, have_OES_framebuffer_objectID)) {
- _env->ThrowNew(UOEClass,
+ jniThrowException(_env, "java/lang/UnsupportedOperationException",
"glGenFramebuffersOES");
return;
}
@@ -6576,7 +6548,7 @@ android_glGenFramebuffersOES__ILjava_nio_IntBuffer_2
framebuffers = (GLuint *)getPointer(_env, framebuffers_buf, &_array, &_remaining);
if (_remaining < n) {
_exception = 1;
- _env->ThrowNew(IAEClass, "remaining() < n");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < n");
goto exit;
}
glGenFramebuffersOES(
@@ -6595,7 +6567,7 @@ static void
android_glGenRenderbuffersOES__I_3II
(JNIEnv *_env, jobject _this, jint n, jintArray renderbuffers_ref, jint offset) {
if (! supportsExtension(_env, _this, have_OES_framebuffer_objectID)) {
- _env->ThrowNew(UOEClass,
+ jniThrowException(_env, "java/lang/UnsupportedOperationException",
"glGenRenderbuffersOES");
return;
}
@@ -6606,18 +6578,18 @@ android_glGenRenderbuffersOES__I_3II
if (!renderbuffers_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "renderbuffers == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "renderbuffers == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(renderbuffers_ref) - offset;
if (_remaining < n) {
_exception = 1;
- _env->ThrowNew(IAEClass, "length - offset < n");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < n");
goto exit;
}
renderbuffers_base = (GLuint *)
@@ -6641,7 +6613,7 @@ static void
android_glGenRenderbuffersOES__ILjava_nio_IntBuffer_2
(JNIEnv *_env, jobject _this, jint n, jobject renderbuffers_buf) {
if (! supportsExtension(_env, _this, have_OES_framebuffer_objectID)) {
- _env->ThrowNew(UOEClass,
+ jniThrowException(_env, "java/lang/UnsupportedOperationException",
"glGenRenderbuffersOES");
return;
}
@@ -6653,7 +6625,7 @@ android_glGenRenderbuffersOES__ILjava_nio_IntBuffer_2
renderbuffers = (GLuint *)getPointer(_env, renderbuffers_buf, &_array, &_remaining);
if (_remaining < n) {
_exception = 1;
- _env->ThrowNew(IAEClass, "remaining() < n");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < n");
goto exit;
}
glGenRenderbuffersOES(
@@ -6672,7 +6644,7 @@ static void
android_glGetFramebufferAttachmentParameterivOES__III_3II
(JNIEnv *_env, jobject _this, jint target, jint attachment, jint pname, jintArray params_ref, jint offset) {
if (! supportsExtension(_env, _this, have_OES_framebuffer_objectID)) {
- _env->ThrowNew(UOEClass,
+ jniThrowException(_env, "java/lang/UnsupportedOperationException",
"glGetFramebufferAttachmentParameterivOES");
return;
}
@@ -6683,12 +6655,12 @@ android_glGetFramebufferAttachmentParameterivOES__III_3II
if (!params_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -6715,7 +6687,7 @@ static void
android_glGetFramebufferAttachmentParameterivOES__IIILjava_nio_IntBuffer_2
(JNIEnv *_env, jobject _this, jint target, jint attachment, jint pname, jobject params_buf) {
if (! supportsExtension(_env, _this, have_OES_framebuffer_objectID)) {
- _env->ThrowNew(UOEClass,
+ jniThrowException(_env, "java/lang/UnsupportedOperationException",
"glGetFramebufferAttachmentParameterivOES");
return;
}
@@ -6741,7 +6713,7 @@ static void
android_glGetRenderbufferParameterivOES__II_3II
(JNIEnv *_env, jobject _this, jint target, jint pname, jintArray params_ref, jint offset) {
if (! supportsExtension(_env, _this, have_OES_framebuffer_objectID)) {
- _env->ThrowNew(UOEClass,
+ jniThrowException(_env, "java/lang/UnsupportedOperationException",
"glGetRenderbufferParameterivOES");
return;
}
@@ -6752,12 +6724,12 @@ android_glGetRenderbufferParameterivOES__II_3II
if (!params_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -6783,7 +6755,7 @@ static void
android_glGetRenderbufferParameterivOES__IILjava_nio_IntBuffer_2
(JNIEnv *_env, jobject _this, jint target, jint pname, jobject params_buf) {
if (! supportsExtension(_env, _this, have_OES_framebuffer_objectID)) {
- _env->ThrowNew(UOEClass,
+ jniThrowException(_env, "java/lang/UnsupportedOperationException",
"glGetRenderbufferParameterivOES");
return;
}
@@ -6808,7 +6780,7 @@ static void
android_glGetTexGenfv__II_3FI
(JNIEnv *_env, jobject _this, jint coord, jint pname, jfloatArray params_ref, jint offset) {
if (! supportsExtension(_env, _this, have_OES_texture_cube_mapID)) {
- _env->ThrowNew(UOEClass,
+ jniThrowException(_env, "java/lang/UnsupportedOperationException",
"glGetTexGenfv");
return;
}
@@ -6819,12 +6791,12 @@ android_glGetTexGenfv__II_3FI
if (!params_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -6850,7 +6822,7 @@ static void
android_glGetTexGenfv__IILjava_nio_FloatBuffer_2
(JNIEnv *_env, jobject _this, jint coord, jint pname, jobject params_buf) {
if (! supportsExtension(_env, _this, have_OES_texture_cube_mapID)) {
- _env->ThrowNew(UOEClass,
+ jniThrowException(_env, "java/lang/UnsupportedOperationException",
"glGetTexGenfv");
return;
}
@@ -6875,7 +6847,7 @@ static void
android_glGetTexGeniv__II_3II
(JNIEnv *_env, jobject _this, jint coord, jint pname, jintArray params_ref, jint offset) {
if (! supportsExtension(_env, _this, have_OES_texture_cube_mapID)) {
- _env->ThrowNew(UOEClass,
+ jniThrowException(_env, "java/lang/UnsupportedOperationException",
"glGetTexGeniv");
return;
}
@@ -6886,12 +6858,12 @@ android_glGetTexGeniv__II_3II
if (!params_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -6917,7 +6889,7 @@ static void
android_glGetTexGeniv__IILjava_nio_IntBuffer_2
(JNIEnv *_env, jobject _this, jint coord, jint pname, jobject params_buf) {
if (! supportsExtension(_env, _this, have_OES_texture_cube_mapID)) {
- _env->ThrowNew(UOEClass,
+ jniThrowException(_env, "java/lang/UnsupportedOperationException",
"glGetTexGeniv");
return;
}
@@ -6942,7 +6914,7 @@ static void
android_glGetTexGenxv__II_3II
(JNIEnv *_env, jobject _this, jint coord, jint pname, jintArray params_ref, jint offset) {
if (! supportsExtension(_env, _this, have_OES_texture_cube_mapID)) {
- _env->ThrowNew(UOEClass,
+ jniThrowException(_env, "java/lang/UnsupportedOperationException",
"glGetTexGenxv");
return;
}
@@ -6953,12 +6925,12 @@ android_glGetTexGenxv__II_3II
if (!params_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -6984,7 +6956,7 @@ static void
android_glGetTexGenxv__IILjava_nio_IntBuffer_2
(JNIEnv *_env, jobject _this, jint coord, jint pname, jobject params_buf) {
if (! supportsExtension(_env, _this, have_OES_texture_cube_mapID)) {
- _env->ThrowNew(UOEClass,
+ jniThrowException(_env, "java/lang/UnsupportedOperationException",
"glGetTexGenxv");
return;
}
@@ -7009,7 +6981,7 @@ static jboolean
android_glIsFramebufferOES__I
(JNIEnv *_env, jobject _this, jint framebuffer) {
if (! supportsExtension(_env, _this, have_OES_framebuffer_objectID)) {
- _env->ThrowNew(UOEClass,
+ jniThrowException(_env, "java/lang/UnsupportedOperationException",
"glIsFramebufferOES");
return JNI_FALSE;
}
@@ -7025,7 +6997,7 @@ static jboolean
android_glIsRenderbufferOES__I
(JNIEnv *_env, jobject _this, jint renderbuffer) {
if (! supportsExtension(_env, _this, have_OES_framebuffer_objectID)) {
- _env->ThrowNew(UOEClass,
+ jniThrowException(_env, "java/lang/UnsupportedOperationException",
"glIsRenderbufferOES");
return JNI_FALSE;
}
@@ -7041,7 +7013,7 @@ static void
android_glRenderbufferStorageOES__IIII
(JNIEnv *_env, jobject _this, jint target, jint internalformat, jint width, jint height) {
if (! supportsExtension(_env, _this, have_OES_framebuffer_objectID)) {
- _env->ThrowNew(UOEClass,
+ jniThrowException(_env, "java/lang/UnsupportedOperationException",
"glRenderbufferStorageOES");
return;
}
@@ -7058,7 +7030,7 @@ static void
android_glTexGenf__IIF
(JNIEnv *_env, jobject _this, jint coord, jint pname, jfloat param) {
if (! supportsExtension(_env, _this, have_OES_texture_cube_mapID)) {
- _env->ThrowNew(UOEClass,
+ jniThrowException(_env, "java/lang/UnsupportedOperationException",
"glTexGenf");
return;
}
@@ -7074,7 +7046,7 @@ static void
android_glTexGenfv__II_3FI
(JNIEnv *_env, jobject _this, jint coord, jint pname, jfloatArray params_ref, jint offset) {
if (! supportsExtension(_env, _this, have_OES_texture_cube_mapID)) {
- _env->ThrowNew(UOEClass,
+ jniThrowException(_env, "java/lang/UnsupportedOperationException",
"glTexGenfv");
return;
}
@@ -7085,12 +7057,12 @@ android_glTexGenfv__II_3FI
if (!params_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -7116,7 +7088,7 @@ static void
android_glTexGenfv__IILjava_nio_FloatBuffer_2
(JNIEnv *_env, jobject _this, jint coord, jint pname, jobject params_buf) {
if (! supportsExtension(_env, _this, have_OES_texture_cube_mapID)) {
- _env->ThrowNew(UOEClass,
+ jniThrowException(_env, "java/lang/UnsupportedOperationException",
"glTexGenfv");
return;
}
@@ -7141,7 +7113,7 @@ static void
android_glTexGeni__III
(JNIEnv *_env, jobject _this, jint coord, jint pname, jint param) {
if (! supportsExtension(_env, _this, have_OES_texture_cube_mapID)) {
- _env->ThrowNew(UOEClass,
+ jniThrowException(_env, "java/lang/UnsupportedOperationException",
"glTexGeni");
return;
}
@@ -7157,7 +7129,7 @@ static void
android_glTexGeniv__II_3II
(JNIEnv *_env, jobject _this, jint coord, jint pname, jintArray params_ref, jint offset) {
if (! supportsExtension(_env, _this, have_OES_texture_cube_mapID)) {
- _env->ThrowNew(UOEClass,
+ jniThrowException(_env, "java/lang/UnsupportedOperationException",
"glTexGeniv");
return;
}
@@ -7168,12 +7140,12 @@ android_glTexGeniv__II_3II
if (!params_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -7199,7 +7171,7 @@ static void
android_glTexGeniv__IILjava_nio_IntBuffer_2
(JNIEnv *_env, jobject _this, jint coord, jint pname, jobject params_buf) {
if (! supportsExtension(_env, _this, have_OES_texture_cube_mapID)) {
- _env->ThrowNew(UOEClass,
+ jniThrowException(_env, "java/lang/UnsupportedOperationException",
"glTexGeniv");
return;
}
@@ -7224,7 +7196,7 @@ static void
android_glTexGenx__III
(JNIEnv *_env, jobject _this, jint coord, jint pname, jint param) {
if (! supportsExtension(_env, _this, have_OES_texture_cube_mapID)) {
- _env->ThrowNew(UOEClass,
+ jniThrowException(_env, "java/lang/UnsupportedOperationException",
"glTexGenx");
return;
}
@@ -7240,7 +7212,7 @@ static void
android_glTexGenxv__II_3II
(JNIEnv *_env, jobject _this, jint coord, jint pname, jintArray params_ref, jint offset) {
if (! supportsExtension(_env, _this, have_OES_texture_cube_mapID)) {
- _env->ThrowNew(UOEClass,
+ jniThrowException(_env, "java/lang/UnsupportedOperationException",
"glTexGenxv");
return;
}
@@ -7251,12 +7223,12 @@ android_glTexGenxv__II_3II
if (!params_ref) {
_exception = 1;
- _env->ThrowNew(IAEClass, "params == null");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
goto exit;
}
if (offset < 0) {
_exception = 1;
- _env->ThrowNew(IAEClass, "offset < 0");
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
goto exit;
}
_remaining = _env->GetArrayLength(params_ref) - offset;
@@ -7282,7 +7254,7 @@ static void
android_glTexGenxv__IILjava_nio_IntBuffer_2
(JNIEnv *_env, jobject _this, jint coord, jint pname, jobject params_buf) {
if (! supportsExtension(_env, _this, have_OES_texture_cube_mapID)) {
- _env->ThrowNew(UOEClass,
+ jniThrowException(_env, "java/lang/UnsupportedOperationException",
"glTexGenxv");
return;
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 60cf939..419578c 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -358,6 +358,14 @@
android:description="@string/permdesc_nfc"
android:label="@string/permlab_nfc" />
+ <!-- Allows applications to provide VPN functionality.
+ @hide Pending API council approval -->
+ <permission android:name="android.permission.VPN"
+ android:permissionGroup="android.permission-group.NETWORK"
+ android:protectionLevel="dangerous"
+ android:description="@string/permdesc_vpn"
+ android:label="@string/permlab_vpn" />
+
<!-- Allows an application to use SIP service -->
<permission android:name="android.permission.USE_SIP"
android:permissionGroup="android.permission-group.NETWORK"
@@ -621,6 +629,13 @@
android:label="@string/permlab_reorderTasks"
android:description="@string/permdesc_reorderTasks" />
+ <!-- Allows an application to change to remove/kill tasks -->
+ <permission android:name="android.permission.REMOVE_TASKS"
+ android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
+ android:protectionLevel="signature"
+ android:label="@string/permlab_removeTasks"
+ android:description="@string/permdesc_removeTasks" />
+
<!-- Allows an application to modify the current configuration, such
as locale. -->
<permission android:name="android.permission.CHANGE_CONFIGURATION"
@@ -1234,6 +1249,14 @@
android:description="@string/permdesc_backup"
android:protectionLevel="signatureOrSystem" />
+ <!-- Allows a package to launch the secure full-backup confirmation UI.
+ ONLY the system process may hold this permission.
+ @hide -->
+ <permission android:name="android.permission.CONFIRM_FULL_BACKUP"
+ android:label="@string/permlab_confirm_full_backup"
+ android:description="@string/permdesc_confirm_full_backup"
+ android:protectionLevel="signature" />
+
<!-- Must be required by a {@link android.widget.RemoteViewsService},
to ensure that only the system can bind to it. -->
<permission android:name="android.permission.BIND_REMOTEVIEWS"
@@ -1322,12 +1345,17 @@
android:protectionLevel="signature" />
<uses-permission android:name="android.intent.category.MASTER_CLEAR.permission.C2D_MESSAGE"/>
+ <!-- The system process is explicitly the only one allowed to launch the
+ confirmation UI for full backup/restore -->
+ <uses-permission android:name="android.permission.CONFIRM_FULL_BACKUP"/>
+
<application android:process="system"
android:persistent="true"
android:hasCode="false"
android:label="@string/android_system_label"
android:allowClearUserData="false"
android:backupAgent="com.android.server.SystemBackupAgent"
+ android:fullBackupAgent="com.android.server.SystemBackupAgent"
android:killAfterRestore="false"
android:icon="@drawable/ic_launcher_android">
<activity android:name="com.android.internal.app.ChooserActivity"
diff --git a/core/res/res/drawable-hdpi/btn_cab_done_default_holo.9.png b/core/res/res/drawable-hdpi/btn_cab_done_default_holo.9.png
index c66716f..f5e6054 100644
--- a/core/res/res/drawable-hdpi/btn_cab_done_default_holo.9.png
+++ b/core/res/res/drawable-hdpi/btn_cab_done_default_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_cab_done_focused_holo.9.png b/core/res/res/drawable-hdpi/btn_cab_done_focused_holo.9.png
index 0f1d9a0..1121070 100644
--- a/core/res/res/drawable-hdpi/btn_cab_done_focused_holo.9.png
+++ b/core/res/res/drawable-hdpi/btn_cab_done_focused_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_cab_done_pressed_holo.9.png b/core/res/res/drawable-hdpi/btn_cab_done_pressed_holo.9.png
index 391227e..1a072a9 100644
--- a/core/res/res/drawable-hdpi/btn_cab_done_pressed_holo.9.png
+++ b/core/res/res/drawable-hdpi/btn_cab_done_pressed_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/keyboard_textfield_selected.9.png b/core/res/res/drawable-hdpi/keyboard_textfield_selected.9.png
deleted file mode 100644
index 61db22c..0000000
--- a/core/res/res/drawable-hdpi/keyboard_textfield_selected.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/password_keyboard_background_holo.9.png b/core/res/res/drawable-hdpi/password_keyboard_background_holo.9.png
new file mode 100644
index 0000000..c56c704
--- /dev/null
+++ b/core/res/res/drawable-hdpi/password_keyboard_background_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_adb.png b/core/res/res/drawable-hdpi/stat_sys_adb.png
index 9c56e24..615e8b3 100755
--- a/core/res/res/drawable-hdpi/stat_sys_adb.png
+++ b/core/res/res/drawable-hdpi/stat_sys_adb.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/text_edit_suggestions_bottom_window.9.png b/core/res/res/drawable-hdpi/text_edit_suggestions_bottom_window.9.png
new file mode 100644
index 0000000..c97514f
--- /dev/null
+++ b/core/res/res/drawable-hdpi/text_edit_suggestions_bottom_window.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/text_edit_suggestions_top_window.9.png b/core/res/res/drawable-hdpi/text_edit_suggestions_top_window.9.png
new file mode 100644
index 0000000..ff6b34a
--- /dev/null
+++ b/core/res/res/drawable-hdpi/text_edit_suggestions_top_window.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_bg_activated_holo_dark.9.png b/core/res/res/drawable-hdpi/textfield_bg_activated_holo_dark.9.png
new file mode 100644
index 0000000..a233b0d
--- /dev/null
+++ b/core/res/res/drawable-hdpi/textfield_bg_activated_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_bg_default_holo_dark.9.png b/core/res/res/drawable-hdpi/textfield_bg_default_holo_dark.9.png
new file mode 100644
index 0000000..403f502
--- /dev/null
+++ b/core/res/res/drawable-hdpi/textfield_bg_default_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_bg_disabled_focused_holo_dark.9.png b/core/res/res/drawable-hdpi/textfield_bg_disabled_focused_holo_dark.9.png
new file mode 100644
index 0000000..0ded801
--- /dev/null
+++ b/core/res/res/drawable-hdpi/textfield_bg_disabled_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_bg_disabled_holo_dark.9.png b/core/res/res/drawable-hdpi/textfield_bg_disabled_holo_dark.9.png
new file mode 100644
index 0000000..27237b8
--- /dev/null
+++ b/core/res/res/drawable-hdpi/textfield_bg_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_bg_focused_holo_dark.9.png b/core/res/res/drawable-hdpi/textfield_bg_focused_holo_dark.9.png
new file mode 100644
index 0000000..0e451f1
--- /dev/null
+++ b/core/res/res/drawable-hdpi/textfield_bg_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldpi/keyboard_textfield_selected.9.png b/core/res/res/drawable-ldpi/keyboard_textfield_selected.9.png
deleted file mode 100644
index d6478fb..0000000
--- a/core/res/res/drawable-ldpi/keyboard_textfield_selected.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_cab_done_default_holo.9.png b/core/res/res/drawable-mdpi/btn_cab_done_default_holo.9.png
index 3a113ee..7af26ca 100644
--- a/core/res/res/drawable-mdpi/btn_cab_done_default_holo.9.png
+++ b/core/res/res/drawable-mdpi/btn_cab_done_default_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_cab_done_focused_holo.9.png b/core/res/res/drawable-mdpi/btn_cab_done_focused_holo.9.png
index 8496965..486c37a 100644
--- a/core/res/res/drawable-mdpi/btn_cab_done_focused_holo.9.png
+++ b/core/res/res/drawable-mdpi/btn_cab_done_focused_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_cab_done_pressed_holo.9.png b/core/res/res/drawable-mdpi/btn_cab_done_pressed_holo.9.png
index dc70c6a..3cfb4bd 100644
--- a/core/res/res/drawable-mdpi/btn_cab_done_pressed_holo.9.png
+++ b/core/res/res/drawable-mdpi/btn_cab_done_pressed_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/keyboard_textfield_selected.9.png b/core/res/res/drawable-mdpi/keyboard_textfield_selected.9.png
deleted file mode 100644
index 6e703af..0000000
--- a/core/res/res/drawable-mdpi/keyboard_textfield_selected.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/pointer_spot_anchor.png b/core/res/res/drawable-mdpi/pointer_spot_anchor.png
new file mode 100644
index 0000000..d7aca36
--- /dev/null
+++ b/core/res/res/drawable-mdpi/pointer_spot_anchor.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/pointer_spot_anchor_icon.xml b/core/res/res/drawable-mdpi/pointer_spot_anchor_icon.xml
new file mode 100644
index 0000000..2222b8e
--- /dev/null
+++ b/core/res/res/drawable-mdpi/pointer_spot_anchor_icon.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
+ android:bitmap="@drawable/pointer_spot_anchor"
+ android:hotSpotX="33"
+ android:hotSpotY="33" />
diff --git a/core/res/res/drawable-mdpi/pointer_spot_hover.png b/core/res/res/drawable-mdpi/pointer_spot_hover.png
new file mode 100644
index 0000000..5041aa3
--- /dev/null
+++ b/core/res/res/drawable-mdpi/pointer_spot_hover.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/pointer_spot_hover_icon.xml b/core/res/res/drawable-mdpi/pointer_spot_hover_icon.xml
new file mode 100644
index 0000000..dc62a69
--- /dev/null
+++ b/core/res/res/drawable-mdpi/pointer_spot_hover_icon.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
+ android:bitmap="@drawable/pointer_spot_hover"
+ android:hotSpotX="33"
+ android:hotSpotY="33" />
diff --git a/core/res/res/drawable-mdpi/pointer_spot_touch.png b/core/res/res/drawable-mdpi/pointer_spot_touch.png
new file mode 100644
index 0000000..64a42a1
--- /dev/null
+++ b/core/res/res/drawable-mdpi/pointer_spot_touch.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/pointer_spot_touch_icon.xml b/core/res/res/drawable-mdpi/pointer_spot_touch_icon.xml
new file mode 100644
index 0000000..4bffee6
--- /dev/null
+++ b/core/res/res/drawable-mdpi/pointer_spot_touch_icon.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
+ android:bitmap="@drawable/pointer_spot_touch"
+ android:hotSpotX="24"
+ android:hotSpotY="24" />
diff --git a/core/res/res/drawable-mdpi/stat_sys_adb.png b/core/res/res/drawable-mdpi/stat_sys_adb.png
index 1400bb3..6ba480d 100644
--- a/core/res/res/drawable-mdpi/stat_sys_adb.png
+++ b/core/res/res/drawable-mdpi/stat_sys_adb.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/text_edit_suggestions_bottom_window.9.png b/core/res/res/drawable-mdpi/text_edit_suggestions_bottom_window.9.png
new file mode 100644
index 0000000..88be6e1
--- /dev/null
+++ b/core/res/res/drawable-mdpi/text_edit_suggestions_bottom_window.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/text_edit_suggestions_top_window.9.png b/core/res/res/drawable-mdpi/text_edit_suggestions_top_window.9.png
new file mode 100644
index 0000000..41886eb
--- /dev/null
+++ b/core/res/res/drawable-mdpi/text_edit_suggestions_top_window.9.png
Binary files differ
diff --git a/core/res/res/drawable-nodpi/platlogo.png b/core/res/res/drawable-nodpi/platlogo.png
index 67e6ac3..c235005 100644
--- a/core/res/res/drawable-nodpi/platlogo.png
+++ b/core/res/res/drawable-nodpi/platlogo.png
Binary files differ
diff --git a/core/res/res/drawable/btn_cab_done.xml b/core/res/res/drawable/btn_cab_done.xml
index 154837d..e3cf461 100644
--- a/core/res/res/drawable/btn_cab_done.xml
+++ b/core/res/res/drawable/btn_cab_done.xml
@@ -16,11 +16,11 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_window_focused="false" android:state_enabled="true"
- android:drawable="@drawable/btn_cab_done_holo" />
+ android:drawable="@drawable/btn_cab_done_default_holo" />
<item android:state_pressed="true"
android:drawable="@drawable/btn_cab_done_pressed_holo" />
<item android:state_focused="true" android:state_enabled="true"
android:drawable="@drawable/btn_cab_done_focused_holo" />
<item android:state_enabled="true"
- android:drawable="@drawable/btn_cab_done_holo" />
+ android:drawable="@drawable/btn_cab_done_default_holo" />
</selector>
diff --git a/core/res/res/layout-large/action_bar_home.xml b/core/res/res/layout-large/action_bar_home.xml
new file mode 100644
index 0000000..86580bc
--- /dev/null
+++ b/core/res/res/layout-large/action_bar_home.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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.
+-->
+
+<view xmlns:android="http://schemas.android.com/apk/res/android"
+ class="com.android.internal.widget.ActionBarView$HomeView"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:background="?android:attr/selectableItemBackground" >
+ <ImageView android:id="@android:id/up"
+ android:src="?android:attr/homeAsUpIndicator"
+ android:layout_gravity="center_vertical|left"
+ android:visibility="gone"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginRight="-12dip" />
+ <ImageView android:id="@android:id/home"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingLeft="16dip"
+ android:paddingRight="16dip"
+ android:paddingTop="4dip"
+ android:paddingBottom="4dip"
+ android:adjustViewBounds="true"
+ android:layout_gravity="center"
+ android:scaleType="fitCenter" />
+</view>
diff --git a/core/res/res/layout-large/action_mode_close_item.xml b/core/res/res/layout-large/action_mode_close_item.xml
new file mode 100644
index 0000000..321622e
--- /dev/null
+++ b/core/res/res/layout-large/action_mode_close_item.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/action_mode_close_button"
+ android:background="@drawable/btn_cab_done"
+ android:focusable="true"
+ android:clickable="true"
+ android:paddingLeft="16dip"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent">
+ <ImageView android:layout_width="48dip"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:scaleType="center"
+ android:src="@drawable/ic_cab_close_holo" />
+ <TextView android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:layout_marginLeft="8dip"
+ android:layout_marginRight="16dip"
+ android:textAppearance="@android:style/TextAppearance.Holo.Medium"
+ android:textColor="@android:color/white"
+ android:text="@string/action_mode_done" />
+</LinearLayout>
diff --git a/core/res/res/layout-xlarge/status_bar_latest_event_content.xml b/core/res/res/layout-xlarge/status_bar_latest_event_content.xml
deleted file mode 100644
index 676c38b..0000000
--- a/core/res/res/layout-xlarge/status_bar_latest_event_content.xml
+++ /dev/null
@@ -1,48 +0,0 @@
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- >
- <ImageView android:id="@+id/icon"
- android:layout_width="@dimen/notification_large_icon_width"
- android:layout_height="@dimen/notification_large_icon_height"
- android:background="@drawable/notify_panel_notification_icon_bg"
- android:scaleType="center"
- />
- <LinearLayout
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:layout_weight="1"
- android:orientation="vertical"
- android:paddingLeft="16dp"
- >
- <TextView android:id="@+id/title"
- android:textAppearance="@style/TextAppearance.StatusBar.EventContent.Title"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:singleLine="true"
- android:ellipsize="marquee"
- android:fadingEdge="horizontal"
- android:layout_marginBottom="-3dp"
- />
- <TextView android:id="@+id/text"
- android:textAppearance="@style/TextAppearance.StatusBar.EventContent"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:layout_marginTop="-2dp"
- android:singleLine="true"
- android:ellipsize="marquee"
- android:fadingEdge="horizontal"
- />
- </LinearLayout>
- <TextView android:id="@+id/info"
- android:textAppearance="@style/TextAppearance.StatusBar.EventContent.Info"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:singleLine="true"
- android:gravity="center_vertical"
- android:paddingLeft="8dp"
- />
-</LinearLayout>
-
diff --git a/core/res/res/layout/action_bar_home.xml b/core/res/res/layout/action_bar_home.xml
index a4c0962..7f7c55c 100644
--- a/core/res/res/layout/action_bar_home.xml
+++ b/core/res/res/layout/action_bar_home.xml
@@ -25,12 +25,15 @@
android:visibility="gone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginRight="@dimen/action_bar_home_up_margin" />
+ android:layout_marginRight="-4dip" />
<ImageView android:id="@android:id/home"
android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:paddingLeft="@dimen/action_bar_home_image_padding"
- android:paddingRight="@dimen/action_bar_home_image_padding"
+ android:layout_height="wrap_content"
+ android:paddingLeft="8dip"
+ android:paddingRight="8dip"
+ android:paddingTop="@dimen/action_bar_icon_vertical_padding"
+ android:paddingBottom="@dimen/action_bar_icon_vertical_padding"
android:layout_gravity="center"
- android:scaleType="center" />
+ android:adjustViewBounds="true"
+ android:scaleType="fitCenter" />
</view>
diff --git a/core/res/res/layout/action_bar_title_item.xml b/core/res/res/layout/action_bar_title_item.xml
index 0cf4222..d8b729d 100644
--- a/core/res/res/layout/action_bar_title_item.xml
+++ b/core/res/res/layout/action_bar_title_item.xml
@@ -28,5 +28,6 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
- android:ellipsize="end" />
+ android:ellipsize="end"
+ android:visibility="gone" />
</LinearLayout>
diff --git a/core/res/res/layout/action_menu_item_layout.xml b/core/res/res/layout/action_menu_item_layout.xml
index 15dfea3..5e828fa 100644
--- a/core/res/res/layout/action_menu_item_layout.xml
+++ b/core/res/res/layout/action_menu_item_layout.xml
@@ -24,17 +24,18 @@
android:paddingLeft="12dip"
android:paddingRight="12dip"
android:minWidth="64dip"
- android:minHeight="?attr/actionBarSize">
+ android:minHeight="?attr/actionBarSize"
+ android:focusable="true">
<ImageButton android:id="@+id/imageButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:visibility="gone"
- android:paddingLeft="4dip"
- android:paddingRight="4dip"
- android:minHeight="56dip"
- android:scaleType="center"
- android:background="@null" />
+ android:padding="@dimen/action_bar_icon_vertical_padding"
+ android:scaleType="fitCenter"
+ android:adjustViewBounds="true"
+ android:background="@null"
+ android:focusable="false" />
<Button android:id="@+id/textButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -44,6 +45,6 @@
style="?attr/buttonStyleSmall"
android:textColor="?attr/actionMenuTextColor"
android:background="@null"
- android:paddingLeft="4dip"
- android:paddingRight="4dip" />
+ android:padding="4dip"
+ android:focusable="false" />
</com.android.internal.view.menu.ActionMenuItemView>
diff --git a/core/res/res/layout/action_menu_layout.xml b/core/res/res/layout/action_menu_layout.xml
index 18d5531..5696d87 100644
--- a/core/res/res/layout/action_menu_layout.xml
+++ b/core/res/res/layout/action_menu_layout.xml
@@ -17,4 +17,7 @@
<com.android.internal.view.menu.ActionMenuView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
+ android:layout_height="wrap_content"
+ android:divider="?android:attr/dividerVertical"
+ android:dividerPadding="12dip"
+ android:gravity="center_vertical" />
diff --git a/core/res/res/layout/action_mode_close_item.xml b/core/res/res/layout/action_mode_close_item.xml
index 7badbac..2a4d8e0 100644
--- a/core/res/res/layout/action_mode_close_item.xml
+++ b/core/res/res/layout/action_mode_close_item.xml
@@ -19,20 +19,12 @@
android:background="@drawable/btn_cab_done"
android:focusable="true"
android:clickable="true"
- android:paddingLeft="16dip"
+ android:paddingLeft="8dip"
android:layout_width="wrap_content"
android:layout_height="match_parent">
- <ImageView android:layout_width="48dip"
+ <ImageView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
- android:scaleType="center"
+ android:scaleType="fitCenter"
android:src="@drawable/ic_cab_close_holo" />
- <TextView android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:layout_marginLeft="8dip"
- android:layout_marginRight="16dip"
- android:textAppearance="@android:style/TextAppearance.Holo.Medium"
- android:textColor="@android:color/white"
- android:text="@string/action_mode_done" />
</LinearLayout>
diff --git a/core/res/res/layout/input_method_extract_view.xml b/core/res/res/layout/input_method_extract_view.xml
index 689ba7b..7d59d02 100644
--- a/core/res/res/layout/input_method_extract_view.xml
+++ b/core/res/res/layout/input_method_extract_view.xml
@@ -31,7 +31,6 @@
android:gravity="top"
android:minLines="1"
android:inputType="text"
- android:background="@android:drawable/extract_edit_text"
>
</android.inputmethodservice.ExtractEditText>
diff --git a/core/res/res/layout/keyguard_screen_password_landscape.xml b/core/res/res/layout/keyguard_screen_password_landscape.xml
index 4d8c688..aac0853 100644
--- a/core/res/res/layout/keyguard_screen_password_landscape.xml
+++ b/core/res/res/layout/keyguard_screen_password_landscape.xml
@@ -16,59 +16,81 @@
** limitations under the License.
*/
-->
+
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:orientation="vertical"
- android:gravity="center_horizontal">
+ android:orientation="vertical">
- <LinearLayout
+ <View
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
- <!-- "Enter PIN(Password) to unlock" -->
- <TextView android:id="@+id/status1"
- android:layout_width="0dip"
+ android:layout_height="0dip"
+ android:layout_weight="1"
+ />
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <!-- left side: status -->
+ <include layout="@layout/keyguard_screen_status_land"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_weight="1"
- android:orientation="horizontal"
- android:layout_marginRight="6dip"
- android:layout_marginLeft="6dip"
- android:layout_marginTop="10dip"
- android:layout_marginBottom="10dip"
- android:gravity="left"
- android:ellipsize="marquee"
- android:text="@android:string/keyguard_password_enter_password_code"
- android:textAppearance="?android:attr/textAppearanceLarge"
- />
+ android:layout_centerVertical="true"
+ android:layout_alignParentLeft="true"/>
- <!-- Password entry field -->
- <EditText android:id="@+id/passwordEntry"
- android:layout_width="0dip"
+ <!-- right side: password -->
+ <LinearLayout
+ android:layout_width="300dip"
android:layout_height="wrap_content"
- android:layout_weight="1"
- android:singleLine="true"
- android:textStyle="bold"
- android:inputType="textPassword"
- android:gravity="center"
- android:layout_gravity="center"
- android:textSize="24sp"
- android:textAppearance="?android:attr/textAppearanceLarge"
- android:background="@drawable/password_field_default"
- android:textColor="#ffffffff"
- />
- </LinearLayout>
+ android:orientation="vertical"
+ android:layout_alignParentRight="true"
+ android:layout_centerVertical="true">
+
+ <!-- Password entry field -->
+ <EditText android:id="@+id/passwordEntry"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:singleLine="true"
+ android:textStyle="normal"
+ android:inputType="textPassword"
+ android:gravity="center"
+ android:textSize="24sp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:background="@drawable/lockscreen_password_field_dark"
+ android:textColor="#ffffffff"
+ />
+
+ <!-- Numeric keyboard -->
+ <com.android.internal.widget.PasswordEntryKeyboardView android:id="@+id/keyboard"
+ android:layout_width="300dip"
+ android:layout_height="400dip"
+ android:background="#40000000"
+ android:layout_marginTop="5dip"
+ android:keyBackground="@drawable/btn_keyboard_key_fulltrans"
+ android:visibility="gone"
+ />
+ </LinearLayout>
+
+ </RelativeLayout>
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1"
+ />
<!-- Alphanumeric keyboard -->
- <com.android.internal.widget.PasswordEntryKeyboardView android:id="@+id/keyboard"
- android:layout_alignParentBottom="true"
+ <com.android.internal.widget.PasswordEntryKeyboardView android:id="@+id/keyboardAlpha"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:background="#00000000"
+ android:background="@drawable/password_keyboard_background_holo"
android:keyBackground="@drawable/btn_keyboard_key_fulltrans"
+ android:keyTextSize="18dip"
+ android:visibility="gone"
/>
- <!-- emergency call button -->
+ <!-- emergency call button NOT CURRENTLY USED -->
<Button
android:id="@+id/emergencyCall"
android:layout_width="wrap_content"
@@ -76,6 +98,7 @@
android:drawableLeft="@drawable/ic_emergency"
android:drawablePadding="8dip"
android:text="@string/lockscreen_emergency_call"
+ android:visibility="gone"
style="@style/Widget.Button.Transparent"
/>
diff --git a/core/res/res/layout/keyguard_screen_password_portrait.xml b/core/res/res/layout/keyguard_screen_password_portrait.xml
index 93966de..7805672 100644
--- a/core/res/res/layout/keyguard_screen_password_portrait.xml
+++ b/core/res/res/layout/keyguard_screen_password_portrait.xml
@@ -19,74 +19,68 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:orientation="vertical"
- android:gravity="center_horizontal">
+ android:orientation="vertical">
- <!-- "Enter PIN(Password) to unlock" -->
- <TextView android:id="@+id/status1"
- android:layout_width="match_parent"
+ <!-- top: status -->
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <include layout="@layout/keyguard_screen_status_port"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentTop="true"
+ android:layout_alignParentLeft="true"/>
+ </RelativeLayout>
+
+ <!-- emergency call button -->
+ <Button
+ android:id="@+id/emergencyCall"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:layout_marginRight="6dip"
- android:layout_marginLeft="6dip"
- android:layout_marginTop="10dip"
- android:layout_marginBottom="10dip"
- android:gravity="center"
- android:text="@android:string/keyguard_password_enter_password_code"
- android:textAppearance="?android:attr/textAppearanceLarge"
+ android:drawableLeft="@drawable/ic_emergency"
+ android:drawablePadding="8dip"
+ android:text="@string/lockscreen_emergency_call"
+ android:visibility="gone"
+ style="@style/Widget.Button.Transparent"
/>
- <!-- spacer above text entry field -->
- <View
- android:id="@+id/spacerBottom"
- android:layout_width="fill_parent"
- android:layout_height="1dip"
- android:layout_marginTop="6dip"
- android:background="@android:drawable/divider_horizontal_dark"
- />
+ <!-- bottom: password -->
<!-- Password entry field -->
<EditText android:id="@+id/passwordEntry"
- android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:layout_width="match_parent"
android:singleLine="true"
- android:textStyle="bold"
+ android:textStyle="normal"
android:inputType="textPassword"
android:gravity="center"
- android:layout_gravity="center"
- android:textSize="32sp"
- android:layout_marginTop="15dip"
- android:layout_marginLeft="30dip"
- android:layout_marginRight="30dip"
- android:textAppearance="?android:attr/textAppearanceLarge"
- android:background="@drawable/password_field_default"
- android:textColor="#ffffffff"
- />
+ android:textSize="22sp"
+ android:background="@drawable/lockscreen_password_field_dark"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="#ffffffff"/>
<View
android:layout_width="match_parent"
android:layout_height="0dip"
- android:layout_weight="1" />
+ android:layout_weight="1"
+ />
- <!-- Alphanumeric keyboard -->
+ <!-- Numeric keyboard -->
<com.android.internal.widget.PasswordEntryKeyboardView android:id="@+id/keyboard"
- android:layout_alignParentBottom="true"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="#00000000"
+ android:layout_height="260dip"
+ android:background="#40000000"
android:keyBackground="@drawable/btn_keyboard_key_fulltrans"
/>
- <!-- emergency call button -->
- <Button
- android:id="@+id/emergencyCall"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:drawableLeft="@drawable/ic_emergency"
- android:drawablePadding="8dip"
- android:layout_marginTop="20dip"
- android:layout_marginBottom="20dip"
- android:text="@string/lockscreen_emergency_call"
- style="@style/Widget.Button.Transparent"
+ <!-- Alphanumeric keyboard -->
+ <com.android.internal.widget.PasswordEntryKeyboardView android:id="@+id/keyboardAlpha"
+ android:layout_width="match_parent"
+ android:layout_height="400dip"
+ android:background="@drawable/password_keyboard_background_holo"
+ android:keyBackground="@drawable/btn_keyboard_key_fulltrans"
+ android:keyTextSize="22dip"
+ android:visibility="gone"
/>
-</LinearLayout>
+
+</LinearLayout> \ No newline at end of file
diff --git a/core/res/res/layout/keyguard_screen_status_land.xml b/core/res/res/layout/keyguard_screen_status_land.xml
new file mode 100644
index 0000000..259a3af
--- /dev/null
+++ b/core/res/res/layout/keyguard_screen_status_land.xml
@@ -0,0 +1,148 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2010, 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.
+*/
+-->
+
+<!-- Status to show on the left side of lock screen -->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="left"
+ >
+
+ <TextView
+ android:id="@+id/carrier"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textSize="17sp"
+ android:drawablePadding="4dip"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ />
+
+ <com.android.internal.widget.DigitalClock android:id="@+id/time"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentTop="true"
+ android:layout_alignParentLeft="true"
+ android:layout_marginTop="8dip"
+ android:layout_marginBottom="8dip">
+
+ <!-- Because we can't have multi-tone fonts, we render two TextViews, one on
+ top of the other. Hence the redundant layout... -->
+ <TextView android:id="@+id/timeDisplayBackground"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:ellipsize="none"
+ android:textSize="40sp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="@color/lockscreen_clock_background"
+ android:layout_marginBottom="6dip"
+ />
+
+ <TextView android:id="@+id/timeDisplayForeground"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:ellipsize="none"
+ android:textSize="40sp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="@color/lockscreen_clock_foreground"
+ android:layout_alignLeft="@id/timeDisplayBackground"
+ android:layout_alignTop="@id/timeDisplayBackground"
+ android:layout_marginBottom="6dip"
+ />
+
+ <TextView android:id="@+id/am_pm"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_toRightOf="@id/timeDisplayBackground"
+ android:layout_alignBaseline="@id/timeDisplayBackground"
+ android:singleLine="true"
+ android:ellipsize="none"
+ android:textSize="18sp"
+ android:layout_marginLeft="8dip"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="@color/lockscreen_clock_am_pm"
+ />
+
+ </com.android.internal.widget.DigitalClock>
+
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/time"
+ android:layout_marginTop="10dip">
+
+ <TextView
+ android:id="@+id/date"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textSize="17sp"/>
+
+ <TextView
+ android:id="@+id/alarm_status"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="30dip"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textSize="17sp"/>
+
+ </LinearLayout>
+
+ <!-- Status2 is generally charge status -->
+ <TextView
+ android:id="@+id/status2"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentTop="true"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textSize="17sp"
+ android:layout_marginTop="10dip"
+ android:drawablePadding="4dip"
+ android:visibility="gone"
+ />
+
+ <!-- Status1 is generally battery status and informational messages -->
+ <TextView
+ android:id="@+id/status1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="10dip"
+ android:textSize="17sp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ />
+
+ <TextView
+ android:id="@+id/propertyOf"
+ android:lineSpacingExtra="8dip"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textSize="17sp"
+ android:layout_marginTop="20dip"
+ android:singleLine="false"
+ android:textColor="@color/lockscreen_owner_info"
+ android:visibility="gone"
+ />
+</LinearLayout>
diff --git a/core/res/res/layout/keyguard_screen_status_port.xml b/core/res/res/layout/keyguard_screen_status_port.xml
new file mode 100644
index 0000000..680c073
--- /dev/null
+++ b/core/res/res/layout/keyguard_screen_status_port.xml
@@ -0,0 +1,145 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2010, 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.
+*/
+-->
+
+<!-- Status to show on the left side of lock screen -->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="left">
+
+ <TextView
+ android:id="@+id/carrier"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textSize="17sp"
+ android:drawablePadding="4dip"
+ android:layout_marginTop="10dip"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ />
+
+ <com.android.internal.widget.DigitalClock android:id="@+id/time"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dip"
+ android:layout_marginBottom="8dip">
+
+ <!-- Because we can't have multi-tone fonts, we render two TextViews, one on
+ top of the other. Hence the redundant layout... -->
+ <TextView android:id="@+id/timeDisplayBackground"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:ellipsize="none"
+ android:textSize="60sp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="@color/lockscreen_clock_background"
+ android:layout_marginBottom="6dip"
+ />
+
+ <TextView android:id="@+id/timeDisplayForeground"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:ellipsize="none"
+ android:textSize="60sp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="@color/lockscreen_clock_foreground"
+ android:layout_marginBottom="6dip"
+ android:layout_alignLeft="@id/timeDisplayBackground"
+ android:layout_alignTop="@id/timeDisplayBackground"
+ />
+
+ <TextView android:id="@+id/am_pm"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_toRightOf="@id/timeDisplayBackground"
+ android:layout_alignBaseline="@id/timeDisplayBackground"
+ android:singleLine="true"
+ android:ellipsize="none"
+ android:textSize="22sp"
+ android:layout_marginLeft="8dip"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="@color/lockscreen_clock_am_pm"
+ />
+
+ </com.android.internal.widget.DigitalClock>
+
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/time"
+ android:layout_marginTop="10dip">
+
+ <TextView
+ android:id="@+id/date"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textSize="17sp"/>
+
+ <TextView
+ android:id="@+id/alarm_status"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="30dip"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textSize="17sp"/>
+
+ </LinearLayout>
+
+ <!-- used for status such as the next alarm, and charging status. -->
+ <TextView
+ android:id="@+id/status2"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentTop="true"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textSize="17sp"
+ android:layout_marginTop="10dip"
+ android:drawablePadding="4dip"
+ android:visibility="gone"
+ />
+
+ <TextView
+ android:id="@+id/status1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="10dip"
+ android:textSize="17sp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ />
+
+ <TextView
+ android:id="@+id/propertyOf"
+ android:lineSpacingExtra="8dip"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="20dip"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textSize="17sp"
+ android:singleLine="false"
+ android:visibility="gone"
+ android:textColor="@color/lockscreen_owner_info"
+ />
+</LinearLayout>
diff --git a/core/res/res/layout/preference_list_content.xml b/core/res/res/layout/preference_list_content.xml
index 5d034a5..925b715 100644
--- a/core/res/res/layout/preference_list_content.xml
+++ b/core/res/res/layout/preference_list_content.xml
@@ -36,8 +36,6 @@
android:layout_height="match_parent"
android:layout_marginRight="@dimen/preference_screen_side_margin_negative"
android:layout_marginLeft="@dimen/preference_screen_side_margin"
- android:layout_marginTop="32dp"
- android:layout_marginBottom="32dp"
android:layout_weight="10">
<ListView android:id="@android:id/list"
@@ -61,33 +59,9 @@
android:layout_width="0px"
android:layout_height="match_parent"
android:layout_weight="20"
- android:layout_marginLeft="@dimen/preference_screen_side_margin"
- android:layout_marginRight="@dimen/preference_screen_side_margin"
- android:layout_marginTop="16dp"
- android:layout_marginBottom="16dp"
- android:background="?attr/detailsElementBackground"
android:orientation="vertical"
android:visibility="gone" >
- <!-- Breadcrumb inserted here -->
- <android.app.FragmentBreadCrumbs
- android:id="@android:id/title"
- android:layout_height="72dip"
- android:layout_width="match_parent"
- android:paddingTop="16dip"
- android:paddingBottom="8dip"
- android:gravity="center_vertical|left"
- android:layout_marginLeft="48dip"
- android:layout_marginRight="48dip"
- />
-
- <ImageView
- android:layout_width="match_parent"
- android:layout_height="1dip"
- android:paddingLeft="32dip"
- android:paddingRight="32dip"
- android:src="#404040"
- />
<android.preference.PreferenceFrameLayout android:id="@+id/prefs"
android:layout_width="match_parent"
android:layout_height="0dip"
diff --git a/core/res/res/layout/preference_list_content_large.xml b/core/res/res/layout/preference_list_content_large.xml
new file mode 100644
index 0000000..14d188e
--- /dev/null
+++ b/core/res/res/layout/preference_list_content_large.xml
@@ -0,0 +1,140 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/assets/res/layout/list_content.xml
+**
+** Copyright 2011, 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.
+*/
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent">
+
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="0px"
+ android:layout_weight="1">
+
+ <LinearLayout
+ android:id="@+id/headers"
+ android:orientation="vertical"
+ android:layout_width="0px"
+ android:layout_height="match_parent"
+ android:layout_marginRight="@dimen/preference_screen_side_margin_negative"
+ android:layout_marginLeft="@dimen/preference_screen_side_margin"
+ android:layout_marginTop="32dp"
+ android:layout_marginBottom="32dp"
+ android:layout_weight="10">
+
+ <ListView android:id="@android:id/list"
+ android:layout_width="match_parent"
+ android:layout_height="0px"
+ android:layout_weight="1"
+ android:drawSelectorOnTop="false"
+ android:cacheColorHint="@android:color/transparent"
+ android:listPreferredItemHeight="48dp"
+ android:scrollbarAlwaysDrawVerticalTrack="true" />
+
+ <FrameLayout android:id="@+id/list_footer"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="0" />
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/prefs_frame"
+ android:layout_width="0px"
+ android:layout_height="match_parent"
+ android:layout_weight="20"
+ android:layout_marginLeft="@dimen/preference_screen_side_margin"
+ android:layout_marginRight="@dimen/preference_screen_side_margin"
+ android:layout_marginTop="16dp"
+ android:layout_marginBottom="16dp"
+ android:background="?attr/detailsElementBackground"
+ android:orientation="vertical"
+ android:visibility="gone" >
+
+ <!-- Breadcrumb inserted here -->
+ <android.app.FragmentBreadCrumbs
+ android:id="@android:id/title"
+ android:layout_height="72dip"
+ android:layout_width="match_parent"
+ android:paddingTop="16dip"
+ android:paddingBottom="8dip"
+ android:gravity="center_vertical|left"
+ android:layout_marginLeft="48dip"
+ android:layout_marginRight="48dip"
+ />
+
+ <ImageView
+ android:layout_width="match_parent"
+ android:layout_height="1dip"
+ android:paddingLeft="32dip"
+ android:paddingRight="32dip"
+ android:src="#404040"
+ />
+ <android.preference.PreferenceFrameLayout android:id="@+id/prefs"
+ android:layout_width="match_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1"
+ android:layout_marginTop="-1dip"
+ />
+ </LinearLayout>
+ </LinearLayout>
+
+ <RelativeLayout android:id="@+id/button_bar"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:layout_weight="0"
+ android:background="@android:drawable/bottom_bar"
+ android:visibility="gone">
+
+ <Button android:id="@+id/back_button"
+ android:layout_width="150dip"
+ android:layout_height="wrap_content"
+ android:layout_margin="5dip"
+ android:layout_alignParentLeft="true"
+ android:drawableLeft="@drawable/ic_btn_back"
+ android:drawablePadding="3dip"
+ android:text="@string/back_button_label"
+ />
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true">
+
+ <Button android:id="@+id/skip_button"
+ android:layout_width="150dip"
+ android:layout_height="wrap_content"
+ android:layout_margin="5dip"
+ android:text="@string/skip_button_label"
+ android:visibility="gone"
+ />
+
+ <Button android:id="@+id/next_button"
+ android:layout_width="150dip"
+ android:layout_height="wrap_content"
+ android:layout_margin="5dip"
+ android:drawableRight="@drawable/ic_btn_next"
+ android:drawablePadding="3dip"
+ android:text="@string/next_button_label"
+ />
+ </LinearLayout>
+ </RelativeLayout>
+</LinearLayout>
diff --git a/core/res/res/layout-xlarge/preference_list_content_single.xml b/core/res/res/layout/preference_list_content_single_large.xml
index 6725996..6725996 100644
--- a/core/res/res/layout-xlarge/preference_list_content_single.xml
+++ b/core/res/res/layout/preference_list_content_single_large.xml
diff --git a/core/res/res/layout/preference_list_fragment.xml b/core/res/res/layout/preference_list_fragment.xml
index 393cecf..4044371 100644
--- a/core/res/res/layout/preference_list_fragment.xml
+++ b/core/res/res/layout/preference_list_fragment.xml
@@ -28,10 +28,6 @@
android:layout_width="match_parent"
android:layout_height="0px"
android:layout_weight="1"
- android:paddingTop="0dip"
- android:paddingBottom="48dip"
- android:paddingLeft="32dip"
- android:paddingRight="32dip"
android:clipToPadding="false"
android:drawSelectorOnTop="false"
android:cacheColorHint="@android:color/transparent"
diff --git a/core/res/res/layout/preference_list_fragment_large.xml b/core/res/res/layout/preference_list_fragment_large.xml
new file mode 100644
index 0000000..cde84ff
--- /dev/null
+++ b/core/res/res/layout/preference_list_fragment_large.xml
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2011, 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.
+*/
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:background="@android:color/transparent"
+ android:layout_removeBorders="true">
+
+ <ListView android:id="@android:id/list"
+ android:layout_width="match_parent"
+ android:layout_height="0px"
+ android:layout_weight="1"
+ android:paddingTop="0dip"
+ android:paddingBottom="48dip"
+ android:paddingLeft="32dip"
+ android:paddingRight="32dip"
+ android:clipToPadding="false"
+ android:drawSelectorOnTop="false"
+ android:cacheColorHint="@android:color/transparent"
+ android:scrollbarAlwaysDrawVerticalTrack="true" />
+
+ <RelativeLayout android:id="@+id/button_bar"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:layout_weight="0"
+ android:background="@android:drawable/bottom_bar"
+ android:visibility="gone">
+
+ <Button android:id="@+id/back_button"
+ android:layout_width="150dip"
+ android:layout_height="wrap_content"
+ android:layout_margin="5dip"
+ android:layout_alignParentLeft="true"
+ android:drawableLeft="@drawable/ic_btn_back"
+ android:drawablePadding="3dip"
+ android:text="@string/back_button_label"
+ />
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true">
+
+ <Button android:id="@+id/skip_button"
+ android:layout_width="150dip"
+ android:layout_height="wrap_content"
+ android:layout_margin="5dip"
+ android:text="@string/skip_button_label"
+ android:visibility="gone"
+ />
+
+ <Button android:id="@+id/next_button"
+ android:layout_width="150dip"
+ android:layout_height="wrap_content"
+ android:layout_margin="5dip"
+ android:drawableRight="@drawable/ic_btn_next"
+ android:drawablePadding="3dip"
+ android:text="@string/next_button_label"
+ />
+ </LinearLayout>
+ </RelativeLayout>
+</LinearLayout>
diff --git a/core/res/res/layout/screen_action_bar.xml b/core/res/res/layout/screen_action_bar.xml
index 70af265..9742b94 100644
--- a/core/res/res/layout/screen_action_bar.xml
+++ b/core/res/res/layout/screen_action_bar.xml
@@ -43,9 +43,10 @@ This is an optimized layout for a screen with the Action Bar enabled.
android:layout_weight="1"
android:foregroundGravity="fill_horizontal|top"
android:foreground="?android:attr/windowContentOverlay" />
- <LinearLayout android:id="@+id/lower_action_context_bar"
+ <com.android.internal.widget.ActionBarContainer android:id="@+id/split_action_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="?android:attr/actionBarStyle"
- android:visibility="gone" />
+ android:visibility="gone"
+ android:gravity="center"/>
</LinearLayout>
diff --git a/core/res/res/layout/screen_action_bar_overlay.xml b/core/res/res/layout/screen_action_bar_overlay.xml
index b486ee7..086acdd 100644
--- a/core/res/res/layout/screen_action_bar_overlay.xml
+++ b/core/res/res/layout/screen_action_bar_overlay.xml
@@ -46,10 +46,11 @@ the Action Bar enabled overlaying application content.
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/action_bar_container" />
- <LinearLayout android:id="@+id/lower_action_context_bar"
+ <com.android.internal.widget.ActionBarContainer android:id="@+id/split_action_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_gravity="bottom"
+ android:layout_alignParentBottom="true"
style="?android:attr/actionBarStyle"
- android:visibility="gone" />
+ android:visibility="gone"
+ android:gravity="center"/>
</RelativeLayout>
diff --git a/core/res/res/layout/status_bar_latest_event_content.xml b/core/res/res/layout/status_bar_latest_event_content.xml
index c64b90e..676c38b 100644
--- a/core/res/res/layout/status_bar_latest_event_content.xml
+++ b/core/res/res/layout/status_bar_latest_event_content.xml
@@ -1,56 +1,48 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- android:paddingTop="7dp"
- android:paddingLeft="5dp"
- >
-
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ >
+ <ImageView android:id="@+id/icon"
+ android:layout_width="@dimen/notification_large_icon_width"
+ android:layout_height="@dimen/notification_large_icon_height"
+ android:background="@drawable/notify_panel_notification_icon_bg"
+ android:scaleType="center"
+ />
<LinearLayout
- android:layout_width="match_parent"
+ android:layout_width="0dp"
android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:paddingTop="3dp"
+ android:layout_gravity="center_vertical"
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:paddingLeft="16dp"
>
- <!--com.android.server.status.AnimatedImageView android:id="@+id/icon" -->
- <ImageView android:id="@+id/icon"
- android:layout_width="25dp"
- android:layout_height="25dp"
- android:scaleType="fitCenter"
- android:src="@drawable/arrow_down_float"/>
<TextView android:id="@+id/title"
android:textAppearance="@style/TextAppearance.StatusBar.EventContent.Title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_weight="1"
android:singleLine="true"
android:ellipsize="marquee"
android:fadingEdge="horizontal"
- android:paddingLeft="4dp"
+ android:layout_marginBottom="-3dp"
/>
- </LinearLayout>
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- >
<TextView android:id="@+id/text"
android:textAppearance="@style/TextAppearance.StatusBar.EventContent"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
+ android:layout_marginTop="-2dp"
android:singleLine="true"
android:ellipsize="marquee"
android:fadingEdge="horizontal"
- android:paddingLeft="4dp"
- />
- <android.widget.DateTimeView android:id="@+id/time"
- android:textAppearance="@style/TextAppearance.StatusBar.EventContent"
- android:layout_marginLeft="4dp"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:singleLine="true"
- android:paddingRight="5dp"
/>
</LinearLayout>
+ <TextView android:id="@+id/info"
+ android:textAppearance="@style/TextAppearance.StatusBar.EventContent.Info"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:singleLine="true"
+ android:gravity="center_vertical"
+ android:paddingLeft="8dp"
+ />
</LinearLayout>
+
diff --git a/core/res/res/layout-xlarge/status_bar_latest_event_content_large_icon.xml b/core/res/res/layout/status_bar_latest_event_content_large_icon.xml
index ebdaaa3..ebdaaa3 100644
--- a/core/res/res/layout-xlarge/status_bar_latest_event_content_large_icon.xml
+++ b/core/res/res/layout/status_bar_latest_event_content_large_icon.xml
diff --git a/core/res/res/layout/tab_indicator_holo.xml b/core/res/res/layout/tab_indicator_holo.xml
index d37476b..60c80e9 100644
--- a/core/res/res/layout/tab_indicator_holo.xml
+++ b/core/res/res/layout/tab_indicator_holo.xml
@@ -15,32 +15,24 @@
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
- android:layout_height="56dip"
- android:layout_weight="0"
- android:layout_marginLeft="0dip"
- android:layout_marginRight="0dip"
+ android:layout_width="0dp"
+ android:layout_height="58dp"
+ android:layout_weight="1"
+ android:layout_marginLeft="-3dip"
+ android:layout_marginRight="-3dip"
+ android:paddingBottom="8dp"
android:background="@android:drawable/tab_indicator_holo">
- <View android:id="@+id/tab_indicator_left_spacer"
- android:layout_width="16dip"
- android:layout_height="0dip" />
-
<ImageView android:id="@+id/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_centerVertical="true"
- android:visibility="gone"
- android:layout_toRightOf="@id/tab_indicator_left_spacer"
- android:paddingRight="8dip" />
+ android:layout_centerHorizontal="true" />
<TextView android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_centerVertical="true"
- android:layout_toRightOf="@id/icon"
- android:paddingLeft="0dip"
- android:paddingRight="16dip"
+ android:layout_alignParentBottom="true"
+ android:layout_centerHorizontal="true"
style="?android:attr/tabWidgetStyle" />
-
+
</RelativeLayout>
diff --git a/core/res/res/layout/tab_indicator_holo_large.xml b/core/res/res/layout/tab_indicator_holo_large.xml
new file mode 100644
index 0000000..bdd8d11
--- /dev/null
+++ b/core/res/res/layout/tab_indicator_holo_large.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 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.
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="56dip"
+ android:layout_weight="0"
+ android:layout_marginLeft="0dip"
+ android:layout_marginRight="0dip"
+ android:background="@android:drawable/tab_indicator_holo">
+
+ <View android:id="@+id/tab_indicator_left_spacer"
+ android:layout_width="16dip"
+ android:layout_height="0dip" />
+
+ <ImageView android:id="@+id/icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerVertical="true"
+ android:visibility="gone"
+ android:layout_toRightOf="@id/tab_indicator_left_spacer"
+ android:paddingRight="8dip" />
+
+ <TextView android:id="@+id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerVertical="true"
+ android:layout_toRightOf="@id/icon"
+ android:paddingLeft="0dip"
+ android:paddingRight="16dip"
+ style="?android:attr/tabWidgetStyle" />
+
+</RelativeLayout>
diff --git a/core/res/res/layout/text_edit_suggestion_item.xml b/core/res/res/layout/text_edit_suggestion_item.xml
new file mode 100644
index 0000000..ef537d9
--- /dev/null
+++ b/core/res/res/layout/text_edit_suggestion_item.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 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.
+-->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingLeft="16dip"
+ android:paddingRight="16dip"
+ android:paddingTop="8dip"
+ android:paddingBottom="8dip"
+ android:layout_gravity="left|center_vertical"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="@android:color/dim_foreground_light" />
+
diff --git a/core/res/res/layout/text_edit_suggestions_bottom_window.xml b/core/res/res/layout/text_edit_suggestions_bottom_window.xml
new file mode 100644
index 0000000..588bfbd
--- /dev/null
+++ b/core/res/res/layout/text_edit_suggestions_bottom_window.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:background="@android:drawable/text_edit_suggestions_bottom_window">
+
+</LinearLayout>
diff --git a/core/res/res/drawable/extract_edit_text.xml b/core/res/res/layout/text_edit_suggestions_top_window.xml
index c7f66f6..67faa37 100644
--- a/core/res/res/drawable/extract_edit_text.xml
+++ b/core/res/res/layout/text_edit_suggestions_top_window.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2007 The Android Open Source Project
+<!-- Copyright (C) 2011 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -14,8 +14,10 @@
limitations under the License.
-->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_focused="true" android:drawable="@drawable/keyboard_textfield_selected" />
- <item android:drawable="@drawable/textfield_disabled" />
-</selector>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:background="@android:drawable/text_edit_suggestions_top_window">
+</LinearLayout>
diff --git a/core/res/res/raw/execute_script_android.js b/core/res/res/raw/execute_script_android.js
new file mode 100644
index 0000000..84fcbd4
--- /dev/null
+++ b/core/res/res/raw/execute_script_android.js
@@ -0,0 +1,8 @@
+function(){return function(){function h(a){var b=typeof a;if(b=="object")if(a){if(a instanceof Array)return"array";else if(a instanceof Object)return b;var c=Object.prototype.toString.call(a);if(c=="[object Window]")return"object";if(c=="[object Array]"||typeof a.length=="number"&&typeof a.splice!="undefined"&&typeof a.propertyIsEnumerable!="undefined"&&!a.propertyIsEnumerable("splice"))return"array";if(c=="[object Function]"||typeof a.call!="undefined"&&typeof a.propertyIsEnumerable!="undefined"&&!a.propertyIsEnumerable("call"))return"function"}else return"null";
+else if(b=="function"&&typeof a.call=="undefined")return"object";return b}function i(a){var b=h(a);return b=="array"||b=="object"&&typeof a.length=="number"}function j(a){a=h(a);return a=="object"||a=="array"||a=="function"}var k=Date.now||function(){return+new Date};function l(a,b){function c(){}c.prototype=b.prototype;a.c=b.prototype;a.prototype=new c};function m(a){this.stack=Error().stack||"";if(a)this.message=String(a)}l(m,Error);m.prototype.name="CustomError";function n(a,b,c){var d={};for(var e in a)if(b.call(c,a[e],e,a))d[e]=a[e];return d}function o(a,b,c){var d={};for(var e in a)d[e]=b.call(c,a[e],e,a);return d}function p(a,b,c){for(var d in a)if(b.call(c,a[d],d,a))return d};function q(a,b){m.call(this,b);this.code=a;this.name=r[a]||r[13]}l(q,m);var r,s={NoSuchElementError:7,NoSuchFrameError:8,UnknownCommandError:9,StaleElementReferenceError:10,ElementNotVisibleError:11,InvalidElementStateError:12,UnknownError:13,ElementNotSelectableError:15,XPathLookupError:19,NoSuchWindowError:23,InvalidCookieDomainError:24,UnableToSetCookieError:25,ModalDialogOpenedError:26,ModalDialogOpenError:27,ScriptTimeoutError:28},t={};for(var u in s)t[s[u]]=u;r=t;
+q.prototype.toString=function(){return"["+this.name+"] "+this.message};function v(a){for(var b=1;b<arguments.length;b++){var c=String(arguments[b]).replace(/\$/g,"$$$$");a=a.replace(/\%s/,c)}return a};function w(a,b){b.unshift(a);m.call(this,v.apply(null,b));b.shift();this.b=a}l(w,m);w.prototype.name="AssertionError";function x(a,b){if(!a){var c=Array.prototype.slice.call(arguments,2),d="Assertion failed";if(b){d+=": "+b;var e=c}throw new w(""+d,e||[]);}return a};var y=Array.prototype,z=y.map?function(a,b,c){x(a.length!=null);return y.map.call(a,b,c)}:function(a,b,c){var d=a.length,e=Array(d),f=typeof a=="string"?a.split(""):a;for(var g=0;g<d;g++)if(g in f)e[g]=b.call(c,f[g],g,a);return e};var A="",B;if(B=/WebKit\/(\S+)/){var C=B.exec(this.navigator?this.navigator.userAgent:null);A=C?C[1]:""};function D(){}
+function E(a,b,c){switch(typeof b){case "string":F(a,b,c);break;case "number":c.push(isFinite(b)&&!isNaN(b)?b:"null");break;case "boolean":c.push(b);break;case "undefined":c.push("null");break;case "object":if(b==null){c.push("null");break}if(h(b)=="array"){var d=b.length;c.push("[");var e="";for(var f=0;f<d;f++){c.push(e);E(a,b[f],c);e=","}c.push("]");break}c.push("{");d="";for(e in b)if(Object.prototype.hasOwnProperty.call(b,e)){f=b[e];if(typeof f!="function"){c.push(d);F(a,e,c);c.push(":");E(a,
+f,c);d=","}}c.push("}");break;case "function":break;default:throw Error("Unknown type: "+typeof b);}}var G={'"':'\\"',"\\":"\\\\","/":"\\/","\u0008":"\\b","\u000c":"\\f","\n":"\\n","\r":"\\r","\t":"\\t","\u000b":"\\u000b"},H=/\uffff/.test("\uffff")?/[\\\"\x00-\x1f\x7f-\uffff]/g:/[\\\"\x00-\x1f\x7f-\xff]/g;
+function F(a,b,c){c.push('"',b.replace(H,function(d){if(d in G)return G[d];var e=d.charCodeAt(0),f="\\u";if(e<16)f+="000";else if(e<256)f+="00";else if(e<4096)f+="0";return G[d]=f+e.toString(16)}),'"')};function I(a){switch(h(a)){case "string":case "number":case "boolean":return a;case "function":return a.toString();case "array":return z(a,I);case "object":a=a;if("nodeType"in a&&(a.nodeType==1||a.nodeType==9)){var b={};b.ELEMENT=J(a);return b}if(i(a))return z(a,I);a=n(a,function(c,d){return typeof d=="number"||typeof d=="string"});return o(a,I);default:return null}}
+function K(a,b){if(h(a)=="array")return z(a,function(c){return K(c,b)});else if(j(a))return"ELEMENT"in a?L(a.ELEMENT,b):o(a,function(c){return K(c,b)});return a}function M(a){a=a||document;var b=a.$wdc_;if(!b){b=a.$wdc_={};b.a=k()}return b}function J(a){var b=M(a.ownerDocument),c=p(b,function(d){return d==a});if(!c){c=":wdc:"+b.a++;b[c]=a}return c}
+function L(a,b){a=decodeURIComponent(a);var c=b||document,d=M(c);if(!(a in d))throw new q(10,"Element does not exist in cache");var e=d[a];for(var f=e;f;){if(f==c.documentElement)return e;f=f.parentNode}delete d[a];throw new q(10,"Element is no longer attached to the DOM");};function N(a,b,c){var d;try{if(typeof a=="string")a=new Function(a);var e=K(b),f=a.apply(null,e);d={status:0,value:I(f)}}catch(g){d={status:"code"in g?g.code:13,value:{message:g.message}}}if(c){a=[];E(new D,d,a);d=a.join("")}else d=d;return d}var O="_".split("."),P=this;!(O[0]in P)&&P.execScript&&P.execScript("var "+O[0]);for(var Q;O.length&&(Q=O.shift());)if(!O.length&&N!==undefined)P[Q]=N;else P=P[Q]?P[Q]:P[Q]={};; return this._.apply(null,arguments);}.apply({navigator:typeof window!='undefined'?window.navigator:null}, arguments);}
diff --git a/core/res/res/raw/find_element_android.js b/core/res/res/raw/find_element_android.js
new file mode 100644
index 0000000..c62f851
--- /dev/null
+++ b/core/res/res/raw/find_element_android.js
@@ -0,0 +1,27 @@
+function(){return function(){var i=this;
+function j(a){var b=typeof a;if(b=="object")if(a){if(a instanceof Array)return"array";else if(a instanceof Object)return b;var c=Object.prototype.toString.call(a);if(c=="[object Window]")return"object";if(c=="[object Array]"||typeof a.length=="number"&&typeof a.splice!="undefined"&&typeof a.propertyIsEnumerable!="undefined"&&!a.propertyIsEnumerable("splice"))return"array";if(c=="[object Function]"||typeof a.call!="undefined"&&typeof a.propertyIsEnumerable!="undefined"&&!a.propertyIsEnumerable("call"))return"function"}else return"null";else if(b==
+"function"&&typeof a.call=="undefined")return"object";return b}function aa(a){var b=j(a);return b=="array"||b=="object"&&typeof a.length=="number"}function k(a){return typeof a=="string"}function l(a){return j(a)=="function"}function ba(a){a=j(a);return a=="object"||a=="array"||a=="function"}var ca=Date.now||function(){return+new Date};function m(a,b){function c(){}c.prototype=b.prototype;a.n=b.prototype;a.prototype=new c};function n(a){this.stack=Error().stack||"";if(a)this.message=String(a)}m(n,Error);n.prototype.name="CustomError";function da(a,b,c){var d={};for(var f in a)if(b.call(c,a[f],f,a))d[f]=a[f];return d}function ea(a,b,c){var d={};for(var f in a)d[f]=b.call(c,a[f],f,a);return d}function fa(a,b,c){for(var d in a)if(b.call(c,a[d],d,a))return d};function o(a,b){n.call(this,b);this.code=a;this.name=p[a]||p[13]}m(o,n);var p,ga={NoSuchElementError:7,NoSuchFrameError:8,UnknownCommandError:9,StaleElementReferenceError:10,ElementNotVisibleError:11,InvalidElementStateError:12,UnknownError:13,ElementNotSelectableError:15,XPathLookupError:19,NoSuchWindowError:23,InvalidCookieDomainError:24,UnableToSetCookieError:25,ModalDialogOpenedError:26,ModalDialogOpenError:27,ScriptTimeoutError:28},ha={};for(var ia in ga)ha[ga[ia]]=ia;p=ha;
+o.prototype.toString=function(){return"["+this.name+"] "+this.message};function ja(a){for(var b=1;b<arguments.length;b++){var c=String(arguments[b]).replace(/\$/g,"$$$$");a=a.replace(/\%s/,c)}return a}function q(a){return a.replace(/^[\s\xa0]+|[\s\xa0]+$/g,"")}function r(a,b){if(a<b)return-1;else if(a>b)return 1;return 0}var ka={};function la(a){return ka[a]||(ka[a]=String(a).replace(/\-([a-z])/g,function(b,c){return c.toUpperCase()}))};function s(a,b){b.unshift(a);n.call(this,ja.apply(null,b));b.shift();this.q=a}m(s,n);s.prototype.name="AssertionError";function t(a,b){if(!a){var c=Array.prototype.slice.call(arguments,2),d="Assertion failed";if(b){d+=": "+b;var f=c}throw new s(""+d,f||[]);}return a};var u=Array.prototype,ma=u.indexOf?function(a,b,c){t(a.length!=null);return u.indexOf.call(a,b,c)}:function(a,b,c){c=c==null?0:c<0?Math.max(0,a.length+c):c;if(k(a)){if(!k(b)||b.length!=1)return-1;return a.indexOf(b,c)}for(c=c;c<a.length;c++)if(c in a&&a[c]===b)return c;return-1},na=u.forEach?function(a,b,c){t(a.length!=null);u.forEach.call(a,b,c)}:function(a,b,c){var d=a.length,f=k(a)?a.split(""):a;for(var e=0;e<d;e++)e in f&&b.call(c,f[e],e,a)},v=u.filter?function(a,b,c){t(a.length!=null);return u.filter.call(a,
+b,c)}:function(a,b,c){var d=a.length,f=[],e=0,g=k(a)?a.split(""):a;for(var h=0;h<d;h++)if(h in g){var G=g[h];if(b.call(c,G,h,a))f[e++]=G}return f},w=u.map?function(a,b,c){t(a.length!=null);return u.map.call(a,b,c)}:function(a,b,c){var d=a.length,f=Array(d),e=k(a)?a.split(""):a;for(var g=0;g<d;g++)if(g in e)f[g]=b.call(c,e[g],g,a);return f},oa=u.some?function(a,b,c){t(a.length!=null);return u.some.call(a,b,c)}:function(a,b,c){var d=a.length,f=k(a)?a.split(""):a;for(var e=0;e<d;e++)if(e in f&&b.call(c,
+f[e],e,a))return true;return false};function x(a,b,c){a:{var d=a.length,f=k(a)?a.split(""):a;for(var e=0;e<d;e++)if(e in f&&b.call(c,f[e],e,a)){b=e;break a}b=-1}return b<0?null:k(a)?a.charAt(b):a[b]};var A=true,pa,qa="",B;if(A)B=/WebKit\/(\S+)/;if(B){var ra=B.exec(i.navigator?i.navigator.userAgent:null);qa=ra?ra[1]:""}pa=qa;var sa={};var ta;function C(a,b){this.width=a;this.height=b}C.prototype.toString=function(){return"("+this.width+" x "+this.height+")"};function D(a){return a?new E(F(a)):ta||(ta=new E)}function H(a,b){if(a.contains&&b.nodeType==1)return a==b||a.contains(b);if(typeof a.compareDocumentPosition!="undefined")return a==b||Boolean(a.compareDocumentPosition(b)&16);for(;b&&a!=b;)b=b.parentNode;return b==a}function F(a){return a.nodeType==9?a:a.ownerDocument||a.document}function ua(a,b){var c=[];return va(a,b,c,true)?c[0]:undefined}
+function va(a,b,c,d){if(a!=null){var f=0;for(var e;e=a.childNodes[f];f++){if(b(e)){c.push(e);if(d)return true}if(va(e,b,c,d))return true}}return false}function wa(a,b,c,d){if(!c)a=a.parentNode;c=d==null;for(var f=0;a&&(c||f<=d);){if(b(a))return a;a=a.parentNode;f++}return null}function E(a){this.g=a||i.document||document}
+function I(a,b,c,d){a=d||a.g;b=b&&b!="*"?b.toUpperCase():"";if(d=a.querySelectorAll){if(d=a.querySelector){if(!(d=!A)){if(!(d=document.compatMode=="CSS1Compat")){if(!(d=sa["528"])){d=0;var f=q(String(pa)).split("."),e=q(String("528")).split("."),g=Math.max(f.length,e.length);for(var h=0;d==0&&h<g;h++){var G=f[h]||"",Oa=e[h]||"",Pa=RegExp("(\\d*)(\\D*)","g"),Qa=RegExp("(\\d*)(\\D*)","g");do{var y=Pa.exec(G)||["","",""],z=Qa.exec(Oa)||["","",""];if(y[0].length==0&&z[0].length==0)break;d=r(y[1].length==
+0?0:parseInt(y[1],10),z[1].length==0?0:parseInt(z[1],10))||r(y[2].length==0,z[2].length==0)||r(y[2],z[2])}while(d==0)}d=sa["528"]=d>=0}d=d}d=d}d=d}d=d}if(d&&(b||c))c=a.querySelectorAll(b+(c?"."+c:""));else if(c&&a.getElementsByClassName){a=a.getElementsByClassName(c);if(b){d={};e=f=0;for(;g=a[e];e++)if(b==g.nodeName)d[f++]=g;d.length=f;c=d}else c=a}else{a=a.getElementsByTagName(b||"*");if(c){d={};f=0;for(e=0;g=a[e];e++){b=g.className;if(typeof b.split=="function"&&ma(b.split(/\s+/),c)>=0)d[f++]=g}d.length=
+f;c=d}else c=a}return c}E.prototype.contains=H;function xa(){}
+function J(a,b,c){switch(typeof b){case "string":ya(a,b,c);break;case "number":c.push(isFinite(b)&&!isNaN(b)?b:"null");break;case "boolean":c.push(b);break;case "undefined":c.push("null");break;case "object":if(b==null){c.push("null");break}if(j(b)=="array"){var d=b.length;c.push("[");var f="";for(var e=0;e<d;e++){c.push(f);J(a,b[e],c);f=","}c.push("]");break}c.push("{");d="";for(f in b)if(Object.prototype.hasOwnProperty.call(b,f)){e=b[f];if(typeof e!="function"){c.push(d);ya(a,f,c);c.push(":");J(a,
+e,c);d=","}}c.push("}");break;case "function":break;default:throw Error("Unknown type: "+typeof b);}}var K={'"':'\\"',"\\":"\\\\","/":"\\/","\u0008":"\\b","\u000c":"\\f","\n":"\\n","\r":"\\r","\t":"\\t","\u000b":"\\u000b"},za=/\uffff/.test("\uffff")?/[\\\"\x00-\x1f\x7f-\uffff]/g:/[\\\"\x00-\x1f\x7f-\xff]/g;
+function ya(a,b,c){c.push('"',b.replace(za,function(d){if(d in K)return K[d];var f=d.charCodeAt(0),e="\\u";if(f<16)e+="000";else if(f<256)e+="00";else if(f<4096)e+="0";return K[d]=e+f.toString(16)}),'"')};function L(a){switch(j(a)){case "string":case "number":case "boolean":return a;case "function":return a.toString();case "array":return w(a,L);case "object":a=a;if("nodeType"in a&&(a.nodeType==1||a.nodeType==9)){var b={};b.ELEMENT=Aa(a);return b}if(aa(a))return w(a,L);a=da(a,function(c,d){return typeof d=="number"||k(d)});return ea(a,L);default:return null}}
+function M(a,b){if(j(a)=="array")return w(a,function(c){return M(c,b)});else if(ba(a))return"ELEMENT"in a?Ba(a.ELEMENT,b):ea(a,function(c){return M(c,b)});return a}function Ca(a){a=a||document;var b=a.$wdc_;if(!b){b=a.$wdc_={};b.m=ca()}return b}function Aa(a){var b=Ca(a.ownerDocument),c=fa(b,function(d){return d==a});if(!c){c=":wdc:"+b.m++;b[c]=a}return c}
+function Ba(a,b){a=decodeURIComponent(a);var c=b||document,d=Ca(c);if(!(a in d))throw new o(10,"Element does not exist in cache");var f=d[a];for(var e=f;e;){if(e==c.documentElement)return f;e=e.parentNode}delete d[a];throw new o(10,"Element is no longer attached to the DOM");};var Da=window;function Ea(){var a=i.Components;if(!a)return false;try{a.o["@mozilla.org/uuid-generator;1"].p(a.l.s);return true}catch(b){return false}};var N={};N.b=function(a,b){if(!a)throw Error("No class name specified");a=q(a);if(a.split(/\s+/).length>1)throw Error("Compound class names not permitted");var c=I(D(b),"*",a,b);return c.length?c[0]:null};N.e=function(a,b){if(!a)throw Error("No class name specified");a=q(a);if(a.split(/\s+/).length>1)throw Error("Compound class names not permitted");return I(D(b),"*",a,b)};var O={};O.b=function(a,b){if(!l(b.querySelector)&&0)throw Error("CSS selection is not supported");if(!a)throw Error("No selector specified");if(a.split(/,/).length>1)throw Error("Compound selectors not permitted");a=q(a);var c=b.querySelector(a);return c&&c.nodeType==1?c:null};O.e=function(a,b){if(!l(b.querySelectorAll)&&0)throw Error("CSS selection is not supported");if(!a)throw Error("No selector specified");if(a.split(/,/).length>1)throw Error("Compound selectors not permitted");a=q(a);return b.querySelectorAll(a)};function Fa(a,b){if(typeof a.selectNodes!="undefined"){var c=F(a);typeof c.setProperty!="undefined"&&c.setProperty("SelectionLanguage","XPath");return a.selectNodes(b)}else if(document.implementation.hasFeature("XPath","3.0")){c=F(a);var d=c.createNSResolver(c.documentElement);c=c.evaluate(b,a,d,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null);d=[];var f=c.snapshotLength;for(var e=0;e<f;e++)d.push(c.snapshotItem(e));return d}else return[]};var P={};
+P.b=function(a,b){var c=function(d,f){var e=F(d);if(d.selectSingleNode){e.setProperty&&e.setProperty("SelectionLanguage","XPath");return d.selectSingleNode(f)}else if(e.implementation.hasFeature("XPath","3.0")){var g=e.createNSResolver(e.documentElement),h;if(typeof XPathResult!="undefined")h=XPathResult.FIRST_ORDERED_NODE_TYPE;else{if(!Ea())throw Error("Document claims it supports XPath yet XPathResult is not defined. Please report this to Selenium developers");h=Components.l.r.FIRST_ORDERED_NODE_TYPE}return e.evaluate(f,d,
+g,h,null).singleNodeValue}return null}(b,a);if(!c)return null;if(c.nodeType!=1)throw Error("Returned node is not an element: "+a);return c};P.e=function(a,b){var c=Fa(b,a);na(c,function(d){if(d.nodeType!=1)throw Error("Returned nodes must be elements: "+a);});return c};var Ga="StopIteration"in i?i.StopIteration:Error("StopIteration");function Ha(){}Ha.prototype.next=function(){throw Ga;};function Q(a,b,c,d,f){this.a=!!b;a&&R(this,a,d);this.f=f!=undefined?f:this.d||0;if(this.a)this.f*=-1;this.k=!c}m(Q,Ha);Q.prototype.c=null;Q.prototype.d=0;Q.prototype.j=false;function R(a,b,c,d){if(a.c=b)a.d=typeof c=="number"?c:a.c.nodeType!=1?0:a.a?-1:1;if(typeof d=="number")a.f=d}
+Q.prototype.next=function(){var a;if(this.j){if(!this.c||this.k&&this.f==0)throw Ga;a=this.c;var b=this.a?-1:1;if(this.d==b){var c=this.a?a.lastChild:a.firstChild;c?R(this,c):R(this,a,b*-1)}else(c=this.a?a.previousSibling:a.nextSibling)?R(this,c):R(this,a.parentNode,b*-1);this.f+=this.d*(this.a?-1:1)}else this.j=true;a=this.c;if(!this.c)throw Ga;return a};
+Q.prototype.splice=function(){var a=this.c,b=this.a?1:-1;if(this.d==b){this.d=b*-1;this.f+=this.d*(this.a?-1:1)}this.a=!this.a;Q.prototype.next.call(this);this.a=!this.a;b=aa(arguments[0])?arguments[0]:arguments;for(var c=b.length-1;c>=0;c--)a.parentNode&&a.parentNode.insertBefore(b[c],a.nextSibling);a&&a.parentNode&&a.parentNode.removeChild(a)};function Ia(a,b,c,d){Q.call(this,a,b,c,null,d)}m(Ia,Q);Ia.prototype.next=function(){do Ia.n.next.call(this);while(this.d==-1);return this.c};function Ja(a,b){var c=F(a);if(c.defaultView&&c.defaultView.getComputedStyle)if(c=c.defaultView.getComputedStyle(a,null))return c[b]||c.getPropertyValue(b);return""};function S(a,b){return!!a&&a.nodeType==1&&(!b||a.tagName.toUpperCase()==b)}
+var Ka=["async","autofocus","autoplay","checked","compact","complete","controls","declare","defaultchecked","defaultselected","defer","disabled","draggable","ended","formnovalidate","hidden","indeterminate","iscontenteditable","ismap","itemscope","loop","multiple","muted","nohref","noresize","noshade","novalidate","nowrap","open","paused","pubdate","readonly","required","reversed","scoped","seamless","seeking","selected","spellcheck","truespeed","willvalidate"];
+function T(a,b){if(8==a.nodeType)return null;b=b.toLowerCase();if(b=="style"){var c=q(a.style.cssText).toLowerCase();return c.charAt(c.length-1)==";"?c:c+";"}c=a.getAttributeNode(b);if(!c)return null;if(ma(Ka,b)>=0)return"true";return c.specified?c.value:null}function U(a){for(a=a.parentNode;a&&a.nodeType!=1&&a.nodeType!=9&&a.nodeType!=11;)a=a.parentNode;return S(a)?a:null}function V(a,b){b=la(String(b));return Ja(a,b)||La(a,b)}
+function La(a,b){var c=(a.currentStyle||a.style)[b];if(c!="inherit")return c!==undefined?c:null;return(c=U(a))?La(c,b):null}
+function Ma(a){if(l(a.getBBox))return a.getBBox();var b;if((Ja(a,"display")||(a.currentStyle?a.currentStyle.display:null)||a.style.display)!="none")b=new C(a.offsetWidth,a.offsetHeight);else{b=a.style;var c=b.display,d=b.visibility,f=b.position;b.visibility="hidden";b.position="absolute";b.display="inline";var e;e=a.offsetWidth;a=a.offsetHeight;b.display=c;b.position=f;b.visibility=d;b=new C(e,a)}return b}
+function W(a,b){function c(e){if(V(e,"display")=="none")return false;e=U(e);return!e||c(e)}function d(e){var g=Ma(e);if(g.height>0&&g.width>0)return true;if(e.innerText||e.textContent)if(Na.test(e.innerText||e.textContent))return true;return A&&oa(e.childNodes,function(h){return S(h)&&d(h)})}if(!S(a))throw Error("Argument to isShown must be of type Element");if(S(a,"TITLE"))return(F(a)?F(a).parentWindow||F(a).defaultView:window)==Da;if(S(a,"OPTION")||S(a,"OPTGROUP")){var f=wa(a,function(e){return S(e,
+"SELECT")});return!!f&&W(f)}if(S(a,"MAP")){if(!a.name)return false;f=F(a);f=f.evaluate?P.b('/descendant::*[@usemap = "#'+a.name+'"]',f):ua(f,function(e){return S(e)&&T(e,"usemap")=="#"+a.name});return!!f&&W(f)}if(S(a,"AREA")){f=wa(a,function(e){return S(e,"MAP")});return!!f&&W(f)}if(S(a,"INPUT")&&a.type.toLowerCase()=="hidden")return false;if(V(a,"visibility")=="hidden")return false;if(!c(a))return false;if(!b&&Ra(a)==0)return false;if(!d(a))return false;return true}
+function Sa(a){var b=[];Ta(a,b);b=w(b,q);return q(b.join("\n"))}function Ta(a,b){if(S(a,"BR"))b.push("");else{var c=Ua(a);c&&b[b.length-1]&&b.push("");na(a.childNodes,function(d){if(d.nodeType==3){var f=U(d);if(f){W(f);if(f&&W(f)){d=d.nodeValue.replace(Va," ");f=b.pop()||"";var e=f.length-1;if(e>=0&&f.indexOf(" ",e)==e&&d.lastIndexOf(" ",0)==0)d=d.substr(1);b.push(f+d)}}}else S(d)&&Ta(d,b)});c&&b[b.length-1]&&b.push("")}}function Ua(a){a=V(a,"display");return a=="block"||a=="list-item"}
+var Wa="[\\s\\xa0"+String.fromCharCode(160)+"]+",Va=RegExp(Wa,"g"),Na=RegExp("^"+Wa+"$");function Ra(a){var b=1,c=V(a,"opacity");if(c)b=Number(c);if(a=U(a))b*=Ra(a);return b};var Xa={};Xa.b=function(a,b){var c=D(b),d=k(a)?c.g.getElementById(a):a;if(!d)return null;if(T(d,"id")==a&&H(b,d))return d;c=I(c,"*");return x(c,function(f){return T(f,"id")==a&&H(b,f)})};Xa.e=function(a,b){var c=I(D(b),"*",null,b);return v(c,function(d){return T(d,"id")==a})};var X={},Ya={};X.i=function(a,b,c){b=I(D(b),"A",null,b);return x(b,function(d){d=Sa(d);return c&&d.indexOf(a)!=-1||d==a})};X.h=function(a,b,c){b=I(D(b),"A",null,b);return v(b,function(d){d=Sa(d);return c&&d.indexOf(a)!=-1||d==a})};X.b=function(a,b){return X.i(a,b,false)};X.e=function(a,b){return X.h(a,b,false)};Ya.b=function(a,b){return X.i(a,b,true)};Ya.e=function(a,b){return X.h(a,b,true)};var Za={};Za.b=function(a,b){var c=I(D(b),"*",null,b);return x(c,function(d){return T(d,"name")==a})};Za.e=function(a,b){var c=I(D(b),"*",null,b);return v(c,function(d){return T(d,"name")==a})};var $a={};$a.b=function(a,b){return I(D(b),a,null,b)[0]||null};$a.e=function(a,b){return I(D(b),a,null,b)};var ab={className:N,css:O,id:Xa,linkText:X,name:Za,partialLinkText:Ya,tagName:$a,xpath:P};function bb(a,b){var c;a:{for(c in a)if(!Object.prototype[c]){c=c;break a}c=null}if(c){var d=ab[c];if(d&&l(d.b))return d.b(a[c],b||F(Da))}throw Error("Unsupported locator strategy: "+c);};function cb(a,b,c){var d={};d[a]=b;a=bb;c=[d,c];var f;try{if(k(a))a=new Function(a);var e=M(c),g=a.apply(null,e);f={status:0,value:L(g)}}catch(h){f={status:"code"in h?h.code:13,value:{message:h.message}}}e=[];J(new xa,f,e);return e.join("")}var Y="_".split("."),Z=i;!(Y[0]in Z)&&Z.execScript&&Z.execScript("var "+Y[0]);for(var $;Y.length&&($=Y.shift());)if(!Y.length&&cb!==undefined)Z[$]=cb;else Z=Z[$]?Z[$]:Z[$]={};; return this._.apply(null,arguments);}.apply({navigator:typeof window!='undefined'?window.navigator:null}, arguments);}
diff --git a/core/res/res/raw/find_elements_android.js b/core/res/res/raw/find_elements_android.js
new file mode 100644
index 0000000..3a57689
--- /dev/null
+++ b/core/res/res/raw/find_elements_android.js
@@ -0,0 +1,27 @@
+function(){return function(){var i=this;
+function j(a){var b=typeof a;if(b=="object")if(a){if(a instanceof Array)return"array";else if(a instanceof Object)return b;var c=Object.prototype.toString.call(a);if(c=="[object Window]")return"object";if(c=="[object Array]"||typeof a.length=="number"&&typeof a.splice!="undefined"&&typeof a.propertyIsEnumerable!="undefined"&&!a.propertyIsEnumerable("splice"))return"array";if(c=="[object Function]"||typeof a.call!="undefined"&&typeof a.propertyIsEnumerable!="undefined"&&!a.propertyIsEnumerable("call"))return"function"}else return"null";else if(b==
+"function"&&typeof a.call=="undefined")return"object";return b}function aa(a){var b=j(a);return b=="array"||b=="object"&&typeof a.length=="number"}function k(a){return typeof a=="string"}function l(a){return j(a)=="function"}function ba(a){a=j(a);return a=="object"||a=="array"||a=="function"}var ca=Date.now||function(){return+new Date};function m(a,b){function c(){}c.prototype=b.prototype;a.n=b.prototype;a.prototype=new c};function n(a){this.stack=Error().stack||"";if(a)this.message=String(a)}m(n,Error);n.prototype.name="CustomError";function da(a,b,c){var d={};for(var f in a)if(b.call(c,a[f],f,a))d[f]=a[f];return d}function ea(a,b,c){var d={};for(var f in a)d[f]=b.call(c,a[f],f,a);return d}function fa(a,b,c){for(var d in a)if(b.call(c,a[d],d,a))return d};function o(a,b){n.call(this,b);this.code=a;this.name=p[a]||p[13]}m(o,n);var p,ga={NoSuchElementError:7,NoSuchFrameError:8,UnknownCommandError:9,StaleElementReferenceError:10,ElementNotVisibleError:11,InvalidElementStateError:12,UnknownError:13,ElementNotSelectableError:15,XPathLookupError:19,NoSuchWindowError:23,InvalidCookieDomainError:24,UnableToSetCookieError:25,ModalDialogOpenedError:26,ModalDialogOpenError:27,ScriptTimeoutError:28},ha={};for(var ia in ga)ha[ga[ia]]=ia;p=ha;
+o.prototype.toString=function(){return"["+this.name+"] "+this.message};function ja(a){for(var b=1;b<arguments.length;b++){var c=String(arguments[b]).replace(/\$/g,"$$$$");a=a.replace(/\%s/,c)}return a}function q(a){return a.replace(/^[\s\xa0]+|[\s\xa0]+$/g,"")}function r(a,b){if(a<b)return-1;else if(a>b)return 1;return 0}var ka={};function la(a){return ka[a]||(ka[a]=String(a).replace(/\-([a-z])/g,function(b,c){return c.toUpperCase()}))};function s(a,b){b.unshift(a);n.call(this,ja.apply(null,b));b.shift();this.q=a}m(s,n);s.prototype.name="AssertionError";function t(a,b){if(!a){var c=Array.prototype.slice.call(arguments,2),d="Assertion failed";if(b){d+=": "+b;var f=c}throw new s(""+d,f||[]);}return a};var u=Array.prototype,ma=u.indexOf?function(a,b,c){t(a.length!=null);return u.indexOf.call(a,b,c)}:function(a,b,c){c=c==null?0:c<0?Math.max(0,a.length+c):c;if(k(a)){if(!k(b)||b.length!=1)return-1;return a.indexOf(b,c)}for(c=c;c<a.length;c++)if(c in a&&a[c]===b)return c;return-1},na=u.forEach?function(a,b,c){t(a.length!=null);u.forEach.call(a,b,c)}:function(a,b,c){var d=a.length,f=k(a)?a.split(""):a;for(var e=0;e<d;e++)e in f&&b.call(c,f[e],e,a)},v=u.filter?function(a,b,c){t(a.length!=null);return u.filter.call(a,
+b,c)}:function(a,b,c){var d=a.length,f=[],e=0,g=k(a)?a.split(""):a;for(var h=0;h<d;h++)if(h in g){var G=g[h];if(b.call(c,G,h,a))f[e++]=G}return f},w=u.map?function(a,b,c){t(a.length!=null);return u.map.call(a,b,c)}:function(a,b,c){var d=a.length,f=Array(d),e=k(a)?a.split(""):a;for(var g=0;g<d;g++)if(g in e)f[g]=b.call(c,e[g],g,a);return f},oa=u.some?function(a,b,c){t(a.length!=null);return u.some.call(a,b,c)}:function(a,b,c){var d=a.length,f=k(a)?a.split(""):a;for(var e=0;e<d;e++)if(e in f&&b.call(c,
+f[e],e,a))return true;return false};function x(a,b,c){a:{var d=a.length,f=k(a)?a.split(""):a;for(var e=0;e<d;e++)if(e in f&&b.call(c,f[e],e,a)){b=e;break a}b=-1}return b<0?null:k(a)?a.charAt(b):a[b]};var A=true,pa,qa="",B;if(A)B=/WebKit\/(\S+)/;if(B){var ra=B.exec(i.navigator?i.navigator.userAgent:null);qa=ra?ra[1]:""}pa=qa;var sa={};var ta;function C(a,b){this.width=a;this.height=b}C.prototype.toString=function(){return"("+this.width+" x "+this.height+")"};function D(a){return a?new E(F(a)):ta||(ta=new E)}function H(a,b){if(a.contains&&b.nodeType==1)return a==b||a.contains(b);if(typeof a.compareDocumentPosition!="undefined")return a==b||Boolean(a.compareDocumentPosition(b)&16);for(;b&&a!=b;)b=b.parentNode;return b==a}function F(a){return a.nodeType==9?a:a.ownerDocument||a.document}function ua(a,b){var c=[];return va(a,b,c,true)?c[0]:undefined}
+function va(a,b,c,d){if(a!=null){var f=0;for(var e;e=a.childNodes[f];f++){if(b(e)){c.push(e);if(d)return true}if(va(e,b,c,d))return true}}return false}function wa(a,b,c,d){if(!c)a=a.parentNode;c=d==null;for(var f=0;a&&(c||f<=d);){if(b(a))return a;a=a.parentNode;f++}return null}function E(a){this.g=a||i.document||document}
+function I(a,b,c,d){a=d||a.g;b=b&&b!="*"?b.toUpperCase():"";if(d=a.querySelectorAll){if(d=a.querySelector){if(!(d=!A)){if(!(d=document.compatMode=="CSS1Compat")){if(!(d=sa["528"])){d=0;var f=q(String(pa)).split("."),e=q(String("528")).split("."),g=Math.max(f.length,e.length);for(var h=0;d==0&&h<g;h++){var G=f[h]||"",Oa=e[h]||"",Pa=RegExp("(\\d*)(\\D*)","g"),Qa=RegExp("(\\d*)(\\D*)","g");do{var y=Pa.exec(G)||["","",""],z=Qa.exec(Oa)||["","",""];if(y[0].length==0&&z[0].length==0)break;d=r(y[1].length==
+0?0:parseInt(y[1],10),z[1].length==0?0:parseInt(z[1],10))||r(y[2].length==0,z[2].length==0)||r(y[2],z[2])}while(d==0)}d=sa["528"]=d>=0}d=d}d=d}d=d}d=d}if(d&&(b||c))c=a.querySelectorAll(b+(c?"."+c:""));else if(c&&a.getElementsByClassName){a=a.getElementsByClassName(c);if(b){d={};e=f=0;for(;g=a[e];e++)if(b==g.nodeName)d[f++]=g;d.length=f;c=d}else c=a}else{a=a.getElementsByTagName(b||"*");if(c){d={};f=0;for(e=0;g=a[e];e++){b=g.className;if(typeof b.split=="function"&&ma(b.split(/\s+/),c)>=0)d[f++]=g}d.length=
+f;c=d}else c=a}return c}E.prototype.contains=H;function xa(){}
+function J(a,b,c){switch(typeof b){case "string":ya(a,b,c);break;case "number":c.push(isFinite(b)&&!isNaN(b)?b:"null");break;case "boolean":c.push(b);break;case "undefined":c.push("null");break;case "object":if(b==null){c.push("null");break}if(j(b)=="array"){var d=b.length;c.push("[");var f="";for(var e=0;e<d;e++){c.push(f);J(a,b[e],c);f=","}c.push("]");break}c.push("{");d="";for(f in b)if(Object.prototype.hasOwnProperty.call(b,f)){e=b[f];if(typeof e!="function"){c.push(d);ya(a,f,c);c.push(":");J(a,
+e,c);d=","}}c.push("}");break;case "function":break;default:throw Error("Unknown type: "+typeof b);}}var K={'"':'\\"',"\\":"\\\\","/":"\\/","\u0008":"\\b","\u000c":"\\f","\n":"\\n","\r":"\\r","\t":"\\t","\u000b":"\\u000b"},za=/\uffff/.test("\uffff")?/[\\\"\x00-\x1f\x7f-\uffff]/g:/[\\\"\x00-\x1f\x7f-\xff]/g;
+function ya(a,b,c){c.push('"',b.replace(za,function(d){if(d in K)return K[d];var f=d.charCodeAt(0),e="\\u";if(f<16)e+="000";else if(f<256)e+="00";else if(f<4096)e+="0";return K[d]=e+f.toString(16)}),'"')};function L(a){switch(j(a)){case "string":case "number":case "boolean":return a;case "function":return a.toString();case "array":return w(a,L);case "object":a=a;if("nodeType"in a&&(a.nodeType==1||a.nodeType==9)){var b={};b.ELEMENT=Aa(a);return b}if(aa(a))return w(a,L);a=da(a,function(c,d){return typeof d=="number"||k(d)});return ea(a,L);default:return null}}
+function M(a,b){if(j(a)=="array")return w(a,function(c){return M(c,b)});else if(ba(a))return"ELEMENT"in a?Ba(a.ELEMENT,b):ea(a,function(c){return M(c,b)});return a}function Ca(a){a=a||document;var b=a.$wdc_;if(!b){b=a.$wdc_={};b.m=ca()}return b}function Aa(a){var b=Ca(a.ownerDocument),c=fa(b,function(d){return d==a});if(!c){c=":wdc:"+b.m++;b[c]=a}return c}
+function Ba(a,b){a=decodeURIComponent(a);var c=b||document,d=Ca(c);if(!(a in d))throw new o(10,"Element does not exist in cache");var f=d[a];for(var e=f;e;){if(e==c.documentElement)return f;e=e.parentNode}delete d[a];throw new o(10,"Element is no longer attached to the DOM");};var Da=window;function Ea(){var a=i.Components;if(!a)return false;try{a.o["@mozilla.org/uuid-generator;1"].p(a.l.s);return true}catch(b){return false}};var N={};N.d=function(a,b){if(!a)throw Error("No class name specified");a=q(a);if(a.split(/\s+/).length>1)throw Error("Compound class names not permitted");var c=I(D(b),"*",a,b);return c.length?c[0]:null};N.b=function(a,b){if(!a)throw Error("No class name specified");a=q(a);if(a.split(/\s+/).length>1)throw Error("Compound class names not permitted");return I(D(b),"*",a,b)};var O={};O.d=function(a,b){if(!l(b.querySelector)&&0)throw Error("CSS selection is not supported");if(!a)throw Error("No selector specified");if(a.split(/,/).length>1)throw Error("Compound selectors not permitted");a=q(a);var c=b.querySelector(a);return c&&c.nodeType==1?c:null};O.b=function(a,b){if(!l(b.querySelectorAll)&&0)throw Error("CSS selection is not supported");if(!a)throw Error("No selector specified");if(a.split(/,/).length>1)throw Error("Compound selectors not permitted");a=q(a);return b.querySelectorAll(a)};function Fa(a,b){if(typeof a.selectNodes!="undefined"){var c=F(a);typeof c.setProperty!="undefined"&&c.setProperty("SelectionLanguage","XPath");return a.selectNodes(b)}else if(document.implementation.hasFeature("XPath","3.0")){c=F(a);var d=c.createNSResolver(c.documentElement);c=c.evaluate(b,a,d,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null);d=[];var f=c.snapshotLength;for(var e=0;e<f;e++)d.push(c.snapshotItem(e));return d}else return[]};var P={};
+P.d=function(a,b){var c=function(d,f){var e=F(d);if(d.selectSingleNode){e.setProperty&&e.setProperty("SelectionLanguage","XPath");return d.selectSingleNode(f)}else if(e.implementation.hasFeature("XPath","3.0")){var g=e.createNSResolver(e.documentElement),h;if(typeof XPathResult!="undefined")h=XPathResult.FIRST_ORDERED_NODE_TYPE;else{if(!Ea())throw Error("Document claims it supports XPath yet XPathResult is not defined. Please report this to Selenium developers");h=Components.l.r.FIRST_ORDERED_NODE_TYPE}return e.evaluate(f,d,
+g,h,null).singleNodeValue}return null}(b,a);if(!c)return null;if(c.nodeType!=1)throw Error("Returned node is not an element: "+a);return c};P.b=function(a,b){var c=Fa(b,a);na(c,function(d){if(d.nodeType!=1)throw Error("Returned nodes must be elements: "+a);});return c};var Ga="StopIteration"in i?i.StopIteration:Error("StopIteration");function Ha(){}Ha.prototype.next=function(){throw Ga;};function Q(a,b,c,d,f){this.a=!!b;a&&R(this,a,d);this.f=f!=undefined?f:this.e||0;if(this.a)this.f*=-1;this.k=!c}m(Q,Ha);Q.prototype.c=null;Q.prototype.e=0;Q.prototype.j=false;function R(a,b,c,d){if(a.c=b)a.e=typeof c=="number"?c:a.c.nodeType!=1?0:a.a?-1:1;if(typeof d=="number")a.f=d}
+Q.prototype.next=function(){var a;if(this.j){if(!this.c||this.k&&this.f==0)throw Ga;a=this.c;var b=this.a?-1:1;if(this.e==b){var c=this.a?a.lastChild:a.firstChild;c?R(this,c):R(this,a,b*-1)}else(c=this.a?a.previousSibling:a.nextSibling)?R(this,c):R(this,a.parentNode,b*-1);this.f+=this.e*(this.a?-1:1)}else this.j=true;a=this.c;if(!this.c)throw Ga;return a};
+Q.prototype.splice=function(){var a=this.c,b=this.a?1:-1;if(this.e==b){this.e=b*-1;this.f+=this.e*(this.a?-1:1)}this.a=!this.a;Q.prototype.next.call(this);this.a=!this.a;b=aa(arguments[0])?arguments[0]:arguments;for(var c=b.length-1;c>=0;c--)a.parentNode&&a.parentNode.insertBefore(b[c],a.nextSibling);a&&a.parentNode&&a.parentNode.removeChild(a)};function Ia(a,b,c,d){Q.call(this,a,b,c,null,d)}m(Ia,Q);Ia.prototype.next=function(){do Ia.n.next.call(this);while(this.e==-1);return this.c};function Ja(a,b){var c=F(a);if(c.defaultView&&c.defaultView.getComputedStyle)if(c=c.defaultView.getComputedStyle(a,null))return c[b]||c.getPropertyValue(b);return""};function S(a,b){return!!a&&a.nodeType==1&&(!b||a.tagName.toUpperCase()==b)}
+var Ka=["async","autofocus","autoplay","checked","compact","complete","controls","declare","defaultchecked","defaultselected","defer","disabled","draggable","ended","formnovalidate","hidden","indeterminate","iscontenteditable","ismap","itemscope","loop","multiple","muted","nohref","noresize","noshade","novalidate","nowrap","open","paused","pubdate","readonly","required","reversed","scoped","seamless","seeking","selected","spellcheck","truespeed","willvalidate"];
+function T(a,b){if(8==a.nodeType)return null;b=b.toLowerCase();if(b=="style"){var c=q(a.style.cssText).toLowerCase();return c.charAt(c.length-1)==";"?c:c+";"}c=a.getAttributeNode(b);if(!c)return null;if(ma(Ka,b)>=0)return"true";return c.specified?c.value:null}function U(a){for(a=a.parentNode;a&&a.nodeType!=1&&a.nodeType!=9&&a.nodeType!=11;)a=a.parentNode;return S(a)?a:null}function V(a,b){b=la(String(b));return Ja(a,b)||La(a,b)}
+function La(a,b){var c=(a.currentStyle||a.style)[b];if(c!="inherit")return c!==undefined?c:null;return(c=U(a))?La(c,b):null}
+function Ma(a){if(l(a.getBBox))return a.getBBox();var b;if((Ja(a,"display")||(a.currentStyle?a.currentStyle.display:null)||a.style.display)!="none")b=new C(a.offsetWidth,a.offsetHeight);else{b=a.style;var c=b.display,d=b.visibility,f=b.position;b.visibility="hidden";b.position="absolute";b.display="inline";var e;e=a.offsetWidth;a=a.offsetHeight;b.display=c;b.position=f;b.visibility=d;b=new C(e,a)}return b}
+function W(a,b){function c(e){if(V(e,"display")=="none")return false;e=U(e);return!e||c(e)}function d(e){var g=Ma(e);if(g.height>0&&g.width>0)return true;if(e.innerText||e.textContent)if(Na.test(e.innerText||e.textContent))return true;return A&&oa(e.childNodes,function(h){return S(h)&&d(h)})}if(!S(a))throw Error("Argument to isShown must be of type Element");if(S(a,"TITLE"))return(F(a)?F(a).parentWindow||F(a).defaultView:window)==Da;if(S(a,"OPTION")||S(a,"OPTGROUP")){var f=wa(a,function(e){return S(e,
+"SELECT")});return!!f&&W(f)}if(S(a,"MAP")){if(!a.name)return false;f=F(a);f=f.evaluate?P.d('/descendant::*[@usemap = "#'+a.name+'"]',f):ua(f,function(e){return S(e)&&T(e,"usemap")=="#"+a.name});return!!f&&W(f)}if(S(a,"AREA")){f=wa(a,function(e){return S(e,"MAP")});return!!f&&W(f)}if(S(a,"INPUT")&&a.type.toLowerCase()=="hidden")return false;if(V(a,"visibility")=="hidden")return false;if(!c(a))return false;if(!b&&Ra(a)==0)return false;if(!d(a))return false;return true}
+function Sa(a){var b=[];Ta(a,b);b=w(b,q);return q(b.join("\n"))}function Ta(a,b){if(S(a,"BR"))b.push("");else{var c=Ua(a);c&&b[b.length-1]&&b.push("");na(a.childNodes,function(d){if(d.nodeType==3){var f=U(d);if(f){W(f);if(f&&W(f)){d=d.nodeValue.replace(Va," ");f=b.pop()||"";var e=f.length-1;if(e>=0&&f.indexOf(" ",e)==e&&d.lastIndexOf(" ",0)==0)d=d.substr(1);b.push(f+d)}}}else S(d)&&Ta(d,b)});c&&b[b.length-1]&&b.push("")}}function Ua(a){a=V(a,"display");return a=="block"||a=="list-item"}
+var Wa="[\\s\\xa0"+String.fromCharCode(160)+"]+",Va=RegExp(Wa,"g"),Na=RegExp("^"+Wa+"$");function Ra(a){var b=1,c=V(a,"opacity");if(c)b=Number(c);if(a=U(a))b*=Ra(a);return b};var Xa={};Xa.d=function(a,b){var c=D(b),d=k(a)?c.g.getElementById(a):a;if(!d)return null;if(T(d,"id")==a&&H(b,d))return d;c=I(c,"*");return x(c,function(f){return T(f,"id")==a&&H(b,f)})};Xa.b=function(a,b){var c=I(D(b),"*",null,b);return v(c,function(d){return T(d,"id")==a})};var X={},Ya={};X.i=function(a,b,c){b=I(D(b),"A",null,b);return x(b,function(d){d=Sa(d);return c&&d.indexOf(a)!=-1||d==a})};X.h=function(a,b,c){b=I(D(b),"A",null,b);return v(b,function(d){d=Sa(d);return c&&d.indexOf(a)!=-1||d==a})};X.d=function(a,b){return X.i(a,b,false)};X.b=function(a,b){return X.h(a,b,false)};Ya.d=function(a,b){return X.i(a,b,true)};Ya.b=function(a,b){return X.h(a,b,true)};var Za={};Za.d=function(a,b){var c=I(D(b),"*",null,b);return x(c,function(d){return T(d,"name")==a})};Za.b=function(a,b){var c=I(D(b),"*",null,b);return v(c,function(d){return T(d,"name")==a})};var $a={};$a.d=function(a,b){return I(D(b),a,null,b)[0]||null};$a.b=function(a,b){return I(D(b),a,null,b)};var ab={className:N,css:O,id:Xa,linkText:X,name:Za,partialLinkText:Ya,tagName:$a,xpath:P};function bb(a,b){var c;a:{for(c in a)if(!Object.prototype[c]){c=c;break a}c=null}if(c){var d=ab[c];if(d&&l(d.b))return d.b(a[c],b||F(Da))}throw Error("Unsupported locator strategy: "+c);};function cb(a,b,c){var d={};d[a]=b;a=bb;c=[d,c];var f;try{if(k(a))a=new Function(a);var e=M(c),g=a.apply(null,e);f={status:0,value:L(g)}}catch(h){f={status:"code"in h?h.code:13,value:{message:h.message}}}e=[];J(new xa,f,e);return e.join("")}var Y="_".split("."),Z=i;!(Y[0]in Z)&&Z.execScript&&Z.execScript("var "+Y[0]);for(var $;Y.length&&($=Y.shift());)if(!Y.length&&cb!==undefined)Z[$]=cb;else Z=Z[$]?Z[$]:Z[$]={};; return this._.apply(null,arguments);}.apply({navigator:typeof window!='undefined'?window.navigator:null}, arguments);}
diff --git a/core/res/res/raw/get_attribute_value_android.js b/core/res/res/raw/get_attribute_value_android.js
new file mode 100644
index 0000000..dc43fa7
--- /dev/null
+++ b/core/res/res/raw/get_attribute_value_android.js
@@ -0,0 +1,26 @@
+function(){return function(){var h,n=this;function p(){}
+function q(a){var b=typeof a;if(b=="object")if(a){if(a instanceof Array)return"array";else if(a instanceof Object)return b;var c=Object.prototype.toString.call(a);if(c=="[object Window]")return"object";if(c=="[object Array]"||typeof a.length=="number"&&typeof a.splice!="undefined"&&typeof a.propertyIsEnumerable!="undefined"&&!a.propertyIsEnumerable("splice"))return"array";if(c=="[object Function]"||typeof a.call!="undefined"&&typeof a.propertyIsEnumerable!="undefined"&&!a.propertyIsEnumerable("call"))return"function"}else return"null";
+else if(b=="function"&&typeof a.call=="undefined")return"object";return b}function aa(a){var b=q(a);return b=="array"||b=="object"&&typeof a.length=="number"}function r(a){return typeof a=="string"}function s(a){a=q(a);return a=="object"||a=="array"||a=="function"}function u(a){return a[ba]||(a[ba]=++ca)}var ba="closure_uid_"+Math.floor(Math.random()*2147483648).toString(36),ca=0,da=Date.now||function(){return+new Date};
+function v(a,b){function c(){}c.prototype=b.prototype;a.q=b.prototype;a.prototype=new c};function w(a){this.stack=Error().stack||"";if(a)this.message=String(a)}v(w,Error);w.prototype.name="CustomError";function ea(a,b,c){var d={};for(var e in a)if(b.call(c,a[e],e,a))d[e]=a[e];return d}function fa(a,b,c){var d={};for(var e in a)d[e]=b.call(c,a[e],e,a);return d}function ga(a,b,c){for(var d in a)if(b.call(c,a[d],d,a))return d};function x(a,b){w.call(this,b);this.code=a;this.name=y[a]||y[13]}v(x,w);var y,ha={NoSuchElementError:7,NoSuchFrameError:8,UnknownCommandError:9,StaleElementReferenceError:10,ElementNotVisibleError:11,InvalidElementStateError:12,UnknownError:13,ElementNotSelectableError:15,XPathLookupError:19,NoSuchWindowError:23,InvalidCookieDomainError:24,UnableToSetCookieError:25,ModalDialogOpenedError:26,ModalDialogOpenError:27,ScriptTimeoutError:28},ia={};for(var ja in ha)ia[ha[ja]]=ja;y=ia;
+x.prototype.toString=function(){return"["+this.name+"] "+this.message};function ka(a){for(var b=1;b<arguments.length;b++){var c=String(arguments[b]).replace(/\$/g,"$$$$");a=a.replace(/\%s/,c)}return a}function z(a){return a.replace(/^[\s\xa0]+|[\s\xa0]+$/g,"")}
+function la(a,b){var c=0,d=z(String(a)).split("."),e=z(String(b)).split("."),f=Math.max(d.length,e.length);for(var g=0;c==0&&g<f;g++){var i=d[g]||"",l=e[g]||"",j=RegExp("(\\d*)(\\D*)","g"),t=RegExp("(\\d*)(\\D*)","g");do{var m=j.exec(i)||["","",""],k=t.exec(l)||["","",""];if(m[0].length==0&&k[0].length==0)break;c=A(m[1].length==0?0:parseInt(m[1],10),k[1].length==0?0:parseInt(k[1],10))||A(m[2].length==0,k[2].length==0)||A(m[2],k[2])}while(c==0)}return c}
+function A(a,b){if(a<b)return-1;else if(a>b)return 1;return 0};function B(a,b){b.unshift(a);w.call(this,ka.apply(null,b));b.shift();this.J=a}v(B,w);B.prototype.name="AssertionError";function C(a,b){if(!a){var c=Array.prototype.slice.call(arguments,2),d="Assertion failed";if(b){d+=": "+b;var e=c}throw new B(""+d,e||[]);}return a};var D=Array.prototype,E=D.indexOf?function(a,b,c){C(a.length!=null);return D.indexOf.call(a,b,c)}:function(a,b,c){c=c==null?0:c<0?Math.max(0,a.length+c):c;if(r(a)){if(!r(b)||b.length!=1)return-1;return a.indexOf(b,c)}for(c=c;c<a.length;c++)if(c in a&&a[c]===b)return c;return-1},F=D.map?function(a,b,c){C(a.length!=null);return D.map.call(a,b,c)}:function(a,b,c){var d=a.length,e=Array(d),f=r(a)?a.split(""):a;for(var g=0;g<d;g++)if(g in f)e[g]=b.call(c,f[g],g,a);return e};var ma=n.navigator,na=(ma&&ma.platform||"").indexOf("Mac")!=-1,oa="",pa;if(pa=/WebKit\/(\S+)/){var qa=pa.exec(n.navigator?n.navigator.userAgent:null);oa=qa?qa[1]:""};var G="StopIteration"in n?n.StopIteration:Error("StopIteration");function ra(){}ra.prototype.next=function(){throw G;};function H(a,b,c,d,e){this.a=!!b;a&&I(this,a,d);this.h=e!=undefined?e:this.d||0;if(this.a)this.h*=-1;this.C=!c}v(H,ra);h=H.prototype;h.c=null;h.d=0;h.B=false;function I(a,b,c,d){if(a.c=b)a.d=typeof c=="number"?c:a.c.nodeType!=1?0:a.a?-1:1;if(typeof d=="number")a.h=d}
+h.next=function(){var a;if(this.B){if(!this.c||this.C&&this.h==0)throw G;a=this.c;var b=this.a?-1:1;if(this.d==b){var c=this.a?a.lastChild:a.firstChild;c?I(this,c):I(this,a,b*-1)}else(c=this.a?a.previousSibling:a.nextSibling)?I(this,c):I(this,a.parentNode,b*-1);this.h+=this.d*(this.a?-1:1)}else this.B=true;a=this.c;if(!this.c)throw G;return a};
+h.splice=function(){var a=this.c,b=this.a?1:-1;if(this.d==b){this.d=b*-1;this.h+=this.d*(this.a?-1:1)}this.a=!this.a;H.prototype.next.call(this);this.a=!this.a;b=aa(arguments[0])?arguments[0]:arguments;for(var c=b.length-1;c>=0;c--)a.parentNode&&a.parentNode.insertBefore(b[c],a.nextSibling);a&&a.parentNode&&a.parentNode.removeChild(a)};function J(a,b,c,d){H.call(this,a,b,c,null,d)}v(J,H);J.prototype.next=function(){do J.q.next.call(this);while(this.d==-1);return this.c};var sa={"class":"className",readonly:"readOnly"},ta=["checked","disabled","draggable","hidden"],ua=["async","autofocus","autoplay","checked","compact","complete","controls","declare","defaultchecked","defaultselected","defer","disabled","draggable","ended","formnovalidate","hidden","indeterminate","iscontenteditable","ismap","itemscope","loop","multiple","muted","nohref","noresize","noshade","novalidate","nowrap","open","paused","pubdate","readonly","required","reversed","scoped","seamless","seeking",
+"selected","spellcheck","truespeed","willvalidate"];function va(a,b){if(8==a.nodeType)return null;b=b.toLowerCase();if(b=="style"){var c=z(a.style.cssText).toLowerCase();return c.charAt(c.length-1)==";"?c:c+";"}c=a.getAttributeNode(b);if(!c)return null;if(E(ua,b)>=0)return"true";return c.specified?c.value:null}String.fromCharCode(160);var K;var wa=[];function L(){if(xa)M[u(this)]=this}var xa=false,M={};L.prototype.t=false;L.prototype.k=function(){if(!this.t){this.t=true;this.g();if(xa){var a=u(this);if(!M.hasOwnProperty(a))throw Error(this+" did not call the goog.Disposable base constructor or was disposed of after a clearUndisposedObjects call");delete M[a]}}};L.prototype.g=function(){};function N(a,b){L.call(this);this.type=a;this.currentTarget=this.target=b}v(N,L);N.prototype.g=function(){delete this.type;delete this.target;delete this.currentTarget};N.prototype.p=false;N.prototype.G=true;new Function("a","return a");function O(a,b){a&&this.m(a,b)}v(O,N);h=O.prototype;h.target=null;h.relatedTarget=null;h.offsetX=0;h.offsetY=0;h.clientX=0;h.clientY=0;h.screenX=0;h.screenY=0;h.button=0;h.keyCode=0;h.charCode=0;h.ctrlKey=false;h.altKey=false;h.shiftKey=false;h.metaKey=false;h.F=false;h.u=null;
+h.m=function(a,b){var c=this.type=a.type;N.call(this,c);this.target=a.target||a.srcElement;this.currentTarget=b;var d=a.relatedTarget;if(!d)if(c=="mouseover")d=a.fromElement;else if(c=="mouseout")d=a.toElement;this.relatedTarget=d;this.offsetX=a.offsetX!==undefined?a.offsetX:a.layerX;this.offsetY=a.offsetY!==undefined?a.offsetY:a.layerY;this.clientX=a.clientX!==undefined?a.clientX:a.pageX;this.clientY=a.clientY!==undefined?a.clientY:a.pageY;this.screenX=a.screenX||0;this.screenY=a.screenY||0;this.button=
+a.button;this.keyCode=a.keyCode||0;this.charCode=a.charCode||(c=="keypress"?a.keyCode:0);this.ctrlKey=a.ctrlKey;this.altKey=a.altKey;this.shiftKey=a.shiftKey;this.metaKey=a.metaKey;this.F=na?a.metaKey:a.ctrlKey;this.H=a.H;this.u=a;delete this.G;delete this.p};h.g=function(){O.q.g.call(this);this.relatedTarget=this.currentTarget=this.target=this.u=null};function ya(){}var za=0;h=ya.prototype;h.key=0;h.j=false;h.r=false;h.m=function(a,b,c,d,e,f){if(q(a)=="function")this.v=true;else if(a&&a.handleEvent&&q(a.handleEvent)=="function")this.v=false;else throw Error("Invalid listener argument");this.n=a;this.A=b;this.src=c;this.type=d;this.capture=!!e;this.D=f;this.r=false;this.key=++za;this.j=false};h.handleEvent=function(a){if(this.v)return this.n.call(this.D||this.src,a);return this.n.handleEvent.call(this.n,a)};function P(a,b){L.call(this);this.w=b;this.e=[];if(a>this.w)throw Error("[goog.structs.SimplePool] Initial cannot be greater than max");for(var c=0;c<a;c++)this.e.push(this.b?this.b():{})}v(P,L);P.prototype.b=null;P.prototype.s=null;function Aa(a){if(a.e.length)return a.e.pop();return a.b?a.b():{}}function Q(a,b){a.e.length<a.w?a.e.push(b):Ba(a,b)}function Ba(a,b){if(a.s)a.s(b);else if(s(b))if(q(b.k)=="function")b.k();else for(var c in b)delete b[c]}
+P.prototype.g=function(){P.q.g.call(this);for(var a=this.e;a.length;)Ba(this,a.pop());delete this.e};var Ca;var Da=(Ca="ScriptEngine"in n&&n.ScriptEngine()=="JScript")?n.ScriptEngineMajorVersion()+"."+n.ScriptEngineMinorVersion()+"."+n.ScriptEngineBuildVersion():"0";var R,S,T,Ea,Fa,Ga,Ha,Ia;
+(function(){function a(){return{f:0,i:0}}function b(){return[]}function c(){function k(o){return g.call(k.src,k.key,o)}return k}function d(){return new ya}function e(){return new O}var f=Ca&&!(la(Da,"5.7")>=0),g;Ea=function(k){g=k};if(f){R=function(k){Q(i,k)};S=function(){return Aa(l)};T=function(k){Q(l,k)};Fa=function(){Q(j,c())};Ga=function(k){Q(t,k)};Ha=function(){return Aa(m)};Ia=function(k){Q(m,k)};var i=new P(0,600);i.b=a;var l=new P(0,600);l.b=b;var j=new P(0,600);j.b=c;var t=new P(0,600);
+t.b=d;var m=new P(0,600);m.b=e}else{R=p;S=b;Ga=Fa=T=p;Ha=e;Ia=p}})();var U={},V={},Ja={},Ka={};function La(a,b,c,d){if(!d.l)if(d.z){var e=0;for(var f=0;e<d.length;e++)if(d[e].j){var g=d[e].A;g.src=null;Fa(g);Ga(d[e])}else{if(e!=f)d[f]=d[e];f++}d.length=f;d.z=false;if(f==0){T(d);delete V[a][b][c];V[a][b].f--;if(V[a][b].f==0){R(V[a][b]);delete V[a][b];V[a].f--}if(V[a].f==0){R(V[a]);delete V[a]}}}}function Ma(a){if(a in Ka)return Ka[a];return Ka[a]="on"+a}
+function Na(a,b,c,d,e){var f=1;b=u(b);if(a[b]){a.i--;a=a[b];if(a.l)a.l++;else a.l=1;try{var g=a.length;for(var i=0;i<g;i++){var l=a[i];if(l&&!l.j)f&=Oa(l,e)!==false}}finally{a.l--;La(c,d,b,a)}}return Boolean(f)}
+function Oa(a,b){var c=a.handleEvent(b);if(a.r){var d=a.key;if(U[d]){var e=U[d];if(!e.j){var f=e.src,g=e.type,i=e.A,l=e.capture;if(f.removeEventListener){if(f==n||!f.I)f.removeEventListener(g,i,l)}else f.detachEvent&&f.detachEvent(Ma(g),i);f=u(f);i=V[g][l][f];if(Ja[f]){var j=Ja[f],t=E(j,e);if(t>=0){C(j.length!=null);D.splice.call(j,t,1)}j.length==0&&delete Ja[f]}e.j=true;i.z=true;La(g,l,f,i);delete U[d]}}}return c}
+function W(a,b){if(!U[a])return true;var c=U[a],d=c.type,e=V;if(!(d in e))return true;e=e[d];var f,g;if(K===undefined)K=false;if(K){var i;if(!(i=b))a:{i="window.event".split(".");var l=n;for(;f=i.shift();)if(l[f]!=null)l=l[f];else{i=null;break a}i=l}f=i;i=true in e;l=false in e;if(i){if(f.keyCode<0||f.returnValue!=undefined)return true;a:{var j=false;if(f.keyCode==0)try{f.keyCode=-1;break a}catch(t){j=true}if(j||f.returnValue==undefined)f.returnValue=true}}j=Ha();j.m(f,this);f=true;try{if(i){var m=
+S();for(var k=j.currentTarget;k;k=k.parentNode)m.push(k);g=e[true];g.i=g.f;for(var o=m.length-1;!j.p&&o>=0&&g.i;o--){j.currentTarget=m[o];f&=Na(g,m[o],d,true,j)}if(l){g=e[false];g.i=g.f;for(o=0;!j.p&&o<m.length&&g.i;o++){j.currentTarget=m[o];f&=Na(g,m[o],d,false,j)}}}else f=Oa(c,j)}finally{if(m){m.length=0;T(m)}j.k();Ia(j)}return f}d=new O(b,this);try{f=Oa(c,d)}finally{d.k()}return f}Ea(W);wa[wa.length]=function(a){W=a(W);Ea(W)};function Pa(){}
+function Qa(a,b,c){switch(typeof b){case "string":Ra(a,b,c);break;case "number":c.push(isFinite(b)&&!isNaN(b)?b:"null");break;case "boolean":c.push(b);break;case "undefined":c.push("null");break;case "object":if(b==null){c.push("null");break}if(q(b)=="array"){var d=b.length;c.push("[");var e="";for(var f=0;f<d;f++){c.push(e);Qa(a,b[f],c);e=","}c.push("]");break}c.push("{");d="";for(e in b)if(Object.prototype.hasOwnProperty.call(b,e)){f=b[e];if(typeof f!="function"){c.push(d);Ra(a,e,c);c.push(":");
+Qa(a,f,c);d=","}}c.push("}");break;case "function":break;default:throw Error("Unknown type: "+typeof b);}}var Sa={'"':'\\"',"\\":"\\\\","/":"\\/","\u0008":"\\b","\u000c":"\\f","\n":"\\n","\r":"\\r","\t":"\\t","\u000b":"\\u000b"},Ta=/\uffff/.test("\uffff")?/[\\\"\x00-\x1f\x7f-\uffff]/g:/[\\\"\x00-\x1f\x7f-\xff]/g;
+function Ra(a,b,c){c.push('"',b.replace(Ta,function(d){if(d in Sa)return Sa[d];var e=d.charCodeAt(0),f="\\u";if(e<16)f+="000";else if(e<256)f+="00";else if(e<4096)f+="0";return Sa[d]=f+e.toString(16)}),'"')};function X(a){switch(q(a)){case "string":case "number":case "boolean":return a;case "function":return a.toString();case "array":return F(a,X);case "object":a=a;if("nodeType"in a&&(a.nodeType==1||a.nodeType==9)){var b={};b.ELEMENT=Ua(a);return b}if(aa(a))return F(a,X);a=ea(a,function(c,d){return typeof d=="number"||r(d)});return fa(a,X);default:return null}}
+function Va(a,b){if(q(a)=="array")return F(a,function(c){return Va(c,b)});else if(s(a))return"ELEMENT"in a?Wa(a.ELEMENT,b):fa(a,function(c){return Va(c,b)});return a}function Xa(a){a=a||document;var b=a.$wdc_;if(!b){b=a.$wdc_={};b.o=da()}if(!b.o)b.o=da();return b}function Ua(a){var b=Xa(a.ownerDocument),c=ga(b,function(d){return d==a});if(!c){c=":wdc:"+b.o++;b[c]=a}return c}
+function Wa(a,b){a=decodeURIComponent(a);var c=b||document,d=Xa(c);if(!(a in d))throw new x(10,"Element does not exist in cache");var e=d[a];for(var f=e;f;){if(f==c.documentElement)return e;f=f.parentNode}delete d[a];throw new x(10,"Element is no longer attached to the DOM");};var Ya=["checkbox","radio"];function Za(a){var b=a.tagName.toUpperCase();if(b=="OPTION")return true;if(b=="INPUT")if(E(Ya,a.type)>=0)return true;return false}
+function $a(a,b){var c=null;c=b.toLowerCase();if("style"==b.toLowerCase()){if((c=a.style)&&!r(c))c=c.cssText;return c}if("selected"==c||"checked"==c&&Za(a)){var d;if(Za(a)){d="selected";var e=a.type&&a.type.toLowerCase();if("checkbox"==e||"radio"==e)d="checked";d=!!a[d]}else d=false;return d?"true":null}var f=a.tagName&&"A"==a.tagName.toUpperCase(),g=a.tagName&&"IMG"==a.tagName.toUpperCase();try{e=sa[b]||b;var i=a[e];d=i===undefined&&E(ta,e)>=0?false:i}catch(l){}c=c=="href"&&f||c=="src"&&g||d==null||
+s(d)?va(a,b):d;return c!=null?c.toString():null};function ab(a,b){var c=$a,d=[a,b],e;try{if(r(c))c=new Function(c);var f=Va(d),g=c.apply(null,f);e={status:0,value:X(g)}}catch(i){e={status:"code"in i?i.code:13,value:{message:i.message}}}c=[];Qa(new Pa,e,c);return c.join("")}var Y="_".split("."),Z=n;!(Y[0]in Z)&&Z.execScript&&Z.execScript("var "+Y[0]);for(var $;Y.length&&($=Y.shift());)if(!Y.length&&ab!==undefined)Z[$]=ab;else Z=Z[$]?Z[$]:Z[$]={};; return this._.apply(null,arguments);}.apply({navigator:typeof window!='undefined'?window.navigator:null}, arguments);}
diff --git a/core/res/res/raw/get_size_android.js b/core/res/res/raw/get_size_android.js
new file mode 100644
index 0000000..54c2678
--- /dev/null
+++ b/core/res/res/raw/get_size_android.js
@@ -0,0 +1,11 @@
+function(){return function(){function g(a){var b=typeof a;if(b=="object")if(a){if(a instanceof Array)return"array";else if(a instanceof Object)return b;var c=Object.prototype.toString.call(a);if(c=="[object Window]")return"object";if(c=="[object Array]"||typeof a.length=="number"&&typeof a.splice!="undefined"&&typeof a.propertyIsEnumerable!="undefined"&&!a.propertyIsEnumerable("splice"))return"array";if(c=="[object Function]"||typeof a.call!="undefined"&&typeof a.propertyIsEnumerable!="undefined"&&!a.propertyIsEnumerable("call"))return"function"}else return"null";
+else if(b=="function"&&typeof a.call=="undefined")return"object";return b}function h(a){var b=g(a);return b=="array"||b=="object"&&typeof a.length=="number"}function i(a){a=g(a);return a=="object"||a=="array"||a=="function"}Math.floor(Math.random()*2147483648).toString(36);var j=Date.now||function(){return+new Date};function l(a,b){function c(){}c.prototype=b.prototype;a.h=b.prototype;a.prototype=new c};function m(a){this.stack=Error().stack||"";if(a)this.message=String(a)}l(m,Error);m.prototype.name="CustomError";function n(a,b,c){var d={};for(var e in a)if(b.call(c,a[e],e,a))d[e]=a[e];return d}function o(a,b,c){var d={};for(var e in a)d[e]=b.call(c,a[e],e,a);return d}function p(a,b,c){for(var d in a)if(b.call(c,a[d],d,a))return d};function q(a,b){m.call(this,b);this.code=a;this.name=r[a]||r[13]}l(q,m);var r,s={NoSuchElementError:7,NoSuchFrameError:8,UnknownCommandError:9,StaleElementReferenceError:10,ElementNotVisibleError:11,InvalidElementStateError:12,UnknownError:13,ElementNotSelectableError:15,XPathLookupError:19,NoSuchWindowError:23,InvalidCookieDomainError:24,UnableToSetCookieError:25,ModalDialogOpenedError:26,ModalDialogOpenError:27,ScriptTimeoutError:28},t={};for(var u in s)t[s[u]]=u;r=t;
+q.prototype.toString=function(){return"["+this.name+"] "+this.message};function v(a){for(var b=1;b<arguments.length;b++){var c=String(arguments[b]).replace(/\$/g,"$$$$");a=a.replace(/\%s/,c)}return a};function w(a,b){b.unshift(a);m.call(this,v.apply(null,b));b.shift();this.i=a}l(w,m);w.prototype.name="AssertionError";function x(a,b){if(!a){var c=Array.prototype.slice.call(arguments,2),d="Assertion failed";if(b){d+=": "+b;var e=c}throw new w(""+d,e||[]);}return a};var y=Array.prototype,z=y.map?function(a,b,c){x(a.length!=null);return y.map.call(a,b,c)}:function(a,b,c){var d=a.length,e=Array(d),f=typeof a=="string"?a.split(""):a;for(var k=0;k<d;k++)if(k in f)e[k]=b.call(c,f[k],k,a);return e};var A="",B;if(B=/WebKit\/(\S+)/){var C=B.exec(this.navigator?this.navigator.userAgent:null);A=C?C[1]:""};function D(a,b){this.width=a;this.height=b}D.prototype.toString=function(){return"("+this.width+" x "+this.height+")"};D.prototype.floor=function(){this.width=Math.floor(this.width);this.height=Math.floor(this.height);return this};var E="StopIteration"in this?this.StopIteration:Error("StopIteration");function F(){}F.prototype.next=function(){throw E;};function G(a,b,c,d,e){this.a=!!b;a&&H(this,a,d);this.d=e!=undefined?e:this.c||0;if(this.a)this.d*=-1;this.f=!c}l(G,F);G.prototype.b=null;G.prototype.c=0;G.prototype.e=false;function H(a,b,c,d){if(a.b=b)a.c=typeof c=="number"?c:a.b.nodeType!=1?0:a.a?-1:1;if(typeof d=="number")a.d=d}
+G.prototype.next=function(){var a;if(this.e){if(!this.b||this.f&&this.d==0)throw E;a=this.b;var b=this.a?-1:1;if(this.c==b){var c=this.a?a.lastChild:a.firstChild;c?H(this,c):H(this,a,b*-1)}else(c=this.a?a.previousSibling:a.nextSibling)?H(this,c):H(this,a.parentNode,b*-1);this.d+=this.c*(this.a?-1:1)}else this.e=true;a=this.b;if(!this.b)throw E;return a};
+G.prototype.splice=function(){var a=this.b,b=this.a?1:-1;if(this.c==b){this.c=b*-1;this.d+=this.c*(this.a?-1:1)}this.a=!this.a;G.prototype.next.call(this);this.a=!this.a;b=h(arguments[0])?arguments[0]:arguments;for(var c=b.length-1;c>=0;c--)a.parentNode&&a.parentNode.insertBefore(b[c],a.nextSibling);a&&a.parentNode&&a.parentNode.removeChild(a)};function I(a,b,c,d){G.call(this,a,b,c,null,d)}l(I,G);I.prototype.next=function(){do I.h.next.call(this);while(this.c==-1);return this.b};function J(a){if(g(a.getBBox)=="function")return a.getBBox();var b;b:{b=a.nodeType==9?a:a.ownerDocument||a.document;if(b.defaultView&&b.defaultView.getComputedStyle)if(b=b.defaultView.getComputedStyle(a,null)){b=b.display||b.getPropertyValue("display");break b}b=""}if((b||(a.currentStyle?a.currentStyle.display:null)||a.style.display)!="none")a=new D(a.offsetWidth,a.offsetHeight);else{b=a.style;var c=b.display,d=b.visibility,e=b.position;b.visibility="hidden";b.position="absolute";b.display="inline";
+var f;f=a.offsetWidth;a=a.offsetHeight;b.display=c;b.position=e;b.visibility=d;a=new D(f,a)}return a}String.fromCharCode(160);function K(){}
+function L(a,b,c){switch(typeof b){case "string":M(a,b,c);break;case "number":c.push(isFinite(b)&&!isNaN(b)?b:"null");break;case "boolean":c.push(b);break;case "undefined":c.push("null");break;case "object":if(b==null){c.push("null");break}if(g(b)=="array"){var d=b.length;c.push("[");var e="";for(var f=0;f<d;f++){c.push(e);L(a,b[f],c);e=","}c.push("]");break}c.push("{");d="";for(e in b)if(Object.prototype.hasOwnProperty.call(b,e)){f=b[e];if(typeof f!="function"){c.push(d);M(a,e,c);c.push(":");L(a,
+f,c);d=","}}c.push("}");break;case "function":break;default:throw Error("Unknown type: "+typeof b);}}var N={'"':'\\"',"\\":"\\\\","/":"\\/","\u0008":"\\b","\u000c":"\\f","\n":"\\n","\r":"\\r","\t":"\\t","\u000b":"\\u000b"},O=/\uffff/.test("\uffff")?/[\\\"\x00-\x1f\x7f-\uffff]/g:/[\\\"\x00-\x1f\x7f-\xff]/g;
+function M(a,b,c){c.push('"',b.replace(O,function(d){if(d in N)return N[d];var e=d.charCodeAt(0),f="\\u";if(e<16)f+="000";else if(e<256)f+="00";else if(e<4096)f+="0";return N[d]=f+e.toString(16)}),'"')};function P(a){switch(g(a)){case "string":case "number":case "boolean":return a;case "function":return a.toString();case "array":return z(a,P);case "object":a=a;if("nodeType"in a&&(a.nodeType==1||a.nodeType==9)){var b={};b.ELEMENT=Q(a);return b}if(h(a))return z(a,P);a=n(a,function(c,d){return typeof d=="number"||typeof d=="string"});return o(a,P);default:return null}}
+function R(a,b){if(g(a)=="array")return z(a,function(c){return R(c,b)});else if(i(a))return"ELEMENT"in a?S(a.ELEMENT,b):o(a,function(c){return R(c,b)});return a}function T(a){a=a||document;var b=a.$wdc_;if(!b){b=a.$wdc_={};b.g=j()}return b}function Q(a){var b=T(a.ownerDocument),c=p(b,function(d){return d==a});if(!c){c=":wdc:"+b.g++;b[c]=a}return c}
+function S(a,b){a=decodeURIComponent(a);var c=b||document,d=T(c);if(!(a in d))throw new q(10,"Element does not exist in cache");var e=d[a];for(var f=e;f;){if(f==c.documentElement)return e;f=f.parentNode}delete d[a];throw new q(10,"Element is no longer attached to the DOM");};function U(a){var b=J;a=[a];var c;try{if(typeof b=="string")b=new Function(b);var d=R(a),e=b.apply(null,d);c={status:0,value:P(e)}}catch(f){c={status:"code"in f?f.code:13,value:{message:f.message}}}b=[];L(new K,c,b);return b.join("")}var V="_".split("."),W=this;!(V[0]in W)&&W.execScript&&W.execScript("var "+V[0]);for(var X;V.length&&(X=V.shift());)if(!V.length&&U!==undefined)W[X]=U;else W=W[X]?W[X]:W[X]={};; return this._.apply(null,arguments);}.apply({navigator:typeof window!='undefined'?window.navigator:null}, arguments);}
diff --git a/core/res/res/raw/get_text_android.js b/core/res/res/raw/get_text_android.js
new file mode 100644
index 0000000..f74cb37
--- /dev/null
+++ b/core/res/res/raw/get_text_android.js
@@ -0,0 +1,21 @@
+function(){return function(){var g=this;
+function i(a){var b=typeof a;if(b=="object")if(a){if(a instanceof Array)return"array";else if(a instanceof Object)return b;var c=Object.prototype.toString.call(a);if(c=="[object Window]")return"object";if(c=="[object Array]"||typeof a.length=="number"&&typeof a.splice!="undefined"&&typeof a.propertyIsEnumerable!="undefined"&&!a.propertyIsEnumerable("splice"))return"array";if(c=="[object Function]"||typeof a.call!="undefined"&&typeof a.propertyIsEnumerable!="undefined"&&!a.propertyIsEnumerable("call"))return"function"}else return"null";else if(b==
+"function"&&typeof a.call=="undefined")return"object";return b}function j(a){var b=i(a);return b=="array"||b=="object"&&typeof a.length=="number"}function k(a){return typeof a=="string"}function aa(a){a=i(a);return a=="object"||a=="array"||a=="function"}Math.floor(Math.random()*2147483648).toString(36);var ba=Date.now||function(){return+new Date};function l(a,b){function c(){}c.prototype=b.prototype;a.i=b.prototype;a.prototype=new c};var ca=window;function da(){var a=g.Components;if(!a)return false;try{a.j["@mozilla.org/uuid-generator;1"].k(a.g.n);return true}catch(b){return false}};function m(a){this.stack=Error().stack||"";if(a)this.message=String(a)}l(m,Error);m.prototype.name="CustomError";function ea(a,b,c){var d={};for(var f in a)if(b.call(c,a[f],f,a))d[f]=a[f];return d}function n(a,b,c){var d={};for(var f in a)d[f]=b.call(c,a[f],f,a);return d}function fa(a,b,c){for(var d in a)if(b.call(c,a[d],d,a))return d};function o(a,b){m.call(this,b);this.code=a;this.name=p[a]||p[13]}l(o,m);var p,r={NoSuchElementError:7,NoSuchFrameError:8,UnknownCommandError:9,StaleElementReferenceError:10,ElementNotVisibleError:11,InvalidElementStateError:12,UnknownError:13,ElementNotSelectableError:15,XPathLookupError:19,NoSuchWindowError:23,InvalidCookieDomainError:24,UnableToSetCookieError:25,ModalDialogOpenedError:26,ModalDialogOpenError:27,ScriptTimeoutError:28},s={};for(var t in r)s[r[t]]=t;p=s;
+o.prototype.toString=function(){return"["+this.name+"] "+this.message};function ga(a){for(var b=1;b<arguments.length;b++){var c=String(arguments[b]).replace(/\$/g,"$$$$");a=a.replace(/\%s/,c)}return a}function u(a){return a.replace(/^[\s\xa0]+|[\s\xa0]+$/g,"")}var v={};function ha(a){return v[a]||(v[a]=String(a).replace(/\-([a-z])/g,function(b,c){return c.toUpperCase()}))};function w(a,b){b.unshift(a);m.call(this,ga.apply(null,b));b.shift();this.l=a}l(w,m);w.prototype.name="AssertionError";function x(a,b){if(!a){var c=Array.prototype.slice.call(arguments,2),d="Assertion failed";if(b){d+=": "+b;var f=c}throw new w(""+d,f||[]);}return a};var y=Array.prototype,ia=y.indexOf?function(a,b,c){x(a.length!=null);return y.indexOf.call(a,b,c)}:function(a,b,c){c=c==null?0:c<0?Math.max(0,a.length+c):c;if(k(a)){if(!k(b)||b.length!=1)return-1;return a.indexOf(b,c)}for(c=c;c<a.length;c++)if(c in a&&a[c]===b)return c;return-1},ja=y.forEach?function(a,b,c){x(a.length!=null);y.forEach.call(a,b,c)}:function(a,b,c){var d=a.length,f=k(a)?a.split(""):a;for(var e=0;e<d;e++)e in f&&b.call(c,f[e],e,a)},z=y.map?function(a,b,c){x(a.length!=null);return y.map.call(a,
+b,c)}:function(a,b,c){var d=a.length,f=Array(d),e=k(a)?a.split(""):a;for(var h=0;h<d;h++)if(h in e)f[h]=b.call(c,e[h],h,a);return f},ka=y.some?function(a,b,c){x(a.length!=null);return y.some.call(a,b,c)}:function(a,b,c){var d=a.length,f=k(a)?a.split(""):a;for(var e=0;e<d;e++)if(e in f&&b.call(c,f[e],e,a))return true;return false};var A=true,la="",B;if(A)B=/WebKit\/(\S+)/;if(B){var C=B.exec(g.navigator?g.navigator.userAgent:null);la=C?C[1]:""};function D(a,b){this.width=a;this.height=b}D.prototype.toString=function(){return"("+this.width+" x "+this.height+")"};D.prototype.floor=function(){this.width=Math.floor(this.width);this.height=Math.floor(this.height);return this};function E(a){return a.nodeType==9?a:a.ownerDocument||a.document}function ma(a,b){var c=[];return F(a,b,c,true)?c[0]:undefined}function F(a,b,c,d){if(a!=null){var f=0;for(var e;e=a.childNodes[f];f++){if(b(e)){c.push(e);if(d)return true}if(F(e,b,c,d))return true}}return false}function G(a,b,c,d){if(!c)a=a.parentNode;c=d==null;for(var f=0;a&&(c||f<=d);){if(b(a))return a;a=a.parentNode;f++}return null};function na(a,b){var c=function(d,f){var e=E(d);if(d.selectSingleNode){e.setProperty&&e.setProperty("SelectionLanguage","XPath");return d.selectSingleNode(f)}else if(e.implementation.hasFeature("XPath","3.0")){var h=e.createNSResolver(e.documentElement),q;if(typeof XPathResult!="undefined")q=XPathResult.FIRST_ORDERED_NODE_TYPE;else{if(!da())throw Error("Document claims it supports XPath yet XPathResult is not defined. Please report this to Selenium developers");q=Components.g.m.FIRST_ORDERED_NODE_TYPE}return e.evaluate(f,
+d,h,q,null).singleNodeValue}return null}(b,a);if(!c)return null;if(c.nodeType!=1)throw Error("Returned node is not an element: "+a);return c};var H="StopIteration"in g?g.StopIteration:Error("StopIteration");function I(){}I.prototype.next=function(){throw H;};function J(a,b,c,d,f){this.a=!!b;a&&K(this,a,d);this.d=f!=undefined?f:this.c||0;if(this.a)this.d*=-1;this.f=!c}l(J,I);J.prototype.b=null;J.prototype.c=0;J.prototype.e=false;function K(a,b,c,d){if(a.b=b)a.c=typeof c=="number"?c:a.b.nodeType!=1?0:a.a?-1:1;if(typeof d=="number")a.d=d}
+J.prototype.next=function(){var a;if(this.e){if(!this.b||this.f&&this.d==0)throw H;a=this.b;var b=this.a?-1:1;if(this.c==b){var c=this.a?a.lastChild:a.firstChild;c?K(this,c):K(this,a,b*-1)}else(c=this.a?a.previousSibling:a.nextSibling)?K(this,c):K(this,a.parentNode,b*-1);this.d+=this.c*(this.a?-1:1)}else this.e=true;a=this.b;if(!this.b)throw H;return a};
+J.prototype.splice=function(){var a=this.b,b=this.a?1:-1;if(this.c==b){this.c=b*-1;this.d+=this.c*(this.a?-1:1)}this.a=!this.a;J.prototype.next.call(this);this.a=!this.a;b=j(arguments[0])?arguments[0]:arguments;for(var c=b.length-1;c>=0;c--)a.parentNode&&a.parentNode.insertBefore(b[c],a.nextSibling);a&&a.parentNode&&a.parentNode.removeChild(a)};function L(a,b,c,d){J.call(this,a,b,c,null,d)}l(L,J);L.prototype.next=function(){do L.i.next.call(this);while(this.c==-1);return this.b};function M(a,b){var c=E(a);if(c.defaultView&&c.defaultView.getComputedStyle)if(c=c.defaultView.getComputedStyle(a,null))return c[b]||c.getPropertyValue(b);return""};function N(a,b){return!!a&&a.nodeType==1&&(!b||a.tagName.toUpperCase()==b)}
+var oa=["async","autofocus","autoplay","checked","compact","complete","controls","declare","defaultchecked","defaultselected","defer","disabled","draggable","ended","formnovalidate","hidden","indeterminate","iscontenteditable","ismap","itemscope","loop","multiple","muted","nohref","noresize","noshade","novalidate","nowrap","open","paused","pubdate","readonly","required","reversed","scoped","seamless","seeking","selected","spellcheck","truespeed","willvalidate"];
+function pa(a,b){if(8==a.nodeType)return null;b=b.toLowerCase();if(b=="style"){var c=u(a.style.cssText).toLowerCase();return c.charAt(c.length-1)==";"?c:c+";"}c=a.getAttributeNode(b);if(!c)return null;if(ia(oa,b)>=0)return"true";return c.specified?c.value:null}function O(a){for(a=a.parentNode;a&&a.nodeType!=1&&a.nodeType!=9&&a.nodeType!=11;)a=a.parentNode;return N(a)?a:null}function P(a,b){b=ha(String(b));return M(a,b)||Q(a,b)}
+function Q(a,b){var c=(a.currentStyle||a.style)[b];if(c!="inherit")return c!==undefined?c:null;return(c=O(a))?Q(c,b):null}
+function qa(a){if(i(a.getBBox)=="function")return a.getBBox();var b;if((M(a,"display")||(a.currentStyle?a.currentStyle.display:null)||a.style.display)!="none")b=new D(a.offsetWidth,a.offsetHeight);else{b=a.style;var c=b.display,d=b.visibility,f=b.position;b.visibility="hidden";b.position="absolute";b.display="inline";var e;e=a.offsetWidth;a=a.offsetHeight;b.display=c;b.position=f;b.visibility=d;b=new D(e,a)}return b}
+function R(a,b){function c(e){if(P(e,"display")=="none")return false;e=O(e);return!e||c(e)}function d(e){var h=qa(e);if(h.height>0&&h.width>0)return true;if(e.innerText||e.textContent)if(ra.test(e.innerText||e.textContent))return true;return A&&ka(e.childNodes,function(q){return N(q)&&d(q)})}if(!N(a))throw Error("Argument to isShown must be of type Element");if(N(a,"TITLE"))return(E(a)?E(a).parentWindow||E(a).defaultView:window)==ca;if(N(a,"OPTION")||N(a,"OPTGROUP")){var f=G(a,function(e){return N(e,
+"SELECT")});return!!f&&R(f)}if(N(a,"MAP")){if(!a.name)return false;f=E(a);f=f.evaluate?na('/descendant::*[@usemap = "#'+a.name+'"]',f):ma(f,function(e){return N(e)&&pa(e,"usemap")=="#"+a.name});return!!f&&R(f)}if(N(a,"AREA")){f=G(a,function(e){return N(e,"MAP")});return!!f&&R(f)}if(N(a,"INPUT")&&a.type.toLowerCase()=="hidden")return false;if(P(a,"visibility")=="hidden")return false;if(!c(a))return false;if(!b&&S(a)==0)return false;if(!d(a))return false;return true}
+function sa(a){var b=[];T(a,b);b=z(b,u);return u(b.join("\n"))}function T(a,b){if(N(a,"BR"))b.push("");else{var c=ta(a);c&&b[b.length-1]&&b.push("");ja(a.childNodes,function(d){if(d.nodeType==3){var f=O(d);if(f){R(f);if(f&&R(f)){d=d.nodeValue.replace(ua," ");f=b.pop()||"";var e=f.length-1;if(e>=0&&f.indexOf(" ",e)==e&&d.lastIndexOf(" ",0)==0)d=d.substr(1);b.push(f+d)}}}else N(d)&&T(d,b)});c&&b[b.length-1]&&b.push("")}}function ta(a){a=P(a,"display");return a=="block"||a=="list-item"}
+var va="[\\s\\xa0"+String.fromCharCode(160)+"]+",ua=RegExp(va,"g"),ra=RegExp("^"+va+"$");function S(a){var b=1,c=P(a,"opacity");if(c)b=Number(c);if(a=O(a))b*=S(a);return b};function wa(){}
+function U(a,b,c){switch(typeof b){case "string":xa(a,b,c);break;case "number":c.push(isFinite(b)&&!isNaN(b)?b:"null");break;case "boolean":c.push(b);break;case "undefined":c.push("null");break;case "object":if(b==null){c.push("null");break}if(i(b)=="array"){var d=b.length;c.push("[");var f="";for(var e=0;e<d;e++){c.push(f);U(a,b[e],c);f=","}c.push("]");break}c.push("{");d="";for(f in b)if(Object.prototype.hasOwnProperty.call(b,f)){e=b[f];if(typeof e!="function"){c.push(d);xa(a,f,c);c.push(":");U(a,
+e,c);d=","}}c.push("}");break;case "function":break;default:throw Error("Unknown type: "+typeof b);}}var V={'"':'\\"',"\\":"\\\\","/":"\\/","\u0008":"\\b","\u000c":"\\f","\n":"\\n","\r":"\\r","\t":"\\t","\u000b":"\\u000b"},ya=/\uffff/.test("\uffff")?/[\\\"\x00-\x1f\x7f-\uffff]/g:/[\\\"\x00-\x1f\x7f-\xff]/g;
+function xa(a,b,c){c.push('"',b.replace(ya,function(d){if(d in V)return V[d];var f=d.charCodeAt(0),e="\\u";if(f<16)e+="000";else if(f<256)e+="00";else if(f<4096)e+="0";return V[d]=e+f.toString(16)}),'"')};function W(a){switch(i(a)){case "string":case "number":case "boolean":return a;case "function":return a.toString();case "array":return z(a,W);case "object":a=a;if("nodeType"in a&&(a.nodeType==1||a.nodeType==9)){var b={};b.ELEMENT=za(a);return b}if(j(a))return z(a,W);a=ea(a,function(c,d){return typeof d=="number"||k(d)});return n(a,W);default:return null}}
+function X(a,b){if(i(a)=="array")return z(a,function(c){return X(c,b)});else if(aa(a))return"ELEMENT"in a?Aa(a.ELEMENT,b):n(a,function(c){return X(c,b)});return a}function Ba(a){a=a||document;var b=a.$wdc_;if(!b){b=a.$wdc_={};b.h=ba()}return b}function za(a){var b=Ba(a.ownerDocument),c=fa(b,function(d){return d==a});if(!c){c=":wdc:"+b.h++;b[c]=a}return c}
+function Aa(a,b){a=decodeURIComponent(a);var c=b||document,d=Ba(c);if(!(a in d))throw new o(10,"Element does not exist in cache");var f=d[a];for(var e=f;e;){if(e==c.documentElement)return f;e=e.parentNode}delete d[a];throw new o(10,"Element is no longer attached to the DOM");};function Ca(a){var b=sa;a=[a];var c;try{if(k(b))b=new Function(b);var d=X(a),f=b.apply(null,d);c={status:0,value:W(f)}}catch(e){c={status:"code"in e?e.code:13,value:{message:e.message}}}b=[];U(new wa,c,b);return b.join("")}var Y="_".split("."),Z=g;!(Y[0]in Z)&&Z.execScript&&Z.execScript("var "+Y[0]);for(var $;Y.length&&($=Y.shift());)if(!Y.length&&Ca!==undefined)Z[$]=Ca;else Z=Z[$]?Z[$]:Z[$]={};; return this._.apply(null,arguments);}.apply({navigator:typeof window!='undefined'?window.navigator:null}, arguments);}
diff --git a/core/res/res/raw/get_top_left_coordinates_android.js b/core/res/res/raw/get_top_left_coordinates_android.js
new file mode 100644
index 0000000..398f204
--- /dev/null
+++ b/core/res/res/raw/get_top_left_coordinates_android.js
@@ -0,0 +1,18 @@
+function(){return function(){var h=this;
+function j(a){var b=typeof a;if(b=="object")if(a){if(a instanceof Array)return"array";else if(a instanceof Object)return b;var c=Object.prototype.toString.call(a);if(c=="[object Window]")return"object";if(c=="[object Array]"||typeof a.length=="number"&&typeof a.splice!="undefined"&&typeof a.propertyIsEnumerable!="undefined"&&!a.propertyIsEnumerable("splice"))return"array";if(c=="[object Function]"||typeof a.call!="undefined"&&typeof a.propertyIsEnumerable!="undefined"&&!a.propertyIsEnumerable("call"))return"function"}else return"null";else if(b==
+"function"&&typeof a.call=="undefined")return"object";return b}function l(a){var b=j(a);return b=="array"||b=="object"&&typeof a.length=="number"}function aa(a){a=j(a);return a=="object"||a=="array"||a=="function"}Math.floor(Math.random()*2147483648).toString(36);var ba=Date.now||function(){return+new Date};function m(a,b){function c(){}c.prototype=b.prototype;a.j=b.prototype;a.prototype=new c};function n(a){this.stack=Error().stack||"";if(a)this.message=String(a)}m(n,Error);n.prototype.name="CustomError";function ca(a,b,c){var d={};for(var f in a)if(b.call(c,a[f],f,a))d[f]=a[f];return d}function o(a,b,c){var d={};for(var f in a)d[f]=b.call(c,a[f],f,a);return d}function da(a,b,c){for(var d in a)if(b.call(c,a[d],d,a))return d};function p(a,b){n.call(this,b);this.code=a;this.name=q[a]||q[13]}m(p,n);var q,r={NoSuchElementError:7,NoSuchFrameError:8,UnknownCommandError:9,StaleElementReferenceError:10,ElementNotVisibleError:11,InvalidElementStateError:12,UnknownError:13,ElementNotSelectableError:15,XPathLookupError:19,NoSuchWindowError:23,InvalidCookieDomainError:24,UnableToSetCookieError:25,ModalDialogOpenedError:26,ModalDialogOpenError:27,ScriptTimeoutError:28},s={};for(var t in r)s[r[t]]=t;q=s;
+p.prototype.toString=function(){return"["+this.name+"] "+this.message};function ea(a){for(var b=1;b<arguments.length;b++){var c=String(arguments[b]).replace(/\$/g,"$$$$");a=a.replace(/\%s/,c)}return a}function y(a,b){if(a<b)return-1;else if(a>b)return 1;return 0};function z(a,b){b.unshift(a);n.call(this,ea.apply(null,b));b.shift();this.k=a}m(z,n);z.prototype.name="AssertionError";function fa(a,b){if(!a){var c=Array.prototype.slice.call(arguments,2),d="Assertion failed";if(b){d+=": "+b;var f=c}throw new z(""+d,f||[]);}return a};var A=Array.prototype,B=A.map?function(a,b,c){fa(a.length!=null);return A.map.call(a,b,c)}:function(a,b,c){var d=a.length,f=Array(d),e=typeof a=="string"?a.split(""):a;for(var g=0;g<d;g++)if(g in e)f[g]=b.call(c,e[g],g,a);return f};var C,F="",G;if(G=/WebKit\/(\S+)/){var H=G.exec(h.navigator?h.navigator.userAgent:null);F=H?H[1]:""}C=F;var ga={};var ha;function I(a,b){this.x=a!==undefined?a:0;this.y=b!==undefined?b:0}I.prototype.toString=function(){return"("+this.x+", "+this.y+")"};function J(a,b){this.width=a;this.height=b}J.prototype.toString=function(){return"("+this.width+" x "+this.height+")"};J.prototype.floor=function(){this.width=Math.floor(this.width);this.height=Math.floor(this.height);return this};function K(a){return a?new ia(L(a)):ha||(ha=new ia)}function L(a){return a.nodeType==9?a:a.ownerDocument||a.document}function ia(a){this.e=a||h.document||document}function ja(a){a=a.e.body;return new I(a.scrollLeft,a.scrollTop)};var M="StopIteration"in h?h.StopIteration:Error("StopIteration");function ka(){}ka.prototype.next=function(){throw M;};function N(a,b,c,d,f){this.a=!!b;a&&O(this,a,d);this.d=f!=undefined?f:this.c||0;if(this.a)this.d*=-1;this.h=!c}m(N,ka);N.prototype.b=null;N.prototype.c=0;N.prototype.g=false;function O(a,b,c,d){if(a.b=b)a.c=typeof c=="number"?c:a.b.nodeType!=1?0:a.a?-1:1;if(typeof d=="number")a.d=d}
+N.prototype.next=function(){var a;if(this.g){if(!this.b||this.h&&this.d==0)throw M;a=this.b;var b=this.a?-1:1;if(this.c==b){var c=this.a?a.lastChild:a.firstChild;c?O(this,c):O(this,a,b*-1)}else(c=this.a?a.previousSibling:a.nextSibling)?O(this,c):O(this,a.parentNode,b*-1);this.d+=this.c*(this.a?-1:1)}else this.g=true;a=this.b;if(!this.b)throw M;return a};
+N.prototype.splice=function(){var a=this.b,b=this.a?1:-1;if(this.c==b){this.c=b*-1;this.d+=this.c*(this.a?-1:1)}this.a=!this.a;N.prototype.next.call(this);this.a=!this.a;b=l(arguments[0])?arguments[0]:arguments;for(var c=b.length-1;c>=0;c--)a.parentNode&&a.parentNode.insertBefore(b[c],a.nextSibling);a&&a.parentNode&&a.parentNode.removeChild(a)};function P(a,b,c,d){N.call(this,a,b,c,null,d)}m(P,N);P.prototype.next=function(){do P.j.next.call(this);while(this.c==-1);return this.b};function la(a,b,c,d){this.top=a;this.right=b;this.bottom=c;this.left=d}la.prototype.toString=function(){return"("+this.top+"t, "+this.right+"r, "+this.bottom+"b, "+this.left+"l)"};function Q(a,b,c,d){this.left=a;this.top=b;this.width=c;this.height=d}Q.prototype.toString=function(){return"("+this.left+", "+this.top+" - "+this.width+"w x "+this.height+"h)"};function R(a,b){var c=L(a);if(c.defaultView&&c.defaultView.getComputedStyle)if(c=c.defaultView.getComputedStyle(a,null))return c[b]||c.getPropertyValue(b);return""}function S(a,b){return R(a,b)||(a.currentStyle?a.currentStyle[b]:null)||a.style[b]}
+function ma(a){var b=L(a),c=S(a,"position"),d=c=="fixed"||c=="absolute";for(a=a.parentNode;a&&a!=b;a=a.parentNode){c=S(a,"position");d=d&&c=="static"&&a!=b.documentElement&&a!=b.body;if(!d&&(a.scrollWidth>a.clientWidth||a.scrollHeight>a.clientHeight||c=="fixed"||c=="absolute"))return a}return null}
+function T(a){var b=L(a),c=S(a,"position"),d=new I(0,0),f=(b?b.nodeType==9?b:L(b):document).documentElement;if(a==f)return d;if(a.getBoundingClientRect){a=a.getBoundingClientRect();b=ja(K(b));d.x=a.left+b.x;d.y=a.top+b.y}else if(b.getBoxObjectFor){a=b.getBoxObjectFor(a);b=b.getBoxObjectFor(f);d.x=a.screenX-b.screenX;d.y=a.screenY-b.screenY}else{var e=a;do{d.x+=e.offsetLeft;d.y+=e.offsetTop;if(e!=a){d.x+=e.clientLeft||0;d.y+=e.clientTop||0}if(S(e,"position")=="fixed"){d.x+=b.body.scrollLeft;d.y+=b.body.scrollTop;
+break}e=e.offsetParent}while(e&&e!=a);if(c=="absolute")d.y-=b.body.offsetTop;for(e=a;(e=ma(e))&&e!=b.body&&e!=f;){d.x-=e.scrollLeft;d.y-=e.scrollTop}}return d};String.fromCharCode(160);function na(a,b){b.scrollLeft+=Math.min(a.left,Math.max(a.left-a.width,0));b.scrollTop+=Math.min(a.top,Math.max(a.top-a.height,0))}
+function oa(a,b){var c;c=b?new Q(b.left,b.top,b.width,b.height):new Q(0,0,a.offsetWidth,a.offsetHeight);var d=L(a);for(var f=a.parentNode;f&&f!=d.body&&f!=d.documentElement;){var e=c,g=f,u=T(a),v=T(g),i=void 0;i=void 0;var k=void 0,D=void 0,E=void 0;E=R(g,"borderLeftWidth");D=R(g,"borderRightWidth");k=R(g,"borderTopWidth");i=R(g,"borderBottomWidth");i=new la(parseFloat(k),parseFloat(D),parseFloat(i),parseFloat(E));na(new Q(u.x+e.left-v.x-i.left,u.y+e.top-v.y-i.top,g.clientWidth-e.width,g.clientHeight-
+e.height),g);f=f.parentNode}f=T(a);e=K(d);e=(e.e.parentWindow||e.e.defaultView||window).document;if(!ga["500"]){g=0;u=String(C).replace(/^[\s\xa0]+|[\s\xa0]+$/g,"").split(".");v=String("500").replace(/^[\s\xa0]+|[\s\xa0]+$/g,"").split(".");i=Math.max(u.length,v.length);for(k=0;g==0&&k<i;k++){D=u[k]||"";E=v[k]||"";var sa=RegExp("(\\d*)(\\D*)","g"),ta=RegExp("(\\d*)(\\D*)","g");do{var w=sa.exec(D)||["","",""],x=ta.exec(E)||["","",""];if(w[0].length==0&&x[0].length==0)break;g=y(w[1].length==0?0:parseInt(w[1],
+10),x[1].length==0?0:parseInt(x[1],10))||y(w[2].length==0,x[2].length==0)||y(w[2],x[2])}while(g==0)}ga["500"]=g>=0}e=e.compatMode=="CSS1Compat"?e.documentElement:e.body;e=new J(e.clientWidth,e.clientHeight);na(new Q(f.x+c.left-d.body.scrollLeft,f.y+c.top-d.body.scrollTop,e.width-c.width,e.height-c.height),d.body);d=new I;if(a.nodeType==1)if(a.getBoundingClientRect){f=a.getBoundingClientRect();d.x=f.left;d.y=f.top}else{f=ja(K(a));e=T(a);d.x=e.x-f.x;d.y=e.y-f.y}else{f=j(a.f)=="function";e=a;if(a.targetTouches)e=
+a.targetTouches[0];else if(f&&a.f().targetTouches)e=a.f().targetTouches[0];d.x=e.clientX;d.y=e.clientY}return new I(d.x+c.left,d.y+c.top)};function pa(){}
+function U(a,b,c){switch(typeof b){case "string":qa(a,b,c);break;case "number":c.push(isFinite(b)&&!isNaN(b)?b:"null");break;case "boolean":c.push(b);break;case "undefined":c.push("null");break;case "object":if(b==null){c.push("null");break}if(j(b)=="array"){var d=b.length;c.push("[");var f="";for(var e=0;e<d;e++){c.push(f);U(a,b[e],c);f=","}c.push("]");break}c.push("{");d="";for(f in b)if(Object.prototype.hasOwnProperty.call(b,f)){e=b[f];if(typeof e!="function"){c.push(d);qa(a,f,c);c.push(":");U(a,
+e,c);d=","}}c.push("}");break;case "function":break;default:throw Error("Unknown type: "+typeof b);}}var V={'"':'\\"',"\\":"\\\\","/":"\\/","\u0008":"\\b","\u000c":"\\f","\n":"\\n","\r":"\\r","\t":"\\t","\u000b":"\\u000b"},ra=/\uffff/.test("\uffff")?/[\\\"\x00-\x1f\x7f-\uffff]/g:/[\\\"\x00-\x1f\x7f-\xff]/g;
+function qa(a,b,c){c.push('"',b.replace(ra,function(d){if(d in V)return V[d];var f=d.charCodeAt(0),e="\\u";if(f<16)e+="000";else if(f<256)e+="00";else if(f<4096)e+="0";return V[d]=e+f.toString(16)}),'"')};function W(a){switch(j(a)){case "string":case "number":case "boolean":return a;case "function":return a.toString();case "array":return B(a,W);case "object":a=a;if("nodeType"in a&&(a.nodeType==1||a.nodeType==9)){var b={};b.ELEMENT=ua(a);return b}if(l(a))return B(a,W);a=ca(a,function(c,d){return typeof d=="number"||typeof d=="string"});return o(a,W);default:return null}}
+function X(a,b){if(j(a)=="array")return B(a,function(c){return X(c,b)});else if(aa(a))return"ELEMENT"in a?va(a.ELEMENT,b):o(a,function(c){return X(c,b)});return a}function wa(a){a=a||document;var b=a.$wdc_;if(!b){b=a.$wdc_={};b.i=ba()}return b}function ua(a){var b=wa(a.ownerDocument),c=da(b,function(d){return d==a});if(!c){c=":wdc:"+b.i++;b[c]=a}return c}
+function va(a,b){a=decodeURIComponent(a);var c=b||document,d=wa(c);if(!(a in d))throw new p(10,"Element does not exist in cache");var f=d[a];for(var e=f;e;){if(e==c.documentElement)return f;e=e.parentNode}delete d[a];throw new p(10,"Element is no longer attached to the DOM");};function xa(a){var b=oa;a=[a];var c;try{if(typeof b=="string")b=new Function(b);var d=X(a),f=b.apply(null,d);c={status:0,value:W(f)}}catch(e){c={status:"code"in e?e.code:13,value:{message:e.message}}}b=[];U(new pa,c,b);return b.join("")}var Y="_".split("."),Z=h;!(Y[0]in Z)&&Z.execScript&&Z.execScript("var "+Y[0]);for(var $;Y.length&&($=Y.shift());)if(!Y.length&&xa!==undefined)Z[$]=xa;else Z=Z[$]?Z[$]:Z[$]={};; return this._.apply(null,arguments);}.apply({navigator:typeof window!='undefined'?window.navigator:null}, arguments);}
diff --git a/core/res/res/raw/get_value_of_css_property_android.js b/core/res/res/raw/get_value_of_css_property_android.js
new file mode 100644
index 0000000..0989b04
--- /dev/null
+++ b/core/res/res/raw/get_value_of_css_property_android.js
@@ -0,0 +1,10 @@
+function(){return function(){function g(a){var c=typeof a;if(c=="object")if(a){if(a instanceof Array)return"array";else if(a instanceof Object)return c;var b=Object.prototype.toString.call(a);if(b=="[object Window]")return"object";if(b=="[object Array]"||typeof a.length=="number"&&typeof a.splice!="undefined"&&typeof a.propertyIsEnumerable!="undefined"&&!a.propertyIsEnumerable("splice"))return"array";if(b=="[object Function]"||typeof a.call!="undefined"&&typeof a.propertyIsEnumerable!="undefined"&&!a.propertyIsEnumerable("call"))return"function"}else return"null";
+else if(c=="function"&&typeof a.call=="undefined")return"object";return c}function i(a){var c=g(a);return c=="array"||c=="object"&&typeof a.length=="number"}function j(a){a=g(a);return a=="object"||a=="array"||a=="function"}Math.floor(Math.random()*2147483648).toString(36);var k=Date.now||function(){return+new Date};function l(a,c){function b(){}b.prototype=c.prototype;a.h=c.prototype;a.prototype=new b};function m(a){this.stack=Error().stack||"";if(a)this.message=String(a)}l(m,Error);m.prototype.name="CustomError";function n(a,c,b){var d={};for(var e in a)if(c.call(b,a[e],e,a))d[e]=a[e];return d}function o(a,c,b){var d={};for(var e in a)d[e]=c.call(b,a[e],e,a);return d}function p(a,c,b){for(var d in a)if(c.call(b,a[d],d,a))return d};function q(a,c){m.call(this,c);this.code=a;this.name=r[a]||r[13]}l(q,m);var r,s={NoSuchElementError:7,NoSuchFrameError:8,UnknownCommandError:9,StaleElementReferenceError:10,ElementNotVisibleError:11,InvalidElementStateError:12,UnknownError:13,ElementNotSelectableError:15,XPathLookupError:19,NoSuchWindowError:23,InvalidCookieDomainError:24,UnableToSetCookieError:25,ModalDialogOpenedError:26,ModalDialogOpenError:27,ScriptTimeoutError:28},t={};for(var u in s)t[s[u]]=u;r=t;
+q.prototype.toString=function(){return"["+this.name+"] "+this.message};function v(a){for(var c=1;c<arguments.length;c++){var b=String(arguments[c]).replace(/\$/g,"$$$$");a=a.replace(/\%s/,b)}return a};function w(a,c){c.unshift(a);m.call(this,v.apply(null,c));c.shift();this.i=a}l(w,m);w.prototype.name="AssertionError";function x(a,c){if(!a){var b=Array.prototype.slice.call(arguments,2),d="Assertion failed";if(c){d+=": "+c;var e=b}throw new w(""+d,e||[]);}return a};var y=Array.prototype,A=y.map?function(a,c,b){x(a.length!=null);return y.map.call(a,c,b)}:function(a,c,b){var d=a.length,e=Array(d),f=typeof a=="string"?a.split(""):a;for(var h=0;h<d;h++)if(h in f)e[h]=c.call(b,f[h],h,a);return e};var B="",C;if(C=/WebKit\/(\S+)/){var D=C.exec(this.navigator?this.navigator.userAgent:null);B=D?D[1]:""};var E="StopIteration"in this?this.StopIteration:Error("StopIteration");function F(){}F.prototype.next=function(){throw E;};function G(a,c,b,d,e){this.a=!!c;a&&H(this,a,d);this.d=e!=undefined?e:this.c||0;if(this.a)this.d*=-1;this.f=!b}l(G,F);G.prototype.b=null;G.prototype.c=0;G.prototype.e=false;function H(a,c,b,d){if(a.b=c)a.c=typeof b=="number"?b:a.b.nodeType!=1?0:a.a?-1:1;if(typeof d=="number")a.d=d}
+G.prototype.next=function(){var a;if(this.e){if(!this.b||this.f&&this.d==0)throw E;a=this.b;var c=this.a?-1:1;if(this.c==c){var b=this.a?a.lastChild:a.firstChild;b?H(this,b):H(this,a,c*-1)}else(b=this.a?a.previousSibling:a.nextSibling)?H(this,b):H(this,a.parentNode,c*-1);this.d+=this.c*(this.a?-1:1)}else this.e=true;a=this.b;if(!this.b)throw E;return a};
+G.prototype.splice=function(){var a=this.b,c=this.a?1:-1;if(this.c==c){this.c=c*-1;this.d+=this.c*(this.a?-1:1)}this.a=!this.a;G.prototype.next.call(this);this.a=!this.a;c=i(arguments[0])?arguments[0]:arguments;for(var b=c.length-1;b>=0;b--)a.parentNode&&a.parentNode.insertBefore(c[b],a.nextSibling);a&&a.parentNode&&a.parentNode.removeChild(a)};function I(a,c,b,d){G.call(this,a,c,b,null,d)}l(I,G);I.prototype.next=function(){do I.h.next.call(this);while(this.c==-1);return this.b};function J(a,c){var b=(a.currentStyle||a.style)[c];if(b!="inherit")return b!==undefined?b:null;for(b=a.parentNode;b&&b.nodeType!=1&&b.nodeType!=9&&b.nodeType!=11;)b=b.parentNode;return(b=b&&b.nodeType==1&&1?b:null)?J(b,c):null}String.fromCharCode(160);function K(){}
+function L(a,c,b){switch(typeof c){case "string":M(a,c,b);break;case "number":b.push(isFinite(c)&&!isNaN(c)?c:"null");break;case "boolean":b.push(c);break;case "undefined":b.push("null");break;case "object":if(c==null){b.push("null");break}if(g(c)=="array"){var d=c.length;b.push("[");var e="";for(var f=0;f<d;f++){b.push(e);L(a,c[f],b);e=","}b.push("]");break}b.push("{");d="";for(e in c)if(Object.prototype.hasOwnProperty.call(c,e)){f=c[e];if(typeof f!="function"){b.push(d);M(a,e,b);b.push(":");L(a,
+f,b);d=","}}b.push("}");break;case "function":break;default:throw Error("Unknown type: "+typeof c);}}var N={'"':'\\"',"\\":"\\\\","/":"\\/","\u0008":"\\b","\u000c":"\\f","\n":"\\n","\r":"\\r","\t":"\\t","\u000b":"\\u000b"},O=/\uffff/.test("\uffff")?/[\\\"\x00-\x1f\x7f-\uffff]/g:/[\\\"\x00-\x1f\x7f-\xff]/g;
+function M(a,c,b){b.push('"',c.replace(O,function(d){if(d in N)return N[d];var e=d.charCodeAt(0),f="\\u";if(e<16)f+="000";else if(e<256)f+="00";else if(e<4096)f+="0";return N[d]=f+e.toString(16)}),'"')};function P(a){switch(g(a)){case "string":case "number":case "boolean":return a;case "function":return a.toString();case "array":return A(a,P);case "object":a=a;if("nodeType"in a&&(a.nodeType==1||a.nodeType==9)){var c={};c.ELEMENT=Q(a);return c}if(i(a))return A(a,P);a=n(a,function(b,d){return typeof d=="number"||typeof d=="string"});return o(a,P);default:return null}}
+function R(a,c){if(g(a)=="array")return A(a,function(b){return R(b,c)});else if(j(a))return"ELEMENT"in a?S(a.ELEMENT,c):o(a,function(b){return R(b,c)});return a}function T(a){a=a||document;var c=a.$wdc_;if(!c){c=a.$wdc_={};c.g=k()}return c}function Q(a){var c=T(a.ownerDocument),b=p(c,function(d){return d==a});if(!b){b=":wdc:"+c.g++;c[b]=a}return b}
+function S(a,c){a=decodeURIComponent(a);var b=c||document,d=T(b);if(!(a in d))throw new q(10,"Element does not exist in cache");var e=d[a];for(var f=e;f;){if(f==b.documentElement)return e;f=f.parentNode}delete d[a];throw new q(10,"Element is no longer attached to the DOM");};function U(a,c){var b=J,d=[a,c],e;try{if(typeof b=="string")b=new Function(b);var f=R(d),h=b.apply(null,f);e={status:0,value:P(h)}}catch(z){e={status:"code"in z?z.code:13,value:{message:z.message}}}b=[];L(new K,e,b);return b.join("")}var V="_".split("."),W=this;!(V[0]in W)&&W.execScript&&W.execScript("var "+V[0]);for(var X;V.length&&(X=V.shift());)if(!V.length&&U!==undefined)W[X]=U;else W=W[X]?W[X]:W[X]={};; return this._.apply(null,arguments);}.apply({navigator:typeof window!='undefined'?window.navigator:null}, arguments);}
diff --git a/core/res/res/raw/is_displayed_android.js b/core/res/res/raw/is_displayed_android.js
new file mode 100644
index 0000000..154e319
--- /dev/null
+++ b/core/res/res/raw/is_displayed_android.js
@@ -0,0 +1,20 @@
+function(){return function(){var g=this;
+function i(a){var b=typeof a;if(b=="object")if(a){if(a instanceof Array)return"array";else if(a instanceof Object)return b;var c=Object.prototype.toString.call(a);if(c=="[object Window]")return"object";if(c=="[object Array]"||typeof a.length=="number"&&typeof a.splice!="undefined"&&typeof a.propertyIsEnumerable!="undefined"&&!a.propertyIsEnumerable("splice"))return"array";if(c=="[object Function]"||typeof a.call!="undefined"&&typeof a.propertyIsEnumerable!="undefined"&&!a.propertyIsEnumerable("call"))return"function"}else return"null";else if(b==
+"function"&&typeof a.call=="undefined")return"object";return b}function j(a){var b=i(a);return b=="array"||b=="object"&&typeof a.length=="number"}function k(a){return typeof a=="string"}function aa(a){a=i(a);return a=="object"||a=="array"||a=="function"}Math.floor(Math.random()*2147483648).toString(36);var l=Date.now||function(){return+new Date};function m(a,b){function c(){}c.prototype=b.prototype;a.i=b.prototype;a.prototype=new c};var ba=window;function ca(){var a=g.Components;if(!a)return false;try{a.j["@mozilla.org/uuid-generator;1"].k(a.h.n);return true}catch(b){return false}};function n(a){this.stack=Error().stack||"";if(a)this.message=String(a)}m(n,Error);n.prototype.name="CustomError";function da(a,b,c){var e={};for(var f in a)if(b.call(c,a[f],f,a))e[f]=a[f];return e}function o(a,b,c){var e={};for(var f in a)e[f]=b.call(c,a[f],f,a);return e}function ea(a,b,c){for(var e in a)if(b.call(c,a[e],e,a))return e};function p(a,b){n.call(this,b);this.code=a;this.name=r[a]||r[13]}m(p,n);var r,s={NoSuchElementError:7,NoSuchFrameError:8,UnknownCommandError:9,StaleElementReferenceError:10,ElementNotVisibleError:11,InvalidElementStateError:12,UnknownError:13,ElementNotSelectableError:15,XPathLookupError:19,NoSuchWindowError:23,InvalidCookieDomainError:24,UnableToSetCookieError:25,ModalDialogOpenedError:26,ModalDialogOpenError:27,ScriptTimeoutError:28},t={};for(var u in s)t[s[u]]=u;r=t;
+p.prototype.toString=function(){return"["+this.name+"] "+this.message};function fa(a){for(var b=1;b<arguments.length;b++){var c=String(arguments[b]).replace(/\$/g,"$$$$");a=a.replace(/\%s/,c)}return a}var v={};function ga(a){return v[a]||(v[a]=String(a).replace(/\-([a-z])/g,function(b,c){return c.toUpperCase()}))};function w(a,b){b.unshift(a);n.call(this,fa.apply(null,b));b.shift();this.l=a}m(w,n);w.prototype.name="AssertionError";function x(a,b){if(!a){var c=Array.prototype.slice.call(arguments,2),e="Assertion failed";if(b){e+=": "+b;var f=c}throw new w(""+e,f||[]);}return a};var y=Array.prototype,ha=y.indexOf?function(a,b,c){x(a.length!=null);return y.indexOf.call(a,b,c)}:function(a,b,c){c=c==null?0:c<0?Math.max(0,a.length+c):c;if(k(a)){if(!k(b)||b.length!=1)return-1;return a.indexOf(b,c)}for(c=c;c<a.length;c++)if(c in a&&a[c]===b)return c;return-1},z=y.map?function(a,b,c){x(a.length!=null);return y.map.call(a,b,c)}:function(a,b,c){var e=a.length,f=Array(e),d=k(a)?a.split(""):a;for(var h=0;h<e;h++)if(h in d)f[h]=b.call(c,d[h],h,a);return f},ia=y.some?function(a,b,c){x(a.length!=
+null);return y.some.call(a,b,c)}:function(a,b,c){var e=a.length,f=k(a)?a.split(""):a;for(var d=0;d<e;d++)if(d in f&&b.call(c,f[d],d,a))return true;return false};var A=true,ja="",B;if(A)B=/WebKit\/(\S+)/;if(B){var C=B.exec(g.navigator?g.navigator.userAgent:null);ja=C?C[1]:""};function D(a,b){this.width=a;this.height=b}D.prototype.toString=function(){return"("+this.width+" x "+this.height+")"};D.prototype.floor=function(){this.width=Math.floor(this.width);this.height=Math.floor(this.height);return this};function E(a){return a.nodeType==9?a:a.ownerDocument||a.document}function ka(a,b){var c=[];return F(a,b,c,true)?c[0]:undefined}function F(a,b,c,e){if(a!=null){var f=0;for(var d;d=a.childNodes[f];f++){if(b(d)){c.push(d);if(e)return true}if(F(d,b,c,e))return true}}return false}function G(a,b,c,e){if(!c)a=a.parentNode;c=e==null;for(var f=0;a&&(c||f<=e);){if(b(a))return a;a=a.parentNode;f++}return null};function la(a,b){var c=function(e,f){var d=E(e);if(e.selectSingleNode){d.setProperty&&d.setProperty("SelectionLanguage","XPath");return e.selectSingleNode(f)}else if(d.implementation.hasFeature("XPath","3.0")){var h=d.createNSResolver(d.documentElement),q;if(typeof XPathResult!="undefined")q=XPathResult.FIRST_ORDERED_NODE_TYPE;else{if(!ca())throw Error("Document claims it supports XPath yet XPathResult is not defined. Please report this to Selenium developers");q=Components.h.m.FIRST_ORDERED_NODE_TYPE}return d.evaluate(f,
+e,h,q,null).singleNodeValue}return null}(b,a);if(!c)return null;if(c.nodeType!=1)throw Error("Returned node is not an element: "+a);return c};var H="StopIteration"in g?g.StopIteration:Error("StopIteration");function I(){}I.prototype.next=function(){throw H;};function J(a,b,c,e,f){this.a=!!b;a&&K(this,a,e);this.d=f!=undefined?f:this.c||0;if(this.a)this.d*=-1;this.g=!c}m(J,I);J.prototype.b=null;J.prototype.c=0;J.prototype.f=false;function K(a,b,c,e){if(a.b=b)a.c=typeof c=="number"?c:a.b.nodeType!=1?0:a.a?-1:1;if(typeof e=="number")a.d=e}
+J.prototype.next=function(){var a;if(this.f){if(!this.b||this.g&&this.d==0)throw H;a=this.b;var b=this.a?-1:1;if(this.c==b){var c=this.a?a.lastChild:a.firstChild;c?K(this,c):K(this,a,b*-1)}else(c=this.a?a.previousSibling:a.nextSibling)?K(this,c):K(this,a.parentNode,b*-1);this.d+=this.c*(this.a?-1:1)}else this.f=true;a=this.b;if(!this.b)throw H;return a};
+J.prototype.splice=function(){var a=this.b,b=this.a?1:-1;if(this.c==b){this.c=b*-1;this.d+=this.c*(this.a?-1:1)}this.a=!this.a;J.prototype.next.call(this);this.a=!this.a;b=j(arguments[0])?arguments[0]:arguments;for(var c=b.length-1;c>=0;c--)a.parentNode&&a.parentNode.insertBefore(b[c],a.nextSibling);a&&a.parentNode&&a.parentNode.removeChild(a)};function L(a,b,c,e){J.call(this,a,b,c,null,e)}m(L,J);L.prototype.next=function(){do L.i.next.call(this);while(this.c==-1);return this.b};function M(a,b){var c=E(a);if(c.defaultView&&c.defaultView.getComputedStyle)if(c=c.defaultView.getComputedStyle(a,null))return c[b]||c.getPropertyValue(b);return""};function N(a,b){return!!a&&a.nodeType==1&&(!b||a.tagName.toUpperCase()==b)}
+var ma=["async","autofocus","autoplay","checked","compact","complete","controls","declare","defaultchecked","defaultselected","defer","disabled","draggable","ended","formnovalidate","hidden","indeterminate","iscontenteditable","ismap","itemscope","loop","multiple","muted","nohref","noresize","noshade","novalidate","nowrap","open","paused","pubdate","readonly","required","reversed","scoped","seamless","seeking","selected","spellcheck","truespeed","willvalidate"];
+function na(a,b){if(8==a.nodeType)return null;b=b.toLowerCase();if(b=="style"){var c=a.style.cssText.replace(/^[\s\xa0]+|[\s\xa0]+$/g,"").toLowerCase();return c.charAt(c.length-1)==";"?c:c+";"}c=a.getAttributeNode(b);if(!c)return null;if(ha(ma,b)>=0)return"true";return c.specified?c.value:null}function O(a){for(a=a.parentNode;a&&a.nodeType!=1&&a.nodeType!=9&&a.nodeType!=11;)a=a.parentNode;return N(a)?a:null}function P(a,b){b=ga(String(b));return M(a,b)||Q(a,b)}
+function Q(a,b){var c=(a.currentStyle||a.style)[b];if(c!="inherit")return c!==undefined?c:null;return(c=O(a))?Q(c,b):null}
+function oa(a){if(i(a.getBBox)=="function")return a.getBBox();var b;if((M(a,"display")||(a.currentStyle?a.currentStyle.display:null)||a.style.display)!="none")b=new D(a.offsetWidth,a.offsetHeight);else{b=a.style;var c=b.display,e=b.visibility,f=b.position;b.visibility="hidden";b.position="absolute";b.display="inline";var d;d=a.offsetWidth;a=a.offsetHeight;b.display=c;b.position=f;b.visibility=e;b=new D(d,a)}return b}
+function R(a,b){function c(d){if(P(d,"display")=="none")return false;d=O(d);return!d||c(d)}function e(d){var h=oa(d);if(h.height>0&&h.width>0)return true;if(d.innerText||d.textContent)if(pa.test(d.innerText||d.textContent))return true;return A&&ia(d.childNodes,function(q){return N(q)&&e(q)})}if(!N(a))throw Error("Argument to isShown must be of type Element");if(N(a,"TITLE"))return(E(a)?E(a).parentWindow||E(a).defaultView:window)==ba;if(N(a,"OPTION")||N(a,"OPTGROUP")){var f=G(a,function(d){return N(d,
+"SELECT")});return!!f&&R(f)}if(N(a,"MAP")){if(!a.name)return false;f=E(a);f=f.evaluate?la('/descendant::*[@usemap = "#'+a.name+'"]',f):ka(f,function(d){return N(d)&&na(d,"usemap")=="#"+a.name});return!!f&&R(f)}if(N(a,"AREA")){f=G(a,function(d){return N(d,"MAP")});return!!f&&R(f)}if(N(a,"INPUT")&&a.type.toLowerCase()=="hidden")return false;if(P(a,"visibility")=="hidden")return false;if(!c(a))return false;if(!b&&S(a)==0)return false;if(!e(a))return false;return true}
+var qa="[\\s\\xa0"+String.fromCharCode(160)+"]+",pa=RegExp("^"+qa+"$");function S(a){var b=1,c=P(a,"opacity");if(c)b=Number(c);if(a=O(a))b*=S(a);return b};function ra(a){return R(a,true)};function sa(){}
+function T(a,b,c){switch(typeof b){case "string":U(a,b,c);break;case "number":c.push(isFinite(b)&&!isNaN(b)?b:"null");break;case "boolean":c.push(b);break;case "undefined":c.push("null");break;case "object":if(b==null){c.push("null");break}if(i(b)=="array"){var e=b.length;c.push("[");var f="";for(var d=0;d<e;d++){c.push(f);T(a,b[d],c);f=","}c.push("]");break}c.push("{");e="";for(f in b)if(Object.prototype.hasOwnProperty.call(b,f)){d=b[f];if(typeof d!="function"){c.push(e);U(a,f,c);c.push(":");T(a,
+d,c);e=","}}c.push("}");break;case "function":break;default:throw Error("Unknown type: "+typeof b);}}var V={'"':'\\"',"\\":"\\\\","/":"\\/","\u0008":"\\b","\u000c":"\\f","\n":"\\n","\r":"\\r","\t":"\\t","\u000b":"\\u000b"},ta=/\uffff/.test("\uffff")?/[\\\"\x00-\x1f\x7f-\uffff]/g:/[\\\"\x00-\x1f\x7f-\xff]/g;
+function U(a,b,c){c.push('"',b.replace(ta,function(e){if(e in V)return V[e];var f=e.charCodeAt(0),d="\\u";if(f<16)d+="000";else if(f<256)d+="00";else if(f<4096)d+="0";return V[e]=d+f.toString(16)}),'"')};function W(a){switch(i(a)){case "string":case "number":case "boolean":return a;case "function":return a.toString();case "array":return z(a,W);case "object":a=a;if("nodeType"in a&&(a.nodeType==1||a.nodeType==9)){var b={};b.ELEMENT=ua(a);return b}if(j(a))return z(a,W);a=da(a,function(c,e){return typeof e=="number"||k(e)});return o(a,W);default:return null}}
+function X(a,b){if(i(a)=="array")return z(a,function(c){return X(c,b)});else if(aa(a))return"ELEMENT"in a?va(a.ELEMENT,b):o(a,function(c){return X(c,b)});return a}function wa(a){a=a||document;var b=a.$wdc_;if(!b){b=a.$wdc_={};b.e=l()}if(!b.e)b.e=l();return b}function ua(a){var b=wa(a.ownerDocument),c=ea(b,function(e){return e==a});if(!c){c=":wdc:"+b.e++;b[c]=a}return c}
+function va(a,b){a=decodeURIComponent(a);var c=b||document,e=wa(c);if(!(a in e))throw new p(10,"Element does not exist in cache");var f=e[a];for(var d=f;d;){if(d==c.documentElement)return f;d=d.parentNode}delete e[a];throw new p(10,"Element is no longer attached to the DOM");};function xa(a){var b=ra;a=[a];var c;try{if(k(b))b=new Function(b);var e=X(a),f=b.apply(null,e);c={status:0,value:W(f)}}catch(d){c={status:"code"in d?d.code:13,value:{message:d.message}}}b=[];T(new sa,c,b);return b.join("")}var Y="_".split("."),Z=g;!(Y[0]in Z)&&Z.execScript&&Z.execScript("var "+Y[0]);for(var $;Y.length&&($=Y.shift());)if(!Y.length&&xa!==undefined)Z[$]=xa;else Z=Z[$]?Z[$]:Z[$]={};; return this._.apply(null,arguments);}.apply({navigator:typeof window!='undefined'?window.navigator:null}, arguments);}
diff --git a/core/res/res/raw/is_enabled_android.js b/core/res/res/raw/is_enabled_android.js
new file mode 100644
index 0000000..231309f
--- /dev/null
+++ b/core/res/res/raw/is_enabled_android.js
@@ -0,0 +1,12 @@
+function(){return function(){function g(a){var b=typeof a;if(b=="object")if(a){if(a instanceof Array)return"array";else if(a instanceof Object)return b;var c=Object.prototype.toString.call(a);if(c=="[object Window]")return"object";if(c=="[object Array]"||typeof a.length=="number"&&typeof a.splice!="undefined"&&typeof a.propertyIsEnumerable!="undefined"&&!a.propertyIsEnumerable("splice"))return"array";if(c=="[object Function]"||typeof a.call!="undefined"&&typeof a.propertyIsEnumerable!="undefined"&&!a.propertyIsEnumerable("call"))return"function"}else return"null";
+else if(b=="function"&&typeof a.call=="undefined")return"object";return b}function h(a){var b=g(a);return b=="array"||b=="object"&&typeof a.length=="number"}function i(a){return typeof a=="string"}function k(a){a=g(a);return a=="object"||a=="array"||a=="function"}Math.floor(Math.random()*2147483648).toString(36);var l=Date.now||function(){return+new Date};function m(a,b){function c(){}c.prototype=b.prototype;a.h=b.prototype;a.prototype=new c};function n(a){this.stack=Error().stack||"";if(a)this.message=String(a)}m(n,Error);n.prototype.name="CustomError";function o(a,b,c){var d={};for(var e in a)if(b.call(c,a[e],e,a))d[e]=a[e];return d}function p(a,b,c){var d={};for(var e in a)d[e]=b.call(c,a[e],e,a);return d}function q(a,b,c){for(var d in a)if(b.call(c,a[d],d,a))return d};function r(a,b){n.call(this,b);this.code=a;this.name=s[a]||s[13]}m(r,n);var s,t={NoSuchElementError:7,NoSuchFrameError:8,UnknownCommandError:9,StaleElementReferenceError:10,ElementNotVisibleError:11,InvalidElementStateError:12,UnknownError:13,ElementNotSelectableError:15,XPathLookupError:19,NoSuchWindowError:23,InvalidCookieDomainError:24,UnableToSetCookieError:25,ModalDialogOpenedError:26,ModalDialogOpenError:27,ScriptTimeoutError:28},u={};for(var v in t)u[t[v]]=v;s=u;
+r.prototype.toString=function(){return"["+this.name+"] "+this.message};function w(a){for(var b=1;b<arguments.length;b++){var c=String(arguments[b]).replace(/\$/g,"$$$$");a=a.replace(/\%s/,c)}return a};function x(a,b){b.unshift(a);n.call(this,w.apply(null,b));b.shift();this.i=a}m(x,n);x.prototype.name="AssertionError";function y(a,b){if(!a){var c=Array.prototype.slice.call(arguments,2),d="Assertion failed";if(b){d+=": "+b;var e=c}throw new x(""+d,e||[]);}return a};var z=Array.prototype,A=z.indexOf?function(a,b,c){y(a.length!=null);return z.indexOf.call(a,b,c)}:function(a,b,c){c=c==null?0:c<0?Math.max(0,a.length+c):c;if(i(a)){if(!i(b)||b.length!=1)return-1;return a.indexOf(b,c)}for(c=c;c<a.length;c++)if(c in a&&a[c]===b)return c;return-1},B=z.map?function(a,b,c){y(a.length!=null);return z.map.call(a,b,c)}:function(a,b,c){var d=a.length,e=Array(d),f=i(a)?a.split(""):a;for(var j=0;j<d;j++)if(j in f)e[j]=b.call(c,f[j],j,a);return e};var C="",D;if(D=/WebKit\/(\S+)/){var E=D.exec(this.navigator?this.navigator.userAgent:null);C=E?E[1]:""};var F="StopIteration"in this?this.StopIteration:Error("StopIteration");function G(){}G.prototype.next=function(){throw F;};function H(a,b,c,d,e){this.a=!!b;a&&I(this,a,d);this.d=e!=undefined?e:this.c||0;if(this.a)this.d*=-1;this.f=!c}m(H,G);H.prototype.b=null;H.prototype.c=0;H.prototype.e=false;function I(a,b,c,d){if(a.b=b)a.c=typeof c=="number"?c:a.b.nodeType!=1?0:a.a?-1:1;if(typeof d=="number")a.d=d}
+H.prototype.next=function(){var a;if(this.e){if(!this.b||this.f&&this.d==0)throw F;a=this.b;var b=this.a?-1:1;if(this.c==b){var c=this.a?a.lastChild:a.firstChild;c?I(this,c):I(this,a,b*-1)}else(c=this.a?a.previousSibling:a.nextSibling)?I(this,c):I(this,a.parentNode,b*-1);this.d+=this.c*(this.a?-1:1)}else this.e=true;a=this.b;if(!this.b)throw F;return a};
+H.prototype.splice=function(){var a=this.b,b=this.a?1:-1;if(this.c==b){this.c=b*-1;this.d+=this.c*(this.a?-1:1)}this.a=!this.a;H.prototype.next.call(this);this.a=!this.a;b=h(arguments[0])?arguments[0]:arguments;for(var c=b.length-1;c>=0;c--)a.parentNode&&a.parentNode.insertBefore(b[c],a.nextSibling);a&&a.parentNode&&a.parentNode.removeChild(a)};function J(a,b,c,d){H.call(this,a,b,c,null,d)}m(J,H);J.prototype.next=function(){do J.h.next.call(this);while(this.c==-1);return this.b};var K=["async","autofocus","autoplay","checked","compact","complete","controls","declare","defaultchecked","defaultselected","defer","disabled","draggable","ended","formnovalidate","hidden","indeterminate","iscontenteditable","ismap","itemscope","loop","multiple","muted","nohref","noresize","noshade","novalidate","nowrap","open","paused","pubdate","readonly","required","reversed","scoped","seamless","seeking","selected","spellcheck","truespeed","willvalidate"];
+function L(a,b){if(8==a.nodeType)return null;b=b.toLowerCase();if(b=="style"){var c=a.style.cssText.replace(/^[\s\xa0]+|[\s\xa0]+$/g,"").toLowerCase();return c.charAt(c.length-1)==";"?c:c+";"}c=a.getAttributeNode(b);if(!c)return null;if(A(K,b)>=0)return"true";return c.specified?c.value:null}var M=["BUTTON","INPUT","OPTGROUP","OPTION","SELECT","TEXTAREA"];
+function N(a){var b=a.tagName.toUpperCase();if(!(A(M,b)>=0))return true;if(L(a,"disabled"))return false;if(a.parentNode&&a.parentNode.nodeType==1&&"OPTGROUP"==b||"OPTION"==b)return N(a.parentNode);return true}String.fromCharCode(160);function O(){}
+function P(a,b,c){switch(typeof b){case "string":Q(a,b,c);break;case "number":c.push(isFinite(b)&&!isNaN(b)?b:"null");break;case "boolean":c.push(b);break;case "undefined":c.push("null");break;case "object":if(b==null){c.push("null");break}if(g(b)=="array"){var d=b.length;c.push("[");var e="";for(var f=0;f<d;f++){c.push(e);P(a,b[f],c);e=","}c.push("]");break}c.push("{");d="";for(e in b)if(Object.prototype.hasOwnProperty.call(b,e)){f=b[e];if(typeof f!="function"){c.push(d);Q(a,e,c);c.push(":");P(a,
+f,c);d=","}}c.push("}");break;case "function":break;default:throw Error("Unknown type: "+typeof b);}}var R={'"':'\\"',"\\":"\\\\","/":"\\/","\u0008":"\\b","\u000c":"\\f","\n":"\\n","\r":"\\r","\t":"\\t","\u000b":"\\u000b"},S=/\uffff/.test("\uffff")?/[\\\"\x00-\x1f\x7f-\uffff]/g:/[\\\"\x00-\x1f\x7f-\xff]/g;
+function Q(a,b,c){c.push('"',b.replace(S,function(d){if(d in R)return R[d];var e=d.charCodeAt(0),f="\\u";if(e<16)f+="000";else if(e<256)f+="00";else if(e<4096)f+="0";return R[d]=f+e.toString(16)}),'"')};function T(a){switch(g(a)){case "string":case "number":case "boolean":return a;case "function":return a.toString();case "array":return B(a,T);case "object":a=a;if("nodeType"in a&&(a.nodeType==1||a.nodeType==9)){var b={};b.ELEMENT=U(a);return b}if(h(a))return B(a,T);a=o(a,function(c,d){return typeof d=="number"||i(d)});return p(a,T);default:return null}}
+function V(a,b){if(g(a)=="array")return B(a,function(c){return V(c,b)});else if(k(a))return"ELEMENT"in a?aa(a.ELEMENT,b):p(a,function(c){return V(c,b)});return a}function W(a){a=a||document;var b=a.$wdc_;if(!b){b=a.$wdc_={};b.g=l()}return b}function U(a){var b=W(a.ownerDocument),c=q(b,function(d){return d==a});if(!c){c=":wdc:"+b.g++;b[c]=a}return c}
+function aa(a,b){a=decodeURIComponent(a);var c=b||document,d=W(c);if(!(a in d))throw new r(10,"Element does not exist in cache");var e=d[a];for(var f=e;f;){if(f==c.documentElement)return e;f=f.parentNode}delete d[a];throw new r(10,"Element is no longer attached to the DOM");};function X(a){var b=N;a=[a];var c;try{if(i(b))b=new Function(b);var d=V(a),e=b.apply(null,d);c={status:0,value:T(e)}}catch(f){c={status:"code"in f?f.code:13,value:{message:f.message}}}b=[];P(new O,c,b);return b.join("")}var Y="_".split("."),Z=this;!(Y[0]in Z)&&Z.execScript&&Z.execScript("var "+Y[0]);for(var $;Y.length&&($=Y.shift());)if(!Y.length&&X!==undefined)Z[$]=X;else Z=Z[$]?Z[$]:Z[$]={};; return this._.apply(null,arguments);}.apply({navigator:typeof window!='undefined'?window.navigator:null}, arguments);}
diff --git a/core/res/res/raw/is_selected_android.js b/core/res/res/raw/is_selected_android.js
new file mode 100644
index 0000000..e1c51b6
--- /dev/null
+++ b/core/res/res/raw/is_selected_android.js
@@ -0,0 +1,10 @@
+function(){return function(){function g(a){var b=typeof a;if(b=="object")if(a){if(a instanceof Array)return"array";else if(a instanceof Object)return b;var c=Object.prototype.toString.call(a);if(c=="[object Window]")return"object";if(c=="[object Array]"||typeof a.length=="number"&&typeof a.splice!="undefined"&&typeof a.propertyIsEnumerable!="undefined"&&!a.propertyIsEnumerable("splice"))return"array";if(c=="[object Function]"||typeof a.call!="undefined"&&typeof a.propertyIsEnumerable!="undefined"&&!a.propertyIsEnumerable("call"))return"function"}else return"null";
+else if(b=="function"&&typeof a.call=="undefined")return"object";return b}function h(a){var b=g(a);return b=="array"||b=="object"&&typeof a.length=="number"}function i(a){return typeof a=="string"}function k(a){a=g(a);return a=="object"||a=="array"||a=="function"}Math.floor(Math.random()*2147483648).toString(36);var l=Date.now||function(){return+new Date};function m(a,b){function c(){}c.prototype=b.prototype;a.h=b.prototype;a.prototype=new c};function n(a){this.stack=Error().stack||"";if(a)this.message=String(a)}m(n,Error);n.prototype.name="CustomError";function o(a,b,c){var d={};for(var e in a)if(b.call(c,a[e],e,a))d[e]=a[e];return d}function p(a,b,c){var d={};for(var e in a)d[e]=b.call(c,a[e],e,a);return d}function q(a,b,c){for(var d in a)if(b.call(c,a[d],d,a))return d};function r(a,b){n.call(this,b);this.code=a;this.name=s[a]||s[13]}m(r,n);var s,t={NoSuchElementError:7,NoSuchFrameError:8,UnknownCommandError:9,StaleElementReferenceError:10,ElementNotVisibleError:11,InvalidElementStateError:12,UnknownError:13,ElementNotSelectableError:15,XPathLookupError:19,NoSuchWindowError:23,InvalidCookieDomainError:24,UnableToSetCookieError:25,ModalDialogOpenedError:26,ModalDialogOpenError:27,ScriptTimeoutError:28},u={};for(var v in t)u[t[v]]=v;s=u;
+r.prototype.toString=function(){return"["+this.name+"] "+this.message};function w(a){for(var b=1;b<arguments.length;b++){var c=String(arguments[b]).replace(/\$/g,"$$$$");a=a.replace(/\%s/,c)}return a};function x(a,b){b.unshift(a);n.call(this,w.apply(null,b));b.shift();this.i=a}m(x,n);x.prototype.name="AssertionError";function y(a,b){if(!a){var c=Array.prototype.slice.call(arguments,2),d="Assertion failed";if(b){d+=": "+b;var e=c}throw new x(""+d,e||[]);}return a};var z=Array.prototype,A=z.indexOf?function(a,b,c){y(a.length!=null);return z.indexOf.call(a,b,c)}:function(a,b,c){c=c==null?0:c<0?Math.max(0,a.length+c):c;if(i(a)){if(!i(b)||b.length!=1)return-1;return a.indexOf(b,c)}for(c=c;c<a.length;c++)if(c in a&&a[c]===b)return c;return-1},B=z.map?function(a,b,c){y(a.length!=null);return z.map.call(a,b,c)}:function(a,b,c){var d=a.length,e=Array(d),f=i(a)?a.split(""):a;for(var j=0;j<d;j++)if(j in f)e[j]=b.call(c,f[j],j,a);return e};var C="",D;if(D=/WebKit\/(\S+)/){var E=D.exec(this.navigator?this.navigator.userAgent:null);C=E?E[1]:""};var F="StopIteration"in this?this.StopIteration:Error("StopIteration");function G(){}G.prototype.next=function(){throw F;};function H(a,b,c,d,e){this.a=!!b;a&&I(this,a,d);this.d=e!=undefined?e:this.c||0;if(this.a)this.d*=-1;this.f=!c}m(H,G);H.prototype.b=null;H.prototype.c=0;H.prototype.e=false;function I(a,b,c,d){if(a.b=b)a.c=typeof c=="number"?c:a.b.nodeType!=1?0:a.a?-1:1;if(typeof d=="number")a.d=d}
+H.prototype.next=function(){var a;if(this.e){if(!this.b||this.f&&this.d==0)throw F;a=this.b;var b=this.a?-1:1;if(this.c==b){var c=this.a?a.lastChild:a.firstChild;c?I(this,c):I(this,a,b*-1)}else(c=this.a?a.previousSibling:a.nextSibling)?I(this,c):I(this,a.parentNode,b*-1);this.d+=this.c*(this.a?-1:1)}else this.e=true;a=this.b;if(!this.b)throw F;return a};
+H.prototype.splice=function(){var a=this.b,b=this.a?1:-1;if(this.c==b){this.c=b*-1;this.d+=this.c*(this.a?-1:1)}this.a=!this.a;H.prototype.next.call(this);this.a=!this.a;b=h(arguments[0])?arguments[0]:arguments;for(var c=b.length-1;c>=0;c--)a.parentNode&&a.parentNode.insertBefore(b[c],a.nextSibling);a&&a.parentNode&&a.parentNode.removeChild(a)};function J(a,b,c,d){H.call(this,a,b,c,null,d)}m(J,H);J.prototype.next=function(){do J.h.next.call(this);while(this.c==-1);return this.b};var K={"class":"className",readonly:"readOnly"},L=["checked","disabled","draggable","hidden"];String.fromCharCode(160);function M(a){var b;if(a&&a.nodeType==1&&a.tagName.toUpperCase()=="OPTION")b=true;else if(a&&a.nodeType==1&&a.tagName.toUpperCase()=="INPUT"){b=a.type.toLowerCase();b=b=="checkbox"||b=="radio"}else b=false;if(!b)throw new r(15,"Element is not selectable");b="selected";var c=a.type&&a.type.toLowerCase();if("checkbox"==c||"radio"==c)b="checked";b=K[b]||b;a=a[b];a=a===undefined&&A(L,b)>=0?false:a;return!!a};function N(){}
+function O(a,b,c){switch(typeof b){case "string":P(a,b,c);break;case "number":c.push(isFinite(b)&&!isNaN(b)?b:"null");break;case "boolean":c.push(b);break;case "undefined":c.push("null");break;case "object":if(b==null){c.push("null");break}if(g(b)=="array"){var d=b.length;c.push("[");var e="";for(var f=0;f<d;f++){c.push(e);O(a,b[f],c);e=","}c.push("]");break}c.push("{");d="";for(e in b)if(Object.prototype.hasOwnProperty.call(b,e)){f=b[e];if(typeof f!="function"){c.push(d);P(a,e,c);c.push(":");O(a,
+f,c);d=","}}c.push("}");break;case "function":break;default:throw Error("Unknown type: "+typeof b);}}var Q={'"':'\\"',"\\":"\\\\","/":"\\/","\u0008":"\\b","\u000c":"\\f","\n":"\\n","\r":"\\r","\t":"\\t","\u000b":"\\u000b"},R=/\uffff/.test("\uffff")?/[\\\"\x00-\x1f\x7f-\uffff]/g:/[\\\"\x00-\x1f\x7f-\xff]/g;
+function P(a,b,c){c.push('"',b.replace(R,function(d){if(d in Q)return Q[d];var e=d.charCodeAt(0),f="\\u";if(e<16)f+="000";else if(e<256)f+="00";else if(e<4096)f+="0";return Q[d]=f+e.toString(16)}),'"')};function S(a){switch(g(a)){case "string":case "number":case "boolean":return a;case "function":return a.toString();case "array":return B(a,S);case "object":a=a;if("nodeType"in a&&(a.nodeType==1||a.nodeType==9)){var b={};b.ELEMENT=T(a);return b}if(h(a))return B(a,S);a=o(a,function(c,d){return typeof d=="number"||i(d)});return p(a,S);default:return null}}
+function U(a,b){if(g(a)=="array")return B(a,function(c){return U(c,b)});else if(k(a))return"ELEMENT"in a?V(a.ELEMENT,b):p(a,function(c){return U(c,b)});return a}function W(a){a=a||document;var b=a.$wdc_;if(!b){b=a.$wdc_={};b.g=l()}return b}function T(a){var b=W(a.ownerDocument),c=q(b,function(d){return d==a});if(!c){c=":wdc:"+b.g++;b[c]=a}return c}
+function V(a,b){a=decodeURIComponent(a);var c=b||document,d=W(c);if(!(a in d))throw new r(10,"Element does not exist in cache");var e=d[a];for(var f=e;f;){if(f==c.documentElement)return e;f=f.parentNode}delete d[a];throw new r(10,"Element is no longer attached to the DOM");};function X(a){var b=M;a=[a];var c;try{if(i(b))b=new Function(b);var d=U(a),e=b.apply(null,d);c={status:0,value:S(e)}}catch(f){c={status:"code"in f?f.code:13,value:{message:f.message}}}b=[];O(new N,c,b);return b.join("")}var Y="_".split("."),Z=this;!(Y[0]in Z)&&Z.execScript&&Z.execScript("var "+Y[0]);for(var $;Y.length&&($=Y.shift());)if(!Y.length&&X!==undefined)Z[$]=X;else Z=Z[$]?Z[$]:Z[$]={};; return this._.apply(null,arguments);}.apply({navigator:typeof window!='undefined'?window.navigator:null}, arguments);}
diff --git a/core/res/res/raw/set_selected_android.js b/core/res/res/raw/set_selected_android.js
new file mode 100644
index 0000000..8936f55
--- /dev/null
+++ b/core/res/res/raw/set_selected_android.js
@@ -0,0 +1,27 @@
+function(){return function(){var l=this;
+function m(a){var b=typeof a;if(b=="object")if(a){if(a instanceof Array)return"array";else if(a instanceof Object)return b;var c=Object.prototype.toString.call(a);if(c=="[object Window]")return"object";if(c=="[object Array]"||typeof a.length=="number"&&typeof a.splice!="undefined"&&typeof a.propertyIsEnumerable!="undefined"&&!a.propertyIsEnumerable("splice"))return"array";if(c=="[object Function]"||typeof a.call!="undefined"&&typeof a.propertyIsEnumerable!="undefined"&&!a.propertyIsEnumerable("call"))return"function"}else return"null";else if(b==
+"function"&&typeof a.call=="undefined")return"object";return b}function o(a){var b=m(a);return b=="array"||b=="object"&&typeof a.length=="number"}function p(a){return typeof a=="string"}function q(a){a=m(a);return a=="object"||a=="array"||a=="function"}Math.floor(Math.random()*2147483648).toString(36);var aa=Date.now||function(){return+new Date};function r(a,b){function c(){}c.prototype=b.prototype;a.k=b.prototype;a.prototype=new c};var ba=window;function ca(){var a=l.Components;if(!a)return false;try{a.l["@mozilla.org/uuid-generator;1"].o(a.i.r);return true}catch(b){return false}};function s(a){this.stack=Error().stack||"";if(a)this.message=String(a)}r(s,Error);s.prototype.name="CustomError";function da(a,b,c){var d={};for(var f in a)if(b.call(c,a[f],f,a))d[f]=a[f];return d}function t(a,b,c){var d={};for(var f in a)d[f]=b.call(c,a[f],f,a);return d}function ea(a,b,c){for(var d in a)if(b.call(c,a[d],d,a))return d};function v(a,b){s.call(this,b);this.code=a;this.name=w[a]||w[13]}r(v,s);var w,fa={NoSuchElementError:7,NoSuchFrameError:8,UnknownCommandError:9,StaleElementReferenceError:10,ElementNotVisibleError:11,InvalidElementStateError:12,UnknownError:13,ElementNotSelectableError:15,XPathLookupError:19,NoSuchWindowError:23,InvalidCookieDomainError:24,UnableToSetCookieError:25,ModalDialogOpenedError:26,ModalDialogOpenError:27,ScriptTimeoutError:28},ga={};for(var ha in fa)ga[fa[ha]]=ha;w=ga;
+v.prototype.toString=function(){return"["+this.name+"] "+this.message};function ia(a){for(var b=1;b<arguments.length;b++){var c=String(arguments[b]).replace(/\$/g,"$$$$");a=a.replace(/\%s/,c)}return a}var ja={};function ka(a){return ja[a]||(ja[a]=String(a).replace(/\-([a-z])/g,function(b,c){return c.toUpperCase()}))};function x(a,b){b.unshift(a);s.call(this,ia.apply(null,b));b.shift();this.p=a}r(x,s);x.prototype.name="AssertionError";function y(a,b){if(!a){var c=Array.prototype.slice.call(arguments,2),d="Assertion failed";if(b){d+=": "+b;var f=c}throw new x(""+d,f||[]);}return a};var z=Array.prototype,A=z.indexOf?function(a,b,c){y(a.length!=null);return z.indexOf.call(a,b,c)}:function(a,b,c){c=c==null?0:c<0?Math.max(0,a.length+c):c;if(p(a)){if(!p(b)||b.length!=1)return-1;return a.indexOf(b,c)}for(c=c;c<a.length;c++)if(c in a&&a[c]===b)return c;return-1},B=z.map?function(a,b,c){y(a.length!=null);return z.map.call(a,b,c)}:function(a,b,c){var d=a.length,f=Array(d),e=p(a)?a.split(""):a;for(var g=0;g<d;g++)if(g in e)f[g]=b.call(c,e[g],g,a);return f},la=z.some?function(a,b,c){y(a.length!=
+null);return z.some.call(a,b,c)}:function(a,b,c){var d=a.length,f=p(a)?a.split(""):a;for(var e=0;e<d;e++)if(e in f&&b.call(c,f[e],e,a))return true;return false};var C=true,ma="",D;if(C)D=/WebKit\/(\S+)/;if(D){var na=D.exec(l.navigator?l.navigator.userAgent:null);ma=na?na[1]:""};var E;function F(a,b){this.x=a!==undefined?a:0;this.y=b!==undefined?b:0}F.prototype.toString=function(){return"("+this.x+", "+this.y+")"};function G(a,b){this.width=a;this.height=b}G.prototype.toString=function(){return"("+this.width+" x "+this.height+")"};G.prototype.floor=function(){this.width=Math.floor(this.width);this.height=Math.floor(this.height);return this};function H(a){return a.nodeType==9?a:a.ownerDocument||a.document}function oa(a,b){var c=[];return pa(a,b,c,true)?c[0]:undefined}function pa(a,b,c,d){if(a!=null){var f=0;for(var e;e=a.childNodes[f];f++){if(b(e)){c.push(e);if(d)return true}if(pa(e,b,c,d))return true}}return false}function I(a,b,c,d){if(!c)a=a.parentNode;c=d==null;for(var f=0;a&&(c||f<=d);){if(b(a))return a;a=a.parentNode;f++}return null}function J(a){this.e=a||l.document||document}
+function qa(a){a=!C&&a.e.compatMode=="CSS1Compat"?a.e.documentElement:a.e.body;return new F(a.scrollLeft,a.scrollTop)};function ra(a,b){var c=function(d,f){var e=H(d);if(d.selectSingleNode){e.setProperty&&e.setProperty("SelectionLanguage","XPath");return d.selectSingleNode(f)}else if(e.implementation.hasFeature("XPath","3.0")){var g=e.createNSResolver(e.documentElement),h;if(typeof XPathResult!="undefined")h=XPathResult.FIRST_ORDERED_NODE_TYPE;else{if(!ca())throw Error("Document claims it supports XPath yet XPathResult is not defined. Please report this to Selenium developers");h=Components.i.q.FIRST_ORDERED_NODE_TYPE}return e.evaluate(f,
+d,g,h,null).singleNodeValue}return null}(b,a);if(!c)return null;if(c.nodeType!=1)throw Error("Returned node is not an element: "+a);return c};var K="StopIteration"in l?l.StopIteration:Error("StopIteration");function sa(){}sa.prototype.next=function(){throw K;};function L(a,b,c,d,f){this.a=!!b;a&&M(this,a,d);this.d=f!=undefined?f:this.c||0;if(this.a)this.d*=-1;this.h=!c}r(L,sa);L.prototype.b=null;L.prototype.c=0;L.prototype.g=false;function M(a,b,c,d){if(a.b=b)a.c=typeof c=="number"?c:a.b.nodeType!=1?0:a.a?-1:1;if(typeof d=="number")a.d=d}
+L.prototype.next=function(){var a;if(this.g){if(!this.b||this.h&&this.d==0)throw K;a=this.b;var b=this.a?-1:1;if(this.c==b){var c=this.a?a.lastChild:a.firstChild;c?M(this,c):M(this,a,b*-1)}else(c=this.a?a.previousSibling:a.nextSibling)?M(this,c):M(this,a.parentNode,b*-1);this.d+=this.c*(this.a?-1:1)}else this.g=true;a=this.b;if(!this.b)throw K;return a};
+L.prototype.splice=function(){var a=this.b,b=this.a?1:-1;if(this.c==b){this.c=b*-1;this.d+=this.c*(this.a?-1:1)}this.a=!this.a;L.prototype.next.call(this);this.a=!this.a;b=o(arguments[0])?arguments[0]:arguments;for(var c=b.length-1;c>=0;c--)a.parentNode&&a.parentNode.insertBefore(b[c],a.nextSibling);a&&a.parentNode&&a.parentNode.removeChild(a)};function N(a,b,c,d){L.call(this,a,b,c,null,d)}r(N,L);N.prototype.next=function(){do N.k.next.call(this);while(this.c==-1);return this.b};function ta(a,b){var c=H(a);if(c.defaultView&&c.defaultView.getComputedStyle)if(c=c.defaultView.getComputedStyle(a,null))return c[b]||c.getPropertyValue(b);return""}function O(a,b){return ta(a,b)||(a.currentStyle?a.currentStyle[b]:null)||a.style[b]}
+function ua(a){var b=H(a),c=O(a,"position"),d=c=="fixed"||c=="absolute";for(a=a.parentNode;a&&a!=b;a=a.parentNode){c=O(a,"position");d=d&&c=="static"&&a!=b.documentElement&&a!=b.body;if(!d&&(a.scrollWidth>a.clientWidth||a.scrollHeight>a.clientHeight||c=="fixed"||c=="absolute"))return a}return null};function P(a,b){return!!a&&a.nodeType==1&&(!b||a.tagName.toUpperCase()==b)}
+var va={"class":"className",readonly:"readOnly"},wa=["checked","disabled","draggable","hidden"],xa=["async","autofocus","autoplay","checked","compact","complete","controls","declare","defaultchecked","defaultselected","defer","disabled","draggable","ended","formnovalidate","hidden","indeterminate","iscontenteditable","ismap","itemscope","loop","multiple","muted","nohref","noresize","noshade","novalidate","nowrap","open","paused","pubdate","readonly","required","reversed","scoped","seamless","seeking",
+"selected","spellcheck","truespeed","willvalidate"];function ya(a,b){if(8==a.nodeType)return null;b=b.toLowerCase();if(b=="style"){var c=a.style.cssText.replace(/^[\s\xa0]+|[\s\xa0]+$/g,"").toLowerCase();return c.charAt(c.length-1)==";"?c:c+";"}c=a.getAttributeNode(b);if(!c)return null;if(A(xa,b)>=0)return"true";return c.specified?c.value:null}var za=["BUTTON","INPUT","OPTGROUP","OPTION","SELECT","TEXTAREA"];
+function Aa(a){var b=a.tagName.toUpperCase();if(!(A(za,b)>=0))return true;if(ya(a,"disabled"))return false;if(a.parentNode&&a.parentNode.nodeType==1&&"OPTGROUP"==b||"OPTION"==b)return Aa(a.parentNode);return true}function Q(a){for(a=a.parentNode;a&&a.nodeType!=1&&a.nodeType!=9&&a.nodeType!=11;)a=a.parentNode;return P(a)?a:null}function R(a,b){b=ka(String(b));return ta(a,b)||Ba(a,b)}
+function Ba(a,b){var c=(a.currentStyle||a.style)[b];if(c!="inherit")return c!==undefined?c:null;return(c=Q(a))?Ba(c,b):null}function Ca(a){if(m(a.getBBox)=="function")return a.getBBox();var b;if(O(a,"display")!="none")b=new G(a.offsetWidth,a.offsetHeight);else{b=a.style;var c=b.display,d=b.visibility,f=b.position;b.visibility="hidden";b.position="absolute";b.display="inline";var e;e=a.offsetWidth;a=a.offsetHeight;b.display=c;b.position=f;b.visibility=d;b=new G(e,a)}return b}
+function S(a,b){function c(e){if(R(e,"display")=="none")return false;e=Q(e);return!e||c(e)}function d(e){var g=Ca(e);if(g.height>0&&g.width>0)return true;if(e.innerText||e.textContent)if(Da.test(e.innerText||e.textContent))return true;return C&&la(e.childNodes,function(h){return P(h)&&d(h)})}if(!P(a))throw Error("Argument to isShown must be of type Element");if(P(a,"TITLE"))return(H(a)?H(a).parentWindow||H(a).defaultView:window)==ba;if(P(a,"OPTION")||P(a,"OPTGROUP")){var f=I(a,function(e){return P(e,
+"SELECT")});return!!f&&S(f)}if(P(a,"MAP")){if(!a.name)return false;f=H(a);f=f.evaluate?ra('/descendant::*[@usemap = "#'+a.name+'"]',f):oa(f,function(e){return P(e)&&ya(e,"usemap")=="#"+a.name});return!!f&&S(f)}if(P(a,"AREA")){f=I(a,function(e){return P(e,"MAP")});return!!f&&S(f)}if(P(a,"INPUT")&&a.type.toLowerCase()=="hidden")return false;if(R(a,"visibility")=="hidden")return false;if(!c(a))return false;if(!b&&Ea(a)==0)return false;if(!d(a))return false;return true}
+var Fa="[\\s\\xa0"+String.fromCharCode(160)+"]+",Da=RegExp("^"+Fa+"$");function Ea(a){var b=1,c=R(a,"opacity");if(c)b=Number(c);if(a=Q(a))b*=Ea(a);return b};var Ga=["dragstart","dragexit","mouseover","mouseout"];
+function T(a,b,c){var d=H(a),f=d?d.parentWindow||d.defaultView:window,e=new F;if(a.nodeType==1)if(a.getBoundingClientRect){var g=a.getBoundingClientRect();e.x=g.left;e.y=g.top}else{g=qa(a?new J(H(a)):E||(E=new J));var h,i=H(a);h=O(a,"position");var j=new F(0,0),u=(i?i.nodeType==9?i:H(i):document).documentElement;if(a!=u)if(a.getBoundingClientRect){h=a.getBoundingClientRect();i=qa(i?new J(H(i)):E||(E=new J));j.x=h.left+i.x;j.y=h.top+i.y}else if(i.getBoxObjectFor){h=i.getBoxObjectFor(a);i=i.getBoxObjectFor(u);
+j.x=h.screenX-i.screenX;j.y=h.screenY-i.screenY}else{var k=a;do{j.x+=k.offsetLeft;j.y+=k.offsetTop;if(k!=a){j.x+=k.clientLeft||0;j.y+=k.clientTop||0}if(C&&O(k,"position")=="fixed"){j.x+=i.body.scrollLeft;j.y+=i.body.scrollTop;break}k=k.offsetParent}while(k&&k!=a);if(C&&h=="absolute")j.y-=i.body.offsetTop;for(k=a;(k=ua(k))&&k!=i.body&&k!=u;){j.x-=k.scrollLeft;j.y-=k.scrollTop}}e.x=j.x-g.x;e.y=j.y-g.y}else{g=m(a.f)=="function";j=a;if(a.targetTouches)j=a.targetTouches[0];else if(g&&a.f().targetTouches)j=
+a.f().targetTouches[0];e.x=j.clientX;e.y=j.clientY}var n=c||{};c=(n.x||0)+e.x;e=(n.y||0)+e.y;g=n.button||0;j=n.bubble||true;h=null;if(A(Ga,b)>=0)h=n.related||null;i=!!n.alt;u=!!n.control;k=!!n.shift;n=!!n.meta;if(a.fireEvent&&d&&d.createEventObject){a=d.createEventObject();a.altKey=i;a.m=u;a.metaKey=n;a.shiftKey=k;a.clientX=c;a.clientY=e;a.button=g;a.relatedTarget=h}else{a=d.createEvent("MouseEvents");if(a.initMouseEvent)a.initMouseEvent(b,j,true,f,1,0,0,c,e,u,i,k,n,g,h);else{a.initEvent(b,j,true);
+a.shiftKey=k;a.metaKey=n;a.altKey=i;a.ctrlKey=u;a.button=g}}return a}function U(a,b,c){var d=c||{};c=d.keyCode||0;var f=d.charCode||0,e=!!d.alt,g=!!d.ctrl,h=!!d.shift;d=!!d.meta;a=H(a).createEvent("Events");a.initEvent(b,true,true);a.charCode=f;a.keyCode=c;a.altKey=e;a.ctrlKey=g;a.metaKey=d;a.shiftKey=h;return a}
+function Ha(a,b,c){var d=H(a),f=c||{};c=f.bubble!==false;var e=!!f.alt,g=!!f.control,h=!!f.shift;f=!!f.meta;if(a.fireEvent&&d&&d.createEventObject){a=d.createEventObject();a.altKey=e;a.n=g;a.metaKey=f;a.shiftKey=h}else{a=d.createEvent("HTMLEvents");a.initEvent(b,c,true);a.shiftKey=h;a.metaKey=f;a.altKey=e;a.ctrlKey=g}return a}var V={};V.click=T;V.keydown=U;V.keypress=U;V.keyup=U;V.mousedown=T;V.mousemove=T;V.mouseout=T;V.mouseover=T;V.mouseup=T;
+function Ia(a,b,c){c=(V[b]||Ha)(a,b,c);if(m(a.fireEvent)=="function"||q(a.fireEvent)){try{(H(a)?H(a).parentWindow||H(a).defaultView:window).event=c}catch(d){}a=a.fireEvent("on"+b,c)}else a=a.dispatchEvent(c);return a};function Ja(a){var b;if(P(a,"OPTION"))b=true;else if(P(a,"INPUT")){b=a.type.toLowerCase();b=b=="checkbox"||b=="radio"}else b=false;if(!b)throw new v(15,"Element is not selectable");b="selected";var c=a.type&&a.type.toLowerCase();if("checkbox"==c||"radio"==c)b="checked";b=va[b]||b;a=a[b];a=a===undefined&&A(wa,b)>=0?false:a;return!!a}function Ka(a){return P(a,"SELECT")}
+function La(a,b){if(!Aa(a))throw new v(12,"Element is not currently enabled and may not be manipulated");if(!S(a,true))throw new v(11,"Element is not currently visible and may not be manipulated");if(P(a,"INPUT")){var c=a.type.toLowerCase();if(c=="checkbox"||c=="radio"){if(a.checked!=b){if(a.type=="radio"&&!b)throw new v(12,"You may not deselect a radio button");if(b!=Ja(a)){a.checked=b;Ia(a,"change")}}}else throw new v(15,"You may not select an unselectable input element: "+a.type);}else if(P(a,
+"OPTION")){c=I(a,Ka);if(!c.multiple&&!b)throw new v(15,"You may not deselect an option within a select that does not support multiple selections.");if(b!=Ja(a)){a.selected=b;Ia(c,"change")}}else throw new v(15,"You may not select an unselectable element: "+a.tagName);};function W(a){switch(m(a)){case "string":case "number":case "boolean":return a;case "function":return a.toString();case "array":return B(a,W);case "object":a=a;if("nodeType"in a&&(a.nodeType==1||a.nodeType==9)){var b={};b.ELEMENT=Ma(a);return b}if(o(a))return B(a,W);a=da(a,function(c,d){return typeof d=="number"||p(d)});return t(a,W);default:return null}}
+function X(a,b){if(m(a)=="array")return B(a,function(c){return X(c,b)});else if(q(a))return"ELEMENT"in a?Na(a.ELEMENT,b):t(a,function(c){return X(c,b)});return a}function Oa(a){a=a||document;var b=a.$wdc_;if(!b){b=a.$wdc_={};b.j=aa()}return b}function Ma(a){var b=Oa(a.ownerDocument),c=ea(b,function(d){return d==a});if(!c){c=":wdc:"+b.j++;b[c]=a}return c}
+function Na(a,b){a=decodeURIComponent(a);var c=b||document,d=Oa(c);if(!(a in d))throw new v(10,"Element does not exist in cache");var f=d[a];for(var e=f;e;){if(e==c.documentElement)return f;e=e.parentNode}delete d[a];throw new v(10,"Element is no longer attached to the DOM");};function Pa(a,b){var c=La,d=[a,b];try{if(p(c))c=new Function(c);var f=X(d),e=c.apply(null,f);W(e)}catch(g){}}var Y="_".split("."),Z=l;!(Y[0]in Z)&&Z.execScript&&Z.execScript("var "+Y[0]);for(var $;Y.length&&($=Y.shift());)if(!Y.length&&Pa!==undefined)Z[$]=Pa;else Z=Z[$]?Z[$]:Z[$]={};; return this._.apply(null,arguments);}.apply({navigator:typeof window!='undefined'?window.navigator:null}, arguments);}
diff --git a/core/res/res/raw/submit_android.js b/core/res/res/raw/submit_android.js
new file mode 100644
index 0000000..8dd2e3b
--- /dev/null
+++ b/core/res/res/raw/submit_android.js
@@ -0,0 +1,29 @@
+function(){return function(){var m,o=this;function p(){}
+function r(a){var b=typeof a;if(b=="object")if(a){if(a instanceof Array)return"array";else if(a instanceof Object)return b;var c=Object.prototype.toString.call(a);if(c=="[object Window]")return"object";if(c=="[object Array]"||typeof a.length=="number"&&typeof a.splice!="undefined"&&typeof a.propertyIsEnumerable!="undefined"&&!a.propertyIsEnumerable("splice"))return"array";if(c=="[object Function]"||typeof a.call!="undefined"&&typeof a.propertyIsEnumerable!="undefined"&&!a.propertyIsEnumerable("call"))return"function"}else return"null";
+else if(b=="function"&&typeof a.call=="undefined")return"object";return b}function aa(a){var b=r(a);return b=="array"||b=="object"&&typeof a.length=="number"}function s(a){return typeof a=="string"}function t(a){return r(a)=="function"}function u(a){a=r(a);return a=="object"||a=="array"||a=="function"}function v(a){return a[ba]||(a[ba]=++ca)}var ba="closure_uid_"+Math.floor(Math.random()*2147483648).toString(36),ca=0,da=Date.now||function(){return+new Date};
+function w(a,b){function c(){}c.prototype=b.prototype;a.r=b.prototype;a.prototype=new c};function x(a){this.stack=Error().stack||"";if(a)this.message=String(a)}w(x,Error);x.prototype.name="CustomError";function ea(a,b,c){var d={};for(var f in a)if(b.call(c,a[f],f,a))d[f]=a[f];return d}function fa(a,b,c){var d={};for(var f in a)d[f]=b.call(c,a[f],f,a);return d}function ga(a,b,c){for(var d in a)if(b.call(c,a[d],d,a))return d};function y(a,b){x.call(this,b);this.code=a;this.name=z[a]||z[13]}w(y,x);var z,ha={NoSuchElementError:7,NoSuchFrameError:8,UnknownCommandError:9,StaleElementReferenceError:10,ElementNotVisibleError:11,InvalidElementStateError:12,UnknownError:13,ElementNotSelectableError:15,XPathLookupError:19,NoSuchWindowError:23,InvalidCookieDomainError:24,UnableToSetCookieError:25,ModalDialogOpenedError:26,ModalDialogOpenError:27,ScriptTimeoutError:28},ia={};for(var ja in ha)ia[ha[ja]]=ja;z=ia;
+y.prototype.toString=function(){return"["+this.name+"] "+this.message};function ka(a){for(var b=1;b<arguments.length;b++){var c=String(arguments[b]).replace(/\$/g,"$$$$");a=a.replace(/\%s/,c)}return a}
+function la(a,b){var c=0,d=String(a).replace(/^[\s\xa0]+|[\s\xa0]+$/g,"").split("."),f=String(b).replace(/^[\s\xa0]+|[\s\xa0]+$/g,"").split("."),e=Math.max(d.length,f.length);for(var g=0;c==0&&g<e;g++){var j=d[g]||"",i=f[g]||"",h=RegExp("(\\d*)(\\D*)","g"),n=RegExp("(\\d*)(\\D*)","g");do{var k=h.exec(j)||["","",""],l=n.exec(i)||["","",""];if(k[0].length==0&&l[0].length==0)break;c=A(k[1].length==0?0:parseInt(k[1],10),l[1].length==0?0:parseInt(l[1],10))||A(k[2].length==0,l[2].length==0)||A(k[2],l[2])}while(c==
+0)}return c}function A(a,b){if(a<b)return-1;else if(a>b)return 1;return 0};function B(a,b){b.unshift(a);x.call(this,ka.apply(null,b));b.shift();this.N=a}w(B,x);B.prototype.name="AssertionError";function C(a,b){if(!a){var c=Array.prototype.slice.call(arguments,2),d="Assertion failed";if(b){d+=": "+b;var f=c}throw new B(""+d,f||[]);}return a};var D=Array.prototype,ma=D.indexOf?function(a,b,c){C(a.length!=null);return D.indexOf.call(a,b,c)}:function(a,b,c){c=c==null?0:c<0?Math.max(0,a.length+c):c;if(s(a)){if(!s(b)||b.length!=1)return-1;return a.indexOf(b,c)}for(c=c;c<a.length;c++)if(c in a&&a[c]===b)return c;return-1},na=D.map?function(a,b,c){C(a.length!=null);return D.map.call(a,b,c)}:function(a,b,c){var d=a.length,f=Array(d),e=s(a)?a.split(""):a;for(var g=0;g<d;g++)if(g in e)f[g]=b.call(c,e[g],g,a);return f};var oa=o.navigator,pa=(oa&&oa.platform||"").indexOf("Mac")!=-1,qa="",ra;if(ra=/WebKit\/(\S+)/){var sa=ra.exec(o.navigator?o.navigator.userAgent:null);qa=sa?sa[1]:""};var E;function F(a,b){this.x=a!==undefined?a:0;this.y=b!==undefined?b:0}F.prototype.toString=function(){return"("+this.x+", "+this.y+")"};function G(a){return a.nodeType==9?a:a.ownerDocument||a.document}function H(a){this.D=a||o.document||document}function ta(a){a=a.D.body;return new F(a.scrollLeft,a.scrollTop)};var ua="StopIteration"in o?o.StopIteration:Error("StopIteration");function va(){}va.prototype.next=function(){throw ua;};function I(a,b,c,d,f){this.a=!!b;a&&J(this,a,d);this.h=f!=undefined?f:this.d||0;if(this.a)this.h*=-1;this.C=!c}w(I,va);m=I.prototype;m.c=null;m.d=0;m.B=false;function J(a,b,c,d){if(a.c=b)a.d=typeof c=="number"?c:a.c.nodeType!=1?0:a.a?-1:1;if(typeof d=="number")a.h=d}
+m.next=function(){var a;if(this.B){if(!this.c||this.C&&this.h==0)throw ua;a=this.c;var b=this.a?-1:1;if(this.d==b){var c=this.a?a.lastChild:a.firstChild;c?J(this,c):J(this,a,b*-1)}else(c=this.a?a.previousSibling:a.nextSibling)?J(this,c):J(this,a.parentNode,b*-1);this.h+=this.d*(this.a?-1:1)}else this.B=true;a=this.c;if(!this.c)throw ua;return a};
+m.splice=function(){var a=this.c,b=this.a?1:-1;if(this.d==b){this.d=b*-1;this.h+=this.d*(this.a?-1:1)}this.a=!this.a;I.prototype.next.call(this);this.a=!this.a;b=aa(arguments[0])?arguments[0]:arguments;for(var c=b.length-1;c>=0;c--)a.parentNode&&a.parentNode.insertBefore(b[c],a.nextSibling);a&&a.parentNode&&a.parentNode.removeChild(a)};function wa(a,b,c,d){I.call(this,a,b,c,null,d)}w(wa,I);wa.prototype.next=function(){do wa.r.next.call(this);while(this.d==-1);return this.c};function K(a,b){var c;a:{c=G(a);if(c.defaultView&&c.defaultView.getComputedStyle)if(c=c.defaultView.getComputedStyle(a,null)){c=c[b]||c.getPropertyValue(b);break a}c=""}return c||(a.currentStyle?a.currentStyle[b]:null)||a.style[b]}
+function xa(a){var b=G(a),c=K(a,"position"),d=c=="fixed"||c=="absolute";for(a=a.parentNode;a&&a!=b;a=a.parentNode){c=K(a,"position");d=d&&c=="static"&&a!=b.documentElement&&a!=b.body;if(!d&&(a.scrollWidth>a.clientWidth||a.scrollHeight>a.clientHeight||c=="fixed"||c=="absolute"))return a}return null};String.fromCharCode(160);var ya;var za=["dragstart","dragexit","mouseover","mouseout"];
+function L(a,b,c){var d=G(a),f=d?d.parentWindow||d.defaultView:window,e=new F;if(a.nodeType==1)if(a.getBoundingClientRect){var g=a.getBoundingClientRect();e.x=g.left;e.y=g.top}else{g=ta(a?new H(G(a)):E||(E=new H));var j,i=G(a);j=K(a,"position");var h=new F(0,0),n=(i?i.nodeType==9?i:G(i):document).documentElement;if(a!=n)if(a.getBoundingClientRect){j=a.getBoundingClientRect();i=ta(i?new H(G(i)):E||(E=new H));h.x=j.left+i.x;h.y=j.top+i.y}else if(i.getBoxObjectFor){j=i.getBoxObjectFor(a);i=i.getBoxObjectFor(n);
+h.x=j.screenX-i.screenX;h.y=j.screenY-i.screenY}else{var k=a;do{h.x+=k.offsetLeft;h.y+=k.offsetTop;if(k!=a){h.x+=k.clientLeft||0;h.y+=k.clientTop||0}if(K(k,"position")=="fixed"){h.x+=i.body.scrollLeft;h.y+=i.body.scrollTop;break}k=k.offsetParent}while(k&&k!=a);if(j=="absolute")h.y-=i.body.offsetTop;for(k=a;(k=xa(k))&&k!=i.body&&k!=n;){h.x-=k.scrollLeft;h.y-=k.scrollTop}}e.x=h.x-g.x;e.y=h.y-g.y}else{g=t(a.F);h=a;if(a.targetTouches)h=a.targetTouches[0];else if(g&&a.i.targetTouches)h=a.i.targetTouches[0];
+e.x=h.clientX;e.y=h.clientY}var l=c||{};c=(l.x||0)+e.x;e=(l.y||0)+e.y;g=l.button||0;h=l.bubble||true;j=null;if(ma(za,b)>=0)j=l.related||null;i=!!l.alt;n=!!l.control;k=!!l.shift;l=!!l.meta;if(a.fireEvent&&d&&d.createEventObject){a=d.createEventObject();a.altKey=i;a.K=n;a.metaKey=l;a.shiftKey=k;a.clientX=c;a.clientY=e;a.button=g;a.relatedTarget=j}else{a=d.createEvent("MouseEvents");if(a.initMouseEvent)a.initMouseEvent(b,h,true,f,1,0,0,c,e,n,i,k,l,g,j);else{a.initEvent(b,h,true);a.shiftKey=k;a.metaKey=
+l;a.altKey=i;a.ctrlKey=n;a.button=g}}return a}function Aa(a,b,c){var d=c||{};c=d.keyCode||0;var f=d.charCode||0,e=!!d.alt,g=!!d.ctrl,j=!!d.shift;d=!!d.meta;a=G(a).createEvent("Events");a.initEvent(b,true,true);a.charCode=f;a.keyCode=c;a.altKey=e;a.ctrlKey=g;a.metaKey=d;a.shiftKey=j;return a}
+function Ba(a,b,c){var d=G(a),f=c||{};c=f.bubble!==false;var e=!!f.alt,g=!!f.control,j=!!f.shift;f=!!f.meta;if(a.fireEvent&&d&&d.createEventObject){a=d.createEventObject();a.altKey=e;a.L=g;a.metaKey=f;a.shiftKey=j}else{a=d.createEvent("HTMLEvents");a.initEvent(b,c,true);a.shiftKey=j;a.metaKey=f;a.altKey=e;a.ctrlKey=g}return a}var M={};M.click=L;M.keydown=Aa;M.keypress=Aa;M.keyup=Aa;M.mousedown=L;M.mousemove=L;M.mouseout=L;M.mouseover=L;M.mouseup=L;function Ca(a){a:{a=a;for(var b=0;a;){if(a&&a.nodeType==1&&a.tagName.toUpperCase()=="FORM"){a=a;break a}a=a.parentNode;b++}a=null}if(!a)throw new y(12,"Element was not in a form, so could not submit.");b=a;var c=(M.submit||Ba)(b,"submit",void 0);if(t(b.fireEvent)||u(b.fireEvent)){try{(G(b)?G(b).parentWindow||G(b).defaultView:window).event=c}catch(d){}b=b.fireEvent("onsubmit",c)}else b=b.dispatchEvent(c);b&&a.submit()};var Da=[];function N(){if(Ea)Fa[v(this)]=this}var Ea=false,Fa={};N.prototype.u=false;N.prototype.l=function(){if(!this.u){this.u=true;this.g();if(Ea){var a=v(this);if(!Fa.hasOwnProperty(a))throw Error(this+" did not call the goog.Disposable base constructor or was disposed of after a clearUndisposedObjects call");delete Fa[a]}}};N.prototype.g=function(){};function O(a,b){N.call(this);this.type=a;this.currentTarget=this.target=b}w(O,N);O.prototype.g=function(){delete this.type;delete this.target;delete this.currentTarget};O.prototype.q=false;O.prototype.I=true;new Function("a","return a");function P(a,b){a&&this.n(a,b)}w(P,O);m=P.prototype;m.target=null;m.relatedTarget=null;m.offsetX=0;m.offsetY=0;m.clientX=0;m.clientY=0;m.screenX=0;m.screenY=0;m.button=0;m.keyCode=0;m.charCode=0;m.ctrlKey=false;m.altKey=false;m.shiftKey=false;m.metaKey=false;m.H=false;m.i=null;
+m.n=function(a,b){var c=this.type=a.type;O.call(this,c);this.target=a.target||a.srcElement;this.currentTarget=b;var d=a.relatedTarget;if(!d)if(c=="mouseover")d=a.fromElement;else if(c=="mouseout")d=a.toElement;this.relatedTarget=d;this.offsetX=a.offsetX!==undefined?a.offsetX:a.layerX;this.offsetY=a.offsetY!==undefined?a.offsetY:a.layerY;this.clientX=a.clientX!==undefined?a.clientX:a.pageX;this.clientY=a.clientY!==undefined?a.clientY:a.pageY;this.screenX=a.screenX||0;this.screenY=a.screenY||0;this.button=
+a.button;this.keyCode=a.keyCode||0;this.charCode=a.charCode||(c=="keypress"?a.keyCode:0);this.ctrlKey=a.ctrlKey;this.altKey=a.altKey;this.shiftKey=a.shiftKey;this.metaKey=a.metaKey;this.H=pa?a.metaKey:a.ctrlKey;this.J=a.J;this.i=a;delete this.I;delete this.q};m.F=function(){return this.i};m.g=function(){P.r.g.call(this);this.relatedTarget=this.currentTarget=this.target=this.i=null};function Ga(){}var Ha=0;m=Ga.prototype;m.key=0;m.k=false;m.s=false;m.n=function(a,b,c,d,f,e){if(t(a))this.v=true;else if(a&&a.handleEvent&&t(a.handleEvent))this.v=false;else throw Error("Invalid listener argument");this.o=a;this.A=b;this.src=c;this.type=d;this.capture=!!f;this.G=e;this.s=false;this.key=++Ha;this.k=false};m.handleEvent=function(a){if(this.v)return this.o.call(this.G||this.src,a);return this.o.handleEvent.call(this.o,a)};function Q(a,b){N.call(this);this.w=b;this.e=[];if(a>this.w)throw Error("[goog.structs.SimplePool] Initial cannot be greater than max");for(var c=0;c<a;c++)this.e.push(this.b?this.b():{})}w(Q,N);Q.prototype.b=null;Q.prototype.t=null;function Ia(a){if(a.e.length)return a.e.pop();return a.b?a.b():{}}function R(a,b){a.e.length<a.w?a.e.push(b):Ja(a,b)}function Ja(a,b){if(a.t)a.t(b);else if(u(b))if(t(b.l))b.l();else for(var c in b)delete b[c]}
+Q.prototype.g=function(){Q.r.g.call(this);for(var a=this.e;a.length;)Ja(this,a.pop());delete this.e};var Ka;var La=(Ka="ScriptEngine"in o&&o.ScriptEngine()=="JScript")?o.ScriptEngineMajorVersion()+"."+o.ScriptEngineMinorVersion()+"."+o.ScriptEngineBuildVersion():"0";var S,Ma,T,Na,Oa,Pa,Qa,Ra;
+(function(){function a(){return{f:0,j:0}}function b(){return[]}function c(){function l(q){return g.call(l.src,l.key,q)}return l}function d(){return new Ga}function f(){return new P}var e=Ka&&!(la(La,"5.7")>=0),g;Na=function(l){g=l};if(e){S=function(l){R(j,l)};Ma=function(){return Ia(i)};T=function(l){R(i,l)};Oa=function(){R(h,c())};Pa=function(l){R(n,l)};Qa=function(){return Ia(k)};Ra=function(l){R(k,l)};var j=new Q(0,600);j.b=a;var i=new Q(0,600);i.b=b;var h=new Q(0,600);h.b=c;var n=new Q(0,600);
+n.b=d;var k=new Q(0,600);k.b=f}else{S=p;Ma=b;Pa=Oa=T=p;Qa=f;Ra=p}})();var U={},V={},Sa={},Ta={};function Ua(a,b,c,d){if(!d.m)if(d.z){var f=0;for(var e=0;f<d.length;f++)if(d[f].k){var g=d[f].A;g.src=null;Oa(g);Pa(d[f])}else{if(f!=e)d[e]=d[f];e++}d.length=e;d.z=false;if(e==0){T(d);delete V[a][b][c];V[a][b].f--;if(V[a][b].f==0){S(V[a][b]);delete V[a][b];V[a].f--}if(V[a].f==0){S(V[a]);delete V[a]}}}}function Va(a){if(a in Ta)return Ta[a];return Ta[a]="on"+a}
+function Wa(a,b,c,d,f){var e=1;b=v(b);if(a[b]){a.j--;a=a[b];if(a.m)a.m++;else a.m=1;try{var g=a.length;for(var j=0;j<g;j++){var i=a[j];if(i&&!i.k)e&=Xa(i,f)!==false}}finally{a.m--;Ua(c,d,b,a)}}return Boolean(e)}
+function Xa(a,b){var c=a.handleEvent(b);if(a.s){var d=a.key;if(U[d]){var f=U[d];if(!f.k){var e=f.src,g=f.type,j=f.A,i=f.capture;if(e.removeEventListener){if(e==o||!e.M)e.removeEventListener(g,j,i)}else e.detachEvent&&e.detachEvent(Va(g),j);e=v(e);j=V[g][i][e];if(Sa[e]){var h=Sa[e],n=ma(h,f);if(n>=0){C(h.length!=null);D.splice.call(h,n,1)}h.length==0&&delete Sa[e]}f.k=true;j.z=true;Ua(g,i,e,j);delete U[d]}}}return c}
+function W(a,b){if(!U[a])return true;var c=U[a],d=c.type,f=V;if(!(d in f))return true;f=f[d];var e,g;if(ya===undefined)ya=false;if(ya){var j;if(!(j=b))a:{j="window.event".split(".");var i=o;for(;e=j.shift();)if(i[e]!=null)i=i[e];else{j=null;break a}j=i}e=j;j=true in f;i=false in f;if(j){if(e.keyCode<0||e.returnValue!=undefined)return true;a:{var h=false;if(e.keyCode==0)try{e.keyCode=-1;break a}catch(n){h=true}if(h||e.returnValue==undefined)e.returnValue=true}}h=Qa();h.n(e,this);e=true;try{if(j){var k=
+Ma();for(var l=h.currentTarget;l;l=l.parentNode)k.push(l);g=f[true];g.j=g.f;for(var q=k.length-1;!h.q&&q>=0&&g.j;q--){h.currentTarget=k[q];e&=Wa(g,k[q],d,true,h)}if(i){g=f[false];g.j=g.f;for(q=0;!h.q&&q<k.length&&g.j;q++){h.currentTarget=k[q];e&=Wa(g,k[q],d,false,h)}}}else e=Xa(c,h)}finally{if(k){k.length=0;T(k)}h.l();Ra(h)}return e}d=new P(b,this);try{e=Xa(c,d)}finally{d.l()}return e}Na(W);Da[Da.length]=function(a){W=a(W);Na(W)};function Ya(){}
+function Za(a,b,c){switch(typeof b){case "string":$a(a,b,c);break;case "number":c.push(isFinite(b)&&!isNaN(b)?b:"null");break;case "boolean":c.push(b);break;case "undefined":c.push("null");break;case "object":if(b==null){c.push("null");break}if(r(b)=="array"){var d=b.length;c.push("[");var f="";for(var e=0;e<d;e++){c.push(f);Za(a,b[e],c);f=","}c.push("]");break}c.push("{");d="";for(f in b)if(Object.prototype.hasOwnProperty.call(b,f)){e=b[f];if(typeof e!="function"){c.push(d);$a(a,f,c);c.push(":");
+Za(a,e,c);d=","}}c.push("}");break;case "function":break;default:throw Error("Unknown type: "+typeof b);}}var ab={'"':'\\"',"\\":"\\\\","/":"\\/","\u0008":"\\b","\u000c":"\\f","\n":"\\n","\r":"\\r","\t":"\\t","\u000b":"\\u000b"},bb=/\uffff/.test("\uffff")?/[\\\"\x00-\x1f\x7f-\uffff]/g:/[\\\"\x00-\x1f\x7f-\xff]/g;
+function $a(a,b,c){c.push('"',b.replace(bb,function(d){if(d in ab)return ab[d];var f=d.charCodeAt(0),e="\\u";if(f<16)e+="000";else if(f<256)e+="00";else if(f<4096)e+="0";return ab[d]=e+f.toString(16)}),'"')};function X(a){switch(r(a)){case "string":case "number":case "boolean":return a;case "function":return a.toString();case "array":return na(a,X);case "object":a=a;if("nodeType"in a&&(a.nodeType==1||a.nodeType==9)){var b={};b.ELEMENT=cb(a);return b}if(aa(a))return na(a,X);a=ea(a,function(c,d){return typeof d=="number"||s(d)});return fa(a,X);default:return null}}
+function db(a,b){if(r(a)=="array")return na(a,function(c){return db(c,b)});else if(u(a))return"ELEMENT"in a?eb(a.ELEMENT,b):fa(a,function(c){return db(c,b)});return a}function fb(a){a=a||document;var b=a.$wdc_;if(!b){b=a.$wdc_={};b.p=da()}if(!b.p)b.p=da();return b}function cb(a){var b=fb(a.ownerDocument),c=ga(b,function(d){return d==a});if(!c){c=":wdc:"+b.p++;b[c]=a}return c}
+function eb(a,b){a=decodeURIComponent(a);var c=b||document,d=fb(c);if(!(a in d))throw new y(10,"Element does not exist in cache");var f=d[a];for(var e=f;e;){if(e==c.documentElement)return f;e=e.parentNode}delete d[a];throw new y(10,"Element is no longer attached to the DOM");};function gb(a){var b=Ca;a=[a];var c;try{if(s(b))b=new Function(b);var d=db(a),f=b.apply(null,d);c={status:0,value:X(f)}}catch(e){c={status:"code"in e?e.code:13,value:{message:e.message}}}Za(new Ya,c,[])}var Y="_".split("."),Z=o;!(Y[0]in Z)&&Z.execScript&&Z.execScript("var "+Y[0]);for(var $;Y.length&&($=Y.shift());)if(!Y.length&&gb!==undefined)Z[$]=gb;else Z=Z[$]?Z[$]:Z[$]={};; return this._.apply(null,arguments);}.apply({navigator:typeof window!='undefined'?window.navigator:null}, arguments);}
diff --git a/core/res/res/raw/toggle_android.js b/core/res/res/raw/toggle_android.js
new file mode 100644
index 0000000..d4da5b7
--- /dev/null
+++ b/core/res/res/raw/toggle_android.js
@@ -0,0 +1,30 @@
+function(){return function(){var l=this;
+function m(a){var b=typeof a;if(b=="object")if(a){if(a instanceof Array)return"array";else if(a instanceof Object)return b;var c=Object.prototype.toString.call(a);if(c=="[object Window]")return"object";if(c=="[object Array]"||typeof a.length=="number"&&typeof a.splice!="undefined"&&typeof a.propertyIsEnumerable!="undefined"&&!a.propertyIsEnumerable("splice"))return"array";if(c=="[object Function]"||typeof a.call!="undefined"&&typeof a.propertyIsEnumerable!="undefined"&&!a.propertyIsEnumerable("call"))return"function"}else return"null";else if(b==
+"function"&&typeof a.call=="undefined")return"object";return b}function aa(a){var b=m(a);return b=="array"||b=="object"&&typeof a.length=="number"}function o(a){return typeof a=="string"}function ba(a){a=m(a);return a=="object"||a=="array"||a=="function"}Math.floor(Math.random()*2147483648).toString(36);var ca=Date.now||function(){return+new Date};function p(a,b){function c(){}c.prototype=b.prototype;a.k=b.prototype;a.prototype=new c};var da=window;function ea(){var a=l.Components;if(!a)return false;try{a.l["@mozilla.org/uuid-generator;1"].o(a.i.r);return true}catch(b){return false}};function q(a){this.stack=Error().stack||"";if(a)this.message=String(a)}p(q,Error);q.prototype.name="CustomError";function fa(a,b,c){var d={};for(var f in a)if(b.call(c,a[f],f,a))d[f]=a[f];return d}function ga(a,b,c){var d={};for(var f in a)d[f]=b.call(c,a[f],f,a);return d}function ha(a,b,c){for(var d in a)if(b.call(c,a[d],d,a))return d};function r(a,b){q.call(this,b);this.code=a;this.name=s[a]||s[13]}p(r,q);var s,ia={NoSuchElementError:7,NoSuchFrameError:8,UnknownCommandError:9,StaleElementReferenceError:10,ElementNotVisibleError:11,InvalidElementStateError:12,UnknownError:13,ElementNotSelectableError:15,XPathLookupError:19,NoSuchWindowError:23,InvalidCookieDomainError:24,UnableToSetCookieError:25,ModalDialogOpenedError:26,ModalDialogOpenError:27,ScriptTimeoutError:28},ja={};for(var ka in ia)ja[ia[ka]]=ka;s=ja;
+r.prototype.toString=function(){return"["+this.name+"] "+this.message};function la(a){for(var b=1;b<arguments.length;b++){var c=String(arguments[b]).replace(/\$/g,"$$$$");a=a.replace(/\%s/,c)}return a}var ma={};function na(a){return ma[a]||(ma[a]=String(a).replace(/\-([a-z])/g,function(b,c){return c.toUpperCase()}))};function t(a,b){b.unshift(a);q.call(this,la.apply(null,b));b.shift();this.p=a}p(t,q);t.prototype.name="AssertionError";function v(a,b){if(!a){var c=Array.prototype.slice.call(arguments,2),d="Assertion failed";if(b){d+=": "+b;var f=c}throw new t(""+d,f||[]);}return a};var w=Array.prototype,x=w.indexOf?function(a,b,c){v(a.length!=null);return w.indexOf.call(a,b,c)}:function(a,b,c){c=c==null?0:c<0?Math.max(0,a.length+c):c;if(o(a)){if(!o(b)||b.length!=1)return-1;return a.indexOf(b,c)}for(c=c;c<a.length;c++)if(c in a&&a[c]===b)return c;return-1},y=w.map?function(a,b,c){v(a.length!=null);return w.map.call(a,b,c)}:function(a,b,c){var d=a.length,f=Array(d),e=o(a)?a.split(""):a;for(var g=0;g<d;g++)if(g in e)f[g]=b.call(c,e[g],g,a);return f},oa=w.some?function(a,b,c){v(a.length!=
+null);return w.some.call(a,b,c)}:function(a,b,c){var d=a.length,f=o(a)?a.split(""):a;for(var e=0;e<d;e++)if(e in f&&b.call(c,f[e],e,a))return true;return false};var z=true,pa="",A;if(z)A=/WebKit\/(\S+)/;if(A){var qa=A.exec(l.navigator?l.navigator.userAgent:null);pa=qa?qa[1]:""};var B;function C(a,b){this.x=a!==undefined?a:0;this.y=b!==undefined?b:0}C.prototype.toString=function(){return"("+this.x+", "+this.y+")"};function D(a,b){this.width=a;this.height=b}D.prototype.toString=function(){return"("+this.width+" x "+this.height+")"};D.prototype.floor=function(){this.width=Math.floor(this.width);this.height=Math.floor(this.height);return this};function E(a){return a.nodeType==9?a:a.ownerDocument||a.document}function ra(a,b){var c=[];return sa(a,b,c,true)?c[0]:undefined}function sa(a,b,c,d){if(a!=null){var f=0;for(var e;e=a.childNodes[f];f++){if(b(e)){c.push(e);if(d)return true}if(sa(e,b,c,d))return true}}return false}function F(a,b,c,d){if(!c)a=a.parentNode;c=d==null;for(var f=0;a&&(c||f<=d);){if(b(a))return a;a=a.parentNode;f++}return null}function G(a){this.e=a||l.document||document}
+function ta(a){a=!z&&a.e.compatMode=="CSS1Compat"?a.e.documentElement:a.e.body;return new C(a.scrollLeft,a.scrollTop)};function ua(a,b){var c=function(d,f){var e=E(d);if(d.selectSingleNode){e.setProperty&&e.setProperty("SelectionLanguage","XPath");return d.selectSingleNode(f)}else if(e.implementation.hasFeature("XPath","3.0")){var g=e.createNSResolver(e.documentElement),h;if(typeof XPathResult!="undefined")h=XPathResult.FIRST_ORDERED_NODE_TYPE;else{if(!ea())throw Error("Document claims it supports XPath yet XPathResult is not defined. Please report this to Selenium developers");h=Components.i.q.FIRST_ORDERED_NODE_TYPE}return e.evaluate(f,
+d,g,h,null).singleNodeValue}return null}(b,a);if(!c)return null;if(c.nodeType!=1)throw Error("Returned node is not an element: "+a);return c};var H="StopIteration"in l?l.StopIteration:Error("StopIteration");function va(){}va.prototype.next=function(){throw H;};function I(a,b,c,d,f){this.a=!!b;a&&J(this,a,d);this.d=f!=undefined?f:this.c||0;if(this.a)this.d*=-1;this.h=!c}p(I,va);I.prototype.b=null;I.prototype.c=0;I.prototype.g=false;function J(a,b,c,d){if(a.b=b)a.c=typeof c=="number"?c:a.b.nodeType!=1?0:a.a?-1:1;if(typeof d=="number")a.d=d}
+I.prototype.next=function(){var a;if(this.g){if(!this.b||this.h&&this.d==0)throw H;a=this.b;var b=this.a?-1:1;if(this.c==b){var c=this.a?a.lastChild:a.firstChild;c?J(this,c):J(this,a,b*-1)}else(c=this.a?a.previousSibling:a.nextSibling)?J(this,c):J(this,a.parentNode,b*-1);this.d+=this.c*(this.a?-1:1)}else this.g=true;a=this.b;if(!this.b)throw H;return a};
+I.prototype.splice=function(){var a=this.b,b=this.a?1:-1;if(this.c==b){this.c=b*-1;this.d+=this.c*(this.a?-1:1)}this.a=!this.a;I.prototype.next.call(this);this.a=!this.a;b=aa(arguments[0])?arguments[0]:arguments;for(var c=b.length-1;c>=0;c--)a.parentNode&&a.parentNode.insertBefore(b[c],a.nextSibling);a&&a.parentNode&&a.parentNode.removeChild(a)};function K(a,b,c,d){I.call(this,a,b,c,null,d)}p(K,I);K.prototype.next=function(){do K.k.next.call(this);while(this.c==-1);return this.b};function wa(a,b){var c=E(a);if(c.defaultView&&c.defaultView.getComputedStyle)if(c=c.defaultView.getComputedStyle(a,null))return c[b]||c.getPropertyValue(b);return""}function L(a,b){return wa(a,b)||(a.currentStyle?a.currentStyle[b]:null)||a.style[b]}
+function xa(a){var b=E(a),c=L(a,"position"),d=c=="fixed"||c=="absolute";for(a=a.parentNode;a&&a!=b;a=a.parentNode){c=L(a,"position");d=d&&c=="static"&&a!=b.documentElement&&a!=b.body;if(!d&&(a.scrollWidth>a.clientWidth||a.scrollHeight>a.clientHeight||c=="fixed"||c=="absolute"))return a}return null};function M(a,b){return!!a&&a.nodeType==1&&(!b||a.tagName.toUpperCase()==b)}
+var ya={"class":"className",readonly:"readOnly"},za=["checked","disabled","draggable","hidden"],Aa=["async","autofocus","autoplay","checked","compact","complete","controls","declare","defaultchecked","defaultselected","defer","disabled","draggable","ended","formnovalidate","hidden","indeterminate","iscontenteditable","ismap","itemscope","loop","multiple","muted","nohref","noresize","noshade","novalidate","nowrap","open","paused","pubdate","readonly","required","reversed","scoped","seamless","seeking",
+"selected","spellcheck","truespeed","willvalidate"];function Ba(a,b){if(8==a.nodeType)return null;b=b.toLowerCase();if(b=="style"){var c=a.style.cssText.replace(/^[\s\xa0]+|[\s\xa0]+$/g,"").toLowerCase();return c.charAt(c.length-1)==";"?c:c+";"}c=a.getAttributeNode(b);if(!c)return null;if(x(Aa,b)>=0)return"true";return c.specified?c.value:null}var Ca=["BUTTON","INPUT","OPTGROUP","OPTION","SELECT","TEXTAREA"];
+function Da(a){var b=a.tagName.toUpperCase();if(!(x(Ca,b)>=0))return true;if(Ba(a,"disabled"))return false;if(a.parentNode&&a.parentNode.nodeType==1&&"OPTGROUP"==b||"OPTION"==b)return Da(a.parentNode);return true}function N(a){for(a=a.parentNode;a&&a.nodeType!=1&&a.nodeType!=9&&a.nodeType!=11;)a=a.parentNode;return M(a)?a:null}function O(a,b){b=na(String(b));return wa(a,b)||Ea(a,b)}
+function Ea(a,b){var c=(a.currentStyle||a.style)[b];if(c!="inherit")return c!==undefined?c:null;return(c=N(a))?Ea(c,b):null}function Fa(a){if(m(a.getBBox)=="function")return a.getBBox();var b;if(L(a,"display")!="none")b=new D(a.offsetWidth,a.offsetHeight);else{b=a.style;var c=b.display,d=b.visibility,f=b.position;b.visibility="hidden";b.position="absolute";b.display="inline";var e;e=a.offsetWidth;a=a.offsetHeight;b.display=c;b.position=f;b.visibility=d;b=new D(e,a)}return b}
+function P(a,b){function c(e){if(O(e,"display")=="none")return false;e=N(e);return!e||c(e)}function d(e){var g=Fa(e);if(g.height>0&&g.width>0)return true;if(e.innerText||e.textContent)if(Ga.test(e.innerText||e.textContent))return true;return z&&oa(e.childNodes,function(h){return M(h)&&d(h)})}if(!M(a))throw Error("Argument to isShown must be of type Element");if(M(a,"TITLE"))return(E(a)?E(a).parentWindow||E(a).defaultView:window)==da;if(M(a,"OPTION")||M(a,"OPTGROUP")){var f=F(a,function(e){return M(e,
+"SELECT")});return!!f&&P(f)}if(M(a,"MAP")){if(!a.name)return false;f=E(a);f=f.evaluate?ua('/descendant::*[@usemap = "#'+a.name+'"]',f):ra(f,function(e){return M(e)&&Ba(e,"usemap")=="#"+a.name});return!!f&&P(f)}if(M(a,"AREA")){f=F(a,function(e){return M(e,"MAP")});return!!f&&P(f)}if(M(a,"INPUT")&&a.type.toLowerCase()=="hidden")return false;if(O(a,"visibility")=="hidden")return false;if(!c(a))return false;if(!b&&Ha(a)==0)return false;if(!d(a))return false;return true}
+var Ia="[\\s\\xa0"+String.fromCharCode(160)+"]+",Ga=RegExp("^"+Ia+"$");function Ha(a){var b=1,c=O(a,"opacity");if(c)b=Number(c);if(a=N(a))b*=Ha(a);return b};var Ja=["dragstart","dragexit","mouseover","mouseout"];
+function Q(a,b,c){var d=E(a),f=d?d.parentWindow||d.defaultView:window,e=new C;if(a.nodeType==1)if(a.getBoundingClientRect){var g=a.getBoundingClientRect();e.x=g.left;e.y=g.top}else{g=ta(a?new G(E(a)):B||(B=new G));var h,i=E(a);h=L(a,"position");var j=new C(0,0),u=(i?i.nodeType==9?i:E(i):document).documentElement;if(a!=u)if(a.getBoundingClientRect){h=a.getBoundingClientRect();i=ta(i?new G(E(i)):B||(B=new G));j.x=h.left+i.x;j.y=h.top+i.y}else if(i.getBoxObjectFor){h=i.getBoxObjectFor(a);i=i.getBoxObjectFor(u);
+j.x=h.screenX-i.screenX;j.y=h.screenY-i.screenY}else{var k=a;do{j.x+=k.offsetLeft;j.y+=k.offsetTop;if(k!=a){j.x+=k.clientLeft||0;j.y+=k.clientTop||0}if(z&&L(k,"position")=="fixed"){j.x+=i.body.scrollLeft;j.y+=i.body.scrollTop;break}k=k.offsetParent}while(k&&k!=a);if(z&&h=="absolute")j.y-=i.body.offsetTop;for(k=a;(k=xa(k))&&k!=i.body&&k!=u;){j.x-=k.scrollLeft;j.y-=k.scrollTop}}e.x=j.x-g.x;e.y=j.y-g.y}else{g=m(a.f)=="function";j=a;if(a.targetTouches)j=a.targetTouches[0];else if(g&&a.f().targetTouches)j=
+a.f().targetTouches[0];e.x=j.clientX;e.y=j.clientY}var n=c||{};c=(n.x||0)+e.x;e=(n.y||0)+e.y;g=n.button||0;j=n.bubble||true;h=null;if(x(Ja,b)>=0)h=n.related||null;i=!!n.alt;u=!!n.control;k=!!n.shift;n=!!n.meta;if(a.fireEvent&&d&&d.createEventObject){a=d.createEventObject();a.altKey=i;a.m=u;a.metaKey=n;a.shiftKey=k;a.clientX=c;a.clientY=e;a.button=g;a.relatedTarget=h}else{a=d.createEvent("MouseEvents");if(a.initMouseEvent)a.initMouseEvent(b,j,true,f,1,0,0,c,e,u,i,k,n,g,h);else{a.initEvent(b,j,true);
+a.shiftKey=k;a.metaKey=n;a.altKey=i;a.ctrlKey=u;a.button=g}}return a}function R(a,b,c){var d=c||{};c=d.keyCode||0;var f=d.charCode||0,e=!!d.alt,g=!!d.ctrl,h=!!d.shift;d=!!d.meta;a=E(a).createEvent("Events");a.initEvent(b,true,true);a.charCode=f;a.keyCode=c;a.altKey=e;a.ctrlKey=g;a.metaKey=d;a.shiftKey=h;return a}
+function Ka(a,b,c){var d=E(a),f=c||{};c=f.bubble!==false;var e=!!f.alt,g=!!f.control,h=!!f.shift;f=!!f.meta;if(a.fireEvent&&d&&d.createEventObject){a=d.createEventObject();a.altKey=e;a.n=g;a.metaKey=f;a.shiftKey=h}else{a=d.createEvent("HTMLEvents");a.initEvent(b,c,true);a.shiftKey=h;a.metaKey=f;a.altKey=e;a.ctrlKey=g}return a}var S={};S.click=Q;S.keydown=R;S.keypress=R;S.keyup=R;S.mousedown=Q;S.mousemove=Q;S.mouseout=Q;S.mouseover=Q;S.mouseup=Q;
+function La(a,b,c){c=(S[b]||Ka)(a,b,c);if(m(a.fireEvent)=="function"||ba(a.fireEvent)){try{(E(a)?E(a).parentWindow||E(a).defaultView:window).event=c}catch(d){}a=a.fireEvent("on"+b,c)}else a=a.dispatchEvent(c);return a};function T(a){var b;if(M(a,"OPTION"))b=true;else if(M(a,"INPUT")){b=a.type.toLowerCase();b=b=="checkbox"||b=="radio"}else b=false;if(!b)throw new r(15,"Element is not selectable");b="selected";var c=a.type&&a.type.toLowerCase();if("checkbox"==c||"radio"==c)b="checked";b=ya[b]||b;a=a[b];a=a===undefined&&x(za,b)>=0?false:a;return!!a}function Ma(a){return M(a,"SELECT")}
+function Na(a){if(M(a,"INPUT")&&"radio"==a.type)throw new r(12,"You may not toggle a radio button");var b=!T(a);if(!Da(a))throw new r(12,"Element is not currently enabled and may not be manipulated");if(!P(a,true))throw new r(11,"Element is not currently visible and may not be manipulated");if(M(a,"INPUT")){var c=a.type.toLowerCase();if(c=="checkbox"||c=="radio"){if(a.checked!=b){if(a.type=="radio"&&!b)throw new r(12,"You may not deselect a radio button");if(b!=T(a)){a.checked=b;La(a,"change")}}}else throw new r(15,
+"You may not select an unselectable input element: "+a.type);}else if(M(a,"OPTION")){c=F(a,Ma);if(!c.multiple&&!b)throw new r(15,"You may not deselect an option within a select that does not support multiple selections.");if(b!=T(a)){a.selected=b;La(c,"change")}}else throw new r(15,"You may not select an unselectable element: "+a.tagName);return T(a)};function Oa(){}
+function U(a,b,c){switch(typeof b){case "string":Pa(a,b,c);break;case "number":c.push(isFinite(b)&&!isNaN(b)?b:"null");break;case "boolean":c.push(b);break;case "undefined":c.push("null");break;case "object":if(b==null){c.push("null");break}if(m(b)=="array"){var d=b.length;c.push("[");var f="";for(var e=0;e<d;e++){c.push(f);U(a,b[e],c);f=","}c.push("]");break}c.push("{");d="";for(f in b)if(Object.prototype.hasOwnProperty.call(b,f)){e=b[f];if(typeof e!="function"){c.push(d);Pa(a,f,c);c.push(":");U(a,
+e,c);d=","}}c.push("}");break;case "function":break;default:throw Error("Unknown type: "+typeof b);}}var V={'"':'\\"',"\\":"\\\\","/":"\\/","\u0008":"\\b","\u000c":"\\f","\n":"\\n","\r":"\\r","\t":"\\t","\u000b":"\\u000b"},Qa=/\uffff/.test("\uffff")?/[\\\"\x00-\x1f\x7f-\uffff]/g:/[\\\"\x00-\x1f\x7f-\xff]/g;
+function Pa(a,b,c){c.push('"',b.replace(Qa,function(d){if(d in V)return V[d];var f=d.charCodeAt(0),e="\\u";if(f<16)e+="000";else if(f<256)e+="00";else if(f<4096)e+="0";return V[d]=e+f.toString(16)}),'"')};function W(a){switch(m(a)){case "string":case "number":case "boolean":return a;case "function":return a.toString();case "array":return y(a,W);case "object":a=a;if("nodeType"in a&&(a.nodeType==1||a.nodeType==9)){var b={};b.ELEMENT=Ra(a);return b}if(aa(a))return y(a,W);a=fa(a,function(c,d){return typeof d=="number"||o(d)});return ga(a,W);default:return null}}
+function X(a,b){if(m(a)=="array")return y(a,function(c){return X(c,b)});else if(ba(a))return"ELEMENT"in a?Sa(a.ELEMENT,b):ga(a,function(c){return X(c,b)});return a}function Ta(a){a=a||document;var b=a.$wdc_;if(!b){b=a.$wdc_={};b.j=ca()}return b}function Ra(a){var b=Ta(a.ownerDocument),c=ha(b,function(d){return d==a});if(!c){c=":wdc:"+b.j++;b[c]=a}return c}
+function Sa(a,b){a=decodeURIComponent(a);var c=b||document,d=Ta(c);if(!(a in d))throw new r(10,"Element does not exist in cache");var f=d[a];for(var e=f;e;){if(e==c.documentElement)return f;e=e.parentNode}delete d[a];throw new r(10,"Element is no longer attached to the DOM");};function Ua(a){var b=Na;a=[a];var c;try{if(o(b))b=new Function(b);var d=X(a),f=b.apply(null,d);c={status:0,value:W(f)}}catch(e){c={status:"code"in e?e.code:13,value:{message:e.message}}}b=[];U(new Oa,c,b);return b.join("")}var Y="_".split("."),Z=l;!(Y[0]in Z)&&Z.execScript&&Z.execScript("var "+Y[0]);for(var $;Y.length&&($=Y.shift());)if(!Y.length&&Ua!==undefined)Z[$]=Ua;else Z=Z[$]?Z[$]:Z[$]={};; return this._.apply(null,arguments);}.apply({navigator:typeof window!='undefined'?window.navigator:null}, arguments);}
diff --git a/core/res/res/raw/webdriver_readme.txt b/core/res/res/raw/webdriver_readme.txt
new file mode 100644
index 0000000..5c4667d
--- /dev/null
+++ b/core/res/res/raw/webdriver_readme.txt
@@ -0,0 +1,53 @@
+The JavaScript files *_android.js are used in frameworks/base/core/java/android/webkit/webdriver/
+. Those files contain closure compiled JavaScript from
+http://selenium.googlecode.com. They are under the Apache 2.0 licence:
+/** @license
+Copyright 2010 WebDriver committers
+Copyright 2010 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.
+*/
+
+The licence is not included in the compiled code to minimize the size
+of JavaScript injected into web pages.
+
+Those files can be generated by doing the following:
+$svn checkout http://selenium.googlecode.com/svn/trunk/ .
+$./go //javascript/webdriver-atoms/inject:<js_fragment_name>:android
+
+Where <js_fragment_name> should be replaced by the actual name of the fragment to
+generate. For example to generate is_selected_android.js, execute:
+$./go //javascript/webdriver-atoms/inject:is_selected:android
+
+The build file for those rules is under the following:
+http://code.google.com/p/selenium/source/browse/trunk/javascript/webdriver-atoms/inject/build.desc
+Every js_fragment rule generates a JavaScript file containing the corresponding
+JavaScript code snippet.
+
+The current version of the files was generated using revision 11823.
+
+Here is the build command list executed to generate those files:
+./go //javascript/webdriver-atoms/inject:find_element:android
+./go //javascript/webdriver-atoms/inject:find_elements:android
+./go //javascript/webdriver-atoms/inject:get_text:android
+./go //javascript/webdriver-atoms/inject:is_selected:android
+./go //javascript/webdriver-atoms/inject:get_top_left_coordinates:android
+./go //javascript/webdriver-atoms/inject:get_attribute_value:android
+./go //javascript/webdriver-atoms/inject:get_size:android
+./go //javascript/webdriver-atoms/inject:get_value_of_css_property:android
+./go //javascript/webdriver-atoms/inject:is_enabled:android
+./go //javascript/webdriver-atoms/inject:toggle:android
+./go //javascript/webdriver-atoms/inject:set_selected:android
+./go //javascript/webdriver-atoms/inject:is_displayed:android
+./go //javascript/webdriver-atoms:execute_script:android
+./go //javascript/webdriver-atoms/inject:submit:android
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 8fa82e2..6f70942 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -204,6 +204,8 @@
<string name="permdesc_getTasks" msgid="7048711358713443341">"للسماح للتطبيق باسترداد معلومات حول المهام المُشغلة الحالية والحديثة. قد يسمح ذلك للتطبيقات الضارة باكتشاف معلومات خاصة حول التطبيقات الأخرى."</string>
<string name="permlab_reorderTasks" msgid="5669588525059921549">"إعادة ترتيب التطبيقات التي قيد التشغيل"</string>
<string name="permdesc_reorderTasks" msgid="126252774270522835">"للسماح لتطبيق ما بنقل المهام إلى المقدمة والخلفية. قد تفرض التطبيقات الضارة نفسها إلى المقدمة بدون تحكم منك."</string>
+ <string name="permlab_removeTasks" msgid="4802740047161700683">"إيقاف التطبيقات التي قيد التشغيل"</string>
+ <string name="permdesc_removeTasks" msgid="2000332928514575461">"للسماح لأحد التطبيقات بإزالة المهام وإيقاف التطبيقات المتعلقة بها. يمكن للتطبيقات الضارة تعطيل سلوك التطبيقات الأخرى."</string>
<string name="permlab_setDebugApp" msgid="4339730312925176742">"تمكين تصحيح أخطاء التطبيق"</string>
<string name="permdesc_setDebugApp" msgid="5584310661711990702">"للسماح لتطبيق ما بتشغيل تصحيح الأخطاء لتطبيق آخر. قد تستخدم التطبيقات الضارة ذلك لإنهاء التطبيقات الأخرى."</string>
<string name="permlab_changeConfiguration" msgid="8214475779521218295">"تغيير إعدادات واجهة المستخدم"</string>
@@ -238,6 +240,10 @@
<string name="permdesc_batteryStats" msgid="5847319823772230560">"للسماح بتعديل إحصاءات البطارية المجمّعة. ليس للاستخدام بواسطة التطبيقات العادية."</string>
<string name="permlab_backup" msgid="470013022865453920">"التحكم في النسخة الاحتياطية للنظام واستعادتها"</string>
<string name="permdesc_backup" msgid="4837493065154256525">"للسماح للتطبيق بالتحكم في النسخة الاحتياطية للنظام وآلية الاستعادة. ليس للاستخدام بواسطة التطبيقات العادية."</string>
+ <!-- no translation found for permlab_confirm_full_backup (5557071325804469102) -->
+ <skip />
+ <!-- no translation found for permdesc_confirm_full_backup (9005017754175897954) -->
+ <skip />
<string name="permlab_internalSystemWindow" msgid="2148563628140193231">"عرض النوافذ غير المصرح بها"</string>
<string name="permdesc_internalSystemWindow" msgid="5895082268284998469">"للسماح بإنشاء نوافذ بقصد استخدامها بواسطة واجهة مستخدم النظام الداخلي. ليس للاستخدام بواسطة التطبيقات العادية."</string>
<string name="permlab_systemAlertWindow" msgid="3372321942941168324">"عرض تنبيهات مستوى النظام"</string>
@@ -444,6 +450,10 @@
<string name="permdesc_bluetooth" product="default" msgid="762515380679392945">"للسماح لتطبيق ما بعرض تهيئة هاتف البلوتوث المحلي، وإجراء اتصالات وقبولها باستخدام الأجهزة المقترنة."</string>
<string name="permlab_nfc" msgid="4423351274757876953">"التحكم في اتصال الحقل القريب"</string>
<string name="permdesc_nfc" msgid="9171401851954407226">"للسماح لتطبيق ما بالاتصال بعلامات اتصال حقل قريب (NFC)، والبطاقات وبرامج القراءة."</string>
+ <!-- no translation found for permlab_vpn (8345800584532175312) -->
+ <skip />
+ <!-- no translation found for permdesc_vpn (5617893078989944219) -->
+ <skip />
<string name="permlab_disableKeyguard" msgid="4977406164311535092">"تعطيل تأمين المفاتيح"</string>
<string name="permdesc_disableKeyguard" msgid="3189763479326302017">"للسماح لتطبيق ما بتعطيل تأمين المفاتيح وأي أمان كلمة مرور مرتبطة. ومثال صحيح لذلك هو تعطيل الهاتف لتأمين المفاتيح عند استلام مكالمة هاتفية واردة، ثم إعادة تمكين تأمين المفاتيح عند انتهاء المكالمة."</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"قراءة إعدادات المزامنة"</string>
@@ -947,8 +957,6 @@
<string name="ime_action_default" msgid="2840921885558045721">"تنفيذ"</string>
<string name="dial_number_using" msgid="5789176425167573586">"طلب رقم"\n"باستخدام <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="create_contact_using" msgid="4947405226788104538">"إنشاء جهة اتصال"\n"باستخدام <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="accessibility_compound_button_selected" msgid="5612776946036285686">"محدد"</string>
- <string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"لم يتم التحديد"</string>
<string name="grant_credentials_permission_message_header" msgid="6824538733852821001">"يطلب التطبيق أو التطبيقات التالية الإذن للدخول إلى حسابك، الآن وفي المستقبل."</string>
<string name="grant_credentials_permission_message_footer" msgid="3125211343379376561">"هل تريد السماح بذلك الطلب؟"</string>
<string name="grant_permissions_header_text" msgid="2722567482180797717">"طلب الدخول"</string>
@@ -1019,6 +1027,9 @@
<string name="choose_account_label" msgid="4191313562041125787">"حدد حسابًا."</string>
<string name="number_picker_increment_button" msgid="4830170763103463443">"زيادة"</string>
<string name="number_picker_decrement_button" msgid="2576606679160067262">"تناقص"</string>
+ <string name="action_bar_home_description" msgid="5293600496601490216">"التنقل إلى الشاشة الرئيسية"</string>
+ <string name="action_bar_up_description" msgid="2237496562952152589">"التنقل إلى أعلى"</string>
+ <string name="action_menu_overflow_description" msgid="2295659037509008453">"المزيد من الخيارات"</string>
<!-- no translation found for storage_internal (7556050805474115618) -->
<skip />
<!-- no translation found for storage_sd_card (8921771478629812343) -->
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 3240a04..b01b6bc 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -204,6 +204,8 @@
<string name="permdesc_getTasks" msgid="7048711358713443341">"Разрешава на приложението да извлича информация за задачите, изпълнявани понастоящем и неотдавна. Може да позволи на злонамерените приложения да открият поверителна информация за други приложения."</string>
<string name="permlab_reorderTasks" msgid="5669588525059921549">"пренареждане на изпълняваните приложения"</string>
<string name="permdesc_reorderTasks" msgid="126252774270522835">"Разрешава на приложението да прехвърля задачи на преден и на заден план. Злонамерените приложения могат сами да се изведат на преден план без ваша намеса."</string>
+ <string name="permlab_removeTasks" msgid="4802740047161700683">"спиране на изпълняваните приложения"</string>
+ <string name="permdesc_removeTasks" msgid="2000332928514575461">"Разрешава на приложението да премахва задачи и да прекратява приложенията им. Злонамерените приложения могат да нарушат поведението на други приложения."</string>
<string name="permlab_setDebugApp" msgid="4339730312925176742">"активиране на отстраняването на грешки в приложения"</string>
<string name="permdesc_setDebugApp" msgid="5584310661711990702">"Разрешава на приложението да включва отстраняването на грешки в друго приложение. Злонамерените приложения могат да използват това, за да прекратят други приложения."</string>
<string name="permlab_changeConfiguration" msgid="8214475779521218295">"промяна на настройките ви за потребителския интерфейс"</string>
@@ -238,6 +240,10 @@
<string name="permdesc_batteryStats" msgid="5847319823772230560">"Разрешава промяна на събраните статистически данни за батерията. Не е предназначено за нормални приложения."</string>
<string name="permlab_backup" msgid="470013022865453920">"контролиране на създаването и възстановяването на резервни копия на системата"</string>
<string name="permdesc_backup" msgid="4837493065154256525">"Разрешава на приложението да контролира системния механизъм за създаване и възстановяване на резервни копия. Не е предназначено за нормални приложения."</string>
+ <!-- no translation found for permlab_confirm_full_backup (5557071325804469102) -->
+ <skip />
+ <!-- no translation found for permdesc_confirm_full_backup (9005017754175897954) -->
+ <skip />
<string name="permlab_internalSystemWindow" msgid="2148563628140193231">"показване на неупълномощени прозорци"</string>
<string name="permdesc_internalSystemWindow" msgid="5895082268284998469">"Разрешава създаването на прозорци, предназначени за употреба от вътрешния системен потребителски интерфейс. Не е предназначено за нормални приложения."</string>
<string name="permlab_systemAlertWindow" msgid="3372321942941168324">"показване на сигнали на ниво система"</string>
@@ -444,6 +450,10 @@
<string name="permdesc_bluetooth" product="default" msgid="762515380679392945">"Разрешава на приложението да вижда конфигурацията на локалния Bluetooth телефон и да изгражда и приема връзки със сдвоени устройства."</string>
<string name="permlab_nfc" msgid="4423351274757876953">"контролиране на комуникацията в близкото поле"</string>
<string name="permdesc_nfc" msgid="9171401851954407226">"Разрешава на приложението да комуникира с маркери, карти и четци, ползващи комуникация в близкото поле (NFC)."</string>
+ <!-- no translation found for permlab_vpn (8345800584532175312) -->
+ <skip />
+ <!-- no translation found for permdesc_vpn (5617893078989944219) -->
+ <skip />
<string name="permlab_disableKeyguard" msgid="4977406164311535092">"деактивиране на заключването на клавиатурата"</string>
<string name="permdesc_disableKeyguard" msgid="3189763479326302017">"Разрешава на приложението да деактивира заключването на клавиатурата и свързаната защита с парола. Това е допустимо, когато например телефонът деактивира заключването при получаване на входящо обаждане и после го активира отново, когато обаждането завърши."</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"четене на настройките за синхронизиране"</string>
@@ -947,8 +957,6 @@
<string name="ime_action_default" msgid="2840921885558045721">"Изпълнение"</string>
<string name="dial_number_using" msgid="5789176425167573586">"Набиране"\n"с използване на <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="create_contact_using" msgid="4947405226788104538">"Създаване на контакт"\n"с използване на <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="accessibility_compound_button_selected" msgid="5612776946036285686">"отметнато"</string>
- <string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"няма отметка"</string>
<string name="grant_credentials_permission_message_header" msgid="6824538733852821001">"Следните едно или повече приложения поискаха разрешение за достъп до профила ви сега и в бъдеще."</string>
<string name="grant_credentials_permission_message_footer" msgid="3125211343379376561">"Разрешавате ли тази заявка?"</string>
<string name="grant_permissions_header_text" msgid="2722567482180797717">"Заявка за достъп"</string>
@@ -1019,6 +1027,9 @@
<string name="choose_account_label" msgid="4191313562041125787">"Избор на профил"</string>
<string name="number_picker_increment_button" msgid="4830170763103463443">"Увеличаване"</string>
<string name="number_picker_decrement_button" msgid="2576606679160067262">"Намаляване"</string>
+ <string name="action_bar_home_description" msgid="5293600496601490216">"Придвижване към „Начало“"</string>
+ <string name="action_bar_up_description" msgid="2237496562952152589">"Придвижване нагоре"</string>
+ <string name="action_menu_overflow_description" msgid="2295659037509008453">"Още опции"</string>
<!-- no translation found for storage_internal (7556050805474115618) -->
<skip />
<!-- no translation found for storage_sd_card (8921771478629812343) -->
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 65068af..eb48b67 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -204,6 +204,8 @@
<string name="permdesc_getTasks" msgid="7048711358713443341">"Permet a l\'aplicació recuperar informació sobre les tasques que s\'executen actualment i que s\'han executat recentment. Pot permetre a les aplicacions malicioses descobrir informació privada sobre altres aplicacions."</string>
<string name="permlab_reorderTasks" msgid="5669588525059921549">"canviar l\'ordre de les aplicacions en execució"</string>
<string name="permdesc_reorderTasks" msgid="126252774270522835">"Permet a una aplicació desplaçar tasques al primer i al segon terme. Les aplicacions malicioses poden aparèixer en primer terme sense que ho controleu."</string>
+ <string name="permlab_removeTasks" msgid="4802740047161700683">"atura les aplicacions que s\'estan executant"</string>
+ <string name="permdesc_removeTasks" msgid="2000332928514575461">"Permet que una aplicació elimini tasques i liquidi les seves aplicacions. Les aplicacions malicioses poden alterar el comportament d\'altres aplicacions."</string>
<string name="permlab_setDebugApp" msgid="4339730312925176742">"activar la depuració d\'aplicacions"</string>
<string name="permdesc_setDebugApp" msgid="5584310661711990702">"Permet a una aplicació activar la depuració per a una altra aplicació. Les aplicacions malicioses poden utilitzar-ho per destruir altres aplicacions."</string>
<string name="permlab_changeConfiguration" msgid="8214475779521218295">"canviar la configuració de la IU"</string>
@@ -238,6 +240,10 @@
<string name="permdesc_batteryStats" msgid="5847319823772230560">"Permet la modificació de les estadístiques de la bateria que es recopilen. No indicat per a les aplicacions normals."</string>
<string name="permlab_backup" msgid="470013022865453920">"controlar la còpia de seguretat i restauració del sistema"</string>
<string name="permdesc_backup" msgid="4837493065154256525">"Permet a l\'aplicació controlar el mecanisme de còpia de seguretat i restauració del sistema. No indicat per a les aplicacions normals."</string>
+ <!-- no translation found for permlab_confirm_full_backup (5557071325804469102) -->
+ <skip />
+ <!-- no translation found for permdesc_confirm_full_backup (9005017754175897954) -->
+ <skip />
<string name="permlab_internalSystemWindow" msgid="2148563628140193231">"visualitzar finestres no autoritzades"</string>
<string name="permdesc_internalSystemWindow" msgid="5895082268284998469">"Permet la creació de finestres que utilitzarà la interfície d\'usuari del sistema interna. No indicat per a les aplicacions normals."</string>
<string name="permlab_systemAlertWindow" msgid="3372321942941168324">"visualitzar les alertes del sistema"</string>
@@ -444,6 +450,10 @@
<string name="permdesc_bluetooth" product="default" msgid="762515380679392945">"Permet a una aplicació visualitzar la configuració del telèfon Bluetooth local i establir i acceptar connexions amb els dispositius emparellats."</string>
<string name="permlab_nfc" msgid="4423351274757876953">"controla Near Field Communication (NFC)"</string>
<string name="permdesc_nfc" msgid="9171401851954407226">"Permet que una aplicació es comuniqui amb les etiquetes, les targetes i els lectors de Near Field Communication (NFC)"</string>
+ <!-- no translation found for permlab_vpn (8345800584532175312) -->
+ <skip />
+ <!-- no translation found for permdesc_vpn (5617893078989944219) -->
+ <skip />
<string name="permlab_disableKeyguard" msgid="4977406164311535092">"desactivar el bloqueig del teclat"</string>
<string name="permdesc_disableKeyguard" msgid="3189763479326302017">"Permet a una aplicació desactivar el bloqueig del teclat i qualsevol element de seguretat de contrasenyes associat. Un exemple d\'això és la desactivació per part del telèfon del bloqueig del teclat en rebre una trucada telefònica entrant i després la reactivació del bloqueig del teclat quan finalitza la trucada."</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"llegir la configuració de sincronització"</string>
@@ -947,8 +957,6 @@
<string name="ime_action_default" msgid="2840921885558045721">"Executa"</string>
<string name="dial_number_using" msgid="5789176425167573586">"Marca el número"\n"mitjançant <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="create_contact_using" msgid="4947405226788104538">"Crea un contacte"\n"mitjançant <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="accessibility_compound_button_selected" msgid="5612776946036285686">"marcat"</string>
- <string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"no marcat"</string>
<string name="grant_credentials_permission_message_header" msgid="6824538733852821001">"Les aplicacions següents sol·liciten permís per accedir al vostre compte, ara i en el futur."</string>
<string name="grant_credentials_permission_message_footer" msgid="3125211343379376561">"Voleu permetre aquesta sol·licitud?"</string>
<string name="grant_permissions_header_text" msgid="2722567482180797717">"Sol·licitud d\'accés"</string>
@@ -1019,6 +1027,9 @@
<string name="choose_account_label" msgid="4191313562041125787">"Selecciona un compte"</string>
<string name="number_picker_increment_button" msgid="4830170763103463443">"Incrementa"</string>
<string name="number_picker_decrement_button" msgid="2576606679160067262">"Disminueix"</string>
+ <string name="action_bar_home_description" msgid="5293600496601490216">"Torna a la pàgina d\'inici"</string>
+ <string name="action_bar_up_description" msgid="2237496562952152589">"Mou cap a dalt"</string>
+ <string name="action_menu_overflow_description" msgid="2295659037509008453">"Més opcions"</string>
<!-- no translation found for storage_internal (7556050805474115618) -->
<skip />
<!-- no translation found for storage_sd_card (8921771478629812343) -->
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index f4ce1e6..8fb793f 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -204,6 +204,8 @@
<string name="permdesc_getTasks" msgid="7048711358713443341">"Umožňuje aplikaci načíst informace o aktuálně a nedávno spuštěných úlohách. Toto nastavení může škodlivým aplikacím umožnit odhalení soukromých informací o jiných aplikacích."</string>
<string name="permlab_reorderTasks" msgid="5669588525059921549">"změna uspořádání spuštěných aplikací"</string>
<string name="permdesc_reorderTasks" msgid="126252774270522835">"Umožňuje aplikaci přesouvat úlohy do popředí či pozadí. Škodlivé aplikace mohou vynutit své přesunutí do popředí bez vašeho přičinění."</string>
+ <string name="permlab_removeTasks" msgid="4802740047161700683">"zastavení činnosti aplikací"</string>
+ <string name="permdesc_removeTasks" msgid="2000332928514575461">"Umožňuje aplikaci odebírat úlohy a ukončit činnost souvisejících aplikací. Škodlivé aplikace mohou narušovat chování jiných aplikací."</string>
<string name="permlab_setDebugApp" msgid="4339730312925176742">"povolit ladění aplikací"</string>
<string name="permdesc_setDebugApp" msgid="5584310661711990702">"Umožňuje aplikaci povolit ladění jiné aplikace. Škodlivé aplikace mohou pomocí tohoto nastavení ukončit jiné aplikace."</string>
<string name="permlab_changeConfiguration" msgid="8214475779521218295">"změna vašeho nastavení uživatelského rozhraní"</string>
@@ -238,6 +240,10 @@
<string name="permdesc_batteryStats" msgid="5847319823772230560">"Umožňuje změnu shromážděných statistických údajů o baterii. Není určeno pro běžné aplikace."</string>
<string name="permlab_backup" msgid="470013022865453920">"ovládání zálohování a obnovy systému"</string>
<string name="permdesc_backup" msgid="4837493065154256525">"Umožňuje aplikaci ovládat systémový mechanizmus pro zálohování a obnovu dat. Není určeno pro běžné aplikace."</string>
+ <!-- no translation found for permlab_confirm_full_backup (5557071325804469102) -->
+ <skip />
+ <!-- no translation found for permdesc_confirm_full_backup (9005017754175897954) -->
+ <skip />
<string name="permlab_internalSystemWindow" msgid="2148563628140193231">"zobrazení nepovolených oken"</string>
<string name="permdesc_internalSystemWindow" msgid="5895082268284998469">"Umožňuje vytvoření oken, která mají být použita interním systémem uživatelského rozhraní. Běžné aplikace toto nastavení nepoužívají."</string>
<string name="permlab_systemAlertWindow" msgid="3372321942941168324">"zobrazení upozornění systémové úrovně"</string>
@@ -444,6 +450,10 @@
<string name="permdesc_bluetooth" product="default" msgid="762515380679392945">"Umožňuje aplikaci zobrazit konfiguraci místního telefonu s rozhraním Bluetooth, vytvářet připojení ke spárovaným zařízením a přijímat tato připojení."</string>
<string name="permlab_nfc" msgid="4423351274757876953">"ovládat technologii NFC"</string>
<string name="permdesc_nfc" msgid="9171401851954407226">"Umožňuje aplikaci komunikovat se štítky, kartami a čtečkami s podporou technologie NFC."</string>
+ <!-- no translation found for permlab_vpn (8345800584532175312) -->
+ <skip />
+ <!-- no translation found for permdesc_vpn (5617893078989944219) -->
+ <skip />
<string name="permlab_disableKeyguard" msgid="4977406164311535092">"vypnutí zámku kláves"</string>
<string name="permdesc_disableKeyguard" msgid="3189763479326302017">"Umožňuje aplikaci vypnout zámek kláves a související zabezpečení heslem. Příkladem oprávněného použití této funkce je vypnutí zámku klávesnice při příchozím hovoru a jeho opětovné zapnutí po skončení hovoru."</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"čtení nastavení synchronizace"</string>
@@ -947,8 +957,6 @@
<string name="ime_action_default" msgid="2840921885558045721">"Spustit"</string>
<string name="dial_number_using" msgid="5789176425167573586">"Vytočit číslo"\n" <xliff:g id="NUMBER">%s</xliff:g>."</string>
<string name="create_contact_using" msgid="4947405226788104538">"Vytvořit kontakt"\n"pro <xliff:g id="NUMBER">%s</xliff:g>."</string>
- <string name="accessibility_compound_button_selected" msgid="5612776946036285686">"Zaškrtnuto"</string>
- <string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"Nezaškrtnuto"</string>
<string name="grant_credentials_permission_message_header" msgid="6824538733852821001">"Následující aplikace požadují oprávnění k přístupu do vašeho účtu (nyní i v budoucnu)."</string>
<string name="grant_credentials_permission_message_footer" msgid="3125211343379376561">"Chcete tento požadavek povolit?"</string>
<string name="grant_permissions_header_text" msgid="2722567482180797717">"Požadavek na přístup"</string>
@@ -1019,6 +1027,9 @@
<string name="choose_account_label" msgid="4191313562041125787">"Vybrat účet"</string>
<string name="number_picker_increment_button" msgid="4830170763103463443">"Zvýšení"</string>
<string name="number_picker_decrement_button" msgid="2576606679160067262">"Snížení"</string>
+ <string name="action_bar_home_description" msgid="5293600496601490216">"Přejít na plochu"</string>
+ <string name="action_bar_up_description" msgid="2237496562952152589">"Přejít nahoru"</string>
+ <string name="action_menu_overflow_description" msgid="2295659037509008453">"Další možnosti"</string>
<!-- no translation found for storage_internal (7556050805474115618) -->
<skip />
<!-- no translation found for storage_sd_card (8921771478629812343) -->
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 428979c..b399621 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -204,6 +204,8 @@
<string name="permdesc_getTasks" msgid="7048711358713443341">"Tillader, at et program henter oplysninger om nuværende og for nyligt kørende opgaver. Tillader, at eventuelt ondsindede programmer finder private oplysninger om andre programmer."</string>
<string name="permlab_reorderTasks" msgid="5669588525059921549">"omorganiser kørende programmer"</string>
<string name="permdesc_reorderTasks" msgid="126252774270522835">"Tillader, at et program flytter opgaver til forgrunden og baggrunden. Ondsindede programmer kan tvinge dem selv til forgrunden uden din kontrol."</string>
+ <string name="permlab_removeTasks" msgid="4802740047161700683">"stop kørende programmer"</string>
+ <string name="permdesc_removeTasks" msgid="2000332928514575461">"Tillader, at et program fjerner opgaver og standser deres programmer. Ondsindede programmer kan forstyrre andre programmers opførsel."</string>
<string name="permlab_setDebugApp" msgid="4339730312925176742">"aktiver programfejlretning"</string>
<string name="permdesc_setDebugApp" msgid="5584310661711990702">"Tillader, at et program slår fejlretning af andet program til. Ondsindede programmer kan bruge dette til at standse andre programmer."</string>
<string name="permlab_changeConfiguration" msgid="8214475779521218295">"skift indstillinger for brugergrænsefladen"</string>
@@ -238,6 +240,10 @@
<string name="permdesc_batteryStats" msgid="5847319823772230560">"Tillader ændring af indsamlede batteristatistikker. Ikke til brug for normale programmer."</string>
<string name="permlab_backup" msgid="470013022865453920">"kontroller sikkerhedskopiering af system, og gendan"</string>
<string name="permdesc_backup" msgid="4837493065154256525">"Tillader, at et program kontrollerer systemets sikkerhedskopierings- og gendannelsesfunktion. Ikke til brug til normale programmer."</string>
+ <!-- no translation found for permlab_confirm_full_backup (5557071325804469102) -->
+ <skip />
+ <!-- no translation found for permdesc_confirm_full_backup (9005017754175897954) -->
+ <skip />
<string name="permlab_internalSystemWindow" msgid="2148563628140193231">"vis uautoriserede vinduer"</string>
<string name="permdesc_internalSystemWindow" msgid="5895082268284998469">"Tillader oprettelse af vinduer, der er beregnet til at blive brugt af den interne systembrugergrænseflade. Ikke til brug for normale programmer."</string>
<string name="permlab_systemAlertWindow" msgid="3372321942941168324">"vis underretninger på systemniveau"</string>
@@ -444,6 +450,10 @@
<string name="permdesc_bluetooth" product="default" msgid="762515380679392945">"Tillader, at et program viser konfigurationen af den lokale Bluetooth-telefon samt opretter og accepterer forbindelse med parrede enheder."</string>
<string name="permlab_nfc" msgid="4423351274757876953">"kontrollere Near Field Communication"</string>
<string name="permdesc_nfc" msgid="9171401851954407226">"Tillader, at et program kommunikerer med tags, kort og læsere i Near Field Communication (NFC)."</string>
+ <!-- no translation found for permlab_vpn (8345800584532175312) -->
+ <skip />
+ <!-- no translation found for permdesc_vpn (5617893078989944219) -->
+ <skip />
<string name="permlab_disableKeyguard" msgid="4977406164311535092">"deaktiver tastaturlås"</string>
<string name="permdesc_disableKeyguard" msgid="3189763479326302017">"Tillader, at et program deaktiverer tastaturlåsen og al associeret adgangskodesikkerhed. Et legitimt eksempel på dette er, at telefonen deaktiverer tastaturlåsen, når der modtages et indgående telefonopkald, og genaktiverer tastaturlåsen, når opkaldet er afsluttet."</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"læs indstillinger for synkronisering"</string>
@@ -947,8 +957,6 @@
<string name="ime_action_default" msgid="2840921885558045721">"Udfør"</string>
<string name="dial_number_using" msgid="5789176425167573586">"Ring til nummer"\n"ved hjælp af <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="create_contact_using" msgid="4947405226788104538">"Opret kontakt"\n"ved hjælp af <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="accessibility_compound_button_selected" msgid="5612776946036285686">"kontrolleret"</string>
- <string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"ikke kontrolleret"</string>
<string name="grant_credentials_permission_message_header" msgid="6824538733852821001">"Følgende programmer anmoder om tilladelse til at få adgang til din konto nu og i fremtiden."</string>
<string name="grant_credentials_permission_message_footer" msgid="3125211343379376561">"Vil du tillade denne anmodning?"</string>
<string name="grant_permissions_header_text" msgid="2722567482180797717">"Adgangsanmodning"</string>
@@ -1019,6 +1027,9 @@
<string name="choose_account_label" msgid="4191313562041125787">"Vælg en konto"</string>
<string name="number_picker_increment_button" msgid="4830170763103463443">"Optælling"</string>
<string name="number_picker_decrement_button" msgid="2576606679160067262">"Nedtælling"</string>
+ <string name="action_bar_home_description" msgid="5293600496601490216">"Naviger hjem"</string>
+ <string name="action_bar_up_description" msgid="2237496562952152589">"Naviger op"</string>
+ <string name="action_menu_overflow_description" msgid="2295659037509008453">"Flere valgmuligheder"</string>
<!-- no translation found for storage_internal (7556050805474115618) -->
<skip />
<!-- no translation found for storage_sd_card (8921771478629812343) -->
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 90e960a..0837ee91 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -204,6 +204,8 @@
<string name="permdesc_getTasks" msgid="7048711358713443341">"Ermöglicht der Anwendung, Informationen zu aktuellen und kürzlich ausführten Aufgaben abzurufen. Schädliche Anwendungen können so eventuell geheime Informationen zu anderen Anwendungen entdecken."</string>
<string name="permlab_reorderTasks" msgid="5669588525059921549">"Laufende Anwendungen neu ordnen"</string>
<string name="permdesc_reorderTasks" msgid="126252774270522835">"Ermöglicht einer Anwendung, Aufgaben in den Vorder- und Hintergrund zu verschieben. Schädliche Anwendungen können so ohne Ihr Zutun eine Anzeige im Vordergrund erzwingen."</string>
+ <string name="permlab_removeTasks" msgid="4802740047161700683">"Aktive Apps beenden"</string>
+ <string name="permdesc_removeTasks" msgid="2000332928514575461">"Ermöglicht einer App das Entfernen von Aufgaben und Beenden der entsprechenden Apps. Schädliche Apps können das Verhalten anderer Apps stören."</string>
<string name="permlab_setDebugApp" msgid="4339730312925176742">"Fehlerbeseitigung für Anwendung aktivieren"</string>
<string name="permdesc_setDebugApp" msgid="5584310661711990702">"Ermöglicht einer Anwendung, die Fehlerbeseitigung für eine andere Anwendung zu aktivieren. Schädliche Anwendungen können so andere Anwendungen löschen."</string>
<string name="permlab_changeConfiguration" msgid="8214475779521218295">"UI-Einstellungen ändern"</string>
@@ -238,6 +240,10 @@
<string name="permdesc_batteryStats" msgid="5847319823772230560">"Ermöglicht die Änderung von gesammelten Akku-Daten. Nicht für normale Anwendungen vorgesehen."</string>
<string name="permlab_backup" msgid="470013022865453920">"Systemsicherung und -wiederherstellung kontrollieren"</string>
<string name="permdesc_backup" msgid="4837493065154256525">"Der Anwendung wird die Steuerung des Sicherungs- und Wiederherstellungsmechanismus des Systems ermöglicht. Nicht für normale Anwendungen vorgesehen."</string>
+ <!-- no translation found for permlab_confirm_full_backup (5557071325804469102) -->
+ <skip />
+ <!-- no translation found for permdesc_confirm_full_backup (9005017754175897954) -->
+ <skip />
<string name="permlab_internalSystemWindow" msgid="2148563628140193231">"nicht autorisierte Fenster anzeigen"</string>
<string name="permdesc_internalSystemWindow" msgid="5895082268284998469">"Ermöglicht die Erstellung von Fenstern, die von der Benutzeroberfläche des internen Systems verwendet werden. Nicht für normale Anwendungen geeignet."</string>
<string name="permlab_systemAlertWindow" msgid="3372321942941168324">"Warnungen auf Systemebene anzeigen"</string>
@@ -444,6 +450,10 @@
<string name="permdesc_bluetooth" product="default" msgid="762515380679392945">"Ermöglicht einer Anwendung, die Konfiguration des lokalen Bluetooth-Telefons einzusehen und Verbindungen mit Partnergeräten herzustellen und zu akzeptieren"</string>
<string name="permlab_nfc" msgid="4423351274757876953">"Nahfeldkommunikation steuern"</string>
<string name="permdesc_nfc" msgid="9171401851954407226">"Ermöglicht einer Anwendung die Kommunikation mit Tags für Nahfeldkommunikation, Karten und Lesegeräte"</string>
+ <!-- no translation found for permlab_vpn (8345800584532175312) -->
+ <skip />
+ <!-- no translation found for permdesc_vpn (5617893078989944219) -->
+ <skip />
<string name="permlab_disableKeyguard" msgid="4977406164311535092">"Tastensperre deaktivieren"</string>
<string name="permdesc_disableKeyguard" msgid="3189763479326302017">"Ermöglicht einer Anwendung, die Tastensperre sowie den damit verbundenen Passwortschutz zu deaktivieren. So wird die Tastensperre vom Telefon deaktiviert, wenn ein Anruf eingeht, und nach Beendigung des Anrufs wieder aktiviert."</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"Synchronisierungseinstellungen lesen"</string>
@@ -947,8 +957,6 @@
<string name="ime_action_default" msgid="2840921885558045721">"Ausführen"</string>
<string name="dial_number_using" msgid="5789176425167573586">"Nummer"\n"mit <xliff:g id="NUMBER">%s</xliff:g> wählen"</string>
<string name="create_contact_using" msgid="4947405226788104538">"Neuer Kontakt"\n"mit <xliff:g id="NUMBER">%s</xliff:g> erstellen"</string>
- <string name="accessibility_compound_button_selected" msgid="5612776946036285686">"aktiviert"</string>
- <string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"nicht aktiviert"</string>
<string name="grant_credentials_permission_message_header" msgid="6824538733852821001">"Die folgenden Anwendungen benötigen die Berechtigung zum aktuellen und zukünftigen Zugriff auf Ihr Konto."</string>
<string name="grant_credentials_permission_message_footer" msgid="3125211343379376561">"Möchten Sie diese Anfrage zulassen?"</string>
<string name="grant_permissions_header_text" msgid="2722567482180797717">"Zugriffsanfrage"</string>
@@ -1019,6 +1027,9 @@
<string name="choose_account_label" msgid="4191313562041125787">"Konto auswählen"</string>
<string name="number_picker_increment_button" msgid="4830170763103463443">"Erhöhen"</string>
<string name="number_picker_decrement_button" msgid="2576606679160067262">"Verringern"</string>
+ <string name="action_bar_home_description" msgid="5293600496601490216">"Zur Startseite navigieren"</string>
+ <string name="action_bar_up_description" msgid="2237496562952152589">"Nach oben navigieren"</string>
+ <string name="action_menu_overflow_description" msgid="2295659037509008453">"Weitere Optionen"</string>
<!-- no translation found for storage_internal (7556050805474115618) -->
<skip />
<!-- no translation found for storage_sd_card (8921771478629812343) -->
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 527a743..df83d34 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -204,6 +204,8 @@
<string name="permdesc_getTasks" msgid="7048711358713443341">"Επιτρέπει σε μια εφαρμογή να ανακτήσει πληροφορίες σχετικά με τις τρέχουσες εκτελούμενες εργασίες και στις εργασίες που έχουν πρόσφατα εκτελεστεί. Ενδέχεται να δώσει τη δυνατότητα σε κακόβουλες εφαρμογές να ανακαλύψουν ιδιωτικές πληροφορίες σχετικά με άλλες εφαρμογές."</string>
<string name="permlab_reorderTasks" msgid="5669588525059921549">"αναδιάταξη εκτελούμενων εφαρμογών"</string>
<string name="permdesc_reorderTasks" msgid="126252774270522835">"Επιτρέπει σε μια εφαρμογή τη μετακίνηση εργασιών στο προσκήνιο και στο φόντο. Κακόβουλες εφαρμογές μπορούν να προωθηθούν στο προσκήνιο χωρίς να μπορείτε να τις ελέγξετε."</string>
+ <string name="permlab_removeTasks" msgid="4802740047161700683">"διακοπή εκτέλεσης εφαρμογών"</string>
+ <string name="permdesc_removeTasks" msgid="2000332928514575461">"Επιτρέπει σε μια εφαρμογή την κατάργηση εργασιών και τον τερματισμό των εφαρμογών τους. Οι κακόβουλες εφαρμογές μπορούν να παρεμποδίσουν τη λειτουργία άλλων εφαρμογών."</string>
<string name="permlab_setDebugApp" msgid="4339730312925176742">"ενεργοποίηση εντοπισμού σφαλμάτων εφαρμογής"</string>
<string name="permdesc_setDebugApp" msgid="5584310661711990702">"Επιτρέπει σε μια εφαρμογή να ενεργοποιήσει τον εντοπισμό σφαλμάτων για μια άλλη εφαρμογή. Κακόβουλες εφαρμογές μπορούν να το χρησιμοποιήσουν για να τερματίσουν άλλες εφαρμογές."</string>
<string name="permlab_changeConfiguration" msgid="8214475779521218295">"αλλαγή των ρυθμίσεων του UI"</string>
@@ -238,6 +240,10 @@
<string name="permdesc_batteryStats" msgid="5847319823772230560">"Επιτρέπει την τροποποίηση στατιστικών μπαταρίας που έχουν συλλεχθεί. Δεν πρέπει να χρησιμοποιείται από συνήθεις εφαρμογές."</string>
<string name="permlab_backup" msgid="470013022865453920">"αντίγραφο ασφαλείας και επαναφορά συστήματος"</string>
<string name="permdesc_backup" msgid="4837493065154256525">"Επιτρέπει στην εφαρμογή τον έλεγχο του μηχανισμού δημιουργίας αντιγράφων ασφαλείας και επαναφοράς του συστήματος. Δεν προορίζεται για χρήση από κανονικές εφαρμογές."</string>
+ <!-- no translation found for permlab_confirm_full_backup (5557071325804469102) -->
+ <skip />
+ <!-- no translation found for permdesc_confirm_full_backup (9005017754175897954) -->
+ <skip />
<string name="permlab_internalSystemWindow" msgid="2148563628140193231">"προβολή μη εξουσιοδοτημένων παραθύρων"</string>
<string name="permdesc_internalSystemWindow" msgid="5895082268284998469">"Επιτρέπει τη δημιουργία παραθύρων που πρόκειται να χρησιμοποιηθούν από την εσωτερική διεπαφή χρήστη του συστήματος. Δεν πρέπει να χρησιμοποιείται από κανονικές εφαρμογές."</string>
<string name="permlab_systemAlertWindow" msgid="3372321942941168324">"εμφάνιση ειδοποιήσεων επιπέδου συστήματος"</string>
@@ -444,6 +450,10 @@
<string name="permdesc_bluetooth" product="default" msgid="762515380679392945">"Επιτρέπει σε μια εφαρμογή να προβάλει τη διαμόρφωση του τοπικού τηλεφώνου Bluetooth και επίσης να πραγματοποιεί και να αποδέχεται συνδέσεις με συζευγμένες συσκευές."</string>
<string name="permlab_nfc" msgid="4423351274757876953">"έλεγχος Επικοινωνίας κοντινού πεδίου (Near Field Communication)"</string>
<string name="permdesc_nfc" msgid="9171401851954407226">"Επιτρέπει σε μια εφαρμογή την επικοινωνία με ετικέτες, τις κάρτες και τους αναγνώστες της Επικοινωνίας κοντινού πεδίου (NFC)."</string>
+ <!-- no translation found for permlab_vpn (8345800584532175312) -->
+ <skip />
+ <!-- no translation found for permdesc_vpn (5617893078989944219) -->
+ <skip />
<string name="permlab_disableKeyguard" msgid="4977406164311535092">"απενεργοποίηση κλειδώματος πληκτρολογίου"</string>
<string name="permdesc_disableKeyguard" msgid="3189763479326302017">"Επιτρέπει σε μια εφαρμογή την απενεργοποίηση του κλειδώματος πληκτρολογίου και άλλης σχετικής ασφάλειας με κωδικό πρόσβασης. Για παράδειγμα, η απενεργοποίηση του κλειδώματος πληκτρολογίου όταν λαμβάνεται εισερχόμενη τηλεφωνική κλήση και η επανενεργοποίηση του κλειδώματος πληκτρολογίου όταν η κλήση τερματιστεί."</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"ανάγνωση ρυθμίσεων συγχρονισμού"</string>
@@ -947,8 +957,6 @@
<string name="ime_action_default" msgid="2840921885558045721">"Εκτέλεση"</string>
<string name="dial_number_using" msgid="5789176425167573586">"Κλήση αριθμού"\n"με τη χρήση <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="create_contact_using" msgid="4947405226788104538">"Δημιουργία επαφής"\n"με τη χρήση του <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="accessibility_compound_button_selected" msgid="5612776946036285686">"επιλεγμένο"</string>
- <string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"δεν ελέγχθηκε"</string>
<string name="grant_credentials_permission_message_header" msgid="6824538733852821001">"Από την εφαρμογή ή τις εφαρμογές που ακολουθούν ζητούνται δικαιώματα πρόσβασης στο λογαριασμό σας, τώρα και στο μέλλον."</string>
<string name="grant_credentials_permission_message_footer" msgid="3125211343379376561">"Θέλετε να επιτρέψετε αυτή την αίτηση;"</string>
<string name="grant_permissions_header_text" msgid="2722567482180797717">"Αίτημα πρόσβασης"</string>
@@ -1019,6 +1027,9 @@
<string name="choose_account_label" msgid="4191313562041125787">"Επιλογή λογαριασμού"</string>
<string name="number_picker_increment_button" msgid="4830170763103463443">"Αύξηση"</string>
<string name="number_picker_decrement_button" msgid="2576606679160067262">"Μείωση"</string>
+ <string name="action_bar_home_description" msgid="5293600496601490216">"Πλοήγηση στην αρχική σελίδα"</string>
+ <string name="action_bar_up_description" msgid="2237496562952152589">"Πλοήγηση προς τα επάνω"</string>
+ <string name="action_menu_overflow_description" msgid="2295659037509008453">"Περισσότερες επιλογές"</string>
<!-- no translation found for storage_internal (7556050805474115618) -->
<skip />
<!-- no translation found for storage_sd_card (8921771478629812343) -->
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index df4d462..fa81526 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -204,6 +204,8 @@
<string name="permdesc_getTasks" msgid="7048711358713443341">"Allows application to retrieve information about currently and recently running tasks. May allow malicious applications to discover private information about other applications."</string>
<string name="permlab_reorderTasks" msgid="5669588525059921549">"reorder applications running"</string>
<string name="permdesc_reorderTasks" msgid="126252774270522835">"Allows an application to move tasks to the foreground and background. Malicious applications can force themselves to the front without your control."</string>
+ <string name="permlab_removeTasks" msgid="4802740047161700683">"stop running applications"</string>
+ <string name="permdesc_removeTasks" msgid="2000332928514575461">"Allows an application to remove tasks and kill their applications. Malicious applications can disrupt the behaviour of other applications."</string>
<string name="permlab_setDebugApp" msgid="4339730312925176742">"enable application debugging"</string>
<string name="permdesc_setDebugApp" msgid="5584310661711990702">"Allows an application to turn on debugging for another application. Malicious applications can use this to kill other applications."</string>
<string name="permlab_changeConfiguration" msgid="8214475779521218295">"change your UI settings"</string>
@@ -238,6 +240,8 @@
<string name="permdesc_batteryStats" msgid="5847319823772230560">"Allows the modification of collected battery statistics. Not for use by normal applications."</string>
<string name="permlab_backup" msgid="470013022865453920">"control system back up and restore"</string>
<string name="permdesc_backup" msgid="4837493065154256525">"Allows the application to control the system\'s back-up and restore mechanism. Not for use by normal applications."</string>
+ <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"confirm a full backup or restore operation"</string>
+ <string name="permdesc_confirm_full_backup" msgid="9005017754175897954">"Allows the application to launch the full backup confirmation UI. Not to be used by any application."</string>
<string name="permlab_internalSystemWindow" msgid="2148563628140193231">"display unauthorised windows"</string>
<string name="permdesc_internalSystemWindow" msgid="5895082268284998469">"Allows the creation of windows that are intended to be used by the internal system user interface. Not for use by normal applications."</string>
<string name="permlab_systemAlertWindow" msgid="3372321942941168324">"display system-level alerts"</string>
@@ -444,6 +448,10 @@
<string name="permdesc_bluetooth" product="default" msgid="762515380679392945">"Allows an application to view configuration of the local Bluetooth phone and to make and accept connections with paired devices."</string>
<string name="permlab_nfc" msgid="4423351274757876953">"control Near-Field Communication"</string>
<string name="permdesc_nfc" msgid="9171401851954407226">"Allows an application to communicate with Near-Field Communication (NFC) tags, cards and readers."</string>
+ <!-- no translation found for permlab_vpn (8345800584532175312) -->
+ <skip />
+ <!-- no translation found for permdesc_vpn (5617893078989944219) -->
+ <skip />
<string name="permlab_disableKeyguard" msgid="4977406164311535092">"disable key lock"</string>
<string name="permdesc_disableKeyguard" msgid="3189763479326302017">"Allows an application to disable the key lock and any associated password security. A legitimate example of this is the phone disabling the key lock when receiving an incoming phone call, then re-enabling the key lock when the call is finished."</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"read sync settings"</string>
@@ -947,8 +955,6 @@
<string name="ime_action_default" msgid="2840921885558045721">"Execute"</string>
<string name="dial_number_using" msgid="5789176425167573586">"Dial number"\n", using <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="create_contact_using" msgid="4947405226788104538">"Create contact"\n", using <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="accessibility_compound_button_selected" msgid="5612776946036285686">"ticked"</string>
- <string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"not ticked"</string>
<string name="grant_credentials_permission_message_header" msgid="6824538733852821001">"The following one or more applications request permission to access your account, now and in the future."</string>
<string name="grant_credentials_permission_message_footer" msgid="3125211343379376561">"Do you want to allow this request?"</string>
<string name="grant_permissions_header_text" msgid="2722567482180797717">"Access Request"</string>
@@ -1019,6 +1025,9 @@
<string name="choose_account_label" msgid="4191313562041125787">"Select an account"</string>
<string name="number_picker_increment_button" msgid="4830170763103463443">"Increment"</string>
<string name="number_picker_decrement_button" msgid="2576606679160067262">"Decrement"</string>
+ <string name="action_bar_home_description" msgid="5293600496601490216">"Navigate home"</string>
+ <string name="action_bar_up_description" msgid="2237496562952152589">"Navigate up"</string>
+ <string name="action_menu_overflow_description" msgid="2295659037509008453">"More options"</string>
<!-- no translation found for storage_internal (7556050805474115618) -->
<skip />
<!-- no translation found for storage_sd_card (8921771478629812343) -->
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 56bce39..a0c228c 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -204,6 +204,8 @@
<string name="permdesc_getTasks" msgid="7048711358713443341">"Admite que la aplicación recupere información sobre tareas en ejecución actuales y recientes. Puede permitir que las aplicaciones maliciosas descubran información privada sobre otras aplicaciones."</string>
<string name="permlab_reorderTasks" msgid="5669588525059921549">"reorganizar aplicaciones en ejecución"</string>
<string name="permdesc_reorderTasks" msgid="126252774270522835">"Admite una aplicación que mueve tareas hacia el frente y el fondo. Las aplicaciones maliciosas pueden provocar su propio movimiento hacia el frente sin tu control."</string>
+ <string name="permlab_removeTasks" msgid="4802740047161700683">"detener las aplicaciones en ejecución"</string>
+ <string name="permdesc_removeTasks" msgid="2000332928514575461">"Permite que una aplicación elimine tareas y aplicaciones. Las aplicaciones maliciosas pueden alterar el comportamiento de otras aplicaciones."</string>
<string name="permlab_setDebugApp" msgid="4339730312925176742">"activar la depuración de la aplicación"</string>
<string name="permdesc_setDebugApp" msgid="5584310661711990702">"Admite una aplicación que activa la depuración en otra aplicación. Las aplicaciones maliciosas pueden utilizarlo para suprimir otras aplicaciones."</string>
<string name="permlab_changeConfiguration" msgid="8214475779521218295">"cambiar tu configuración de la interfaz de usuario"</string>
@@ -238,6 +240,10 @@
<string name="permdesc_batteryStats" msgid="5847319823772230560">"Admite la modificación de estadísticas recopiladas sobre la batería. Las aplicaciones normales no deben utilizarlo."</string>
<string name="permlab_backup" msgid="470013022865453920">"copia de seguridad y restauración del sistema de control"</string>
<string name="permdesc_backup" msgid="4837493065154256525">"Permite a la aplicación controlar el mecanismo de restauración y copia de seguridad de los sistemas. No es para uso de las aplicaciones normales."</string>
+ <!-- no translation found for permlab_confirm_full_backup (5557071325804469102) -->
+ <skip />
+ <!-- no translation found for permdesc_confirm_full_backup (9005017754175897954) -->
+ <skip />
<string name="permlab_internalSystemWindow" msgid="2148563628140193231">"mostrar ventanas no autorizadas"</string>
<string name="permdesc_internalSystemWindow" msgid="5895082268284998469">"Permite la creación de ventanas que la interfaz interna del usuario del sistema pretenda utilizar. Las aplicaciones normales no deben utilizarlo."</string>
<string name="permlab_systemAlertWindow" msgid="3372321942941168324">"mostrar alertas a nivel del sistema"</string>
@@ -444,6 +450,10 @@
<string name="permdesc_bluetooth" product="default" msgid="762515380679392945">"Admite una aplicación que ve la configuración del teléfono Bluetooth local, y realiza y acepta conexiones con dispositivos vinculados."</string>
<string name="permlab_nfc" msgid="4423351274757876953">"controlar la Transmisión de datos en proximidad"</string>
<string name="permdesc_nfc" msgid="9171401851954407226">"Permite que una aplicación se comunique con etiquetas, tarjetas y lectores de Transmisión de datos en proximidad (NFC)."</string>
+ <!-- no translation found for permlab_vpn (8345800584532175312) -->
+ <skip />
+ <!-- no translation found for permdesc_vpn (5617893078989944219) -->
+ <skip />
<string name="permlab_disableKeyguard" msgid="4977406164311535092">"desactivar el bloqueo"</string>
<string name="permdesc_disableKeyguard" msgid="3189763479326302017">"Admite una aplicación que desactiva el bloqueo y cualquier seguridad con contraseña relacionada. Un ejemplo legítimo de esto es el bloqueo desactivado por el teléfono cuando recibe una llamada telefónica entrante, y luego la reactivación del bloqueo cuando finaliza la llamada."</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"leer la configuración de sincronización"</string>
@@ -947,8 +957,6 @@
<string name="ime_action_default" msgid="2840921885558045721">"Ejecutar"</string>
<string name="dial_number_using" msgid="5789176425167573586">"Marcar el número"\n"con <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="create_contact_using" msgid="4947405226788104538">"Crear contacto "\n"con <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="accessibility_compound_button_selected" msgid="5612776946036285686">"verificado"</string>
- <string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"no verificado"</string>
<string name="grant_credentials_permission_message_header" msgid="6824538733852821001">"Las siguientes aplicaciones requieren autorización para acceder a tu cuenta ahora y en el futuro."</string>
<string name="grant_credentials_permission_message_footer" msgid="3125211343379376561">"¿Deseas permitir esta solicitud?"</string>
<string name="grant_permissions_header_text" msgid="2722567482180797717">"Solicitud de acceso"</string>
@@ -1019,6 +1027,9 @@
<string name="choose_account_label" msgid="4191313562041125787">"Seleccionar una cuenta"</string>
<string name="number_picker_increment_button" msgid="4830170763103463443">"Incremento"</string>
<string name="number_picker_decrement_button" msgid="2576606679160067262">"Decremento"</string>
+ <string name="action_bar_home_description" msgid="5293600496601490216">"Desplazarse hasta la página principal"</string>
+ <string name="action_bar_up_description" msgid="2237496562952152589">"Desplazarse hacia arriba"</string>
+ <string name="action_menu_overflow_description" msgid="2295659037509008453">"Más opciones"</string>
<!-- no translation found for storage_internal (7556050805474115618) -->
<skip />
<!-- no translation found for storage_sd_card (8921771478629812343) -->
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index cdad52f..9973f0b 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -204,6 +204,8 @@
<string name="permdesc_getTasks" msgid="7048711358713443341">"Permite que la aplicación recupere información sobre tareas que se están ejecutando en este momento o que se han ejecutado recientemente. Puede permitir que las aplicaciones malintencionadas vean información privada sobre otras aplicaciones."</string>
<string name="permlab_reorderTasks" msgid="5669588525059921549">"reorganizar aplicaciones en ejecución"</string>
<string name="permdesc_reorderTasks" msgid="126252774270522835">"Permite que una aplicación mueva tareas a segundo plano y a primer plano. Las aplicaciones malintencionadas pueden aparecer en primer plano sin tu control."</string>
+ <string name="permlab_removeTasks" msgid="4802740047161700683">"detener aplicaciones en ejecución"</string>
+ <string name="permdesc_removeTasks" msgid="2000332928514575461">"Permite que una aplicación elimine tareas y desactive sus aplicaciones. Las aplicaciones malintencionadas pueden alterar el comportamiento de otras aplicaciones."</string>
<string name="permlab_setDebugApp" msgid="4339730312925176742">"habilitar depuración de aplicación"</string>
<string name="permdesc_setDebugApp" msgid="5584310661711990702">"Permite que una aplicación active la depuración de otra aplicación. Las aplicaciones malintencionadas pueden utilizar este permiso para desactivar otras aplicaciones."</string>
<string name="permlab_changeConfiguration" msgid="8214475779521218295">"cambiar la configuración de la interfaz de usuario"</string>
@@ -238,6 +240,10 @@
<string name="permdesc_batteryStats" msgid="5847319823772230560">"Permite la modificación de estadísticas recopiladas sobre la batería. No está destinado al uso por parte de aplicaciones normales."</string>
<string name="permlab_backup" msgid="470013022865453920">"controlar las copias de seguridad y las restauraciones del sistema"</string>
<string name="permdesc_backup" msgid="4837493065154256525">"Permite que la aplicación controle el mecanismo de copia de seguridad y restauración del sistema. Este permiso no está destinado a aplicaciones normales."</string>
+ <!-- no translation found for permlab_confirm_full_backup (5557071325804469102) -->
+ <skip />
+ <!-- no translation found for permdesc_confirm_full_backup (9005017754175897954) -->
+ <skip />
<string name="permlab_internalSystemWindow" msgid="2148563628140193231">"mostrar ventanas no autorizadas"</string>
<string name="permdesc_internalSystemWindow" msgid="5895082268284998469">"Permite la creación de ventanas destinadas al uso por parte de la interfaz de usuario interna del sistema. No está destinado al uso por parte de aplicaciones normales."</string>
<string name="permlab_systemAlertWindow" msgid="3372321942941168324">"mostrar alertas de nivel del sistema"</string>
@@ -444,6 +450,10 @@
<string name="permdesc_bluetooth" product="default" msgid="762515380679392945">"Permite que una aplicación vea la configuración del teléfono Bluetooth local, y cree y acepte conexiones con los dispositivos sincronizados."</string>
<string name="permlab_nfc" msgid="4423351274757876953">"controlar Comunicación de campo cercano (NFC)"</string>
<string name="permdesc_nfc" msgid="9171401851954407226">"Permite que la aplicación se comunique con lectores, tarjetas y etiquetas de Comunicación de campo cercano (NFC)."</string>
+ <!-- no translation found for permlab_vpn (8345800584532175312) -->
+ <skip />
+ <!-- no translation found for permdesc_vpn (5617893078989944219) -->
+ <skip />
<string name="permlab_disableKeyguard" msgid="4977406164311535092">"inhabilitar bloqueo del teclado"</string>
<string name="permdesc_disableKeyguard" msgid="3189763479326302017">"Permite que una aplicación inhabilite el bloqueo del teclado y cualquier protección con contraseña asociada. Un ejemplo legítimo de este permiso es la inhabilitación por parte del teléfono del bloqueo del teclado cuando recibe una llamada telefónica entrante y su posterior habilitación cuando finaliza la llamada."</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"leer la configuración de sincronización"</string>
@@ -947,8 +957,6 @@
<string name="ime_action_default" msgid="2840921885558045721">"Ejecutar"</string>
<string name="dial_number_using" msgid="5789176425167573586">"Marcar número"\n"con <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="create_contact_using" msgid="4947405226788104538">"Crear un contacto"\n"a partir de <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="accessibility_compound_button_selected" msgid="5612776946036285686">"seleccionado"</string>
- <string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"no seleccionado"</string>
<string name="grant_credentials_permission_message_header" msgid="6824538733852821001">"Las siguientes aplicaciones solicitan permiso para acceder a tu cuenta ahora y en el futuro."</string>
<string name="grant_credentials_permission_message_footer" msgid="3125211343379376561">"¿Quieres permitir esta solicitud?"</string>
<string name="grant_permissions_header_text" msgid="2722567482180797717">"Solicitud de acceso"</string>
@@ -1019,6 +1027,9 @@
<string name="choose_account_label" msgid="4191313562041125787">"Seleccionar una cuenta"</string>
<string name="number_picker_increment_button" msgid="4830170763103463443">"Aumentar"</string>
<string name="number_picker_decrement_button" msgid="2576606679160067262">"Disminuir"</string>
+ <string name="action_bar_home_description" msgid="5293600496601490216">"Ir al escritorio"</string>
+ <string name="action_bar_up_description" msgid="2237496562952152589">"Desplazarse hacia arriba"</string>
+ <string name="action_menu_overflow_description" msgid="2295659037509008453">"Más opciones"</string>
<!-- no translation found for storage_internal (7556050805474115618) -->
<skip />
<!-- no translation found for storage_sd_card (8921771478629812343) -->
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 3f91f38..7842216 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -204,6 +204,8 @@
<string name="permdesc_getTasks" msgid="7048711358713443341">"به برنامه کاربردی اجازه می دهد اطلاعات مربوط به کارهای در حال اجرای فعلی و کارهای اخیر را بازیابی کند. ممکن است برنامه های مضر بتوانند اطلاعات خصوصی مربوط به شما را در ارتباط به سایر برنامه ها مشاهده کنند."</string>
<string name="permlab_reorderTasks" msgid="5669588525059921549">"ترتیب بندی مجدد برنامه های در حال اجرا"</string>
<string name="permdesc_reorderTasks" msgid="126252774270522835">"به یک برنامه کاربردی اجازه می دهد تا وظایف را به پیش زمینه و پس زمینه منتقل کند. برنامه های مضر می توانند بدون کنترل شما خودشان را به پیش زمینه منتقل کنند."</string>
+ <string name="permlab_removeTasks" msgid="4802740047161700683">"متوقف کردن برنامه های در حال اجرا"</string>
+ <string name="permdesc_removeTasks" msgid="2000332928514575461">"به یک برنامه کاربردی برای حذف کارها و متوقف کردن برنامه های کاربردی آنها اجازه می دهد. برنامه های مخرب می توانند در رفتار برنامه های دیگر اختلال ایجاد کنند."</string>
<string name="permlab_setDebugApp" msgid="4339730312925176742">"فعال کردن رفع عیب برنامه"</string>
<string name="permdesc_setDebugApp" msgid="5584310661711990702">"به یک برنامه کاربردی اجازه می دهد رفع عیب را برای یک برنامه دیگر فعال کند. برنامه های مضر می توانند از این ویژگی برای از بین بردن سایر برنامه ها استفاده کنند."</string>
<string name="permlab_changeConfiguration" msgid="8214475779521218295">"تغییر تنظیمات UI"</string>
@@ -238,6 +240,10 @@
<string name="permdesc_batteryStats" msgid="5847319823772230560">"امکان تغییر اطلاعات جمع آوری شده مربوط به باتری را فراهم می آورد. برای استفاده با برنامه های معمولی در نظر گرفته نشده است."</string>
<string name="permlab_backup" msgid="470013022865453920">"کنترل نسخه پشتیبان سیستم و بازیابی"</string>
<string name="permdesc_backup" msgid="4837493065154256525">"به برنامه کاربردی اجازه می دهد نسخه پشتیبان سیستم را کنترل کرده و مکانیسم آن را بازیابی کند. برای استفاده با برنامه های معمولی در نظر گرفته نشده است."</string>
+ <!-- no translation found for permlab_confirm_full_backup (5557071325804469102) -->
+ <skip />
+ <!-- no translation found for permdesc_confirm_full_backup (9005017754175897954) -->
+ <skip />
<string name="permlab_internalSystemWindow" msgid="2148563628140193231">"نمایش پنجره های غیرمجاز"</string>
<string name="permdesc_internalSystemWindow" msgid="5895082268284998469">"اجازه می دهد پنجره هایی ایجاد شوند که برای استفاده توسط رابط کاربر سیستم داخلی در نظر گرفته شده است. برای استفاده با برنامه های معمولی در نظر گرفته نشده است."</string>
<string name="permlab_systemAlertWindow" msgid="3372321942941168324">"هشدارهای سطح سیستم نمایش"</string>
@@ -444,6 +450,10 @@
<string name="permdesc_bluetooth" product="default" msgid="762515380679392945">"به یک برنامه کاربردی اجازه می دهد تا پیکربندی تلفن بلوتوث محلی را مشاهده کند، اتصال ها را با دستگاه های جفت شده برقرار کرده، آنها را بپذیرد."</string>
<string name="permlab_nfc" msgid="4423351274757876953">"کنترل ارتباط راه نزدیک"</string>
<string name="permdesc_nfc" msgid="9171401851954407226">"به یک برنامه کاربردی برای ارتباط با برچسب های ارتباط راه نزدیک (NFC)، کارت ها و خواننده ها اجازه می دهد."</string>
+ <!-- no translation found for permlab_vpn (8345800584532175312) -->
+ <skip />
+ <!-- no translation found for permdesc_vpn (5617893078989944219) -->
+ <skip />
<string name="permlab_disableKeyguard" msgid="4977406164311535092">"غیرفعال کردن قفل کلید"</string>
<string name="permdesc_disableKeyguard" msgid="3189763479326302017">"به یک برنامه کاربردی اجازه می دهد قفل کلید و حفاظت رمز ورود همراه با کلیه کلیدها را غیرفعال کند. یک نمونه قانونی از این مورد، غیرفعال شدن قفل کلید در هنگام دریافت تماس تلفنی و سپس فعال کردن قفل کلید پس از پایان تماس است."</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"خواندن تنظیمات همگام سازی"</string>
@@ -947,8 +957,6 @@
<string name="ime_action_default" msgid="2840921885558045721">"اجرا کردن"</string>
<string name="dial_number_using" msgid="5789176425167573586">"شماره گیری "\n"با استفاده از <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="create_contact_using" msgid="4947405226788104538">"ایجاد مخاطب"\n"با استفاده از <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="accessibility_compound_button_selected" msgid="5612776946036285686">"علامت زده شد"</string>
- <string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"بدون علامت"</string>
<string name="grant_credentials_permission_message_header" msgid="6824538733852821001">"یک یا چند برنامه زیر برای دسترسی به حساب شما در زمان حال و آینده درخواست کرده اند."</string>
<string name="grant_credentials_permission_message_footer" msgid="3125211343379376561">"می خواهید به این درخواست اجازه دهید؟"</string>
<string name="grant_permissions_header_text" msgid="2722567482180797717">"درخواست دسترسی"</string>
@@ -1019,6 +1027,9 @@
<string name="choose_account_label" msgid="4191313562041125787">"انتخاب یک حساب"</string>
<string name="number_picker_increment_button" msgid="4830170763103463443">"افزایش"</string>
<string name="number_picker_decrement_button" msgid="2576606679160067262">"کاهش"</string>
+ <string name="action_bar_home_description" msgid="5293600496601490216">"رفتن به صفحه اصلی"</string>
+ <string name="action_bar_up_description" msgid="2237496562952152589">"حرکت به بالا"</string>
+ <string name="action_menu_overflow_description" msgid="2295659037509008453">"سایر گزینه ها"</string>
<!-- no translation found for storage_internal (7556050805474115618) -->
<skip />
<!-- no translation found for storage_sd_card (8921771478629812343) -->
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 6a19c10..631da31 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -204,6 +204,8 @@
<string name="permdesc_getTasks" msgid="7048711358713443341">"Antaa sovelluksen noutaa tietoja käynnissä olevista ja äskettäin käynnissä olleista tehtävistä. Haitalliset sovellukset saattavat saada selville yksityisiä tietoja muista sovelluksista."</string>
<string name="permlab_reorderTasks" msgid="5669588525059921549">"muuttaa käynnissä olevien sovelluksien järjestystä"</string>
<string name="permdesc_reorderTasks" msgid="126252774270522835">"Antaa sovelluksen siirtää tehtäviä etualalle ja taustalle. Haitalliset sovellukset voivat tunkeutua etualalle väkisin."</string>
+ <string name="permlab_removeTasks" msgid="4802740047161700683">"sulje sovellukset"</string>
+ <string name="permdesc_removeTasks" msgid="2000332928514575461">"Antaa sovelluksen poistaa tehtäviä ja sulkea sovelluksia. Haittaohjelmat voivat häiritä muiden sovelluksien toimintaa."</string>
<string name="permlab_setDebugApp" msgid="4339730312925176742">"ota sovelluksen vianetsintä käyttöön"</string>
<string name="permdesc_setDebugApp" msgid="5584310661711990702">"Antaa sovelluksen käynnistää toisen sovelluksen vianetsinnän. Haitalliset sovellukset saattavat sulkea muita sovelluksia."</string>
<string name="permlab_changeConfiguration" msgid="8214475779521218295">"muuta käyttöliittymäsi asetuksia"</string>
@@ -238,6 +240,10 @@
<string name="permdesc_batteryStats" msgid="5847319823772230560">"Antaa sovelluksen muuttaa kerättyjä akkutietoja. Ei tavallisten sovelluksien käyttöön."</string>
<string name="permlab_backup" msgid="470013022865453920">"hallitse järjestelmän varmuuskopiointia ja palauttamista"</string>
<string name="permdesc_backup" msgid="4837493065154256525">"Antaa sovelluksen hallita järjestelmän varmuuskopio- ja palautusmekanismeja. Ei tavallisten sovelluksien käyttöön."</string>
+ <!-- no translation found for permlab_confirm_full_backup (5557071325804469102) -->
+ <skip />
+ <!-- no translation found for permdesc_confirm_full_backup (9005017754175897954) -->
+ <skip />
<string name="permlab_internalSystemWindow" msgid="2148563628140193231">"näytä luvattomia ikkunoita"</string>
<string name="permdesc_internalSystemWindow" msgid="5895082268284998469">"Antaa sovelluksen luoda ikkunoita, jotka on tarkoitettu sisäisen järjestelmän käyttöliittymän käyttöön. Ei tavallisten sovelluksien käyttöön."</string>
<string name="permlab_systemAlertWindow" msgid="3372321942941168324">"näytä järjestelmätason ilmoituksia"</string>
@@ -444,6 +450,10 @@
<string name="permdesc_bluetooth" product="default" msgid="762515380679392945">"Antaa sovelluksen tarkastella paikallisen Bluetooth-puhelimen asetuksia sekä muodostaa ja hyväksyä laitepariyhteyksiä muihin laitteisiin."</string>
<string name="permlab_nfc" msgid="4423351274757876953">"hallitse Near Field Communication -tunnistusta"</string>
<string name="permdesc_nfc" msgid="9171401851954407226">"Antaa sovelluksen viestiä Near Field Communication (NFC) -tunnisteiden, -korttien ja -lukijoiden kanssa."</string>
+ <!-- no translation found for permlab_vpn (8345800584532175312) -->
+ <skip />
+ <!-- no translation found for permdesc_vpn (5617893078989944219) -->
+ <skip />
<string name="permlab_disableKeyguard" msgid="4977406164311535092">"poista näppäinlukitus käytöstä"</string>
<string name="permdesc_disableKeyguard" msgid="3189763479326302017">"Antaa sovelluksen poistaa näppäinlukituksen ja siihen liittyvän salasanasuojauksen käytöstä. Esimerkki: puhelin poistaa näppäinlukituksen käytöstä saapuvan puhelun yhteydessä ja ottaa näppäinlukituksen takaisin käyttöön puhelun päätyttyä."</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"lue synkronointiasetuksia"</string>
@@ -947,8 +957,6 @@
<string name="ime_action_default" msgid="2840921885558045721">"Suorita"</string>
<string name="dial_number_using" msgid="5789176425167573586">"Valitse numero"\n" <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="create_contact_using" msgid="4947405226788104538">"Luo yhteystieto"\n"käyttäen numeroa <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="accessibility_compound_button_selected" msgid="5612776946036285686">"valittu"</string>
- <string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"ei tarkistettu"</string>
<string name="grant_credentials_permission_message_header" msgid="6824538733852821001">"Seuraavat sovellukset pyytävät lupaasi käyttää tiliäsi nyt ja tulevaisuudessa."</string>
<string name="grant_credentials_permission_message_footer" msgid="3125211343379376561">"Sallitko tämän pyynnön?"</string>
<string name="grant_permissions_header_text" msgid="2722567482180797717">"Käyttöoikeuspyyntö"</string>
@@ -1019,6 +1027,9 @@
<string name="choose_account_label" msgid="4191313562041125787">"Valitse tili"</string>
<string name="number_picker_increment_button" msgid="4830170763103463443">"Lisää"</string>
<string name="number_picker_decrement_button" msgid="2576606679160067262">"Vähennä"</string>
+ <string name="action_bar_home_description" msgid="5293600496601490216">"Siirry etusivulle"</string>
+ <string name="action_bar_up_description" msgid="2237496562952152589">"Siirry ylös"</string>
+ <string name="action_menu_overflow_description" msgid="2295659037509008453">"Lisää asetuksia"</string>
<!-- no translation found for storage_internal (7556050805474115618) -->
<skip />
<!-- no translation found for storage_sd_card (8921771478629812343) -->
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index ee76ded..1160ab4 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -204,6 +204,8 @@
<string name="permdesc_getTasks" msgid="7048711358713443341">"Permet à l\'application de récupérer des informations sur des tâches en cours d\'exécution ou récemment utilisées. Des applications malveillantes peuvent ainsi obtenir des informations d\'ordre privé concernant d\'autres applications."</string>
<string name="permlab_reorderTasks" msgid="5669588525059921549">"Réorganisation des applications en cours d\'exécution"</string>
<string name="permdesc_reorderTasks" msgid="126252774270522835">"Permet à une application de placer des tâches au premier plan ou en arrière-plan. Des applications malveillantes peuvent se placer inopinément au premier plan sans votre autorisation."</string>
+ <string name="permlab_removeTasks" msgid="4802740047161700683">"arrêter l\'exécution d\'applications"</string>
+ <string name="permdesc_removeTasks" msgid="2000332928514575461">"Permet à une application de supprimer des tâches et de fermer leurs processus. Des applications malveillantes peuvent perturber le comportement des autres applications."</string>
<string name="permlab_setDebugApp" msgid="4339730312925176742">"Activation du débogage de l\'application"</string>
<string name="permdesc_setDebugApp" msgid="5584310661711990702">"Permet à une application d\'activer le mode de débogage d\'une autre application. Des applications malveillantes peuvent utiliser cette fonctionnalité pour interrompre d\'autres applications de façon inopinée."</string>
<string name="permlab_changeConfiguration" msgid="8214475779521218295">"Modification des paramètres de l\'IU"</string>
@@ -238,6 +240,10 @@
<string name="permdesc_batteryStats" msgid="5847319823772230560">"Autoriser la modification des statistiques de la batterie. Les applications normales n\'utilisent pas cette fonctionnalité."</string>
<string name="permlab_backup" msgid="470013022865453920">"contrôler la sauvegarde et la restauration du système"</string>
<string name="permdesc_backup" msgid="4837493065154256525">"Autorise l\'application à contrôler le mécanisme de sauvegarde et de restauration du système. Ne pas utiliser pour les applications standard."</string>
+ <!-- no translation found for permlab_confirm_full_backup (5557071325804469102) -->
+ <skip />
+ <!-- no translation found for permdesc_confirm_full_backup (9005017754175897954) -->
+ <skip />
<string name="permlab_internalSystemWindow" msgid="2148563628140193231">"Affichage de fenêtres non autorisées"</string>
<string name="permdesc_internalSystemWindow" msgid="5895082268284998469">"Permet de créer des fenêtres conçues pour l\'interface utilisateur du système interne. Les applications normales n\'utilisent pas cette fonctionnalité."</string>
<string name="permlab_systemAlertWindow" msgid="3372321942941168324">"Affichage d\'alertes système"</string>
@@ -444,6 +450,10 @@
<string name="permdesc_bluetooth" product="default" msgid="762515380679392945">"Permet à une application d\'obtenir la configuration du téléphone Bluetooth local, de se connecter à des appareils associés et d\'accepter leur connexion."</string>
<string name="permlab_nfc" msgid="4423351274757876953">"contrôler la communication en champ proche"</string>
<string name="permdesc_nfc" msgid="9171401851954407226">"Permet à une application de communiquer avec des tags, cartes et lecteurs prenant en charge la communication en champ proche (NFC)."</string>
+ <!-- no translation found for permlab_vpn (8345800584532175312) -->
+ <skip />
+ <!-- no translation found for permdesc_vpn (5617893078989944219) -->
+ <skip />
<string name="permlab_disableKeyguard" msgid="4977406164311535092">"Désactivation du verrouillage des touches"</string>
<string name="permdesc_disableKeyguard" msgid="3189763479326302017">"Permet à une application de désactiver le verrouillage des touches et toute sécurité par mot de passe. Exemple : Votre téléphone désactive le verrouillage du clavier lorsque vous recevez un appel, puis le réactive lorsque vous raccrochez."</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"Lecture des paramètres de synchronisation"</string>
@@ -947,8 +957,6 @@
<string name="ime_action_default" msgid="2840921885558045721">"Exécuter"</string>
<string name="dial_number_using" msgid="5789176425167573586">"Composer le numéro"\n"en utilisant <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="create_contact_using" msgid="4947405226788104538">"Ajouter un contact"\n"en utilisant <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="accessibility_compound_button_selected" msgid="5612776946036285686">"sélectionné"</string>
- <string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"non sélectionné"</string>
<string name="grant_credentials_permission_message_header" msgid="6824538733852821001">"Les applications suivantes demandent l\'autorisation d\'accéder à votre compte à partir de maintenant."</string>
<string name="grant_credentials_permission_message_footer" msgid="3125211343379376561">"Voulez-vous autoriser cette demande ?"</string>
<string name="grant_permissions_header_text" msgid="2722567482180797717">"Demande d\'accès"</string>
@@ -1019,6 +1027,9 @@
<string name="choose_account_label" msgid="4191313562041125787">"Sélectionner un compte"</string>
<string name="number_picker_increment_button" msgid="4830170763103463443">"Augmenter"</string>
<string name="number_picker_decrement_button" msgid="2576606679160067262">"Diminuer"</string>
+ <string name="action_bar_home_description" msgid="5293600496601490216">"Retour à l\'accueil"</string>
+ <string name="action_bar_up_description" msgid="2237496562952152589">"Revenir en haut de la page"</string>
+ <string name="action_menu_overflow_description" msgid="2295659037509008453">"Plus d\'options"</string>
<!-- no translation found for storage_internal (7556050805474115618) -->
<skip />
<!-- no translation found for storage_sd_card (8921771478629812343) -->
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 19e0985..cf3f502 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -204,6 +204,8 @@
<string name="permdesc_getTasks" msgid="7048711358713443341">"Aplikaciji omogućuje dohvaćanje podataka o trenutačno ili nedavno pokrenutim zadacima. Zlonamjernim aplikacijama može omogućiti otkrivanje privatnih podataka o drugim aplikacijama."</string>
<string name="permlab_reorderTasks" msgid="5669588525059921549">"promjena redoslijeda pokrenutih aplikacija"</string>
<string name="permdesc_reorderTasks" msgid="126252774270522835">"Aplikaciji omogućuje premještanje zadataka u prvi plan ili u pozadinu. Zlonamjerne aplikacije mogu se prisilno postaviti u prednji plan bez vašeg znanja."</string>
+ <string name="permlab_removeTasks" msgid="4802740047161700683">"zaustavljanje pokrenutih aplikacija"</string>
+ <string name="permdesc_removeTasks" msgid="2000332928514575461">"Omogućuje aplikaciji da ukloni zadatke i uništi njihove aplikacije. Zlonamjerne aplikacije mogu poremetiti ponašanje drugih aplikacija."</string>
<string name="permlab_setDebugApp" msgid="4339730312925176742">"omogućavanje uklanjanja programskih pogrešaka u aplikacijama"</string>
<string name="permdesc_setDebugApp" msgid="5584310661711990702">"Aplikaciji omogućuje uključivanje značajke uklanjanja programske pogreške za drugu aplikaciju. Zlonamjerne aplikacije to mogu koristiti za uklanjanje drugih aplikacija."</string>
<string name="permlab_changeConfiguration" msgid="8214475779521218295">"promjena postavki korisničkog sučelja"</string>
@@ -238,6 +240,10 @@
<string name="permdesc_batteryStats" msgid="5847319823772230560">"Omogućuje izmjenu prikupljene statistike o bateriji. Nije za upotrebu na uobičajenim aplikacijama."</string>
<string name="permlab_backup" msgid="470013022865453920">"sigurnosna kopija i oporavak nadzornog sustava"</string>
<string name="permdesc_backup" msgid="4837493065154256525">"Aplikaciji omogućuje nadzor nad mehanizmom stvaranja sigurnosnih kopija i oporavka sustava. Nije za upotrebu na uobičajenim aplikacijama."</string>
+ <!-- no translation found for permlab_confirm_full_backup (5557071325804469102) -->
+ <skip />
+ <!-- no translation found for permdesc_confirm_full_backup (9005017754175897954) -->
+ <skip />
<string name="permlab_internalSystemWindow" msgid="2148563628140193231">"prikaz neovlaštenih prozora"</string>
<string name="permdesc_internalSystemWindow" msgid="5895082268284998469">"Omogućuje stvaranje prozora kojima je namjena da se koriste u korisničkom sučelju internog sustava. Nije za upotrebu na uobičajenim aplikacijama."</string>
<string name="permlab_systemAlertWindow" msgid="3372321942941168324">"prikaz upozorenja na razini sustava"</string>
@@ -444,6 +450,10 @@
<string name="permdesc_bluetooth" product="default" msgid="762515380679392945">"Aplikaciji omogućuje pregled konfiguracije lokalnog Bluetooth telefona i uspostavljanje i prihvaćanje veza sa sparenim uređajima."</string>
<string name="permlab_nfc" msgid="4423351274757876953">"upravljaj beskontaktnom (NFC) komunikacijom"</string>
<string name="permdesc_nfc" msgid="9171401851954407226">"Aplikaciji omogućuje komunikaciju s Near Field Communication (NFC) oznakama, karticama i čitačima."</string>
+ <!-- no translation found for permlab_vpn (8345800584532175312) -->
+ <skip />
+ <!-- no translation found for permdesc_vpn (5617893078989944219) -->
+ <skip />
<string name="permlab_disableKeyguard" msgid="4977406164311535092">"onemogući zaključavanje tipkovnice"</string>
<string name="permdesc_disableKeyguard" msgid="3189763479326302017">"Aplikaciji omogućuje isključivanje zaključavanja tipkovnice i svih povezanih sigurnosnih zaporki. Jasan primjer toga daje isključivanje zaključavanja telefona kod primanja poziva, koje se ponovno aktivira nakon završetka poziva."</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"čitanje postavki sinkronizacije"</string>
@@ -947,8 +957,6 @@
<string name="ime_action_default" msgid="2840921885558045721">"Pokreni"</string>
<string name="dial_number_using" msgid="5789176425167573586">"Biraj broj"\n"koristeći <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="create_contact_using" msgid="4947405226788104538">"Stvori kontakt"\n"koristeći <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="accessibility_compound_button_selected" msgid="5612776946036285686">"označeno"</string>
- <string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"nije označeno"</string>
<string name="grant_credentials_permission_message_header" msgid="6824538733852821001">"Sljedeća aplikacija ili više njih zahtijevaju dopuštenje za pristup vašem računu, sad i u budućnosti."</string>
<string name="grant_credentials_permission_message_footer" msgid="3125211343379376561">"Dopuštate li taj zahtjev?"</string>
<string name="grant_permissions_header_text" msgid="2722567482180797717">"Zahtjev za pristup"</string>
@@ -1019,6 +1027,9 @@
<string name="choose_account_label" msgid="4191313562041125787">"Odaberite račun"</string>
<string name="number_picker_increment_button" msgid="4830170763103463443">"Povećaj"</string>
<string name="number_picker_decrement_button" msgid="2576606679160067262">"Smanji"</string>
+ <string name="action_bar_home_description" msgid="5293600496601490216">"Kreni na početnu"</string>
+ <string name="action_bar_up_description" msgid="2237496562952152589">"Kreni gore"</string>
+ <string name="action_menu_overflow_description" msgid="2295659037509008453">"Više opcija"</string>
<!-- no translation found for storage_internal (7556050805474115618) -->
<skip />
<!-- no translation found for storage_sd_card (8921771478629812343) -->
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 7da0eb6..340c2f9 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -204,6 +204,8 @@
<string name="permdesc_getTasks" msgid="7048711358713443341">"Lehetővé teszi az alkalmazás számára a jelenleg és a nemrég futó feladatok adatainak lekérését. A rosszindulatú alkalmazások privát adatokhoz juthatnak más alkalmazásokból."</string>
<string name="permlab_reorderTasks" msgid="5669588525059921549">"futó alkalmazások átrendezése"</string>
<string name="permdesc_reorderTasks" msgid="126252774270522835">"Lehetővé teszi egy alkalmazás számára, hogy feladatokat helyezzen át az előtérből a háttérbe és fordítva. A rosszindulatú alkalmazások az előtérbe helyezhetik magukat az Ön engedélye nélkül."</string>
+ <string name="permlab_removeTasks" msgid="4802740047161700683">"futó alkalmazások leállítása"</string>
+ <string name="permdesc_removeTasks" msgid="2000332928514575461">"Lehetővé teszi az alkalmazás számára, hogy töröljön feladatokat és leállítsa alkalmazásaikat. Rosszindulatú alkalmazások megzavarhatják más alkalmazások viselkedését."</string>
<string name="permlab_setDebugApp" msgid="4339730312925176742">"hibakeresés engedélyezése alkalmazásoknál"</string>
<string name="permdesc_setDebugApp" msgid="5584310661711990702">"Lehetővé teszi egy alkalmazás számára, hogy hibakeresést végezzen egy másik alkalmazáson. A rosszindulatú alkalmazások ezzel leállíthatnak más alkalmazásokat."</string>
<string name="permlab_changeConfiguration" msgid="8214475779521218295">"a felhasználói felület beállításainak módosítása"</string>
@@ -238,6 +240,10 @@
<string name="permdesc_batteryStats" msgid="5847319823772230560">"Lehetővé teszi a begyűjtött akkumulátoradatok módosítását. A normál alkalmazások nem használják ezt."</string>
<string name="permlab_backup" msgid="470013022865453920">"rendszer biztonsági mentésének és helyreállításának vezérlése"</string>
<string name="permdesc_backup" msgid="4837493065154256525">"Lehetővé teszi az alkalmazás számára a rendszer biztonsági mentési és helyreállítási mechanizmusának vezérlését. A normál alkalmazások nem használják ezt."</string>
+ <!-- no translation found for permlab_confirm_full_backup (5557071325804469102) -->
+ <skip />
+ <!-- no translation found for permdesc_confirm_full_backup (9005017754175897954) -->
+ <skip />
<string name="permlab_internalSystemWindow" msgid="2148563628140193231">"azonosítatlan ablakok megjelenítése"</string>
<string name="permdesc_internalSystemWindow" msgid="5895082268284998469">"Lehetővé teszi olyan ablakok létrehozását, amelyeket a belső rendszer felhasználói felülete általi használatra szántak. A normál alkalmazások nem használják ezt."</string>
<string name="permlab_systemAlertWindow" msgid="3372321942941168324">"rendszerszintű riasztások megjelenítése"</string>
@@ -444,6 +450,10 @@
<string name="permdesc_bluetooth" product="default" msgid="762515380679392945">"Lehetővé teszi egy alkalmazás számára a helyi Bluetooth telefon konfigurációjának megtekintését, valamint kapcsolatok kezdeményezését és fogadását a párosított eszközökkel."</string>
<string name="permlab_nfc" msgid="4423351274757876953">"NFC technológia vezérlése"</string>
<string name="permdesc_nfc" msgid="9171401851954407226">"Lehetővé teszi az alkalmazások számára, hogy NFC (Near Field Communication - kis hatósugarú vezeték nélküli kommunikáció) technológiát használó címkékkel, kártyákkal és leolvasókkal kommunikáljanak."</string>
+ <!-- no translation found for permlab_vpn (8345800584532175312) -->
+ <skip />
+ <!-- no translation found for permdesc_vpn (5617893078989944219) -->
+ <skip />
<string name="permlab_disableKeyguard" msgid="4977406164311535092">"billentyűzár kikapcsolása"</string>
<string name="permdesc_disableKeyguard" msgid="3189763479326302017">"Lehetővé teszi egy alkalmazás számára a billentyűzár és a kapcsolódó jelszavas biztonság kikapcsolását. Ennek egy szabályos példája, amikor a telefon kikapcsolja a billentyűzárat egy beérkező hívás fogadásakor, majd a hívás befejezése után újra bekapcsolja azt."</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"szinkronizálási beállítások olvasása"</string>
@@ -947,8 +957,6 @@
<string name="ime_action_default" msgid="2840921885558045721">"Végrehajtás"</string>
<string name="dial_number_using" msgid="5789176425167573586">"Szám hívása"\n"ezzel: <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="create_contact_using" msgid="4947405226788104538">"Névjegy létrehozása "\n"a(z) <xliff:g id="NUMBER">%s</xliff:g> szám használatával"</string>
- <string name="accessibility_compound_button_selected" msgid="5612776946036285686">"bejelölve"</string>
- <string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"nincs bejelölve"</string>
<string name="grant_credentials_permission_message_header" msgid="6824538733852821001">"A következő egy vagy több alkalmazás hozzáférési engedélyt kér a fiókjához mostanra és a jövőre nézve is."</string>
<string name="grant_credentials_permission_message_footer" msgid="3125211343379376561">"Engedélyezi ezt a kérelmet?"</string>
<string name="grant_permissions_header_text" msgid="2722567482180797717">"Hozzáférési kérelem"</string>
@@ -1019,6 +1027,9 @@
<string name="choose_account_label" msgid="4191313562041125787">"Fiók kiválasztása"</string>
<string name="number_picker_increment_button" msgid="4830170763103463443">"Növelés"</string>
<string name="number_picker_decrement_button" msgid="2576606679160067262">"Csökkentés"</string>
+ <string name="action_bar_home_description" msgid="5293600496601490216">"Ugrás a főoldalra"</string>
+ <string name="action_bar_up_description" msgid="2237496562952152589">"Felfele mozgás"</string>
+ <string name="action_menu_overflow_description" msgid="2295659037509008453">"További lehetőségek"</string>
<!-- no translation found for storage_internal (7556050805474115618) -->
<skip />
<!-- no translation found for storage_sd_card (8921771478629812343) -->
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 1e3dbe8..d224384 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -204,6 +204,8 @@
<string name="permdesc_getTasks" msgid="7048711358713443341">"Mengizinkan aplikasi mengambil informasi tentang tugas yang sedang dan baru saja dijalankan. Aplikasi hasad dapat menemukan informasi bajakan tentang aplikasi lain."</string>
<string name="permlab_reorderTasks" msgid="5669588525059921549">"atur urutan aplikasi yang berjalan"</string>
<string name="permdesc_reorderTasks" msgid="126252774270522835">"Mengizinkan aplikasi memindah tugas ke latar depan dan latar belakang. Aplikasi hasad dapat memaksa dirinya ke latar depan tanpa sepengetahuan Anda."</string>
+ <string name="permlab_removeTasks" msgid="4802740047161700683">"menghentikan aplikasi yang berjalan"</string>
+ <string name="permdesc_removeTasks" msgid="2000332928514575461">"Memungkinkan aplikasi menghapus tugas dan menghentikan aplikasinya. Aplikasi jahat dapat mengganggu perilaku aplikasi lain."</string>
<string name="permlab_setDebugApp" msgid="4339730312925176742">"aktifkan debugging aplikasi"</string>
<string name="permdesc_setDebugApp" msgid="5584310661711990702">"Mengizinkan aplikasi menghidupkan debug untuk aplikasi lain. Aplikasi hasad dapat menggunakan ini untuk menghentikan aplikasi penting lainnya."</string>
<string name="permlab_changeConfiguration" msgid="8214475779521218295">"ubah setelan UI Anda"</string>
@@ -238,6 +240,10 @@
<string name="permdesc_batteryStats" msgid="5847319823772230560">"Mengizinkan modifikasi statistik baterai yang terkumpul. Tidak untuk digunakan untuk aplikasi normal."</string>
<string name="permlab_backup" msgid="470013022865453920">"mengontrol cadangan dan pemulihan sistem"</string>
<string name="permdesc_backup" msgid="4837493065154256525">"Mengizinkan aplikasi mengontrol cadangan sistem dan mengembalikan mekanisme. Tidak untuk digunakan oleh aplikasi normal."</string>
+ <!-- no translation found for permlab_confirm_full_backup (5557071325804469102) -->
+ <skip />
+ <!-- no translation found for permdesc_confirm_full_backup (9005017754175897954) -->
+ <skip />
<string name="permlab_internalSystemWindow" msgid="2148563628140193231">"tampilkan jendela yang tidak diizinkan"</string>
<string name="permdesc_internalSystemWindow" msgid="5895082268284998469">"Mengizinkan pembuatan jendela yang dimaksudkan untuk digunakan oleh antarmuka pengguna sistem internal. Bukan untuk digunakan oleh aplikasi normal."</string>
<string name="permlab_systemAlertWindow" msgid="3372321942941168324">"tampilkan lansiran tingkat sistem"</string>
@@ -444,6 +450,10 @@
<string name="permdesc_bluetooth" product="default" msgid="762515380679392945">"Mengizinkan aplikasi melihat konfigurasi ponsel Bluetooth lokal, dan membuat dan menerima panggilan dengan perangkat yang disandingkan."</string>
<string name="permlab_nfc" msgid="4423351274757876953">"kontrol NFC"</string>
<string name="permdesc_nfc" msgid="9171401851954407226">"Mengizinkan aplikasi berkomunikasi dengan tag, kartu, dan pembaca Near Field Communication (NFC)."</string>
+ <!-- no translation found for permlab_vpn (8345800584532175312) -->
+ <skip />
+ <!-- no translation found for permdesc_vpn (5617893078989944219) -->
+ <skip />
<string name="permlab_disableKeyguard" msgid="4977406164311535092">"nonaktifkan kunci tombol"</string>
<string name="permdesc_disableKeyguard" msgid="3189763479326302017">"Mengizinkan aplikasi menonaktifkan keylock dan keamanan sandi terkait mana pun. Contoh yang sah adalah ponsel menonaktifkan keylock ketika menerima panggilan telepon masuk, kemudian mengaktifkan keylock sekali lagi setelah panggilan selesai."</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"baca setelan sinkron"</string>
@@ -947,8 +957,6 @@
<string name="ime_action_default" msgid="2840921885558045721">"Lakukan"</string>
<string name="dial_number_using" msgid="5789176425167573586">"Panggil nomor "\n"menggunakan<xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="create_contact_using" msgid="4947405226788104538">"Buat kenalan "\n"menggunakan <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="accessibility_compound_button_selected" msgid="5612776946036285686">"diperiksa"</string>
- <string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"tidak diperiksa"</string>
<string name="grant_credentials_permission_message_header" msgid="6824538733852821001">"Satu aplikasi berikut atau lebih membutuhkan izin untuk mengakses akun Anda, sekarang dan di masa depan."</string>
<string name="grant_credentials_permission_message_footer" msgid="3125211343379376561">"Apakah Anda ingin mengizinkan permintaan ini?"</string>
<string name="grant_permissions_header_text" msgid="2722567482180797717">"Akses Permintaan"</string>
@@ -1019,6 +1027,9 @@
<string name="choose_account_label" msgid="4191313562041125787">"Pilih akun"</string>
<string name="number_picker_increment_button" msgid="4830170763103463443">"Penambahan"</string>
<string name="number_picker_decrement_button" msgid="2576606679160067262">"Pengurangan"</string>
+ <string name="action_bar_home_description" msgid="5293600496601490216">"Navigasi ke beranda"</string>
+ <string name="action_bar_up_description" msgid="2237496562952152589">"Navigasi naik"</string>
+ <string name="action_menu_overflow_description" msgid="2295659037509008453">"Opsi lainnya"</string>
<!-- no translation found for storage_internal (7556050805474115618) -->
<skip />
<!-- no translation found for storage_sd_card (8921771478629812343) -->
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index b758705..eb450bb 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -204,6 +204,8 @@
<string name="permdesc_getTasks" msgid="7048711358713443341">"Consente all\'applicazione di recuperare informazioni sulle attività in esecuzione ed eseguite di recente. Le applicazioni dannose potrebbero essere in grado di scoprire informazioni riservate su altre applicazioni."</string>
<string name="permlab_reorderTasks" msgid="5669588525059921549">"riordinamento applicazioni in esecuz."</string>
<string name="permdesc_reorderTasks" msgid="126252774270522835">"Consente a un\'applicazione di spostare attività in primo e secondo piano. Le applicazioni dannose possono imporsi ponendosi automaticamente in primo piano."</string>
+ <string name="permlab_removeTasks" msgid="4802740047161700683">"interruzione dell\'esecuzione di applicazioni"</string>
+ <string name="permdesc_removeTasks" msgid="2000332928514575461">"Consente a un\'applicazione di rimuovere le attività e terminare le loro applicazioni. Le applicazioni dannose possono disturbare il comportamento di altre applicazioni."</string>
<string name="permlab_setDebugApp" msgid="4339730312925176742">"attivazione debug delle applicazioni"</string>
<string name="permdesc_setDebugApp" msgid="5584310661711990702">"Consente a un\'applicazione di attivare il debug per un\'altra applicazione. Le applicazioni dannose possono sfruttare questa possibilità per interrompere altre applicazioni."</string>
<string name="permlab_changeConfiguration" msgid="8214475779521218295">"modifica impostazioni UI"</string>
@@ -238,6 +240,8 @@
<string name="permdesc_batteryStats" msgid="5847319823772230560">"Consente la modifica delle statistiche sulla batteria raccolte. Da non usare per normali applicazioni."</string>
<string name="permlab_backup" msgid="470013022865453920">"controllo del backup di sistema e ripristino"</string>
<string name="permdesc_backup" msgid="4837493065154256525">"Consente all\'applicazione di controllare il meccanismo di backup e ripristino del sistema. Da non usare per normali applicazioni."</string>
+ <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"conferma di un\'operazione completa di backup o di ripristino"</string>
+ <string name="permdesc_confirm_full_backup" msgid="9005017754175897954">"Consente all\'applicazione di lanciare l\'interfaccia utente di conferma del backup completo. Non utilizzabile da qualsiasi applicazione."</string>
<string name="permlab_internalSystemWindow" msgid="2148563628140193231">"visualizzazione finestre non autorizzate"</string>
<string name="permdesc_internalSystemWindow" msgid="5895082268284998469">"Consente la creazione di finestre destinate all\'uso nell\'interfaccia utente di sistema interna. Da non usare per normali applicazioni."</string>
<string name="permlab_systemAlertWindow" msgid="3372321942941168324">"visualizzazione avvisi di sistema"</string>
@@ -444,6 +448,10 @@
<string name="permdesc_bluetooth" product="default" msgid="762515380679392945">"Consente a un\'applicazione di visualizzare la configurazione del telefono Bluetooth locale e di stabilire e accettare connessioni con dispositivi associati."</string>
<string name="permlab_nfc" msgid="4423351274757876953">"controllo Near Field Communication"</string>
<string name="permdesc_nfc" msgid="9171401851954407226">"Consente a un\'applicazione di comunicare con tag, schede e lettori NFC (Near Field Communication)."</string>
+ <!-- no translation found for permlab_vpn (8345800584532175312) -->
+ <skip />
+ <!-- no translation found for permdesc_vpn (5617893078989944219) -->
+ <skip />
<string name="permlab_disableKeyguard" msgid="4977406164311535092">"disattivazione blocco tastiera"</string>
<string name="permdesc_disableKeyguard" msgid="3189763479326302017">"Consente la disattivazione da parte di un\'applicazione del blocco tastiera e di eventuali protezioni tramite password associate. Un valido esempio è la disattivazione da parte del telefono del blocco tastiera quando riceve una telefonata in entrata, e la successiva riattivazione del blocco al termine della chiamata."</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"lettura impostazioni di sincronizz."</string>
@@ -947,8 +955,6 @@
<string name="ime_action_default" msgid="2840921885558045721">"Esegui"</string>
<string name="dial_number_using" msgid="5789176425167573586">"Componi numero"\n"utilizzando <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="create_contact_using" msgid="4947405226788104538">"Crea contatto"\n"utilizzando <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="accessibility_compound_button_selected" msgid="5612776946036285686">"selezionato"</string>
- <string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"non selezionato"</string>
<string name="grant_credentials_permission_message_header" msgid="6824538733852821001">"Le seguenti applicazioni richiedono l\'autorizzazione per accedere al tuo account, adesso e in futuro."</string>
<string name="grant_credentials_permission_message_footer" msgid="3125211343379376561">"Accettare la richiesta?"</string>
<string name="grant_permissions_header_text" msgid="2722567482180797717">"Richiesta di accesso"</string>
@@ -1019,6 +1025,9 @@
<string name="choose_account_label" msgid="4191313562041125787">"Seleziona un account"</string>
<string name="number_picker_increment_button" msgid="4830170763103463443">"Aumenta"</string>
<string name="number_picker_decrement_button" msgid="2576606679160067262">"Diminuisci"</string>
+ <string name="action_bar_home_description" msgid="5293600496601490216">"Vai alla home page"</string>
+ <string name="action_bar_up_description" msgid="2237496562952152589">"Vai in alto"</string>
+ <string name="action_menu_overflow_description" msgid="2295659037509008453">"Altre opzioni"</string>
<!-- no translation found for storage_internal (7556050805474115618) -->
<skip />
<!-- no translation found for storage_sd_card (8921771478629812343) -->
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 03131bf..3f3ae23 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -204,6 +204,8 @@
<string name="permdesc_getTasks" msgid="7048711358713443341">"מאפשר ליישום לאחזר מידע על משימות הפועלות כעת ושפעלו לאחרונה. עלול לאפשר ליישומים זדוניים לגלות מידע פרטי על יישומים אחרים."</string>
<string name="permlab_reorderTasks" msgid="5669588525059921549">"שנה את סדר היישומים הפועלים"</string>
<string name="permdesc_reorderTasks" msgid="126252774270522835">"מאפשר ליישום להעביר משימות לחזית ולרקע. יישומים זדוניים עלולים לאלץ את עצמם לחזית מבלי שתוכל לשלוט בהם."</string>
+ <string name="permlab_removeTasks" msgid="4802740047161700683">"הפסקת יישומים פועלים"</string>
+ <string name="permdesc_removeTasks" msgid="2000332928514575461">"מאפשר ליישום להסיר משימות ולסיים את פעולת היישומים שלהן. יישומים זדוניים עלולים לשבש את פעולת היישומים האחרים."</string>
<string name="permlab_setDebugApp" msgid="4339730312925176742">"הפוך איתור באגים ביישום לפעיל"</string>
<string name="permdesc_setDebugApp" msgid="5584310661711990702">"מאפשר ליישום להפעיל איתור באגים עבור יישום אחר. יישומים זדוניים יכולים להשתמש ביכולת זו כדי להשמיד יישומים אחרים."</string>
<string name="permlab_changeConfiguration" msgid="8214475779521218295">"שנה את הגדרות ממשק המשתמש"</string>
@@ -238,6 +240,10 @@
<string name="permdesc_batteryStats" msgid="5847319823772230560">"מאפשר שינוי של נתונים סטטיסטיים הנאספים על הסוללה. לא לשימוש של יישומים רגילים."</string>
<string name="permlab_backup" msgid="470013022865453920">"שלוט בגיבוי ובשחזור של המערכת"</string>
<string name="permdesc_backup" msgid="4837493065154256525">"מאפשר ליישום לשלוט במנגנון הגיבוי והשחזור של המערכת. לא לשימוש של יישומים רגילים."</string>
+ <!-- no translation found for permlab_confirm_full_backup (5557071325804469102) -->
+ <skip />
+ <!-- no translation found for permdesc_confirm_full_backup (9005017754175897954) -->
+ <skip />
<string name="permlab_internalSystemWindow" msgid="2148563628140193231">"הצג חלונות לא מורשים"</string>
<string name="permdesc_internalSystemWindow" msgid="5895082268284998469">"מאפשר יצירת חלונות המיועדים לשימוש של ממשק המשתמש במערכת הפנימית. לא לשימוש של יישומים רגילים."</string>
<string name="permlab_systemAlertWindow" msgid="3372321942941168324">"הצג התראות ברמת המערכת"</string>
@@ -444,6 +450,10 @@
<string name="permdesc_bluetooth" product="default" msgid="762515380679392945">"מאפשר ליישום להציג תצורה של מכשיר Bluetooth המקומי, וליצור ולקבל חיבורים עם מכשירים מותאמים."</string>
<string name="permlab_nfc" msgid="4423351274757876953">"שלוט ב-Near Field Communication"</string>
<string name="permdesc_nfc" msgid="9171401851954407226">"מאפשר ליישום לקיים תקשורת עם תגיות, כרטיסים וקוראים מסוג Near Field Communication ‏(NFC)."</string>
+ <!-- no translation found for permlab_vpn (8345800584532175312) -->
+ <skip />
+ <!-- no translation found for permdesc_vpn (5617893078989944219) -->
+ <skip />
<string name="permlab_disableKeyguard" msgid="4977406164311535092">"השבת נעילת מקשים"</string>
<string name="permdesc_disableKeyguard" msgid="3189763479326302017">"מאפשר ליישום להשבית את נעילת המקשים ואבטחת סיסמה משויכת. דוגמה תקפה לכך היא טלפון המשבית את נעילת המקשים בעת קבלת שיחת טלפון נכנסת, ולאחר מכן מפעיל מחדש את נעילת המקשים עם סיום השיחה."</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"קרא הגדרות סנכרון"</string>
@@ -947,8 +957,6 @@
<string name="ime_action_default" msgid="2840921885558045721">"בצע"</string>
<string name="dial_number_using" msgid="5789176425167573586">"חייג למספר"\n"באמצעות <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="create_contact_using" msgid="4947405226788104538">"צור איש קשר"\n"באמצעות <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="accessibility_compound_button_selected" msgid="5612776946036285686">"נבדק"</string>
- <string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"לא נבדק"</string>
<string name="grant_credentials_permission_message_header" msgid="6824538733852821001">"היישומים הבאים מבקשים הרשאה לגשת לחשבונך, כעת ובעתיד."</string>
<string name="grant_credentials_permission_message_footer" msgid="3125211343379376561">"האם ברצונך לאפשר בקשה זו?"</string>
<string name="grant_permissions_header_text" msgid="2722567482180797717">"בקשת גישה"</string>
@@ -1019,6 +1027,9 @@
<string name="choose_account_label" msgid="4191313562041125787">"בחר חשבון"</string>
<string name="number_picker_increment_button" msgid="4830170763103463443">"הוספה"</string>
<string name="number_picker_decrement_button" msgid="2576606679160067262">"הפחתה"</string>
+ <string name="action_bar_home_description" msgid="5293600496601490216">"נווט לדף הבית"</string>
+ <string name="action_bar_up_description" msgid="2237496562952152589">"נווט למעלה"</string>
+ <string name="action_menu_overflow_description" msgid="2295659037509008453">"אפשרויות נוספות"</string>
<!-- no translation found for storage_internal (7556050805474115618) -->
<skip />
<!-- no translation found for storage_sd_card (8921771478629812343) -->
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 76616af..4563091 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -204,6 +204,8 @@
<string name="permdesc_getTasks" msgid="7048711358713443341">"現在実行中または最近実行したタスクに関する情報の取得をアプリケーションに許可します。悪意のあるアプリケーションが他のアプリケーションの非公開情報を取得する恐れがあります。"</string>
<string name="permlab_reorderTasks" msgid="5669588525059921549">"実行中のアプリケーションの順序の変更"</string>
<string name="permdesc_reorderTasks" msgid="126252774270522835">"タスクをフォアグラウンドやバックグラウンドに移動することをアプリケーションに許可します。悪意のあるアプリケーションが優先されて、コントロールできなくなる恐れがあります。"</string>
+ <string name="permlab_removeTasks" msgid="4802740047161700683">"アプリケーションの実行停止"</string>
+ <string name="permdesc_removeTasks" msgid="2000332928514575461">"タスクの削除とアプリケーションの終了をアプリケーションに許可します。悪意のあるアプリケーションが他のアプリケーションの動作を妨害する恐れがあります。"</string>
<string name="permlab_setDebugApp" msgid="4339730312925176742">"アプリケーションのデバッグを有効にする"</string>
<string name="permdesc_setDebugApp" msgid="5584310661711990702">"別のアプリケーションをデバッグモードにすることをアプリケーションに許可します。悪意のあるアプリケーションが別のアプリケーションを終了させる恐れがあります。"</string>
<string name="permlab_changeConfiguration" msgid="8214475779521218295">"UI設定の変更"</string>
@@ -238,6 +240,10 @@
<string name="permdesc_batteryStats" msgid="5847319823772230560">"収集した電池統計情報の変更を許可します。通常のアプリケーションでは使用しません。"</string>
<string name="permlab_backup" msgid="470013022865453920">"システムのバックアップと復元を制御する"</string>
<string name="permdesc_backup" msgid="4837493065154256525">"システムのバックアップと復元メカニズムの制御をアプリケーションに許可します。通常のアプリケーションでは使用しません。"</string>
+ <!-- no translation found for permlab_confirm_full_backup (5557071325804469102) -->
+ <skip />
+ <!-- no translation found for permdesc_confirm_full_backup (9005017754175897954) -->
+ <skip />
<string name="permlab_internalSystemWindow" msgid="2148563628140193231">"未許可のウィンドウの表示"</string>
<string name="permdesc_internalSystemWindow" msgid="5895082268284998469">"内部システムのユーザーインターフェースで使用するためのウィンドウ作成を許可します。通常のアプリケーションでは使用しません。"</string>
<string name="permlab_systemAlertWindow" msgid="3372321942941168324">"システムレベルの警告の表示"</string>
@@ -444,6 +450,10 @@
<string name="permdesc_bluetooth" product="default" msgid="762515380679392945">"このBluetooth端末の設定表示、および別の端末をペアとして設定し接続を承認することをアプリケーションに許可します。"</string>
<string name="permlab_nfc" msgid="4423351274757876953">"NFCの管理"</string>
<string name="permdesc_nfc" msgid="9171401851954407226">"NFCタグ、カード、リーダーとの通信をアプリケーションに許可します。"</string>
+ <!-- no translation found for permlab_vpn (8345800584532175312) -->
+ <skip />
+ <!-- no translation found for permdesc_vpn (5617893078989944219) -->
+ <skip />
<string name="permlab_disableKeyguard" msgid="4977406164311535092">"キーロックを無効にする"</string>
<string name="permdesc_disableKeyguard" msgid="3189763479326302017">"キーロックや関連するパスワードセキュリティを無効にすることをアプリケーションに許可します。正当な利用の例では、かかってきた電話を受信する際にキーロックを無効にし、通話の終了時にキーロックを有効にし直します。"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"同期設定の読み取り"</string>
@@ -947,8 +957,6 @@
<string name="ime_action_default" msgid="2840921885558045721">"実行"</string>
<string name="dial_number_using" msgid="5789176425167573586">"<xliff:g id="NUMBER">%s</xliff:g>を使って"\n"発信"</string>
<string name="create_contact_using" msgid="4947405226788104538">"<xliff:g id="NUMBER">%s</xliff:g>を使って"\n"連絡先を新規登録"</string>
- <string name="accessibility_compound_button_selected" msgid="5612776946036285686">"オン"</string>
- <string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"オフ"</string>
<string name="grant_credentials_permission_message_header" msgid="6824538733852821001">"下記のアプリケーションが、アカウントへのアクセスを今後も許可するようにリクエストしています。"</string>
<string name="grant_credentials_permission_message_footer" msgid="3125211343379376561">"このリクエストを許可してもよろしいですか?"</string>
<string name="grant_permissions_header_text" msgid="2722567482180797717">"アクセスリクエスト"</string>
@@ -1019,6 +1027,9 @@
<string name="choose_account_label" msgid="4191313562041125787">"アカウントを選択"</string>
<string name="number_picker_increment_button" msgid="4830170763103463443">"増やす"</string>
<string name="number_picker_decrement_button" msgid="2576606679160067262">"減らす"</string>
+ <string name="action_bar_home_description" msgid="5293600496601490216">"ホームへ移動"</string>
+ <string name="action_bar_up_description" msgid="2237496562952152589">"上へ移動"</string>
+ <string name="action_menu_overflow_description" msgid="2295659037509008453">"その他のオプション"</string>
<!-- no translation found for storage_internal (7556050805474115618) -->
<skip />
<!-- no translation found for storage_sd_card (8921771478629812343) -->
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 15d4f05..3d692ea 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -204,6 +204,8 @@
<string name="permdesc_getTasks" msgid="7048711358713443341">"애플리케이션이 현재 실행 중이거나 최근에 실행된 작업에 대한 정보를 검색할 수 있도록 합니다. 이 경우 악성 애플리케이션이 다른 애플리케이션에 대한 개인 정보를 검색할 수 있습니다."</string>
<string name="permlab_reorderTasks" msgid="5669588525059921549">"실행 중인 애플리케이션 순서 재지정"</string>
<string name="permdesc_reorderTasks" msgid="126252774270522835">"애플리케이션이 작업을 포그라운드나 백그라운드로 이동할 수 있도록 합니다. 이 경우 악성 애플리케이션이 사용자의 조작 없이 앞으로 이동할 수 있습니다."</string>
+ <string name="permlab_removeTasks" msgid="4802740047161700683">"실행 중인 애플리케이션 중지"</string>
+ <string name="permdesc_removeTasks" msgid="2000332928514575461">"애플리케이션을 사용하여 작업을 삭제하거나 다른 애플리케이션을 중지시킬 수 있습니다. 악성 애플리케이션은 다른 애플리케이션의 동작을 멈추게 할 수 있습니다."</string>
<string name="permlab_setDebugApp" msgid="4339730312925176742">"애플리케이션 디버깅 사용"</string>
<string name="permdesc_setDebugApp" msgid="5584310661711990702">"애플리케이션이 다른 애플리케이션에 대해 디버깅을 사용할 수 있도록 합니다. 이 경우 악성 애플리케이션이 다른 애플리케이션을 중지시킬 수 있습니다."</string>
<string name="permlab_changeConfiguration" msgid="8214475779521218295">"UI 설정 변경"</string>
@@ -238,6 +240,10 @@
<string name="permdesc_batteryStats" msgid="5847319823772230560">"수집된 배터리 통계를 수정할 수 있도록 합니다. 일반 애플리케이션에서는 사용하지 않습니다."</string>
<string name="permlab_backup" msgid="470013022865453920">"시스템 백업 및 복원 관리"</string>
<string name="permdesc_backup" msgid="4837493065154256525">"애플리케이션이 시스템의 백업 및 복원 매커니즘을 제어할 수 있도록 합니다. 일반 애플리케이션에서는 사용하지 않습니다."</string>
+ <!-- no translation found for permlab_confirm_full_backup (5557071325804469102) -->
+ <skip />
+ <!-- no translation found for permdesc_confirm_full_backup (9005017754175897954) -->
+ <skip />
<string name="permlab_internalSystemWindow" msgid="2148563628140193231">"인증되지 않은 창 표시"</string>
<string name="permdesc_internalSystemWindow" msgid="5895082268284998469">"내부 시스템 사용자 인터페이스에서 사용하는 창을 만들 수 있도록 합니다. 일반 애플리케이션에서는 사용하지 않습니다."</string>
<string name="permlab_systemAlertWindow" msgid="3372321942941168324">"시스템 수준 경고 표시"</string>
@@ -444,6 +450,10 @@
<string name="permdesc_bluetooth" product="default" msgid="762515380679392945">"애플리케이션이 로컬 Bluetooth 전화의 구성을 보고 페어링된 장치에 연결하며 연결을 수락할 수 있도록 합니다."</string>
<string name="permlab_nfc" msgid="4423351274757876953">"NFC(Near Field Communication) 제어"</string>
<string name="permdesc_nfc" msgid="9171401851954407226">"애플리케이션에서 NFC(Near Field Communication) 태그, 카드 및 리더와 통신할 수 있습니다."</string>
+ <!-- no translation found for permlab_vpn (8345800584532175312) -->
+ <skip />
+ <!-- no translation found for permdesc_vpn (5617893078989944219) -->
+ <skip />
<string name="permlab_disableKeyguard" msgid="4977406164311535092">"키 잠금 사용 중지"</string>
<string name="permdesc_disableKeyguard" msgid="3189763479326302017">"애플리케이션이 키 잠금 및 관련 비밀번호 보안을 사용 중지할 수 있도록 합니다. 예를 들어, 휴대전화가 수신전화를 받을 때 키 잠금을 사용 중지했다가 통화가 끝나면 키 잠금을 다시 사용할 수 있습니다."</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"동기화 설정 읽기"</string>
@@ -947,8 +957,6 @@
<string name="ime_action_default" msgid="2840921885558045721">"실행"</string>
<string name="dial_number_using" msgid="5789176425167573586">"전화하기 "\n"<xliff:g id="NUMBER">%s</xliff:g>에 연결"</string>
<string name="create_contact_using" msgid="4947405226788104538">"전화번호부에"\n"<xliff:g id="NUMBER">%s</xliff:g> 추가"</string>
- <string name="accessibility_compound_button_selected" msgid="5612776946036285686">"선택함"</string>
- <string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"선택 안함"</string>
<string name="grant_credentials_permission_message_header" msgid="6824538733852821001">"다음 애플리케이션에서 계정 액세스 요청이 들어왔습니다."</string>
<string name="grant_credentials_permission_message_footer" msgid="3125211343379376561">"요청을 허용하시겠습니까?"</string>
<string name="grant_permissions_header_text" msgid="2722567482180797717">"액세스 요청"</string>
@@ -1019,6 +1027,9 @@
<string name="choose_account_label" msgid="4191313562041125787">"계정 선택"</string>
<string name="number_picker_increment_button" msgid="4830170763103463443">"올리기"</string>
<string name="number_picker_decrement_button" msgid="2576606679160067262">"줄이기"</string>
+ <string name="action_bar_home_description" msgid="5293600496601490216">"홈 탐색"</string>
+ <string name="action_bar_up_description" msgid="2237496562952152589">"위로 탐색"</string>
+ <string name="action_menu_overflow_description" msgid="2295659037509008453">"옵션 더보기"</string>
<!-- no translation found for storage_internal (7556050805474115618) -->
<skip />
<!-- no translation found for storage_sd_card (8921771478629812343) -->
diff --git a/core/res/res/values-land/dimens.xml b/core/res/res/values-land/dimens.xml
index fbfc3bf..8def578e 100644
--- a/core/res/res/values-land/dimens.xml
+++ b/core/res/res/values-land/dimens.xml
@@ -1,27 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
-/*
+/*
**
** Copyright 2010, The Android Open Source Project
**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
**
-** http://www.apache.org/licenses/LICENSE-2.0
+** http://www.apache.org/licenses/LICENSE-2.0
**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<resources>
- <dimen name="password_keyboard_key_height">47dip</dimen>
+ <!-- Default height of a key in the password keyboard for alpha -->
+ <dimen name="password_keyboard_key_height_alpha">47dip</dimen>
+ <!-- Default height of a key in the password keyboard for numeric -->
+ <dimen name="password_keyboard_key_height_numeric">60dip</dimen>
+ <!-- Default correction for the space key in the password keyboard -->
<dimen name="password_keyboard_spacebar_vertical_correction">2dip</dimen>
<dimen name="preference_screen_side_margin">96dp</dimen>
<dimen name="preference_screen_side_margin_negative">-100dp</dimen>
<dimen name="preference_widget_width">72dp</dimen>
+
+ <!-- Default height of an action bar. -->
+ <dimen name="action_bar_default_height">40dip</dimen>
+
</resources>
diff --git a/core/res/res/values-large/dimens.xml b/core/res/res/values-large/dimens.xml
index 1944029..55eb145 100644
--- a/core/res/res/values-large/dimens.xml
+++ b/core/res/res/values-large/dimens.xml
@@ -40,4 +40,13 @@
<item type="dimen" name="dialog_min_width_major">55%</item>
<item type="dimen" name="dialog_min_width_minor">80%</item>
+
+ <!-- Preference UI dimensions for larger screens. -->
+ <dimen name="preference_widget_width">56dp</dimen>
+ <!-- The maximum number of action buttons that should be permitted within
+ an action bar/action mode. This will be used to determine how many
+ showAsAction="ifRoom" items can fit. "always" items can override this. -->
+ <integer name="max_action_buttons">5</integer>
+ <!-- Default height of an action bar. -->
+ <dimen name="action_bar_default_height">56dip</dimen>
</resources>
diff --git a/core/res/res/values-w720dp/dimens.xml b/core/res/res/values-large/styles.xml
index a74c41c..96a8c84 100644
--- a/core/res/res/values-w720dp/dimens.xml
+++ b/core/res/res/values-large/styles.xml
@@ -15,10 +15,10 @@
-->
<resources>
- <!-- Size of the margin between the 'up' visual and the image in
- the action bar home section. -->
- <dimen name="action_bar_home_up_margin">-12dip</dimen>
- <!-- Size of the padding on either side of the app-supplied image
- in the action bar home section. -->
- <dimen name="action_bar_home_image_padding">16dip</dimen>
+ <style name="Widget.Holo.PreferenceFrameLayout">
+ <item name="android:borderTop">0dip</item>
+ <item name="android:borderBottom">48dip</item>
+ <item name="android:borderLeft">32dip</item>
+ <item name="android:borderRight">32dip</item>
+ </style>
</resources>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 8d2f565..69457ef 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -204,6 +204,8 @@
<string name="permdesc_getTasks" msgid="7048711358713443341">"Leidžia programai nuskaityti informaciją apie dabar ir neseniai veikusias užduotis. Gali leisti kenkėjiškoms programoms atrasti privačią informaciją apie kitas programas."</string>
<string name="permlab_reorderTasks" msgid="5669588525059921549">"iš naujo užsakyti veikiančias programas"</string>
<string name="permdesc_reorderTasks" msgid="126252774270522835">"Leidžia programai perkelti užduotis į aktyvųjį langą ir foną. Kenkėjiškos programos gali persikelti į priekį jums nieko nedarant."</string>
+ <string name="permlab_removeTasks" msgid="4802740047161700683">"sustabdyti vykdomas programas"</string>
+ <string name="permdesc_removeTasks" msgid="2000332928514575461">"Leidžiama programai pašalinti užduotis ir naikinti programas. Kenkėjiškos programos gali trukdyti kitoms programoms veikti."</string>
<string name="permlab_setDebugApp" msgid="4339730312925176742">"įgalinti programos derinimą"</string>
<string name="permdesc_setDebugApp" msgid="5584310661711990702">"Leidžia programai įjungti kitos programos derinimą. Kenkėjiškos programos tai gali naudoti, kad nutrauktų kitas programas."</string>
<string name="permlab_changeConfiguration" msgid="8214475779521218295">"keisti UI nustatymus"</string>
@@ -238,6 +240,10 @@
<string name="permdesc_batteryStats" msgid="5847319823772230560">"Leidžia keisti surinktą akumuliatoriaus statistiką. Neskirta įprastų programų naudojimui."</string>
<string name="permlab_backup" msgid="470013022865453920">"valdyti sistemos atsarginę kopiją ir atkūrimą"</string>
<string name="permdesc_backup" msgid="4837493065154256525">"Leidžia programai valdyti atsarginę sistemos kopiją ir atkurti mechanizmą. Neskirta naudoti įprastose programose."</string>
+ <!-- no translation found for permlab_confirm_full_backup (5557071325804469102) -->
+ <skip />
+ <!-- no translation found for permdesc_confirm_full_backup (9005017754175897954) -->
+ <skip />
<string name="permlab_internalSystemWindow" msgid="2148563628140193231">"pateikti neteisėtus langus"</string>
<string name="permdesc_internalSystemWindow" msgid="5895082268284998469">"Leidžia sukurti langus, kurie bus naudojami vidinės sistemos naudotojo sąsajos. Neskirta naudoti įprastose programose."</string>
<string name="permlab_systemAlertWindow" msgid="3372321942941168324">"pateikti sistemos lygio įspėjimus"</string>
@@ -444,6 +450,10 @@
<string name="permdesc_bluetooth" product="default" msgid="762515380679392945">"Leidžia programai žiūrėti vietinio „Bluetooth“ telefono konfigūraciją ir užmegzti bei priimti susietų įrenginių ryšius."</string>
<string name="permlab_nfc" msgid="4423351274757876953">"valdyti artimo lauko perdavimą (angl. „Near Field Communication“)"</string>
<string name="permdesc_nfc" msgid="9171401851954407226">"Leidžiama programai perduoti artimo lauko perdavimo (angl. „Near Field Communication“, NFC) žymas, korteles ir skaitymo programas."</string>
+ <!-- no translation found for permlab_vpn (8345800584532175312) -->
+ <skip />
+ <!-- no translation found for permdesc_vpn (5617893078989944219) -->
+ <skip />
<string name="permlab_disableKeyguard" msgid="4977406164311535092">"išjungti užraktą"</string>
<string name="permdesc_disableKeyguard" msgid="3189763479326302017">"Leidžia programai išjungti užraktą ir visą susijusią slaptažodžio apsaugą. Patikimas pavyzdys būtų užrakto išjungimas telefone gaunant įeinantį skambutį ir įgalinant jį vėl, kai skambutis baigtas."</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"skaityti sinchronizavimo nustatymus"</string>
@@ -947,8 +957,6 @@
<string name="ime_action_default" msgid="2840921885558045721">"Vykdyti"</string>
<string name="dial_number_using" msgid="5789176425167573586">"Rinkti numerį "\n"naudojant <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="create_contact_using" msgid="4947405226788104538">"Sukurti adresatą"\n"naudojant <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="accessibility_compound_button_selected" msgid="5612776946036285686">"pažymėtas"</string>
- <string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"nepatikrinta"</string>
<string name="grant_credentials_permission_message_header" msgid="6824538733852821001">"Viena ar daugiau programų, pateiktų toliau, prašo leidimo pasiekti jūsų paskyrą dabar ir ateityje."</string>
<string name="grant_credentials_permission_message_footer" msgid="3125211343379376561">"Ar norite leisti šią užklausą?"</string>
<string name="grant_permissions_header_text" msgid="2722567482180797717">"Prieigos užklausa"</string>
@@ -1019,6 +1027,9 @@
<string name="choose_account_label" msgid="4191313562041125787">"Pasirinkti paskyrą"</string>
<string name="number_picker_increment_button" msgid="4830170763103463443">"Padidinti"</string>
<string name="number_picker_decrement_button" msgid="2576606679160067262">"Sumažinti"</string>
+ <string name="action_bar_home_description" msgid="5293600496601490216">"Naršyti pagrindinį puslapį"</string>
+ <string name="action_bar_up_description" msgid="2237496562952152589">"Naršyti į viršų"</string>
+ <string name="action_menu_overflow_description" msgid="2295659037509008453">"Daugiau parinkčių"</string>
<!-- no translation found for storage_internal (7556050805474115618) -->
<skip />
<!-- no translation found for storage_sd_card (8921771478629812343) -->
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 8e7a839..e931728 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -204,6 +204,8 @@
<string name="permdesc_getTasks" msgid="7048711358713443341">"Ļauj lietojumprogrammai izgūt informāciju par pašlaik un nesen darbinātajiem uzdevumiem. Var atļaut lietojumprogrammām atklāt privātu informāciju par citām lietojumprogrammām."</string>
<string name="permlab_reorderTasks" msgid="5669588525059921549">"pārkārtot aktīvās lietojumprogrammas"</string>
<string name="permdesc_reorderTasks" msgid="126252774270522835">"Ļauj lietojumprogrammai pārvietot uzdevumus priekšplānā vai fonā. Ļaunprātīgas lietojumprogrammas var tikt parādītas priekšplānā bez jūsu vadības."</string>
+ <string name="permlab_removeTasks" msgid="4802740047161700683">"pārtrauc lietojumprogrammu darbību"</string>
+ <string name="permdesc_removeTasks" msgid="2000332928514575461">"Ļauj lietojumprogrammai noņemt uzdevumus un dzēst to lietojumprogrammas. Ļaunprātīgas lietojumprogrammas var traucēt citu lietojumprogrammu darbību."</string>
<string name="permlab_setDebugApp" msgid="4339730312925176742">"iespējot lietojumprogrammas atkļūdošanu"</string>
<string name="permdesc_setDebugApp" msgid="5584310661711990702">"Ļauj lietojumprogrammai ieslēgt citas lietojumprogrammas atkļūdošanu. Ļaunprātīgas lietojumprogrammas var to izmantot, lai pārtrauktu citu lietojumprogrammu darbību."</string>
<string name="permlab_changeConfiguration" msgid="8214475779521218295">"mainīt lietotāja saskarnes iestatījumus"</string>
@@ -238,6 +240,10 @@
<string name="permdesc_batteryStats" msgid="5847319823772230560">"Ļauj pārveidot apkopoto akumulatora jaudas statistiku. Nav paredzēts izmantošanai parastajās lietojumprogrammās."</string>
<string name="permlab_backup" msgid="470013022865453920">"kontrolēt sistēmas dublējumu un atjaunošanu"</string>
<string name="permdesc_backup" msgid="4837493065154256525">"Ļauj lietojumprogrammai kontrolēt sistēmas dublēšanas un atjaunošanas mehānismu. Nav paredzēts izmantošanai parastajās lietojumprogrammās."</string>
+ <!-- no translation found for permlab_confirm_full_backup (5557071325804469102) -->
+ <skip />
+ <!-- no translation found for permdesc_confirm_full_backup (9005017754175897954) -->
+ <skip />
<string name="permlab_internalSystemWindow" msgid="2148563628140193231">"attēlot neautorizētus logus"</string>
<string name="permdesc_internalSystemWindow" msgid="5895082268284998469">"Ļauj izveidot logus, kas ir paredzēti izmantošanai iekšējās sistēmas lietotāja saskarnē. Nav paredzēts izmantošanai parastajās lietojumprogrammās."</string>
<string name="permlab_systemAlertWindow" msgid="3372321942941168324">"rādīt sistēmas līmeņa brīdinājumus"</string>
@@ -444,6 +450,10 @@
<string name="permdesc_bluetooth" product="default" msgid="762515380679392945">"Ļauj lietojumprogrammai skatīt vietējā Bluetooth tālruņa konfigurāciju, kā arī veidot un pieņemt savienojumus ar pārī savienotām ierīcēm."</string>
<string name="permlab_nfc" msgid="4423351274757876953">"kontrolē tuvlauka saziņu"</string>
<string name="permdesc_nfc" msgid="9171401851954407226">"Ļauj lietojumprogrammai sazināties ar tuvlauka saziņas (Near Field Communication — NFC) atzīmēm, kartēm un lasītājiem."</string>
+ <!-- no translation found for permlab_vpn (8345800584532175312) -->
+ <skip />
+ <!-- no translation found for permdesc_vpn (5617893078989944219) -->
+ <skip />
<string name="permlab_disableKeyguard" msgid="4977406164311535092">"atspējot atslēgas slēgu"</string>
<string name="permdesc_disableKeyguard" msgid="3189763479326302017">"Ļauj lietojumprogrammai atspējot atslēgas slēgu un jebkādu saistīto paroles drošību. Atbilstošs tā piemērs: tālrunis atspējo atslēgas slēgu, saņemot ienākošu tālruņa zvanu, pēc tam atkārtoti iespējo atslēgas slēgu, kad saruna ir pabeigta."</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"lasīt sinhronizācijas iestatījumus"</string>
@@ -947,8 +957,6 @@
<string name="ime_action_default" msgid="2840921885558045721">"Izpildīt"</string>
<string name="dial_number_using" msgid="5789176425167573586">"Sastādiet numuru,"\n"izmantojot <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="create_contact_using" msgid="4947405226788104538">"Izveidot kontaktpersonu,"\n"izmantojot šo numuru: <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="accessibility_compound_button_selected" msgid="5612776946036285686">"atzīmēts"</string>
- <string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"nav atzīmēts"</string>
<string name="grant_credentials_permission_message_header" msgid="6824538733852821001">"Viena vai vairākas no tālāk minētajām lietojumprogrammām pieprasīja atļauju piekļūt jūsu kontam tagad un nākotnē."</string>
<string name="grant_credentials_permission_message_footer" msgid="3125211343379376561">"Vai vēlaties atļaut šo pieprasījumu?"</string>
<string name="grant_permissions_header_text" msgid="2722567482180797717">"Piekļuves pieprasījums"</string>
@@ -1019,6 +1027,9 @@
<string name="choose_account_label" msgid="4191313562041125787">"Atlasīt kontu"</string>
<string name="number_picker_increment_button" msgid="4830170763103463443">"Palielināt"</string>
<string name="number_picker_decrement_button" msgid="2576606679160067262">"Samazināt"</string>
+ <string name="action_bar_home_description" msgid="5293600496601490216">"Pārvietoties uz sākuma ekrānu"</string>
+ <string name="action_bar_up_description" msgid="2237496562952152589">"Pārvietoties augšup"</string>
+ <string name="action_menu_overflow_description" msgid="2295659037509008453">"Vairāk opciju"</string>
<!-- no translation found for storage_internal (7556050805474115618) -->
<skip />
<!-- no translation found for storage_sd_card (8921771478629812343) -->
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 233844a..6fe5dd7 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -204,6 +204,8 @@
<string name="permdesc_getTasks" msgid="7048711358713443341">"Tillater applikasjonen å hente informasjon om aktive og nylig kjørte programmer. Kan tillate ondsinnede applikasjoner å oppdage privat informasjon om andre applikasjoner."</string>
<string name="permlab_reorderTasks" msgid="5669588525059921549">"omordne kjørende applikasjoner"</string>
<string name="permdesc_reorderTasks" msgid="126252774270522835">"Tillater applikasjonen å flytte programmer til forgrunnen eller bakgrunnen. Ondsinnede applikasjoner kan tvinge seg selv til fronten."</string>
+ <string name="permlab_removeTasks" msgid="4802740047161700683">"stopp kjøring av applikasjoner"</string>
+ <string name="permdesc_removeTasks" msgid="2000332928514575461">"Gjør applikasjoner i stand til å fjerne og ødelegge for oppgaver. Skadelige applikasjoner kan forstyrre oppførselen til andre applikasjoner."</string>
<string name="permlab_setDebugApp" msgid="4339730312925176742">"aktiver applikasjonsdebugging"</string>
<string name="permdesc_setDebugApp" msgid="5584310661711990702">"Lar applikasjonen skru på debugging for en annen applikasjon. Ondsinnede applikasjoner kan bruke dette til å drepe andre applikasjoner."</string>
<string name="permlab_changeConfiguration" msgid="8214475779521218295">"endre innstillingene for brukergrensesnitt"</string>
@@ -238,6 +240,10 @@
<string name="permdesc_batteryStats" msgid="5847319823772230560">"Lar applikasjonen endre på innsamlet batteristatistikk. Ikke ment for vanlige applikasjoner."</string>
<string name="permlab_backup" msgid="470013022865453920">"kontrollere backup og gjenoppretting"</string>
<string name="permdesc_backup" msgid="4837493065154256525">"Lar applikasjonen kontrollere systemets backup- og gjenopprettingsmekanisme. Ikke ment for vanlige applikasjoner."</string>
+ <!-- no translation found for permlab_confirm_full_backup (5557071325804469102) -->
+ <skip />
+ <!-- no translation found for permdesc_confirm_full_backup (9005017754175897954) -->
+ <skip />
<string name="permlab_internalSystemWindow" msgid="2148563628140193231">"vis uautoriserte vinduer"</string>
<string name="permdesc_internalSystemWindow" msgid="5895082268284998469">"Tillater at det opprettes vinduer ment for bruk av systemets interne brukergrensesnitt. Ikke ment for vanlige applikasjoner."</string>
<string name="permlab_systemAlertWindow" msgid="3372321942941168324">"vise advarsler på systemnivå"</string>
@@ -444,6 +450,10 @@
<string name="permdesc_bluetooth" product="default" msgid="762515380679392945">"Lar applikasjonen se konfigurasjonen til den lokale Bluetooth-telefonen, og å opprette og godta tilkoblinger med parede enheter."</string>
<string name="permlab_nfc" msgid="4423351274757876953">"kontroller overføring av data med NFC-teknologi"</string>
<string name="permdesc_nfc" msgid="9171401851954407226">"Tillater programmet å kommunisere data via koder, kort og lesere for NFC-teknologi."</string>
+ <!-- no translation found for permlab_vpn (8345800584532175312) -->
+ <skip />
+ <!-- no translation found for permdesc_vpn (5617893078989944219) -->
+ <skip />
<string name="permlab_disableKeyguard" msgid="4977406164311535092">"slå av tastaturlås"</string>
<string name="permdesc_disableKeyguard" msgid="3189763479326302017">"Lar applikasjonen slå av tastaturlåsen og enhver tilknyttet passordsikkerhet. Et legitimt eksempel på dette er at telefonen slår av tastaturlåsen når den mottar et innkommende anrop, og så slår den på igjen når samtalen er over."</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"lese synkroniseringsinnstillinger"</string>
@@ -947,8 +957,6 @@
<string name="ime_action_default" msgid="2840921885558045721">"Utfør"</string>
<string name="dial_number_using" msgid="5789176425167573586">"Ring nummeret"\n"<xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="create_contact_using" msgid="4947405226788104538">"Lag kontakt"\n"med nummeret <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="accessibility_compound_button_selected" msgid="5612776946036285686">"valgt"</string>
- <string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"ikke valgt"</string>
<string name="grant_credentials_permission_message_header" msgid="6824538733852821001">"Ett eller flere av de følgende programmene ber om tillatelse til å få tilgang til kontoen din fra nå av."</string>
<string name="grant_credentials_permission_message_footer" msgid="3125211343379376561">"Vil du tillate dette?"</string>
<string name="grant_permissions_header_text" msgid="2722567482180797717">"Forespørsel om tilgang"</string>
@@ -1019,6 +1027,9 @@
<string name="choose_account_label" msgid="4191313562041125787">"Velg en konto"</string>
<string name="number_picker_increment_button" msgid="4830170763103463443">"Øke"</string>
<string name="number_picker_decrement_button" msgid="2576606679160067262">"Senke"</string>
+ <string name="action_bar_home_description" msgid="5293600496601490216">"Gå til startsiden"</string>
+ <string name="action_bar_up_description" msgid="2237496562952152589">"Gå opp"</string>
+ <string name="action_menu_overflow_description" msgid="2295659037509008453">"Flere alternativer"</string>
<!-- no translation found for storage_internal (7556050805474115618) -->
<skip />
<!-- no translation found for storage_sd_card (8921771478629812343) -->
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index c7922a0..81fa796 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -204,6 +204,8 @@
<string name="permdesc_getTasks" msgid="7048711358713443341">"Hiermee kan een app informatie over huidige en recent uitgevoerde taken ophalen. Schadelijke apps kunnen op deze manier mogelijk privé-informatie over andere apps achterhalen."</string>
<string name="permlab_reorderTasks" msgid="5669588525059921549">"actieve toepassingen opnieuw indelen"</string>
<string name="permdesc_reorderTasks" msgid="126252774270522835">"Hiermee kan een app taken naar de voor- en achtergrond verplaatsen. Schadelijke apps kunnen zichzelf op de voorgrond plaatsen zonder dat u hier iets aan kunt doen."</string>
+ <string name="permlab_removeTasks" msgid="4802740047161700683">"stoppen met toepassingen uitvoeren"</string>
+ <string name="permdesc_removeTasks" msgid="2000332928514575461">"Hiermee kan een app taken verwijderen en de bijbehorende apps sluiten. Schadelijke apps kunnen het gedrag van andere apps beïnvloeden."</string>
<string name="permlab_setDebugApp" msgid="4339730312925176742">"foutopsporing in toepassingen inschakelen"</string>
<string name="permdesc_setDebugApp" msgid="5584310661711990702">"Hiermee kan een app de foutopsporing voor een andere app inschakelen. Schadelijke apps kunnen dit gebruiken om andere apps af te sluiten."</string>
<string name="permlab_changeConfiguration" msgid="8214475779521218295">"uw UI-instellingen wijzigen"</string>
@@ -238,6 +240,10 @@
<string name="permdesc_batteryStats" msgid="5847319823772230560">"Hiermee kunnen verzamelde accustatistieken worden gewijzigd. Niet voor gebruik door normale toepassingen."</string>
<string name="permlab_backup" msgid="470013022865453920">"systeemback-up en -herstel beheren"</string>
<string name="permdesc_backup" msgid="4837493065154256525">"Hiermee kan de app het mechanisme voor systeemback-up en -herstel beheren. Niet voor gebruik door normale apps."</string>
+ <!-- no translation found for permlab_confirm_full_backup (5557071325804469102) -->
+ <skip />
+ <!-- no translation found for permdesc_confirm_full_backup (9005017754175897954) -->
+ <skip />
<string name="permlab_internalSystemWindow" msgid="2148563628140193231">"niet-geautoriseerde vensters weergeven"</string>
<string name="permdesc_internalSystemWindow" msgid="5895082268284998469">"Hiermee kunnen vensters worden gemaakt die door de interne systeemgebruikersinterface worden gebruikt. Niet voor gebruik door normale toepassingen."</string>
<string name="permlab_systemAlertWindow" msgid="3372321942941168324">"waarschuwingen op systeemniveau weergeven"</string>
@@ -444,6 +450,10 @@
<string name="permdesc_bluetooth" product="default" msgid="762515380679392945">"Hiermee kan een app de configuratie van een lokale Bluetooth-telefoon bekijken en verbindingen met gekoppelde apparaten maken en accepteren."</string>
<string name="permlab_nfc" msgid="4423351274757876953">"Near Field Communication regelen"</string>
<string name="permdesc_nfc" msgid="9171401851954407226">"Hiermee kan een app communiceren met NFC-tags (Near Field Communication), kaarten en lezers."</string>
+ <!-- no translation found for permlab_vpn (8345800584532175312) -->
+ <skip />
+ <!-- no translation found for permdesc_vpn (5617893078989944219) -->
+ <skip />
<string name="permlab_disableKeyguard" msgid="4977406164311535092">"toetsvergrendeling uitschakelen"</string>
<string name="permdesc_disableKeyguard" msgid="3189763479326302017">"Hiermee kan een app de toetsvergrendeling en bijbehorende wachtwoordbeveiliging uitschakelen. Een voorbeeld: de telefoon schakelt de toetsvergrendeling uit wanneer een oproep binnenkomt en schakelt de toetsvergrendeling weer in zodra de oproep wordt beëindigd."</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"synchronisatie-instellingen lezen"</string>
@@ -947,8 +957,6 @@
<string name="ime_action_default" msgid="2840921885558045721">"Uitvoeren"</string>
<string name="dial_number_using" msgid="5789176425167573586">"Nummer bellen"\n"met <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="create_contact_using" msgid="4947405226788104538">"Contact maken"\n"met <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="accessibility_compound_button_selected" msgid="5612776946036285686">"aangevinkt"</string>
- <string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"niet aangevinkt"</string>
<string name="grant_credentials_permission_message_header" msgid="6824538733852821001">"De volgende toepassingen vragen toegang tot uw account, nu en in de toekomst."</string>
<string name="grant_credentials_permission_message_footer" msgid="3125211343379376561">"Wilt u dit verzoek toestaan?"</string>
<string name="grant_permissions_header_text" msgid="2722567482180797717">"Toegangsverzoek"</string>
@@ -1019,6 +1027,9 @@
<string name="choose_account_label" msgid="4191313562041125787">"Selecteer een account"</string>
<string name="number_picker_increment_button" msgid="4830170763103463443">"Hoger"</string>
<string name="number_picker_decrement_button" msgid="2576606679160067262">"Lager"</string>
+ <string name="action_bar_home_description" msgid="5293600496601490216">"Navigeren naar startpositie"</string>
+ <string name="action_bar_up_description" msgid="2237496562952152589">"Omhoog navigeren"</string>
+ <string name="action_menu_overflow_description" msgid="2295659037509008453">"Meer opties"</string>
<!-- no translation found for storage_internal (7556050805474115618) -->
<skip />
<!-- no translation found for storage_sd_card (8921771478629812343) -->
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 23f48ff..28a28a5 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -204,6 +204,8 @@
<string name="permdesc_getTasks" msgid="7048711358713443341">"Umożliwia aplikacji pobieranie informacji na temat obecnie i ostatnio uruchomionych zadań. Może pozwolić szkodliwym aplikacjom na uzyskanie prywatnych informacji na temat innych aplikacji."</string>
<string name="permlab_reorderTasks" msgid="5669588525059921549">"zmienianie porządku uruchomionych aplikacji"</string>
<string name="permdesc_reorderTasks" msgid="126252774270522835">"Pozwala aplikacji na przenoszenie zadań z tła na pierwszy plan. Szkodliwe aplikacje mogą wymusić działanie pierwszoplanowe bez kontroli użytkownika."</string>
+ <string name="permlab_removeTasks" msgid="4802740047161700683">"zatrzymywanie uruchomionych aplikacji"</string>
+ <string name="permdesc_removeTasks" msgid="2000332928514575461">"Zezwala aplikacji na usuwanie zadań i kończenie związanych z nimi aplikacji. Złośliwe aplikacje mogą zakłócać działanie innych aplikacji."</string>
<string name="permlab_setDebugApp" msgid="4339730312925176742">"włączenie debugowania aplikacji"</string>
<string name="permdesc_setDebugApp" msgid="5584310661711990702">"Pozwala aplikacji na włączenie debugowania innej aplikacji. Szkodliwe aplikacje mogą to wykorzystać do wyłączenia innych programów."</string>
<string name="permlab_changeConfiguration" msgid="8214475779521218295">"zmienianie ustawień interfejsu użytkownika"</string>
@@ -238,6 +240,10 @@
<string name="permdesc_batteryStats" msgid="5847319823772230560">"Pozwala na zmianę zebranych statystyk dotyczących baterii. Nie do wykorzystania przez normalne aplikacje."</string>
<string name="permlab_backup" msgid="470013022865453920">"kontrolowanie tworzenia i przywracania kopii zapasowych systemu"</string>
<string name="permdesc_backup" msgid="4837493065154256525">"Zezwala aplikacji na kontrolowanie mechanizmu tworzenia i przywracania kopii zapasowych systemu. Opcja nie jest przeznaczona dla zwykłych aplikacji."</string>
+ <!-- no translation found for permlab_confirm_full_backup (5557071325804469102) -->
+ <skip />
+ <!-- no translation found for permdesc_confirm_full_backup (9005017754175897954) -->
+ <skip />
<string name="permlab_internalSystemWindow" msgid="2148563628140193231">"wyświetlanie nieuwierzytelnionych okien"</string>
<string name="permdesc_internalSystemWindow" msgid="5895082268284998469">"Pozwala na tworzenie okien, które przeznaczone są do wykorzystania przez wewnętrzny interfejs użytkownika systemu. Nie do wykorzystania przez normalne aplikacje."</string>
<string name="permlab_systemAlertWindow" msgid="3372321942941168324">"wyświetlanie ostrzeżeń systemowych"</string>
@@ -444,6 +450,10 @@
<string name="permdesc_bluetooth" product="default" msgid="762515380679392945">"Pozwala aplikacji na wyświetlanie konfiguracji lokalnego telefonu Bluetooth oraz na tworzenie i akceptowanie połączeń ze sparowanymi urządzeniami."</string>
<string name="permlab_nfc" msgid="4423351274757876953">"kontrolowanie łączności Near Field Communication"</string>
<string name="permdesc_nfc" msgid="9171401851954407226">"Zezwala aplikacji na komunikowanie się z użyciem tagów, kart i czytników Near Field Communication (NFC)."</string>
+ <!-- no translation found for permlab_vpn (8345800584532175312) -->
+ <skip />
+ <!-- no translation found for permdesc_vpn (5617893078989944219) -->
+ <skip />
<string name="permlab_disableKeyguard" msgid="4977406164311535092">"wyłączanie blokady klawiatury"</string>
<string name="permdesc_disableKeyguard" msgid="3189763479326302017">"Pozwala aplikacji na wyłączenie blokady klawiatury i wszystkich związanych z tym haseł zabezpieczających. Typowym przykładem takiego działania jest wyłączanie blokady klawiatury, gdy pojawia się połączenie przychodzące, a następnie ponowne jej włączanie po zakończeniu połączenia."</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"czytanie ustawień synchronizowania"</string>
@@ -947,8 +957,6 @@
<string name="ime_action_default" msgid="2840921885558045721">"Wykonaj"</string>
<string name="dial_number_using" msgid="5789176425167573586">"Połącz"\n"z numerem <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="create_contact_using" msgid="4947405226788104538">"Utwórz kontakt"\n"dla numeru <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="accessibility_compound_button_selected" msgid="5612776946036285686">"zaznaczone"</string>
- <string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"niezaznaczone"</string>
<string name="grant_credentials_permission_message_header" msgid="6824538733852821001">"Co najmniej jedna z następujących aplikacji żąda uprawnień dostępu do Twojego konta – teraz i w przyszłości."</string>
<string name="grant_credentials_permission_message_footer" msgid="3125211343379376561">"Chcesz zezwolić na to żądanie?"</string>
<string name="grant_permissions_header_text" msgid="2722567482180797717">"Żądanie dostępu"</string>
@@ -1019,6 +1027,9 @@
<string name="choose_account_label" msgid="4191313562041125787">"Wybierz konto"</string>
<string name="number_picker_increment_button" msgid="4830170763103463443">"Zwiększ"</string>
<string name="number_picker_decrement_button" msgid="2576606679160067262">"Zmniejsz"</string>
+ <string name="action_bar_home_description" msgid="5293600496601490216">"Przejdź do strony głównej"</string>
+ <string name="action_bar_up_description" msgid="2237496562952152589">"Przejdź wyżej"</string>
+ <string name="action_menu_overflow_description" msgid="2295659037509008453">"Więcej opcji"</string>
<!-- no translation found for storage_internal (7556050805474115618) -->
<skip />
<!-- no translation found for storage_sd_card (8921771478629812343) -->
diff --git a/core/res/res/values-port/dimens.xml b/core/res/res/values-port/dimens.xml
new file mode 100644
index 0000000..bf0a342
--- /dev/null
+++ b/core/res/res/values-port/dimens.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2011, 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.
+*/
+-->
+<resources>
+ <!-- The maximum number of action buttons that should be permitted within
+ an action bar/action mode. This will be used to determine how many
+ showAsAction="ifRoom" items can fit. "always" items can override this. -->
+ <integer name="max_action_buttons">2</integer>
+</resources>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 0b21b0c..e812d68 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -204,6 +204,8 @@
<string name="permdesc_getTasks" msgid="7048711358713443341">"Permite à aplicação obter informações sobre tarefas actualmente em execução e recentemente executadas. Pode permitir que aplicações maliciosas descubram informações privadas sobre outras aplicações."</string>
<string name="permlab_reorderTasks" msgid="5669588525059921549">"reordenar aplicações em execução"</string>
<string name="permdesc_reorderTasks" msgid="126252774270522835">"Permite a uma aplicação mover tarefas do primeiro e do segundo planos. Algumas aplicações maliciosas podem impor-se no primeiro plano sem o controlo do utilizador."</string>
+ <string name="permlab_removeTasks" msgid="4802740047161700683">"parar a execução de aplicações"</string>
+ <string name="permdesc_removeTasks" msgid="2000332928514575461">"Permite que uma aplicação remova tarefas e elimine as respetivas aplicações. As aplicações maliciosas podem perturbar o comportamento de outras aplicações."</string>
<string name="permlab_setDebugApp" msgid="4339730312925176742">"activar depuração da aplicação"</string>
<string name="permdesc_setDebugApp" msgid="5584310661711990702">"Permite a uma aplicação activar a depuração para outra aplicação. Algumas aplicações maliciosas podem utilizar este item para eliminar outras aplicações."</string>
<string name="permlab_changeConfiguration" msgid="8214475779521218295">"alterar definições da IU"</string>
@@ -238,6 +240,10 @@
<string name="permdesc_batteryStats" msgid="5847319823772230560">"Permite a modificação das estatísticas recolhidas sobre a bateria. Não se destina a utilização por aplicações normais."</string>
<string name="permlab_backup" msgid="470013022865453920">"controlar a cópia de segurança e restauro do sistema"</string>
<string name="permdesc_backup" msgid="4837493065154256525">"Permite que a aplicação controle o mecanismo de cópia de segurança e restauro do sistema. Não deve ser utilizado por aplicações normais."</string>
+ <!-- no translation found for permlab_confirm_full_backup (5557071325804469102) -->
+ <skip />
+ <!-- no translation found for permdesc_confirm_full_backup (9005017754175897954) -->
+ <skip />
<string name="permlab_internalSystemWindow" msgid="2148563628140193231">"apresentar janelas não autorizadas"</string>
<string name="permdesc_internalSystemWindow" msgid="5895082268284998469">"Permite a criação de janelas destinadas a utilização pela interface de utilizador interna do sistema. Não se destina a utilização por aplicações normais."</string>
<string name="permlab_systemAlertWindow" msgid="3372321942941168324">"apresentar alertas ao nível do sistema"</string>
@@ -444,6 +450,10 @@
<string name="permdesc_bluetooth" product="default" msgid="762515380679392945">"Permite a uma aplicação ver a configuração do telefone Bluetooth local, bem como efectuar e aceitar ligações com dispositivos emparelhados."</string>
<string name="permlab_nfc" msgid="4423351274757876953">"controlo Near Field Communication"</string>
<string name="permdesc_nfc" msgid="9171401851954407226">"Permite que uma aplicação comunique com etiquetas, cartões e leitores Near Field Communication (NFC)."</string>
+ <!-- no translation found for permlab_vpn (8345800584532175312) -->
+ <skip />
+ <!-- no translation found for permdesc_vpn (5617893078989944219) -->
+ <skip />
<string name="permlab_disableKeyguard" msgid="4977406164311535092">"desactivar bloqueio de teclas"</string>
<string name="permdesc_disableKeyguard" msgid="3189763479326302017">"Permite a uma aplicação desactivar o bloqueio de teclas e qualquer segurança por palavra-passe associada. Um exemplo legítimo é a desactivação do bloqueio de teclas pelo telefone ao receber uma chamada, reactivando, em seguida, o bloqueio de teclas ao terminar a chamada."</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"ler definições de sincronização"</string>
@@ -947,8 +957,6 @@
<string name="ime_action_default" msgid="2840921885558045721">"Executar"</string>
<string name="dial_number_using" msgid="5789176425167573586">"Marcar número"\n"utilizando <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="create_contact_using" msgid="4947405226788104538">"Criar contacto"\n"utilizando <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="accessibility_compound_button_selected" msgid="5612776946036285686">"verificado"</string>
- <string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"não verificado"</string>
<string name="grant_credentials_permission_message_header" msgid="6824538733852821001">"Uma ou várias das aplicações seguintes solicitam permissão para aceder à sua conta, agora e no futuro."</string>
<string name="grant_credentials_permission_message_footer" msgid="3125211343379376561">"Pretende autorizar este pedido?"</string>
<string name="grant_permissions_header_text" msgid="2722567482180797717">"Pedido de acesso"</string>
@@ -1019,6 +1027,9 @@
<string name="choose_account_label" msgid="4191313562041125787">"Seleccionar conta"</string>
<string name="number_picker_increment_button" msgid="4830170763103463443">"Aumentar"</string>
<string name="number_picker_decrement_button" msgid="2576606679160067262">"Diminuir"</string>
+ <string name="action_bar_home_description" msgid="5293600496601490216">"Navegar para página inicial"</string>
+ <string name="action_bar_up_description" msgid="2237496562952152589">"Navegar para cima"</string>
+ <string name="action_menu_overflow_description" msgid="2295659037509008453">"Mais opções"</string>
<!-- no translation found for storage_internal (7556050805474115618) -->
<skip />
<!-- no translation found for storage_sd_card (8921771478629812343) -->
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index db5f0c6..be6dcbe 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -204,6 +204,8 @@
<string name="permdesc_getTasks" msgid="7048711358713443341">"Permite que o aplicativo recupere as informações sobre tarefas em execução no momento ou recentemente. Pode permitir que aplicativos maliciosos descubram informações particulares sobre outros aplicativos."</string>
<string name="permlab_reorderTasks" msgid="5669588525059921549">"reorganizar aplicativos em execução"</string>
<string name="permdesc_reorderTasks" msgid="126252774270522835">"Permite que um aplicativo mova as tarefas para o primeiro e para o segundo planos. Aplicativos maliciosos podem se forçar à frente sem o seu controle."</string>
+ <string name="permlab_removeTasks" msgid="4802740047161700683">"parar aplicativos em execução"</string>
+ <string name="permdesc_removeTasks" msgid="2000332928514575461">"Permite que um aplicativo remova tarefas e desative seus aplicativos. Aplicativos maliciosos podem interferir no comportamento de outros aplicativos."</string>
<string name="permlab_setDebugApp" msgid="4339730312925176742">"ativar depuração do aplicativo"</string>
<string name="permdesc_setDebugApp" msgid="5584310661711990702">"Permite que um aplicativo ative a depuração de outro aplicativo. Aplicativos maliciosos podem usar isso para encerrar outros aplicativos."</string>
<string name="permlab_changeConfiguration" msgid="8214475779521218295">"alterar as suas configurações de UI"</string>
@@ -238,6 +240,10 @@
<string name="permdesc_batteryStats" msgid="5847319823772230560">"Permite a modificação das estatísticas de bateria coletadas. Não deve ser usado por aplicativos normais."</string>
<string name="permlab_backup" msgid="470013022865453920">"controlar backup e restauração do sistema"</string>
<string name="permdesc_backup" msgid="4837493065154256525">"Permite que o aplicativo controle o mecanismo de backup e restauração do sistema. Não deve ser usado por aplicativos normais."</string>
+ <!-- no translation found for permlab_confirm_full_backup (5557071325804469102) -->
+ <skip />
+ <!-- no translation found for permdesc_confirm_full_backup (9005017754175897954) -->
+ <skip />
<string name="permlab_internalSystemWindow" msgid="2148563628140193231">"exibir janelas não autorizadas"</string>
<string name="permdesc_internalSystemWindow" msgid="5895082268284998469">"Permite a criação de janelas destinadas ao uso pela interface de usuário do sistema interno. Não deve ser usado por aplicativos normais."</string>
<string name="permlab_systemAlertWindow" msgid="3372321942941168324">"exibir alertas de nível do sistema"</string>
@@ -444,6 +450,10 @@
<string name="permdesc_bluetooth" product="default" msgid="762515380679392945">"Permite que um aplicativo veja a configuração do telefone Bluetooth local e que possa fazer e aceitar conexões com dispositivos pareados."</string>
<string name="permlab_nfc" msgid="4423351274757876953">"controlar a comunicação a curta distância"</string>
<string name="permdesc_nfc" msgid="9171401851954407226">"Permite que um aplicativo se comunique com tags, cartões e leitores de comunicação a curta distância (NFC)."</string>
+ <!-- no translation found for permlab_vpn (8345800584532175312) -->
+ <skip />
+ <!-- no translation found for permdesc_vpn (5617893078989944219) -->
+ <skip />
<string name="permlab_disableKeyguard" msgid="4977406164311535092">"desativar o bloqueio de teclas"</string>
<string name="permdesc_disableKeyguard" msgid="3189763479326302017">"Permite que um aplicativo desative o bloqueio de teclas e qualquer segurança por senha associada. Um exemplo legítimo disso é a desativação do bloqueio de teclas pelo telefone ao receber uma chamada e a reativação do bloqueio quando a chamada é finalizada."</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"ler as configurações de sincronização"</string>
@@ -947,8 +957,6 @@
<string name="ime_action_default" msgid="2840921885558045721">"Executar"</string>
<string name="dial_number_using" msgid="5789176425167573586">"Discar número"\n"usando <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="create_contact_using" msgid="4947405226788104538">"Criar contato "\n"usando <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="accessibility_compound_button_selected" msgid="5612776946036285686">"selecionado"</string>
- <string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"não selecionado"</string>
<string name="grant_credentials_permission_message_header" msgid="6824538733852821001">"O aplicativo a seguir ou outros aplicativos solicitam permissão para acessar a sua conta, agora e no futuro."</string>
<string name="grant_credentials_permission_message_footer" msgid="3125211343379376561">"Deseja permitir essa solicitação?"</string>
<string name="grant_permissions_header_text" msgid="2722567482180797717">"Solicitação de acesso"</string>
@@ -1019,6 +1027,9 @@
<string name="choose_account_label" msgid="4191313562041125787">"Selecione uma conta"</string>
<string name="number_picker_increment_button" msgid="4830170763103463443">"Incremento"</string>
<string name="number_picker_decrement_button" msgid="2576606679160067262">"Redução"</string>
+ <string name="action_bar_home_description" msgid="5293600496601490216">"Navegar na página inicial"</string>
+ <string name="action_bar_up_description" msgid="2237496562952152589">"Navegar para cima"</string>
+ <string name="action_menu_overflow_description" msgid="2295659037509008453">"Mais opções"</string>
<!-- no translation found for storage_internal (7556050805474115618) -->
<skip />
<!-- no translation found for storage_sd_card (8921771478629812343) -->
diff --git a/core/res/res/values-rm/strings.xml b/core/res/res/values-rm/strings.xml
index f98d57e..9865b47 100644
--- a/core/res/res/values-rm/strings.xml
+++ b/core/res/res/values-rm/strings.xml
@@ -208,6 +208,10 @@
<string name="permdesc_getTasks" msgid="7048711358713443341">"Permetta a l\'applicaziun da recuperar infurmaziuns davart incumbensas che vegnan exequidas u èn gist vegnidas exequidas. Applicaziuns donnegiusas pon uschia obtegnair infurmaziuns privatas concernent autras applicaziuns."</string>
<string name="permlab_reorderTasks" msgid="5669588525059921549">"reorganisar las applicaziuns che vegnan exequidas"</string>
<string name="permdesc_reorderTasks" msgid="126252774270522835">"Permetta ad ina applicaziun da spustar las incumbensas en il fund davant u en il fund davos. Applicaziuns donnegiusas pon sa mussar en il fund davant senza Vossa autorisaziun."</string>
+ <!-- no translation found for permlab_removeTasks (4802740047161700683) -->
+ <skip />
+ <!-- no translation found for permdesc_removeTasks (2000332928514575461) -->
+ <skip />
<string name="permlab_setDebugApp" msgid="4339730312925176742">"activar il debugging da l\'applicaziun"</string>
<string name="permdesc_setDebugApp" msgid="5584310661711990702">"Permetta ad ina applicaziun dad activar il debugging per autras applicaziuns. Applicaziuns donnegiusas pon uschia serrar autras applicaziuns."</string>
<string name="permlab_changeConfiguration" msgid="8214475779521218295">"modifitgar ils parameters da la UI"</string>
@@ -242,6 +246,10 @@
<string name="permdesc_batteryStats" msgid="5847319823772230560">"Permetta da modifitgar las datas statisticas da l\'accu. Betg previs per applicaziuns normalas."</string>
<string name="permlab_backup" msgid="470013022865453920">"controllar las copias da segirezza e la restauraziun dal sistem"</string>
<string name="permdesc_backup" msgid="4837493065154256525">"Permetta a l\'applicaziun da controllar il mecanissem da copias da segirezza e da restauraziun dal sistem. Betg previs per applicaziuns normalas."</string>
+ <!-- no translation found for permlab_confirm_full_backup (5557071325804469102) -->
+ <skip />
+ <!-- no translation found for permdesc_confirm_full_backup (9005017754175897954) -->
+ <skip />
<string name="permlab_internalSystemWindow" msgid="2148563628140193231">"mussar fanestras betg autorisadas"</string>
<string name="permdesc_internalSystemWindow" msgid="5895082268284998469">"Permetta da crear fanestras destinadas per l\'utilisaziun da l\'interfatscha d\'utilisader interna dal sistem. Questa funcziun n\'è betg previsa per applicaziuns normalas."</string>
<string name="permlab_systemAlertWindow" msgid="3372321942941168324">"mussar avertiments dal sistem"</string>
@@ -456,6 +464,10 @@
<skip />
<!-- no translation found for permdesc_nfc (9171401851954407226) -->
<skip />
+ <!-- no translation found for permlab_vpn (8345800584532175312) -->
+ <skip />
+ <!-- no translation found for permdesc_vpn (5617893078989944219) -->
+ <skip />
<string name="permlab_disableKeyguard" msgid="4977406164311535092">"deactivar la bloccaziun da la tastatura"</string>
<string name="permdesc_disableKeyguard" msgid="3189763479326302017">"Permetta ad ina applicaziun da deactivar la bloccaziun da la tastatura e la protecziun cun il pled-clav associada. In exempel dad ina utilisaziun legitima: La bloccaziun da la tastatura vegn deactivada sche Vus retschavais in clom ed ella vegn reactivada sche Vus finis il telefon."</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"leger ils parameters da sincronisaziun"</string>
@@ -1008,8 +1020,6 @@
<string name="ime_action_default" msgid="2840921885558045721">"Exequir"</string>
<string name="dial_number_using" msgid="5789176425167573586">"Cumponer il numer"\n"cun utilisar <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="create_contact_using" msgid="4947405226788104538">"Agiuntar in contact"\n"cun il numer <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="accessibility_compound_button_selected" msgid="5612776946036285686">"activà"</string>
- <string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"betg activà"</string>
<string name="grant_credentials_permission_message_header" msgid="6824538733852821001">"Las suandantas applicaziuns dumondan l\'autorisaziun dad acceder a partir dad ussa a Voss conto."</string>
<string name="grant_credentials_permission_message_footer" msgid="3125211343379376561">"Vulais Vus autorisar questa dumonda?"</string>
<string name="grant_permissions_header_text" msgid="2722567482180797717">"Dumonda d\'access"</string>
@@ -1113,6 +1123,12 @@
<skip />
<!-- no translation found for number_picker_decrement_button (2576606679160067262) -->
<skip />
+ <!-- no translation found for action_bar_home_description (5293600496601490216) -->
+ <skip />
+ <!-- no translation found for action_bar_up_description (2237496562952152589) -->
+ <skip />
+ <!-- no translation found for action_menu_overflow_description (2295659037509008453) -->
+ <skip />
<!-- no translation found for storage_internal (7556050805474115618) -->
<skip />
<!-- no translation found for storage_sd_card (8921771478629812343) -->
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index bc5d350..0daabb8 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -204,6 +204,8 @@
<string name="permdesc_getTasks" msgid="7048711358713443341">"Permite aplicaţiei să regăsească informaţii despre activităţile rulate curent şi recent. Poate permite aplicaţiilor rău-intenţionate să descopere informaţii confidenţiale despre alte aplicaţii."</string>
<string name="permlab_reorderTasks" msgid="5669588525059921549">"reordonare aplicaţii aflate în derulare"</string>
<string name="permdesc_reorderTasks" msgid="126252774270522835">"Permite unei aplicaţii să mute activităţile în prim-plan şi în fundal. Aplicaţiile rău-intenţionate ar putea să apară forţat în prim-plan, fără ca dvs. să puteţi controla acest lucru."</string>
+ <string name="permlab_removeTasks" msgid="4802740047161700683">"oprirea aplicaţiilor care rulează"</string>
+ <string name="permdesc_removeTasks" msgid="2000332928514575461">"Permite unei aplicaţii să elimine sarcini şi să închidă aplicaţiile corespunzătoare acestora. Aplicaţiile rău intenţionate pot perturba comportamentul altor aplicaţii."</string>
<string name="permlab_setDebugApp" msgid="4339730312925176742">"activare depanare aplicaţie"</string>
<string name="permdesc_setDebugApp" msgid="5584310661711990702">"Permite unei aplicaţii să activeze depanarea pentru o altă aplicaţie. Aplicaţiile rău-intenţionate ar putea să utilizeze această permisiune pentru a închide alte aplicaţii."</string>
<string name="permlab_changeConfiguration" msgid="8214475779521218295">"modificare setări UI"</string>
@@ -238,6 +240,10 @@
<string name="permdesc_batteryStats" msgid="5847319823772230560">"Permite modificarea statisticilor colectate despre baterie. Nu se utilizează de aplicaţiile obişnuite."</string>
<string name="permlab_backup" msgid="470013022865453920">"controlare copiere de rezervă şi restabilire a sistemului"</string>
<string name="permdesc_backup" msgid="4837493065154256525">"Permite aplicaţiei să controleze mecanismul de copiere de rezervă şi restabilire. Nu se utilizează de aplicaţiile obişnuite."</string>
+ <!-- no translation found for permlab_confirm_full_backup (5557071325804469102) -->
+ <skip />
+ <!-- no translation found for permdesc_confirm_full_backup (9005017754175897954) -->
+ <skip />
<string name="permlab_internalSystemWindow" msgid="2148563628140193231">"afişare ferestre neautorizate"</string>
<string name="permdesc_internalSystemWindow" msgid="5895082268284998469">"Permite crearea de ferestre menite să fie utilizate de interfaţa utilizatorului a sistemului intern. Nu se utilizează de aplicaţiile obişnuite."</string>
<string name="permlab_systemAlertWindow" msgid="3372321942941168324">"afişare alerte la nivel de sistem"</string>
@@ -444,6 +450,10 @@
<string name="permdesc_bluetooth" product="default" msgid="762515380679392945">"Permite unei aplicaţii să vizualizeze configuraţia telefonului Bluetooth local, să efectueze şi să accepte conexiuni cu dispozitive pereche."</string>
<string name="permlab_nfc" msgid="4423351274757876953">"controlare schimb de date prin Near Field Communication"</string>
<string name="permdesc_nfc" msgid="9171401851954407226">"Permite unei aplicaţii să comunice cu etichetele, cardurile şi cititoarele NFC (Near Field Communication)."</string>
+ <!-- no translation found for permlab_vpn (8345800584532175312) -->
+ <skip />
+ <!-- no translation found for permdesc_vpn (5617893078989944219) -->
+ <skip />
<string name="permlab_disableKeyguard" msgid="4977406164311535092">"dezactivare blocare taste"</string>
<string name="permdesc_disableKeyguard" msgid="3189763479326302017">"Permite unei aplicaţii să dezactiveze blocarea tastelor şi orice modalitate asociată de securitate a parolelor. Un bun exemplu este deblocarea tastelor de către telefon atunci când se primeşte un apel şi reactivarea blocării tastelor la terminarea apelului."</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"citire setări sincronizare"</string>
@@ -947,8 +957,6 @@
<string name="ime_action_default" msgid="2840921885558045721">"Executaţi"</string>
<string name="dial_number_using" msgid="5789176425167573586">"Formaţi numărul"\n"utilizând <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="create_contact_using" msgid="4947405226788104538">"Creaţi contactul"\n"utilizând <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="accessibility_compound_button_selected" msgid="5612776946036285686">"bifat"</string>
- <string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"nebifat"</string>
<string name="grant_credentials_permission_message_header" msgid="6824538733852821001">"Următoarele aplicaţii solicită permisiunea de a accesa contul dvs. acum şi în viitor."</string>
<string name="grant_credentials_permission_message_footer" msgid="3125211343379376561">"Permiteţi această solicitare?"</string>
<string name="grant_permissions_header_text" msgid="2722567482180797717">"Solicitare de acces"</string>
@@ -1019,6 +1027,9 @@
<string name="choose_account_label" msgid="4191313562041125787">"Selectaţi un cont"</string>
<string name="number_picker_increment_button" msgid="4830170763103463443">"Incrementaţi"</string>
<string name="number_picker_decrement_button" msgid="2576606679160067262">"Decrementaţi"</string>
+ <string name="action_bar_home_description" msgid="5293600496601490216">"Navigaţi la ecranul de pornire"</string>
+ <string name="action_bar_up_description" msgid="2237496562952152589">"Navigaţi în sus"</string>
+ <string name="action_menu_overflow_description" msgid="2295659037509008453">"Mai multe opţiuni"</string>
<!-- no translation found for storage_internal (7556050805474115618) -->
<skip />
<!-- no translation found for storage_sd_card (8921771478629812343) -->
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index c87be04..083a044 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -204,6 +204,8 @@
<string name="permdesc_getTasks" msgid="7048711358713443341">"Позволяет приложению получать сведения о последних и текущих задачах. Вредоносные приложения могут получить доступ к конфиденциальной информации о других приложениях."</string>
<string name="permlab_reorderTasks" msgid="5669588525059921549">"изменять порядок запущенных приложений"</string>
<string name="permdesc_reorderTasks" msgid="126252774270522835">"Позволяет приложению переключать режим выполнения задачи с активного на фоновый. Вредоносные приложения могут установить для себя активный режим без уведомления."</string>
+ <string name="permlab_removeTasks" msgid="4802740047161700683">"останавливать запущенные приложения"</string>
+ <string name="permdesc_removeTasks" msgid="2000332928514575461">"Разрешает приложению удалять задачи и их приложения. Вредоносные программы могут нарушать работу других приложений."</string>
<string name="permlab_setDebugApp" msgid="4339730312925176742">"запускать отладку приложения"</string>
<string name="permdesc_setDebugApp" msgid="5584310661711990702">"Позволяет приложению запускать процесс отладки другого приложения. Вредоносные приложения могут использовать эту возможность для остановки других приложений."</string>
<string name="permlab_changeConfiguration" msgid="8214475779521218295">"изменять настройки пользовательского интерфейса"</string>
@@ -238,6 +240,10 @@
<string name="permdesc_batteryStats" msgid="5847319823772230560">"Позволяет изменять собранную статистику батареи. Не предназначено для использования обычными приложениями."</string>
<string name="permlab_backup" msgid="470013022865453920">"управление резервным копированием и восстановлением системы"</string>
<string name="permdesc_backup" msgid="4837493065154256525">"Разрешает приложению контролировать механизмы резервного копирования и восстановления системы. Не используется обычными приложениями."</string>
+ <!-- no translation found for permlab_confirm_full_backup (5557071325804469102) -->
+ <skip />
+ <!-- no translation found for permdesc_confirm_full_backup (9005017754175897954) -->
+ <skip />
<string name="permlab_internalSystemWindow" msgid="2148563628140193231">"показывать неавторизованные окна"</string>
<string name="permdesc_internalSystemWindow" msgid="5895082268284998469">"Разрешает создание окон, предназначенных для использования внутренним пользовательским интерфейсом системы. Не предназначено для использования обычными приложениями."</string>
<string name="permlab_systemAlertWindow" msgid="3372321942941168324">"показывать оповещения системного уровня"</string>
@@ -444,6 +450,10 @@
<string name="permdesc_bluetooth" product="default" msgid="762515380679392945">"Позволяет приложению просматривать конфигурацию локального телефона Bluetooth, создавать подключения с сопряженными устройствами."</string>
<string name="permlab_nfc" msgid="4423351274757876953">"управлять радиосвязью ближнего действия"</string>
<string name="permdesc_nfc" msgid="9171401851954407226">"Позволяет приложению обмениваться данными с метками, картами и считывателями через радиосвязь ближнего действия (NFC)."</string>
+ <!-- no translation found for permlab_vpn (8345800584532175312) -->
+ <skip />
+ <!-- no translation found for permdesc_vpn (5617893078989944219) -->
+ <skip />
<string name="permlab_disableKeyguard" msgid="4977406164311535092">"отключать блокировку клавиатуры"</string>
<string name="permdesc_disableKeyguard" msgid="3189763479326302017">"Позволяет приложению отключить блокировку клавиатуры и другие функции защиты паролем. Примером допустимого использования этой функции является отключение блокировки клавиатуры при получении входящего вызова и включение блокировки после завершения разговора."</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"считывать настройки синхронизации"</string>
@@ -947,8 +957,6 @@
<string name="ime_action_default" msgid="2840921885558045721">"Выполнить"</string>
<string name="dial_number_using" msgid="5789176425167573586">"Набрать номер"\n"<xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="create_contact_using" msgid="4947405226788104538">"Создать контакт"\n"с номером <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="accessibility_compound_button_selected" msgid="5612776946036285686">"отмечено"</string>
- <string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"не проверено"</string>
<string name="grant_credentials_permission_message_header" msgid="6824538733852821001">"Одна или несколько программ требуют разрешения для доступа к вашему аккаунту сейчас и в дальнейшем."</string>
<string name="grant_credentials_permission_message_footer" msgid="3125211343379376561">"Разрешить доступ?"</string>
<string name="grant_permissions_header_text" msgid="2722567482180797717">"Запрос доступа"</string>
@@ -1019,6 +1027,9 @@
<string name="choose_account_label" msgid="4191313562041125787">"Выберите аккаунт"</string>
<string name="number_picker_increment_button" msgid="4830170763103463443">"Увеличить"</string>
<string name="number_picker_decrement_button" msgid="2576606679160067262">"Уменьшить"</string>
+ <string name="action_bar_home_description" msgid="5293600496601490216">"Перейти на главную"</string>
+ <string name="action_bar_up_description" msgid="2237496562952152589">"Перейти вверх"</string>
+ <string name="action_menu_overflow_description" msgid="2295659037509008453">"Ещё"</string>
<!-- no translation found for storage_internal (7556050805474115618) -->
<skip />
<!-- no translation found for storage_sd_card (8921771478629812343) -->
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index d2377bf..f55b0eb 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -204,6 +204,8 @@
<string name="permdesc_getTasks" msgid="7048711358713443341">"Umožňuje aplikácii načítať informácie o aktuálne a nedávno spustených úlohách. Toto nastavenie môže škodlivým aplikáciám umožniť odhaliť súkromné informácie o iných aplikáciách."</string>
<string name="permlab_reorderTasks" msgid="5669588525059921549">"zmena usporiadania spustených aplikácií"</string>
<string name="permdesc_reorderTasks" msgid="126252774270522835">"Umožňuje aplikácii presúvať úlohy do popredia alebo pozadia. Škodlivé aplikácie môžu vynútiť svoje presunutia do popredia bez vášho pričinenia."</string>
+ <string name="permlab_removeTasks" msgid="4802740047161700683">"zastavenie činnosti aplikácií"</string>
+ <string name="permdesc_removeTasks" msgid="2000332928514575461">"Umožňuje aplikácii odstraňovať úlohy a ukončiť činnosť súvisiacich aplikácií. Škodlivé aplikácie môžu narušovať správanie iných aplikácií."</string>
<string name="permlab_setDebugApp" msgid="4339730312925176742">"povoliť ladenie aplikácií"</string>
<string name="permdesc_setDebugApp" msgid="5584310661711990702">"Umožňuje aplikácii povoliť ladenie inej aplikácie. Škodlivé aplikácie môžu pomocou tohto nastavenia ukončiť iné aplikácie."</string>
<string name="permlab_changeConfiguration" msgid="8214475779521218295">"zmeny vašich nastavení používateľského rozhrania"</string>
@@ -238,6 +240,10 @@
<string name="permdesc_batteryStats" msgid="5847319823772230560">"Umožňuje zmenu zhromaždených štatistických údajov o batérii. Nie je určené pre bežné aplikácie."</string>
<string name="permlab_backup" msgid="470013022865453920">"Ovládať zálohovanie a obnovu systému"</string>
<string name="permdesc_backup" msgid="4837493065154256525">"Umožňuje aplikácii ovládať systémový mechanizmus na zálohovanie a obnovu údajov. Nie je určené pre bežné aplikácie."</string>
+ <!-- no translation found for permlab_confirm_full_backup (5557071325804469102) -->
+ <skip />
+ <!-- no translation found for permdesc_confirm_full_backup (9005017754175897954) -->
+ <skip />
<string name="permlab_internalSystemWindow" msgid="2148563628140193231">"zobrazenie neoprávnených okien"</string>
<string name="permdesc_internalSystemWindow" msgid="5895082268284998469">"Umožňuje vytvorenie okien, ktoré majú byť použité interným systémom používateľského rozhrania. Bežné aplikácie toto nastavenie nepoužívajú."</string>
<string name="permlab_systemAlertWindow" msgid="3372321942941168324">"zobrazenie upozornení systémovej úrovne"</string>
@@ -444,6 +450,10 @@
<string name="permdesc_bluetooth" product="default" msgid="762515380679392945">"Umožňuje aplikácii zobraziť konfiguráciu miestneho telefónu s rozhraním Bluetooth, vytvárať pripojenie na spárované zariadenia a prijímať tieto pripojenia."</string>
<string name="permlab_nfc" msgid="4423351274757876953">"ovládať technológiu Near Field Communication"</string>
<string name="permdesc_nfc" msgid="9171401851954407226">"Umožňuje aplikácii komunikovať so štítkami, kartami a čítačkami s podporou technológie Near Field Communication (NFC)."</string>
+ <!-- no translation found for permlab_vpn (8345800584532175312) -->
+ <skip />
+ <!-- no translation found for permdesc_vpn (5617893078989944219) -->
+ <skip />
<string name="permlab_disableKeyguard" msgid="4977406164311535092">"zakázanie uzamknutia klávesnice"</string>
<string name="permdesc_disableKeyguard" msgid="3189763479326302017">"Umožňuje aplikácii vypnúť uzamknutie klávesnice a súvisiace zabezpečenie heslom. Príkladom oprávneného použitia tejto funkcie je vypnutie uzamknutia klávesnice pri prichádzajúcom hovore a jej opätovné zapnutie po skončení hovoru."</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"čítanie nastavení synchronizácie"</string>
@@ -947,8 +957,6 @@
<string name="ime_action_default" msgid="2840921885558045721">"Vykonať"</string>
<string name="dial_number_using" msgid="5789176425167573586">"Vytočiť číslo"\n" <xliff:g id="NUMBER">%s</xliff:g>."</string>
<string name="create_contact_using" msgid="4947405226788104538">"Vytvoriť kontakt"\n"pre <xliff:g id="NUMBER">%s</xliff:g>."</string>
- <string name="accessibility_compound_button_selected" msgid="5612776946036285686">"začiarknuté"</string>
- <string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"nezačiarknuté"</string>
<string name="grant_credentials_permission_message_header" msgid="6824538733852821001">"Nasledujúce aplikácie vyžadujú oprávnenie na prístup do vášho účtu (teraz aj v budúcnosti)."</string>
<string name="grant_credentials_permission_message_footer" msgid="3125211343379376561">"Chcete túto žiadosť povoliť?"</string>
<string name="grant_permissions_header_text" msgid="2722567482180797717">"Žiadosť o prístup"</string>
@@ -1019,6 +1027,9 @@
<string name="choose_account_label" msgid="4191313562041125787">"Vybrať účet"</string>
<string name="number_picker_increment_button" msgid="4830170763103463443">"Zvýšenie"</string>
<string name="number_picker_decrement_button" msgid="2576606679160067262">"Zníženie"</string>
+ <string name="action_bar_home_description" msgid="5293600496601490216">"Prejsť na plochu"</string>
+ <string name="action_bar_up_description" msgid="2237496562952152589">"Prejsť na"</string>
+ <string name="action_menu_overflow_description" msgid="2295659037509008453">"Viac možností"</string>
<!-- no translation found for storage_internal (7556050805474115618) -->
<skip />
<!-- no translation found for storage_sd_card (8921771478629812343) -->
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 9bd375b..16c2c41 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -204,6 +204,8 @@
<string name="permdesc_getTasks" msgid="7048711358713443341">"Programu dovoljuje pridobivanje informacij o trenutnih in nedavno izvajajočih se opravilih. Zlonamerni programi lahko odkrijejo zasebne podatke o drugih programih."</string>
<string name="permlab_reorderTasks" msgid="5669588525059921549">"preurejanje programov, ki se izvajajo"</string>
<string name="permdesc_reorderTasks" msgid="126252774270522835">"Programu dovoljuje premikanje opravil v ospredje in ozadje. Zlonamerni programi se lahko brez vašega nadzora vsilijo v ospredje."</string>
+ <string name="permlab_removeTasks" msgid="4802740047161700683">"ustavitev programov, ki se izvajajo"</string>
+ <string name="permdesc_removeTasks" msgid="2000332928514575461">"Dovoli programu, da odstrani opravila in ukine njihove programe. Zlonamerni programi lahko motijo delovanje drugih programov."</string>
<string name="permlab_setDebugApp" msgid="4339730312925176742">"omogočanje iskanja in odpravljanja napak v programu"</string>
<string name="permdesc_setDebugApp" msgid="5584310661711990702">"Programu dovoljuje vklop funkcije iskanja in odpravljanja napak za drug program. Zlonamerni programi lahko to uporabijo za zapiranje drugih programov."</string>
<string name="permlab_changeConfiguration" msgid="8214475779521218295">"spreminjanje nastavitev UV"</string>
@@ -238,6 +240,10 @@
<string name="permdesc_batteryStats" msgid="5847319823772230560">"Dovoljuje spreminjanje zbranih statističnih podatkov baterije. Ni za uporabo z navadnimi programi."</string>
<string name="permlab_backup" msgid="470013022865453920">"nadzor varnostnega kopiranja sistema in obnovitev"</string>
<string name="permdesc_backup" msgid="4837493065154256525">"Programu dovoljuje nadzor mehanizma za varnostno kopiranje in obnovitev sistema. Ni za uporabo z navadnimi programi."</string>
+ <!-- no translation found for permlab_confirm_full_backup (5557071325804469102) -->
+ <skip />
+ <!-- no translation found for permdesc_confirm_full_backup (9005017754175897954) -->
+ <skip />
<string name="permlab_internalSystemWindow" msgid="2148563628140193231">"prikazovanje nepooblaščenih oken"</string>
<string name="permdesc_internalSystemWindow" msgid="5895082268284998469">"Dovoljuje ustvarjanje oken, ki jih bo uporabljal uporabniški vmesnik notranjega sistema. Ni za uporabo z navadnimi programi."</string>
<string name="permlab_systemAlertWindow" msgid="3372321942941168324">"prikaz opozoril na ravni sistema"</string>
@@ -444,6 +450,10 @@
<string name="permdesc_bluetooth" product="default" msgid="762515380679392945">"Programom dovoljuje ogled konfiguracije lokalnega telefona Bluetooth ter ustvarjanje in sprejemanje povezave s povezanimi napravami."</string>
<string name="permlab_nfc" msgid="4423351274757876953">"nadzor nad komunikacijo s tehnologijo bližnjega polja"</string>
<string name="permdesc_nfc" msgid="9171401851954407226">"Podpira komunikacijo med računalnikom in oznakami, karticami in bralniki komunikacije s tehnologijo bližnjega polja."</string>
+ <!-- no translation found for permlab_vpn (8345800584532175312) -->
+ <skip />
+ <!-- no translation found for permdesc_vpn (5617893078989944219) -->
+ <skip />
<string name="permlab_disableKeyguard" msgid="4977406164311535092">"onemogočanje zaklepa tipkovnice"</string>
<string name="permdesc_disableKeyguard" msgid="3189763479326302017">"Dovoljuje, da program onemogoči zaklep tipk in morebitno povezano varnostno geslo. Legitimen primer je onemogočenje zaklepa tipkovnice pri dohodnem klicu ter vnovičnem omogočanju zaklepa, ko je klic dokončan."</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"branje nastavitev sinhronizacije"</string>
@@ -947,8 +957,6 @@
<string name="ime_action_default" msgid="2840921885558045721">"Izvedi"</string>
<string name="dial_number_using" msgid="5789176425167573586">"Pokliči številko"\n"s številko <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="create_contact_using" msgid="4947405226788104538">"Ustvari stik"\n"s številko <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="accessibility_compound_button_selected" msgid="5612776946036285686">"potrjeno"</string>
- <string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"nepotrjeno"</string>
<string name="grant_credentials_permission_message_header" msgid="6824538733852821001">"Ti programi zahtevajo dovoljenje za dostop do računa zdaj in v prihodnje."</string>
<string name="grant_credentials_permission_message_footer" msgid="3125211343379376561">"Ali želite to zahtevo dovoliti?"</string>
<string name="grant_permissions_header_text" msgid="2722567482180797717">"Zahteva za dostop"</string>
@@ -1019,6 +1027,9 @@
<string name="choose_account_label" msgid="4191313562041125787">"Izberite račun"</string>
<string name="number_picker_increment_button" msgid="4830170763103463443">"Povečaj"</string>
<string name="number_picker_decrement_button" msgid="2576606679160067262">"Zmanjšaj"</string>
+ <string name="action_bar_home_description" msgid="5293600496601490216">"Krmarjenje domov"</string>
+ <string name="action_bar_up_description" msgid="2237496562952152589">"Krmarjenje navzgor"</string>
+ <string name="action_menu_overflow_description" msgid="2295659037509008453">"Več možnosti"</string>
<!-- no translation found for storage_internal (7556050805474115618) -->
<skip />
<!-- no translation found for storage_sd_card (8921771478629812343) -->
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index f1497bd..cf56261 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -204,6 +204,8 @@
<string name="permdesc_getTasks" msgid="7048711358713443341">"Омогућава да апликација преузима информације о тренутно и недавно покренутим задацима. На тај начин злонамерне апликације могу да стекну увид у приватне информације о другим апликацијама."</string>
<string name="permlab_reorderTasks" msgid="5669588525059921549">"промена редоследа покретања апликација"</string>
<string name="permdesc_reorderTasks" msgid="126252774270522835">"Омогућава да апликација премешта задатке у први план и позадину. Злонамерне апликације могу на тај начин да принудно пређу у први план, при чему ви нећете имати контролу над тим."</string>
+ <string name="permlab_removeTasks" msgid="4802740047161700683">"заустављање покренутих апликација"</string>
+ <string name="permdesc_removeTasks" msgid="2000332928514575461">"Омогућава апликацији да уклања задатке и уништава њихове апликације. Злонамерне апликације могу да поремете понашање других апликација."</string>
<string name="permlab_setDebugApp" msgid="4339730312925176742">"омогућавање отклањања грешака у апликацији"</string>
<string name="permdesc_setDebugApp" msgid="5584310661711990702">"Омогућава да апликација укључи отклањање грешака за другу апликацију. Злонамерне апликације могу то да злоупотребе и искористе за онемогућавање других апликација."</string>
<string name="permlab_changeConfiguration" msgid="8214475779521218295">"промена подешавања корисничког интерфејса"</string>
@@ -238,6 +240,10 @@
<string name="permdesc_batteryStats" msgid="5847319823772230560">"Омогућава измену прикупљене статистике о батерији. Не користе је обичне апликације."</string>
<string name="permlab_backup" msgid="470013022865453920">"контрола резервне копије система и враћање почетних вредности"</string>
<string name="permdesc_backup" msgid="4837493065154256525">"Омогућава да апликација контролише резервну копију система и механизам за враћање првобитних поставки. Не користе је обичне апликације."</string>
+ <!-- no translation found for permlab_confirm_full_backup (5557071325804469102) -->
+ <skip />
+ <!-- no translation found for permdesc_confirm_full_backup (9005017754175897954) -->
+ <skip />
<string name="permlab_internalSystemWindow" msgid="2148563628140193231">"приказ неовлашћених прозора"</string>
<string name="permdesc_internalSystemWindow" msgid="5895082268284998469">"Омогућава прављење прозора који су осмишљени за коришћење у корисничком интерфејсу интерног система. Не користе је обичне апликације."</string>
<string name="permlab_systemAlertWindow" msgid="3372321942941168324">"приказ упозорења на нивоу система"</string>
@@ -444,6 +450,10 @@
<string name="permdesc_bluetooth" product="default" msgid="762515380679392945">"Омогућава да апликација види конфигурацију локалног Bluetooth телефона, као и да успоставља и прихвата везе са упареним уређајима."</string>
<string name="permlab_nfc" msgid="4423351274757876953">"контрола комуникације у ужем пољу (Near Field Communication)"</string>
<string name="permdesc_nfc" msgid="9171401851954407226">"Омогућава апликацији да комуницира са ознакама, картицама и читачима комуникације у ужем пољу (Near Field Communication – NFC)."</string>
+ <!-- no translation found for permlab_vpn (8345800584532175312) -->
+ <skip />
+ <!-- no translation found for permdesc_vpn (5617893078989944219) -->
+ <skip />
<string name="permlab_disableKeyguard" msgid="4977406164311535092">"онемогућавање закључавања тастатуре"</string>
<string name="permdesc_disableKeyguard" msgid="3189763479326302017">"Омогућава да апликација онемогући закључавање тастатуре и свих безбедносних мера успостављених на основу лозинке. У оправдане примере додељивања такве дозволе спада онемогућавање закључавања тастатуре при пријему долазећег телефонског позива и поновно омогућавање тастатуре по његовом завршетку."</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"читање подешавања синхронизације"</string>
@@ -947,8 +957,6 @@
<string name="ime_action_default" msgid="2840921885558045721">"Изврши"</string>
<string name="dial_number_using" msgid="5789176425167573586">"Бирај број"\n"користећи <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="create_contact_using" msgid="4947405226788104538">"Креирајте контакт"\n"користећи <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="accessibility_compound_button_selected" msgid="5612776946036285686">"изабрано"</string>
- <string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"није потврђено"</string>
<string name="grant_credentials_permission_message_header" msgid="6824538733852821001">"Следеће апликације захтевају дозволу за приступ налогу, како сада, тако и у будућности."</string>
<string name="grant_credentials_permission_message_footer" msgid="3125211343379376561">"Желите да одобрите овај захтев?"</string>
<string name="grant_permissions_header_text" msgid="2722567482180797717">"Захтев за приступ"</string>
@@ -1019,6 +1027,9 @@
<string name="choose_account_label" msgid="4191313562041125787">"Избор налога"</string>
<string name="number_picker_increment_button" msgid="4830170763103463443">"Повећање"</string>
<string name="number_picker_decrement_button" msgid="2576606679160067262">"Смањење"</string>
+ <string name="action_bar_home_description" msgid="5293600496601490216">"Кретање до Почетне"</string>
+ <string name="action_bar_up_description" msgid="2237496562952152589">"Кретање нагоре"</string>
+ <string name="action_menu_overflow_description" msgid="2295659037509008453">"Још опција"</string>
<!-- no translation found for storage_internal (7556050805474115618) -->
<skip />
<!-- no translation found for storage_sd_card (8921771478629812343) -->
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 2ab22d1..4d96aff 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -204,6 +204,8 @@
<string name="permdesc_getTasks" msgid="7048711358713443341">"Tillåter att program hämtar information om uppgifter som körs och har körts. Skadliga program kan upptäcka privat information om andra program."</string>
<string name="permlab_reorderTasks" msgid="5669588525059921549">"byt ordning på appar som körs"</string>
<string name="permdesc_reorderTasks" msgid="126252774270522835">"Tillåter att ett program flyttar uppgifter till förgrunden eller bakgrunden. Skadliga program kan tvinga sig till förgrunden utan att du kan styra det."</string>
+ <string name="permlab_removeTasks" msgid="4802740047161700683">"Sluta köra appar"</string>
+ <string name="permdesc_removeTasks" msgid="2000332928514575461">"Tillåter att en app tar bort uppgifter och stänger av appar. Skadliga appar kan störa andra appars funktion."</string>
<string name="permlab_setDebugApp" msgid="4339730312925176742">"aktivera felsökning av appar"</string>
<string name="permdesc_setDebugApp" msgid="5584310661711990702">"Tillåter att ett program aktiverar felsökning för ett annat program. Skadliga program kan använda detta för att avsluta andra program."</string>
<string name="permlab_changeConfiguration" msgid="8214475779521218295">"ändra dina gränssnittsinställningar"</string>
@@ -238,6 +240,10 @@
<string name="permdesc_batteryStats" msgid="5847319823772230560">"Tillåter att samlad batteristatistik ändras. Används inte av vanliga program."</string>
<string name="permlab_backup" msgid="470013022865453920">"kontrollera säkerhetskopiering och återställning av systemet"</string>
<string name="permdesc_backup" msgid="4837493065154256525">"Tillåter att programmet styr över systemets mekanism för säkerhetskopiering och återställning. Används inte av vanliga program."</string>
+ <!-- no translation found for permlab_confirm_full_backup (5557071325804469102) -->
+ <skip />
+ <!-- no translation found for permdesc_confirm_full_backup (9005017754175897954) -->
+ <skip />
<string name="permlab_internalSystemWindow" msgid="2148563628140193231">"visa otillåtna fönster"</string>
<string name="permdesc_internalSystemWindow" msgid="5895082268284998469">"Tillåter att fönster skapas och används av det interna systemgränssnittet. Används inte av vanliga program."</string>
<string name="permlab_systemAlertWindow" msgid="3372321942941168324">"visa varningar på systemnivå"</string>
@@ -444,6 +450,10 @@
<string name="permdesc_bluetooth" product="default" msgid="762515380679392945">"Tillåter att ett program ser den lokala Bluetooth-telefonens konfiguration, och skapar och accepterar anslutningar med parkopplade enheter."</string>
<string name="permlab_nfc" msgid="4423351274757876953">"kontrollera närfältskommunikationen"</string>
<string name="permdesc_nfc" msgid="9171401851954407226">"Tillåter att en app kommunicerar med taggar, kort och läsare för närfältskommunikation (NFC)."</string>
+ <!-- no translation found for permlab_vpn (8345800584532175312) -->
+ <skip />
+ <!-- no translation found for permdesc_vpn (5617893078989944219) -->
+ <skip />
<string name="permlab_disableKeyguard" msgid="4977406164311535092">"inaktivera tangentlås"</string>
<string name="permdesc_disableKeyguard" msgid="3189763479326302017">"Tillåter att ett program inaktiverar tangentlåset och tillhörande lösenordsskydd. Ett exempel på detta är att telefonen inaktiverar tangentlåset vid inkommande samtal och sedan aktiverar det igen när samtalet är avslutat."</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"läsa synkroniseringsinställningar"</string>
@@ -947,8 +957,6 @@
<string name="ime_action_default" msgid="2840921885558045721">"Utför"</string>
<string name="dial_number_using" msgid="5789176425167573586">"Slå nummer "\n"med <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="create_contact_using" msgid="4947405226788104538">"Skapa kontakt"\n"med <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="accessibility_compound_button_selected" msgid="5612776946036285686">"markerad"</string>
- <string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"inte markerad"</string>
<string name="grant_credentials_permission_message_header" msgid="6824538733852821001">"Följande appar begär behörighet till konto, både nu och i framtiden."</string>
<string name="grant_credentials_permission_message_footer" msgid="3125211343379376561">"Vill du tillåta den här begäran?"</string>
<string name="grant_permissions_header_text" msgid="2722567482180797717">"Begäran om åtkomst"</string>
@@ -1019,6 +1027,9 @@
<string name="choose_account_label" msgid="4191313562041125787">"Välj ett konto"</string>
<string name="number_picker_increment_button" msgid="4830170763103463443">"Öka"</string>
<string name="number_picker_decrement_button" msgid="2576606679160067262">"Minska"</string>
+ <string name="action_bar_home_description" msgid="5293600496601490216">"Visa startsidan"</string>
+ <string name="action_bar_up_description" msgid="2237496562952152589">"Navigera uppåt"</string>
+ <string name="action_menu_overflow_description" msgid="2295659037509008453">"Fler alternativ"</string>
<!-- no translation found for storage_internal (7556050805474115618) -->
<skip />
<!-- no translation found for storage_sd_card (8921771478629812343) -->
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 55b2667..75a886c 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -204,6 +204,8 @@
<string name="permdesc_getTasks" msgid="7048711358713443341">"อนุญาตให้แอปพลิเคชันเรียกดูข้อมูลเกี่ยวกับงานที่ทำเมื่อไม่นานมานี้และที่กำลังทำอยู่ วิธีนี้อาจทำให้แอปพลิเคชันที่เป็นอันตรายพบข้อมูลที่เป็นความลับเกี่ยวกับแอปพลิเคชันอื่นได้"</string>
<string name="permlab_reorderTasks" msgid="5669588525059921549">"จัดลำดับแอปพลิเคชันที่ทำงานอยู่ใหม่"</string>
<string name="permdesc_reorderTasks" msgid="126252774270522835">"อนุญาตให้แอปพลิเคชันย้ายงานไปที่ด้านหน้าและพื้นหลัง แอปพลิเคชันที่เป็นอันตรายสามารถบังคับตัวเองให้อยู่ด้านหน้าได้โดยไม่ต้องให้คุณควบคุม"</string>
+ <string name="permlab_removeTasks" msgid="4802740047161700683">"หยุดแอปพลิเคชันที่ทำงานอยู่"</string>
+ <string name="permdesc_removeTasks" msgid="2000332928514575461">"อนุญาตให้แอปพลิเคชันนำงานออกและกำจัดการทำงานเหล่านั้น แอปพลิเคชันที่เป็นอันตรายสามารถรบกวนการทำงานของแอปพลิเคชันอื่นๆ ได้"</string>
<string name="permlab_setDebugApp" msgid="4339730312925176742">"เปิดใช้งานการแก้ไขข้อบกพร่องของแอปพลิเคชัน"</string>
<string name="permdesc_setDebugApp" msgid="5584310661711990702">"อนุญาตให้แอปพลิเคชันเปิดการแก้ไขข้อบกพร่องสำหรับแอปพลิเคชันอื่น แอปพลิเคชันที่เป็นอันตรายอาจใช้วิธีนี้จบการทำงานแอปพลิเคชันอื่น"</string>
<string name="permlab_changeConfiguration" msgid="8214475779521218295">"เปลี่ยนการตั้งค่า UI ของคุณ"</string>
@@ -238,6 +240,10 @@
<string name="permdesc_batteryStats" msgid="5847319823772230560">"อนุญาตให้แก้ไขสถิติแบตเตอรี่ที่เก็บไว้ ห้ามใช้โดยแอปพลิเคชันทั่วไป"</string>
<string name="permlab_backup" msgid="470013022865453920">"ควบคุมการสำรองและคืนค่า"</string>
<string name="permdesc_backup" msgid="4837493065154256525">"อนุญาตให้แอปพลิเคชันควบคุมวิธีการสำรองและคืนค่าระบบ ห้ามใช้โดยแอปพลิเคชันทั่วไป"</string>
+ <!-- no translation found for permlab_confirm_full_backup (5557071325804469102) -->
+ <skip />
+ <!-- no translation found for permdesc_confirm_full_backup (9005017754175897954) -->
+ <skip />
<string name="permlab_internalSystemWindow" msgid="2148563628140193231">"แสดงหน้าต่างที่ไม่ได้รับอนุญาต"</string>
<string name="permdesc_internalSystemWindow" msgid="5895082268284998469">"อนุญาตให้สร้างหน้าต่างสำหรับใช้โดยส่วนติดต่อผู้ใช้ของระบบภายใน ห้ามใช้โดยแอปพลิเคชันทั่วไป"</string>
<string name="permlab_systemAlertWindow" msgid="3372321942941168324">"แสดงการแจ้งเตือนในระดับระบบ"</string>
@@ -444,6 +450,10 @@
<string name="permdesc_bluetooth" product="default" msgid="762515380679392945">"อนุญาตให้แอปพลิเคชันดูการกำหนดค่าของโทรศัพท์บลูทูธในพื้นที่ ตลอดจนเชื่อมต่อและยอมรับการเชื่อมต่อด้วยอุปกรณ์ที่จับคู่ไว้"</string>
<string name="permlab_nfc" msgid="4423351274757876953">"ควบคุม Near Field Communication"</string>
<string name="permdesc_nfc" msgid="9171401851954407226">"อนุญาตให้แอปพลิเคชันสื่อสารกับแท็ก Near Field Communication (NFC) การ์ด และโปรแกรมอ่าน"</string>
+ <!-- no translation found for permlab_vpn (8345800584532175312) -->
+ <skip />
+ <!-- no translation found for permdesc_vpn (5617893078989944219) -->
+ <skip />
<string name="permlab_disableKeyguard" msgid="4977406164311535092">"ปิดการใช้งานการล็อกปุ่มกด"</string>
<string name="permdesc_disableKeyguard" msgid="3189763479326302017">"อนุญาตให้แอปพลิเคชันปิดการใช้งานการล็อกปุ่มและการรักษาความปลอดภัยรหัสผ่านที่เกี่ยวข้องใดๆ ตัวอย่างการใช้งานของกรณีนี้คือ โทรศัพท์ปิดการใช้งานการล็อกปุ่มกดเมื่อมีสายเรียกเข้า จากนั้นจึงเปิดการใช้งานการล็อกปุ่มกดใหม่เมื่อวางสายแล้ว"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"อ่านการตั้งค่าการซิงค์แล้ว"</string>
@@ -947,8 +957,6 @@
<string name="ime_action_default" msgid="2840921885558045721">"ปฏิบัติ"</string>
<string name="dial_number_using" msgid="5789176425167573586">"หมุนหมายเลข "\n" โดยใช้ <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="create_contact_using" msgid="4947405226788104538">"สร้างที่อยู่ติดต่อ "\n"โดยใช้ <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="accessibility_compound_button_selected" msgid="5612776946036285686">"เลือกไว้"</string>
- <string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"ไม่ได้ตรวจสอบ"</string>
<string name="grant_credentials_permission_message_header" msgid="6824538733852821001">"แอปพลิเคชันต่อไปนี้ตั้งแต่หนึ่งอย่างขึ้นไปขอให้มีการอนุญาตให้เข้าถึงบัญชีของคุณในขณะนี้และในอนาคต"</string>
<string name="grant_credentials_permission_message_footer" msgid="3125211343379376561">"คุณต้องการอนุญาตหรือไม่"</string>
<string name="grant_permissions_header_text" msgid="2722567482180797717">"คำขอเข้าถึง"</string>
@@ -1019,6 +1027,9 @@
<string name="choose_account_label" msgid="4191313562041125787">"เลือกบัญชี"</string>
<string name="number_picker_increment_button" msgid="4830170763103463443">"การเพิ่ม"</string>
<string name="number_picker_decrement_button" msgid="2576606679160067262">"การลด"</string>
+ <string name="action_bar_home_description" msgid="5293600496601490216">"นำทางไปหน้าแรก"</string>
+ <string name="action_bar_up_description" msgid="2237496562952152589">"นำทางขึ้น"</string>
+ <string name="action_menu_overflow_description" msgid="2295659037509008453">"ตัวเลือกเพิ่มเติม"</string>
<!-- no translation found for storage_internal (7556050805474115618) -->
<skip />
<!-- no translation found for storage_sd_card (8921771478629812343) -->
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index cffa82e..cec9fae 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -204,6 +204,8 @@
<string name="permdesc_getTasks" msgid="7048711358713443341">"Pinapayagan ang application na ibalik ang impormasyon tungkol sa mga kasalukuyan at kamakailang tumatakbong gawain. Maaaring payagan ang mga nakakahamak na application na tuklasin ang pribadong impormasyon tungkol sa ibang mga application."</string>
<string name="permlab_reorderTasks" msgid="5669588525059921549">"muling pagsunud-sunurin ang mga tumatakbong application"</string>
<string name="permdesc_reorderTasks" msgid="126252774270522835">"Pinapayagan ang isang application na ilipat ang mga gawain sa foreground at background. Mapupuwersa ng mga nakakahamak na application ang mga sarili nito sa harapan nang wala ng iyong kontrol."</string>
+ <string name="permlab_removeTasks" msgid="4802740047161700683">"ihinto ang pagpapatakbo ng mga application"</string>
+ <string name="permdesc_removeTasks" msgid="2000332928514575461">"Pinapayagan ang isang application na mag-alis ng mga gawain at ihinto ang mga application nito. Maaaring maantala ng mga nakakahamak na application ang pag-uugali ng ibang mga application."</string>
<string name="permlab_setDebugApp" msgid="4339730312925176742">"paganahin ang debugging ng application"</string>
<string name="permdesc_setDebugApp" msgid="5584310661711990702">"Pinapayagan ang isang application na i-on ang debugging para sa isa pang application. Magagamit ito ng mga nakakahamak na application upang alisin ang ibang mga application."</string>
<string name="permlab_changeConfiguration" msgid="8214475779521218295">"baguhin ang iyong mga setting ng UI"</string>
@@ -238,6 +240,10 @@
<string name="permdesc_batteryStats" msgid="5847319823772230560">"Pinapayagan ang pagbabago ng mga nakolektang istatistika ng baterya. Hindi para sa paggamit ng mga normal na application."</string>
<string name="permlab_backup" msgid="470013022865453920">"kontrolin ang system backup at pagbawi"</string>
<string name="permdesc_backup" msgid="4837493065154256525">"Pinapayagan ang application na kontrolin ang mekanismo sa pag-backup at pagbalik ng system. Hindi para sa paggamit ng mga normal na application."</string>
+ <!-- no translation found for permlab_confirm_full_backup (5557071325804469102) -->
+ <skip />
+ <!-- no translation found for permdesc_confirm_full_backup (9005017754175897954) -->
+ <skip />
<string name="permlab_internalSystemWindow" msgid="2148563628140193231">"ipakita ang mga hindi pinahintulutang window"</string>
<string name="permdesc_internalSystemWindow" msgid="5895082268284998469">"Pinapayagan ang paglikha ng mga window na nilayon para sa paggamit ng user interface ng pangloob na system. Hindi para sa paggamit ng mga normal na application."</string>
<string name="permlab_systemAlertWindow" msgid="3372321942941168324">"ipakita ang mga alerto sa antas ng system"</string>
@@ -444,6 +450,10 @@
<string name="permdesc_bluetooth" product="default" msgid="762515380679392945">"Pinapayagan ang isang application na tingnan ang configuration ng lokal na Bluetooth na telepono, at upang gumawa at tumanggap ng mga koneksyon sa mga nakapares na device."</string>
<string name="permlab_nfc" msgid="4423351274757876953">"kontrolin ang Near Field Communication"</string>
<string name="permdesc_nfc" msgid="9171401851954407226">"Pinapayagan ang application na makipagkomunika sa mga Near Field Communication (NFC) na tag, card, at reader."</string>
+ <!-- no translation found for permlab_vpn (8345800584532175312) -->
+ <skip />
+ <!-- no translation found for permdesc_vpn (5617893078989944219) -->
+ <skip />
<string name="permlab_disableKeyguard" msgid="4977406164311535092">"huwag paganahin ang keylock"</string>
<string name="permdesc_disableKeyguard" msgid="3189763479326302017">"Pinapayagan ang isang application na huwag paganahin ang keylock at ang anumang nauugnay na seguridad sa password. Ang isang lehitimong halimbawa nito ay ang hindi pagpapagana ng telepono sa keylock kapag nakakatanggap ng papasok na tawag sa telepono, pagkatapos ay muling pagaganahin ang keylock kapag tapos na ang tawag."</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"basahin ang mga setting ng sync"</string>
@@ -947,8 +957,6 @@
<string name="ime_action_default" msgid="2840921885558045721">"Isakatuparan"</string>
<string name="dial_number_using" msgid="5789176425167573586">"Mag-dial ng numero"\n"gamit ang <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="create_contact_using" msgid="4947405226788104538">"Lumikha ng contact"\n"gamit ang <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="accessibility_compound_button_selected" msgid="5612776946036285686">"nasuri"</string>
- <string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"hindi sinuri"</string>
<string name="grant_credentials_permission_message_header" msgid="6824538733852821001">"Ang sumusunod na isa o higit pang mga application ay humihilingng pahintulot na ma-access ang iyong account, ngayon at sa hinaharap."</string>
<string name="grant_credentials_permission_message_footer" msgid="3125211343379376561">"Gusto mo bang payagan ang kahilingang ito?"</string>
<string name="grant_permissions_header_text" msgid="2722567482180797717">"Kahilingan sa Access"</string>
@@ -1019,6 +1027,9 @@
<string name="choose_account_label" msgid="4191313562041125787">"Pumili ng account"</string>
<string name="number_picker_increment_button" msgid="4830170763103463443">"Taasan"</string>
<string name="number_picker_decrement_button" msgid="2576606679160067262">"Babaan"</string>
+ <string name="action_bar_home_description" msgid="5293600496601490216">"Magnabiga sa home"</string>
+ <string name="action_bar_up_description" msgid="2237496562952152589">"Magnabiga pataas"</string>
+ <string name="action_menu_overflow_description" msgid="2295659037509008453">"Higit pang mga pagpipilian"</string>
<!-- no translation found for storage_internal (7556050805474115618) -->
<skip />
<!-- no translation found for storage_sd_card (8921771478629812343) -->
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 8814cdc..081fa10 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -204,6 +204,8 @@
<string name="permdesc_getTasks" msgid="7048711358713443341">"Uygulamaların şu anda ve yakın geçmişte çalışmakta olan işlemler hakkında bilgi almasına izin verir. Kötü amaçlı uygulamaların diğer uygulamalar ile ilgili gizli bilgileri keşfetmesine izin verebilir."</string>
<string name="permlab_reorderTasks" msgid="5669588525059921549">"çalışan uygulamaları yeniden sırala"</string>
<string name="permdesc_reorderTasks" msgid="126252774270522835">"Uygulamaların görevleri ön plana ve arka plana taşımasına izin verir. Kötü amaçlı uygulamalar kendilerini sizin denetiminiz dışında zorla ön plana çıkarabilir."</string>
+ <string name="permlab_removeTasks" msgid="4802740047161700683">"çalışan uygulamaları durdur"</string>
+ <string name="permdesc_removeTasks" msgid="2000332928514575461">"Uygulamanın, görevleri kaldırmasına ve bunlara ait uygulamaları kapatmasına izin verir. Kötü niyetli uygulamalar diğer uygulamaların davranışlarını bozabilir."</string>
<string name="permlab_setDebugApp" msgid="4339730312925176742">"uygulama hata ayıklamayı etkinleştir"</string>
<string name="permdesc_setDebugApp" msgid="5584310661711990702">"Bir uygulamanın başka bir uygulama için hata ayıklamayı çalıştırmasına izin verir. Kötü amaçlı uygulamalar bu işlevi başka uygulamaları kapatmak için kullanabilir."</string>
<string name="permlab_changeConfiguration" msgid="8214475779521218295">"kullanıcı arayüzü ayarlarınızı değiştirin"</string>
@@ -238,6 +240,10 @@
<string name="permdesc_batteryStats" msgid="5847319823772230560">"Toplanan pil istatistiklerinin değiştirilmesine izin verir. Normal uygulamalarda kullanılmamalıdır."</string>
<string name="permlab_backup" msgid="470013022865453920">"sistem yedeğini kontrol et ve geri yükle"</string>
<string name="permdesc_backup" msgid="4837493065154256525">"Uygulamaya sistem yedekleme ve geri yükleme mekanizmasını denetleme izni verir. Normal uygulamalar tarafından kullanım için değildir."</string>
+ <!-- no translation found for permlab_confirm_full_backup (5557071325804469102) -->
+ <skip />
+ <!-- no translation found for permdesc_confirm_full_backup (9005017754175897954) -->
+ <skip />
<string name="permlab_internalSystemWindow" msgid="2148563628140193231">"yetkisiz pencereleri görüntüle"</string>
<string name="permdesc_internalSystemWindow" msgid="5895082268284998469">"Dahili sistem kullanıcı arayüzü tarafından kullanılmak üzere tasarlanmış pencerelerin oluşturulmasına izin verir. Normal uygulamalarda kullanılmaz."</string>
<string name="permlab_systemAlertWindow" msgid="3372321942941168324">"sistem düzeyi uyarıları görüntüle"</string>
@@ -444,6 +450,10 @@
<string name="permdesc_bluetooth" product="default" msgid="762515380679392945">"Uygulamaların yerel Bluetooth telefonunun yapılandırmasını görüntülemesine ve eşleşilmiş cihazlar ile bağlantı kurup kabul etmesine izin verir."</string>
<string name="permlab_nfc" msgid="4423351274757876953">"Yakın Alan İletişimini denetle"</string>
<string name="permdesc_nfc" msgid="9171401851954407226">"Bir uyg\'nın Yakın Alan İletişimi etiketleri, kartları ve okuyclr ile iletşm kurmasına izin verir."</string>
+ <!-- no translation found for permlab_vpn (8345800584532175312) -->
+ <skip />
+ <!-- no translation found for permdesc_vpn (5617893078989944219) -->
+ <skip />
<string name="permlab_disableKeyguard" msgid="4977406164311535092">"tuş kilidini devre dışı bırak"</string>
<string name="permdesc_disableKeyguard" msgid="3189763479326302017">"Uygulamaların tuş kilidini ve ilgili şifreli güvenlik önlemini devre dışı bırakmasına izin verir. Bunun geçerli bir örneği gelen bir çağrı alındığında tuş kilidinin devre dışı bırakılması, sonra çağrı bittiğinde kilidin yeniden devreye sokulmasıdır."</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"senk. ayarlarını oku"</string>
@@ -947,8 +957,6 @@
<string name="ime_action_default" msgid="2840921885558045721">"Çalıştır"</string>
<string name="dial_number_using" msgid="5789176425167573586">"Numarayı çevir:"\n"<xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="create_contact_using" msgid="4947405226788104538">"<xliff:g id="NUMBER">%s</xliff:g>"\n" ile kişi oluştur"</string>
- <string name="accessibility_compound_button_selected" msgid="5612776946036285686">"seçildi"</string>
- <string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"seçilmedi"</string>
<string name="grant_credentials_permission_message_header" msgid="6824538733852821001">"Aşağıdaki bir veya daha fazla uygulama, şimdi ve ileride hesabınıza erişmek için izin istiyor."</string>
<string name="grant_credentials_permission_message_footer" msgid="3125211343379376561">"Bu isteğe izin vermek istiyor musunuz?"</string>
<string name="grant_permissions_header_text" msgid="2722567482180797717">"Erişim İsteği"</string>
@@ -1019,6 +1027,9 @@
<string name="choose_account_label" msgid="4191313562041125787">"Bir hesap seçin"</string>
<string name="number_picker_increment_button" msgid="4830170763103463443">"Artır"</string>
<string name="number_picker_decrement_button" msgid="2576606679160067262">"Azalt"</string>
+ <string name="action_bar_home_description" msgid="5293600496601490216">"Ana sayfaya git"</string>
+ <string name="action_bar_up_description" msgid="2237496562952152589">"Yukarı git"</string>
+ <string name="action_menu_overflow_description" msgid="2295659037509008453">"Diğer seçenekler"</string>
<!-- no translation found for storage_internal (7556050805474115618) -->
<skip />
<!-- no translation found for storage_sd_card (8921771478629812343) -->
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index e0b6006..88ddc41 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -204,6 +204,8 @@
<string name="permdesc_getTasks" msgid="7048711358713443341">"Дозволяє програмі отримувати інформацію про теперішні й останні завдання. Може дозволити шкідливим програмам дізнаватися приватну інформацію про інші програми."</string>
<string name="permlab_reorderTasks" msgid="5669588525059921549">"змін. порядок запущених програм"</string>
<string name="permdesc_reorderTasks" msgid="126252774270522835">"Дозволяє програмі робити завдання активними та фоновими. Шкідливі програми можуть примусово ставати активними без контролю з вашого боку."</string>
+ <string name="permlab_removeTasks" msgid="4802740047161700683">"зупиняти запущені програми"</string>
+ <string name="permdesc_removeTasks" msgid="2000332928514575461">"Дозволяє програмі видаляти завдання та завершувати роботу відповідних програм. Шкідливі програми можуть переривати роботу інших програм."</string>
<string name="permlab_setDebugApp" msgid="4339730312925176742">"увімк. налагодження програми"</string>
<string name="permdesc_setDebugApp" msgid="5584310661711990702">"Дозволяє програмі вмикати налагодження для іншої програми. Шкідливі програми можуть використовувати це для заверш. роботи ін. програм."</string>
<string name="permlab_changeConfiguration" msgid="8214475779521218295">"змін. налашт. інтерф. кор."</string>
@@ -238,6 +240,10 @@
<string name="permdesc_batteryStats" msgid="5847319823772230560">"Дозволяє змінювати зібрану статистику батареї. Не для використання звичайними програмами."</string>
<string name="permlab_backup" msgid="470013022865453920">"контр. резерв. копіюв. і відн. сист."</string>
<string name="permdesc_backup" msgid="4837493065154256525">"Дозволяє програмі контролювати механізми резерв. копіюв. і відновл. системи. Не для викор. звичайними програмами."</string>
+ <!-- no translation found for permlab_confirm_full_backup (5557071325804469102) -->
+ <skip />
+ <!-- no translation found for permdesc_confirm_full_backup (9005017754175897954) -->
+ <skip />
<string name="permlab_internalSystemWindow" msgid="2148563628140193231">"відображати несанкціон. вікна"</string>
<string name="permdesc_internalSystemWindow" msgid="5895082268284998469">"Дозволяє створювати вікна, які мають використ-ся інтерфейсом користувача внутрішньої системи. Не для використання звичайними програмами."</string>
<string name="permlab_systemAlertWindow" msgid="3372321942941168324">"відобр. сповіщ. на рівні сист."</string>
@@ -444,6 +450,10 @@
<string name="permdesc_bluetooth" product="default" msgid="762515380679392945">"Дозволяє програмі переглядати конфігурацію локального Bluetooth телефону, створювати та приймати з\'єднання зі спареними пристроями."</string>
<string name="permlab_nfc" msgid="4423351274757876953">"контрол. Near Field Communication"</string>
<string name="permdesc_nfc" msgid="9171401851954407226">"Дозволяє прогр. обмін. даними з тегами, картками та читачами екрана Near Field Communication (NFC)."</string>
+ <!-- no translation found for permlab_vpn (8345800584532175312) -->
+ <skip />
+ <!-- no translation found for permdesc_vpn (5617893078989944219) -->
+ <skip />
<string name="permlab_disableKeyguard" msgid="4977406164311535092">"вимик. блок. клав."</string>
<string name="permdesc_disableKeyguard" msgid="3189763479326302017">"Дозволяє програмі вимикати блокування клавіатури та будь-який пов\'язаний захист паролем. Допустимий приклад, коли телефон вимикає блокування клавіат. при отриманні вхідного дзвінка, після завершення якого блокування клавіатури відновлюється."</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"чит. налашт-ня синхр."</string>
@@ -947,8 +957,6 @@
<string name="ime_action_default" msgid="2840921885558045721">"Запустити"</string>
<string name="dial_number_using" msgid="5789176425167573586">"Набр. номер"\n", викор. <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="create_contact_using" msgid="4947405226788104538">"Створ. контакт"\n", викор. <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="accessibility_compound_button_selected" msgid="5612776946036285686">"перевірено"</string>
- <string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"не перевірено"</string>
<string name="grant_credentials_permission_message_header" msgid="6824538733852821001">"Ця чи більше програм запитують дозвіл на отримання доступу до вашого облік. запису зараз і в майбутньому."</string>
<string name="grant_credentials_permission_message_footer" msgid="3125211343379376561">"Дозволити цей запит?"</string>
<string name="grant_permissions_header_text" msgid="2722567482180797717">"Запит на доступ"</string>
@@ -1019,6 +1027,9 @@
<string name="choose_account_label" msgid="4191313562041125787">"Вибрати обліковий запис"</string>
<string name="number_picker_increment_button" msgid="4830170763103463443">"Додати"</string>
<string name="number_picker_decrement_button" msgid="2576606679160067262">"Відняти"</string>
+ <string name="action_bar_home_description" msgid="5293600496601490216">"Перейти на головну"</string>
+ <string name="action_bar_up_description" msgid="2237496562952152589">"Перейти вгору"</string>
+ <string name="action_menu_overflow_description" msgid="2295659037509008453">"Інші варіанти"</string>
<!-- no translation found for storage_internal (7556050805474115618) -->
<skip />
<!-- no translation found for storage_sd_card (8921771478629812343) -->
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 4b7df31..01a320a 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -204,6 +204,8 @@
<string name="permdesc_getTasks" msgid="7048711358713443341">"Cho phép ứng dụng truy xuất thông tin về các công việc hiện đang chạy. Có thể cho phép các ứng dụng độc hại phát hiện thông tin riêng tư về các ứng dụng khác."</string>
<string name="permlab_reorderTasks" msgid="5669588525059921549">"sắp xếp lại các ứng dụng đang chạy"</string>
<string name="permdesc_reorderTasks" msgid="126252774270522835">"Cho phép ứng dụng di chuyển công việc lên trên nền và dưới nền. Các ứng dụng độc hại có thể tự hiện lên trước mà không cần sự kiểm soát của bạn."</string>
+ <string name="permlab_removeTasks" msgid="4802740047161700683">"ngừng chạy các ứng dụng"</string>
+ <string name="permdesc_removeTasks" msgid="2000332928514575461">"Cho phép ứng dụng xóa tác vụ và hủy các ứng dụng của chúng. Các ứng dụng độc hại có thể làm gián đoạn hoạt động của các ứng dụng khác."</string>
<string name="permlab_setDebugApp" msgid="4339730312925176742">"cho phép gỡ lỗi ứng dụng"</string>
<string name="permdesc_setDebugApp" msgid="5584310661711990702">"Cho phép ứng dụng bật gỡ lỗi cho ứng dụng khác. Các ứng dụng độc hại có thể sử dụng quyền này đề loại bỏ các ứng dụng khác."</string>
<string name="permlab_changeConfiguration" msgid="8214475779521218295">"thay đổi cài đặt giao diện người dùng của bạn"</string>
@@ -238,6 +240,8 @@
<string name="permdesc_batteryStats" msgid="5847319823772230560">"Cho phép sửa đổi các thống kê pin được thu thập. Không dành cho các ứng dụng thông thường."</string>
<string name="permlab_backup" msgid="470013022865453920">"kiểm soát sao lưu và khôi phục hệ thống"</string>
<string name="permdesc_backup" msgid="4837493065154256525">"Cho phép ứng dụng kiểm soát cơ chế sao lưu và khôi phục hệ thống. Không dành cho các ứng dụng thông thường."</string>
+ <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"xác nhận bản sao lưu đầy đủ hoặc khôi phục hoạt động"</string>
+ <string name="permdesc_confirm_full_backup" msgid="9005017754175897954">"Cho phép ứng dụng khởi chạy toàn bộ Giao diện người dùng xác nhận sao lưu. Không được bất kỳ ứng dụng nào sử dụng."</string>
<string name="permlab_internalSystemWindow" msgid="2148563628140193231">"hiển thị các cửa sổ trái phép"</string>
<string name="permdesc_internalSystemWindow" msgid="5895082268284998469">"Cho phép tạo các cửa sổ được giao diện người dùng hệ thống nội bộ sử dụng. Không dành cho các ứng dụng thông thường."</string>
<string name="permlab_systemAlertWindow" msgid="3372321942941168324">"hiển thị thông báo cấp hệ thống"</string>
@@ -444,6 +448,10 @@
<string name="permdesc_bluetooth" product="default" msgid="762515380679392945">"Cho phép ứng dụng xem cấu hình của điện thoại Bluetooth nội hạt cũng như tạo và chấp nhận các kết nối với các thiết bị được ghép nối."</string>
<string name="permlab_nfc" msgid="4423351274757876953">"kiểm soát Liên lạc trường gần"</string>
<string name="permdesc_nfc" msgid="9171401851954407226">"Cho phép ứng dụng liên lạc với thẻ Liên lạc trường gần (NFC), thẻ và trình đọc."</string>
+ <!-- no translation found for permlab_vpn (8345800584532175312) -->
+ <skip />
+ <!-- no translation found for permdesc_vpn (5617893078989944219) -->
+ <skip />
<string name="permlab_disableKeyguard" msgid="4977406164311535092">"vô hiệu hoá khoá phím"</string>
<string name="permdesc_disableKeyguard" msgid="3189763479326302017">"Cho phép ứng dụng vô hiệu hoá khoá phím và bất kỳ bảo mật mật khẩu được liên kết nào. Ví dụ thích hợp của việc này là điện thoại vô hiệu hoá khoá phím khi nhận được cuộc gọi đến sau đó bật lại khoá phím khi cuộc gọi kết thúc."</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"đọc cài đặt đồng bộ hoá"</string>
@@ -947,8 +955,6 @@
<string name="ime_action_default" msgid="2840921885558045721">"Thực hiện"</string>
<string name="dial_number_using" msgid="5789176425167573586">"Quay số"\n"sử dụng <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="create_contact_using" msgid="4947405226788104538">"Tạo liên hệ"\n"sử dụng <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <string name="accessibility_compound_button_selected" msgid="5612776946036285686">"đã chọn"</string>
- <string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"chưa chọn"</string>
<string name="grant_credentials_permission_message_header" msgid="6824538733852821001">"Một hoặc nhiều ứng dụng sau đây yêu cầu quyền truy cập vào tài khoản của bạn, hiện tại và trong tương lai."</string>
<string name="grant_credentials_permission_message_footer" msgid="3125211343379376561">"Bạn có muốn cho phép yêu cầu này không?"</string>
<string name="grant_permissions_header_text" msgid="2722567482180797717">"Yêu cầu Quyền truy cập"</string>
@@ -1019,6 +1025,9 @@
<string name="choose_account_label" msgid="4191313562041125787">"Chọn tài khoản"</string>
<string name="number_picker_increment_button" msgid="4830170763103463443">"Tăng dần"</string>
<string name="number_picker_decrement_button" msgid="2576606679160067262">"Giảm dần"</string>
+ <string name="action_bar_home_description" msgid="5293600496601490216">"Điều hướng về trang chủ"</string>
+ <string name="action_bar_up_description" msgid="2237496562952152589">"Điều hướng lên trên"</string>
+ <string name="action_menu_overflow_description" msgid="2295659037509008453">"Tùy chọn khác"</string>
<!-- no translation found for storage_internal (7556050805474115618) -->
<skip />
<!-- no translation found for storage_sd_card (8921771478629812343) -->
diff --git a/core/res/res/values-w480dp/bools.xml b/core/res/res/values-w480dp/bools.xml
new file mode 100644
index 0000000..8206e79
--- /dev/null
+++ b/core/res/res/values-w480dp/bools.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2011, 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.
+*/
+-->
+<resources>
+ <bool name="allow_action_menu_item_text_with_icon">true</bool>
+ <bool name="action_bar_embed_tabs">true</bool>
+ <bool name="split_action_bar_is_narrow">false</bool>
+</resources>
diff --git a/core/res/res/values-xlarge/styles.xml b/core/res/res/values-xlarge/styles.xml
index dd78920..a39d9d6 100644
--- a/core/res/res/values-xlarge/styles.xml
+++ b/core/res/res/values-xlarge/styles.xml
@@ -36,6 +36,22 @@
<item name="android:textColor">?android:attr/textColorPrimary</item>
</style>
+ <style name="TextAppearance.Holo.Widget.TabWidget">
+ <item name="android:textSize">18sp</item>
+ <item name="android:textStyle">normal</item>
+ <item name="android:textColor">@android:color/tab_indicator_text</item>
+ </style>
+
+ <style name="Widget.Holo.TabWidget" parent="Widget.TabWidget">
+ <item name="android:textAppearance">@style/TextAppearance.Holo.Widget.TabWidget</item>
+ <item name="android:tabStripLeft">@null</item>
+ <item name="android:tabStripRight">@null</item>
+ <item name="android:tabStripEnabled">false</item>
+ <item name="android:divider">@null</item>
+ <item name="android:gravity">left|center_vertical</item>
+ <item name="android:tabLayout">@android:layout/tab_indicator_holo_large</item>
+ </style>
+
<style name="PreferencePanel">
<item name="android:layout_marginLeft">@dimen/preference_screen_side_margin</item>
<item name="android:layout_marginRight">@dimen/preference_screen_side_margin</item>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 2ce123d..3386f97 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -204,6 +204,8 @@
<string name="permdesc_getTasks" msgid="7048711358713443341">"允许应用程序检索有关当前和最近运行的任务的信息。恶意应用程序可借此发现有关其他应用程序的保密信息。"</string>
<string name="permlab_reorderTasks" msgid="5669588525059921549">"对正在运行的应用程序重新排序"</string>
<string name="permdesc_reorderTasks" msgid="126252774270522835">"允许应用程序将任务移至前端和后台。恶意应用程序可借此强行进入前端,而不受您的控制。"</string>
+ <string name="permlab_removeTasks" msgid="4802740047161700683">"停止正在运行的应用程序"</string>
+ <string name="permdesc_removeTasks" msgid="2000332928514575461">"允许应用程序删除任务以及终止执行这些任务的应用程序。恶意应用程序可能会干扰其他应用程序的运行。"</string>
<string name="permlab_setDebugApp" msgid="4339730312925176742">"启用应用程序调试"</string>
<string name="permdesc_setDebugApp" msgid="5584310661711990702">"允许应用程序启动对其他应用程序的调试。恶意应用程序可借此终止其他应用程序。"</string>
<string name="permlab_changeConfiguration" msgid="8214475779521218295">"更改用户界面设置"</string>
@@ -238,6 +240,10 @@
<string name="permdesc_batteryStats" msgid="5847319823772230560">"允许修改收集的电池使用情况统计信息。普通应用程序不能使用此权限。"</string>
<string name="permlab_backup" msgid="470013022865453920">"控制系统备份和还原"</string>
<string name="permdesc_backup" msgid="4837493065154256525">"允许应用程序控制系统的备份和还原机制。普通应用程序不能使用此权限。"</string>
+ <!-- no translation found for permlab_confirm_full_backup (5557071325804469102) -->
+ <skip />
+ <!-- no translation found for permdesc_confirm_full_backup (9005017754175897954) -->
+ <skip />
<string name="permlab_internalSystemWindow" msgid="2148563628140193231">"显示未授权的窗口"</string>
<string name="permdesc_internalSystemWindow" msgid="5895082268284998469">"允许创建专用于内部系统用户界面的窗口。普通应用程序不能使用此权限。"</string>
<string name="permlab_systemAlertWindow" msgid="3372321942941168324">"显示系统级警报"</string>
@@ -444,6 +450,10 @@
<string name="permdesc_bluetooth" product="default" msgid="762515380679392945">"允许应用程序查看本地蓝牙手机的配置,以及建立或接受与配对设备的连接。"</string>
<string name="permlab_nfc" msgid="4423351274757876953">"控制近距离通信"</string>
<string name="permdesc_nfc" msgid="9171401851954407226">"允许应用程序与近距离通信 (NFC) 标签、卡和读卡器进行通信。"</string>
+ <!-- no translation found for permlab_vpn (8345800584532175312) -->
+ <skip />
+ <!-- no translation found for permdesc_vpn (5617893078989944219) -->
+ <skip />
<string name="permlab_disableKeyguard" msgid="4977406164311535092">"停用键锁"</string>
<string name="permdesc_disableKeyguard" msgid="3189763479326302017">"允许应用程序停用键锁和任何关联的密码安全设置。例如,在手机上接听电话时停用键锁,在通话结束后重新启用键锁。"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"读取同步设置"</string>
@@ -947,8 +957,6 @@
<string name="ime_action_default" msgid="2840921885558045721">"执行"</string>
<string name="dial_number_using" msgid="5789176425167573586">"拨打电话"\n"<xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="create_contact_using" msgid="4947405226788104538">"创建电话号码为"\n"<xliff:g id="NUMBER">%s</xliff:g> 的联系人"</string>
- <string name="accessibility_compound_button_selected" msgid="5612776946036285686">"已选中"</string>
- <string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"未选中"</string>
<string name="grant_credentials_permission_message_header" msgid="6824538733852821001">"以下一个或多个应用程序请求获得在目前和将来访问您帐户的权限。"</string>
<string name="grant_credentials_permission_message_footer" msgid="3125211343379376561">"您是否同意此请求?"</string>
<string name="grant_permissions_header_text" msgid="2722567482180797717">"访问请求"</string>
@@ -1019,6 +1027,9 @@
<string name="choose_account_label" msgid="4191313562041125787">"选择帐户"</string>
<string name="number_picker_increment_button" msgid="4830170763103463443">"增加"</string>
<string name="number_picker_decrement_button" msgid="2576606679160067262">"减少"</string>
+ <string name="action_bar_home_description" msgid="5293600496601490216">"导航首页"</string>
+ <string name="action_bar_up_description" msgid="2237496562952152589">"向上导航"</string>
+ <string name="action_menu_overflow_description" msgid="2295659037509008453">"更多选项"</string>
<!-- no translation found for storage_internal (7556050805474115618) -->
<skip />
<!-- no translation found for storage_sd_card (8921771478629812343) -->
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index ffb5487..d4a0243 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -204,6 +204,8 @@
<string name="permdesc_getTasks" msgid="7048711358713443341">"允許應用程式取得最近執行任務的資訊。請注意:惡意程式可能利用此功能找出其他應用程式的隱私資訊。"</string>
<string name="permlab_reorderTasks" msgid="5669588525059921549">"重新安排執行中的應用程式"</string>
<string name="permdesc_reorderTasks" msgid="126252774270522835">"允許應用程式將工作移至前端或背景作業。請注意:惡意程式可能使用此功能自行把自己拉到前端。"</string>
+ <string name="permlab_removeTasks" msgid="4802740047161700683">"停止執行中的應用程式"</string>
+ <string name="permdesc_removeTasks" msgid="2000332928514575461">"允許應用程式移除工作並且關閉應用程式。惡意應用程式會干擾其他應用程式的運行。"</string>
<string name="permlab_setDebugApp" msgid="4339730312925176742">"啟用應用程式偵錯"</string>
<string name="permdesc_setDebugApp" msgid="5584310661711990702">"允許應用程式為其他程式開啟偵錯功能。請注意:惡意程式可利用此功能終止其他應用程式。"</string>
<string name="permlab_changeConfiguration" msgid="8214475779521218295">"變更介面設定"</string>
@@ -238,6 +240,10 @@
<string name="permdesc_batteryStats" msgid="5847319823772230560">"允修改收集到的電池用量統計資料。一般應用程式不應使用這項功能。"</string>
<string name="permlab_backup" msgid="470013022865453920">"控制系統備份與還原"</string>
<string name="permdesc_backup" msgid="4837493065154256525">"允許應用程式控制系統的備份與還原機制。一般應用程式不應使用這項功能。"</string>
+ <!-- no translation found for permlab_confirm_full_backup (5557071325804469102) -->
+ <skip />
+ <!-- no translation found for permdesc_confirm_full_backup (9005017754175897954) -->
+ <skip />
<string name="permlab_internalSystemWindow" msgid="2148563628140193231">"顯示未授權視窗"</string>
<string name="permdesc_internalSystemWindow" msgid="5895082268284998469">"允許內部系統使用介面建立視窗。一般應用程式不會使用此功能。"</string>
<string name="permlab_systemAlertWindow" msgid="3372321942941168324">"顯示系統警示"</string>
@@ -444,6 +450,10 @@
<string name="permdesc_bluetooth" product="default" msgid="762515380679392945">"允許應用程式檢視本機藍牙電話設定,並與其他配對裝置連線。"</string>
<string name="permlab_nfc" msgid="4423351274757876953">"控制近距離無線通訊"</string>
<string name="permdesc_nfc" msgid="9171401851954407226">"允許應用程式使用近距離無線通訊 (NFC) 標記、卡片及讀取程式進行通訊。"</string>
+ <!-- no translation found for permlab_vpn (8345800584532175312) -->
+ <skip />
+ <!-- no translation found for permdesc_vpn (5617893078989944219) -->
+ <skip />
<string name="permlab_disableKeyguard" msgid="4977406164311535092">"停用按鍵鎖定"</string>
<string name="permdesc_disableKeyguard" msgid="3189763479326302017">"允許應用程式停用按鍵鎖定以及其他相關的密碼安全性。例如:收到來電時解除按鍵鎖定,通話結束後重新啟動按鍵鎖定。"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"讀取同步處理設定"</string>
@@ -947,8 +957,6 @@
<string name="ime_action_default" msgid="2840921885558045721">"執行"</string>
<string name="dial_number_using" msgid="5789176425167573586">"使用 <xliff:g id="NUMBER">%s</xliff:g>"\n"撥號"</string>
<string name="create_contact_using" msgid="4947405226788104538">"建立手機號碼為 <xliff:g id="NUMBER">%s</xliff:g>"\n"的聯絡人"</string>
- <string name="accessibility_compound_button_selected" msgid="5612776946036285686">"已勾選"</string>
- <string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"未勾選"</string>
<string name="grant_credentials_permission_message_header" msgid="6824538733852821001">"下列一或多個應用程式要求現在及將來的帳戶存取權限。"</string>
<string name="grant_credentials_permission_message_footer" msgid="3125211343379376561">"您確定要允許這個要求嗎?"</string>
<string name="grant_permissions_header_text" msgid="2722567482180797717">"存取要求"</string>
@@ -1019,6 +1027,9 @@
<string name="choose_account_label" msgid="4191313562041125787">"選取帳戶"</string>
<string name="number_picker_increment_button" msgid="4830170763103463443">"增加"</string>
<string name="number_picker_decrement_button" msgid="2576606679160067262">"減少"</string>
+ <string name="action_bar_home_description" msgid="5293600496601490216">"瀏覽首頁"</string>
+ <string name="action_bar_up_description" msgid="2237496562952152589">"向上瀏覽"</string>
+ <string name="action_menu_overflow_description" msgid="2295659037509008453">"更多選項"</string>
<!-- no translation found for storage_internal (7556050805474115618) -->
<skip />
<!-- no translation found for storage_sd_card (8921771478629812343) -->
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 0d9190f..6c18089 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -299,6 +299,10 @@
when there is not reserved space for their UI (such as an Action Bar). -->
<attr name="windowActionModeOverlay" format="boolean" />
+ <!-- Flag indicating that the action bar should be split to provide more
+ room for elements. -->
+ <attr name="windowSplitActionBar" format="boolean" />
+
<!-- Defines the default soft input state that this window would
like when it is displayed. -->
<attr name="windowSoftInputMode">
@@ -525,10 +529,15 @@
<!-- Reference to a style that will be used for the window containing a text
selection anchor. -->
<attr name="textSelectHandleWindowStyle" format="reference" />
+ <!-- Reference to a style that will be used for the window containing a list of possible
+ text suggestions in an EditText. -->
+ <attr name="textSuggestionsWindowStyle" format="reference" />
<!-- Default ListPopupWindow style. -->
<attr name="listPopupWindowStyle" format="reference" />
<!-- Default PopupMenu style. -->
<attr name="popupMenuStyle" format="reference" />
+ <!-- Default StackView style. -->
+ <attr name="stackViewStyle" format="reference" />
<!-- NumberPicker style. -->
<attr name="numberPickerStyle" format="reference" />
@@ -673,6 +682,15 @@
<!-- Variation of textEditSidePasteWindowLayout displayed when the clipboard is empty. -->
<attr name="textEditSideNoPasteWindowLayout" format="reference" />
+ <!-- Layout of a the view that is used to create the text suggestions popup window in an
+ EditText. This window will be displayed below the text line. -->
+ <attr name="textEditSuggestionsBottomWindowLayout" format="reference" />
+ <!-- Same as textEditSuggestionsBottomWindowLayout, but used when the popup is displayed
+ above the current line of text instead of below. -->
+ <attr name="textEditSuggestionsTopWindowLayout" format="reference" />
+ <!-- Layout of the TextView item that will populate the suggestion popup window. -->
+ <attr name="textEditSuggestionItemLayout" format="reference" />
+
<!-- Theme to use for dialogs spawned from this theme. -->
<attr name="dialogTheme" format="reference" />
<!-- Window decor layout to use in dialog mode with icons -->
@@ -746,6 +764,13 @@
<!-- Default style for the Switch widget. -->
<attr name="switchStyle" format="reference" />
+ <!-- ============== -->
+ <!-- Pointer styles -->
+ <!-- ============== -->
+ <eat-comment />
+
+ <!-- Reference to the Pointer style -->
+ <attr name="pointerStyle" format="reference" />
</declare-styleable>
<!-- **************************************************************** -->
@@ -1377,6 +1402,9 @@
<enum name="KEYCODE_BUTTON_14" value="201" />
<enum name="KEYCODE_BUTTON_15" value="202" />
<enum name="KEYCODE_BUTTON_16" value="203" />
+ <enum name="KEYCODE_LANGUAGE_SWITCH" value="204" />
+ <enum name="KEYCODE_MANNER_MODE" value="205" />
+ <enum name="KEYCODE_3D_MODE" value="206" />
</attr>
<!-- ***************************************************************** -->
@@ -1408,6 +1436,7 @@
<attr name="windowActionBar" />
<attr name="windowActionModeOverlay" />
<attr name="windowActionBarOverlay" />
+ <attr name="windowSplitActionBar" />
<attr name="windowEnableSplitTouch" />
<attr name="windowCloseOnTouchOutside" />
<!-- The minimum width the window is allowed to be, along the major
@@ -1887,6 +1916,22 @@
more information. -->
<enum name="hardware" value="2" />
</attr>
+
+ <!-- Defines the direction of layout drawing. This typically is associated with writing
+ direction of the language script used. The possible values are Left-to-Right,
+ Right-to-Left, Locale and Inherit from parent view. If there is nothing to inherit,
+ Locale is used. Locale fallsback to 'en-US'. Left-to-Right is the direction used in
+ 'en-US'. The default for this attribute is 'inherit'. -->
+ <attr name="horizontalDirection">
+ <!-- Left-to-Right -->
+ <enum name="ltr" value="0" />
+ <!-- Right-to-Left -->
+ <enum name="rtl" value="1" />
+ <!-- Inherit from parent -->
+ <enum name="inherit" value="2" />
+ <!-- Locale -->
+ <enum name="locale" value="3" />
+ </attr>
</declare-styleable>
<!-- Attributes that can be used with a {@link android.view.ViewGroup} or any
@@ -2489,6 +2534,13 @@
<attr name="thumbOffset" format="dimension" />
</declare-styleable>
+ <declare-styleable name="StackView">
+ <!-- Color of the res-out outline. -->
+ <attr name="resOutColor" format="color" />
+ <!-- Color of the outline of click feedback. -->
+ <attr name="clickColor" format="color" />
+ </declare-styleable>
+
<declare-styleable name="RatingBar">
<!-- The number of stars (or rating items) to show. -->
<attr name="numStars" format="integer" />
@@ -2810,6 +2862,16 @@
<!-- Variation of textEditSidePasteWindowLayout displayed when the clipboard is empty. -->
<attr name="textEditSideNoPasteWindowLayout" />
+ <!-- Layout of a the view that is used to create the text suggestions popup window in an
+ EditText. This window will be displayed below the text line. -->
+ <attr name="textEditSuggestionsBottomWindowLayout" />
+ <!-- Same as textEditSuggestionsBottomWindowLayout, but used when the popup is displayed
+ above the current line of text instead of below. -->
+ <attr name="textEditSuggestionsTopWindowLayout" />
+ <!-- Layout of the TextView item that will populate the suggestion popup window. -->
+ <attr name="textEditSuggestionItemLayout" />
+
+
<!-- Reference to a drawable that will be drawn under the insertion cursor. -->
<attr name="textCursorDrawable" />
@@ -3817,6 +3879,9 @@
<li>"state_rect"
<li>"state_grow"
<li>"state_move"
+ <li>"state_hovered"
+ <li>"state_drag_can_accept"
+ <li>"state_drag_hovered"
</ul> -->
<declare-styleable name="DrawableStates">
<!-- State value for {@link android.graphics.drawable.StateListDrawable StateListDrawable},
@@ -3866,6 +3931,17 @@
ignored even if it specifies a solid color, since that optimization
is not needed. -->
<attr name="state_accelerated" format="boolean" />
+ <!-- State value for {@link android.graphics.drawable.StateListDrawable StateListDrawable},
+ set when a pointer is hovering over the view. -->
+ <attr name="state_hovered" format="boolean" />
+ <!-- State for {@link android.graphics.drawable.StateListDrawable StateListDrawable}
+ indicating that the Drawable is in a view that is capable of accepting a drop of
+ the content currently being manipulated in a drag-and-drop operation. -->
+ <attr name="state_drag_can_accept" format="boolean" />
+ <!-- State for {@link android.graphics.drawable.StateListDrawable StateListDrawable}
+ indicating that a drag operation (for which the Drawable's view is a valid recipient)
+ is currently positioned over the Drawable. -->
+ <attr name="state_drag_hovered" format="boolean" />
</declare-styleable>
<declare-styleable name="ViewDrawableStates">
<attr name="state_pressed" />
@@ -3875,6 +3951,9 @@
<attr name="state_enabled" />
<attr name="state_activated" />
<attr name="state_accelerated" />
+ <attr name="state_hovered" />
+ <attr name="state_drag_can_accept" />
+ <attr name="state_drag_hovered" />
</declare-styleable>
<!-- State array representing a menu item that is currently checked. -->
<declare-styleable name="MenuItemCheckedState">
@@ -4795,6 +4874,7 @@
<flag name="homeAsUp" value="0x4" />
<flag name="showTitle" value="0x8" />
<flag name="showCustom" value="0x10" />
+ <flag name="disableHome" value="0x20" />
</attr>
<!-- Specifies title text used for navigationMode="normal" -->
<attr name="title" />
@@ -4827,6 +4907,9 @@
<!-- Specifies padding that should be applied to the left and right sides of
system-provided items in the bar. -->
<attr name="itemPadding" format="dimension" />
+ <!-- Specifies whether tabs should be embedded within the bar itself (true)
+ or displayed elsewhere (false). -->
+ <attr name="embeddedTabs" format="boolean" />
</declare-styleable>
<declare-styleable name="ActionMode">
@@ -4873,6 +4956,17 @@
<attr name="switchPadding" format="dimension" />
</declare-styleable>
+ <declare-styleable name="Pointer">
+ <!-- Reference to a pointer icon drawable with STYLE_ARROW -->
+ <attr name="pointerIconArrow" format="reference" />
+ <!-- Reference to a pointer icon drawable with STYLE_SPOT_HOVER -->
+ <attr name="pointerIconSpotHover" format="reference" />
+ <!-- Reference to a pointer icon drawable with STYLE_SPOT_TOUCH -->
+ <attr name="pointerIconSpotTouch" format="reference" />
+ <!-- Reference to a pointer icon drawable with STYLE_SPOT_ANCHOR -->
+ <attr name="pointerIconSpotAnchor" format="reference" />
+ </declare-styleable>
+
<declare-styleable name="PointerIcon">
<!-- Drawable to use as the icon bitmap. -->
<attr name="bitmap" format="reference" />
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index ceb7752..6b7c2a6 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -695,6 +695,13 @@
<p>The default value of this attribute is <code>false</code>. -->
<attr name="restoreAnyVersion" format="boolean" />
+ <!-- The agent to use for a *full* backup of the package. Only system applications
+ can use this to override the ordinary FullBackupAgent with a custom implementation.
+ It's needed strictly for packages with strongly device-specific data, such as the
+ Settings provider.
+ -->
+ <attr name="fullBackupAgent" format="string" />
+
<!-- The default install location defined by an application. -->
<attr name="installLocation">
<!-- Let the system decide ideal install location -->
@@ -792,6 +799,7 @@
<attr name="killAfterRestore" />
<attr name="restoreNeedsApplication" />
<attr name="restoreAnyVersion" />
+ <attr name="fullBackupAgent" />
<attr name="neverEncrypt" />
<!-- Request that your application's processes be created with
a large Dalvik heap. This applies to <em>all</em> processes
@@ -800,7 +808,7 @@
to allow multiple applications to use a process, they all must
use this option consistently or will get unpredictable results. -->
<attr name="largeHeap" format="boolean" />
- <!-- Declare that this applicationn can't participate in the normal
+ <!-- Declare that this application can't participate in the normal
state save/restore mechanism. Since it is not able to save and
restore its state on demand,
it can not participate in the normal activity lifecycle. It will
@@ -1179,6 +1187,10 @@
component specific values). -->
<attr name="enabled" />
<attr name="exported" />
+ <!-- If set to true, this service with be automatically stopped
+ when the user remove a task rooted in an activity owned by
+ the application. The default is false. -->
+ <attr name="stopWithTask" format="boolean" />
</declare-styleable>
<!-- The <code>receiver</code> tag declares an
diff --git a/core/res/res/values/bools.xml b/core/res/res/values/bools.xml
new file mode 100644
index 0000000..8e27be4
--- /dev/null
+++ b/core/res/res/values/bools.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2011, 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.
+*/
+-->
+<resources>
+ <bool name="allow_action_menu_item_text_with_icon">false</bool>
+ <bool name="action_bar_embed_tabs">false</bool>
+ <bool name="split_action_bar_is_narrow">true</bool>
+</resources>
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index 9c59cb6..6529fe1 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -60,6 +60,7 @@
<color name="highlighted_text_light">#9983CC39</color>
<color name="link_text_dark">#5c5cff</color>
<color name="link_text_light">#0000ee</color>
+ <color name="suggestion_highlight_text">#177bbd</color>
<drawable name="stat_notify_sync_noanim">@drawable/stat_notify_sync_anim0</drawable>
<drawable name="stat_sys_download_done">@drawable/stat_sys_download_anim0</drawable>
@@ -76,6 +77,7 @@
<drawable name="dialog_holo_light_frame">@drawable/dialog_full_holo_light</drawable>
<drawable name="input_method_fullscreen_background">#fff9f9f9</drawable>
+ <drawable name="input_method_fullscreen_background_holo">@drawable/screen_background_holo_dark</drawable>
<!-- For date picker widget -->
<drawable name="selected_day_background">#ff0092f4</drawable>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index c27fc9b..4dd4ff2 100755
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -183,6 +183,15 @@
The driver commands needed to support the feature are BGSCAN-START and BGSCAN-STOP -->
<bool translatable="false" name="config_wifi_background_scan_support">false</bool>
+ <!-- Integer indicating wpa_supplicant scan interval in milliseconds -->
+ <integer translatable="false" name="config_wifi_supplicant_scan_interval">15000</integer>
+
+ <!-- Integer indicating the framework scan interval in milliseconds. This is used in the scenario
+ where the chipset does not support background scanning (config_wifi_background_scan_suport
+ is false) to set up a periodic wake up scan so that the device can connect to a new access
+ point on the move. A value of 0 means no periodic scans will be used in the framework. -->
+ <integer translatable="false" name="config_wifi_framework_scan_interval">300000</integer>
+
<!-- Flag indicating whether the keyguard should be bypassed when
the slider is open. This can be set or unset depending how easily
the slider can be opened (for example, in a pocket or purse). -->
@@ -500,10 +509,10 @@
<string name="gsm_alphabet_default_charset"></string>
<!-- Enables SIP on WIFI only -->
- <bool name="config_sip_wifi_only">true</bool>
+ <bool name="config_sip_wifi_only">false</bool>
<!-- Enables built-in SIP phone capability -->
- <bool name="config_built_in_sip_phone">false</bool>
+ <bool name="config_built_in_sip_phone">true</bool>
<!-- Boolean indicating if restoring network selection should be skipped -->
<!-- The restoring is handled by modem if it is true-->
@@ -518,6 +527,9 @@
which typically is /data/data/com.android.providers.downloads/files -->
<integer name="config_downloadDataDirSize">100</integer>
+ <!-- Max number of downloads allowed to proceed concurrently -->
+ <integer name="config_MaxConcurrentDownloadsAllowed">5</integer>
+
<!-- When the free space available in DownloadManager's data dir falls
below the percentage value specified by this param, DownloadManager
starts removing files to try to make percentage of available
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 1782f02..3f4010b 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -19,21 +19,23 @@
-->
<resources>
<!-- The width that is used when creating thumbnails of applications. -->
- <dimen name="thumbnail_width">0dp</dimen>
+ <dimen name="thumbnail_width">120dp</dimen>
<!-- The height that is used when creating thumbnails of applications. -->
- <dimen name="thumbnail_height">0dp</dimen>
+ <dimen name="thumbnail_height">120dp</dimen>
<!-- The standard size (both width and height) of an application icon that
will be displayed in the app launcher and elsewhere. -->
<dimen name="app_icon_size">48dip</dimen>
<!-- The maximum number of action buttons that should be permitted within
an action bar/action mode. This will be used to determine how many
showAsAction="ifRoom" items can fit. "always" items can override this. -->
- <integer name="max_action_buttons">5</integer>
+ <integer name="max_action_buttons">3</integer>
<dimen name="toast_y_offset">64dip</dimen>
<!-- Height of the status bar -->
<dimen name="status_bar_height">25dip</dimen>
<!-- Height of the status bar -->
<dimen name="status_bar_icon_size">25dip</dimen>
+ <!-- Size of the giant number (unread count) in the notifications -->
+ <dimen name="status_bar_content_number_size">48sp</dimen>
<!-- Margin at the edge of the screen to ignore touch events for in the windowshade. -->
<dimen name="status_bar_edge_ignore">5dp</dimen>
<!-- Size of the fastscroll hint letter -->
@@ -50,12 +52,13 @@
<dimen name="password_keyboard_key_height_numeric">56dip</dimen>
<!-- Default correction for the space key in the password keyboard -->
<dimen name="password_keyboard_spacebar_vertical_correction">4dip</dimen>
+
<!-- Preference activity side margins -->
<dimen name="preference_screen_side_margin">0dp</dimen>
<!-- Preference activity side margins negative-->
<dimen name="preference_screen_side_margin_negative">0dp</dimen>
<!-- Preference widget area width (to the left of the text) -->
- <dimen name="preference_widget_width">56dp</dimen>
+ <dimen name="preference_widget_width">8dp</dimen>
<!-- The platform's desired minimum size for a dialog's width when it
is along the major axis (that is the screen is landscape). This may
@@ -80,11 +83,8 @@
<!-- Dialog button bar height -->
<dimen name="alert_dialog_button_bar_height">48dip</dimen>
- <!-- Size of the margin between the 'up' visual and the image in
- the action bar home section. -->
- <dimen name="action_bar_home_up_margin">-4dip</dimen>
- <!-- Size of the padding on either side of the app-supplied image
- in the action bar home section. -->
- <dimen name="action_bar_home_image_padding">8dip</dimen>
-
+ <!-- Default height of an action bar. -->
+ <dimen name="action_bar_default_height">48dip</dimen>
+ <!-- Vertical padding around action bar icons. -->
+ <dimen name="action_bar_icon_vertical_padding">4dip</dimen>
</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index f1ec398..8ad8f67 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1648,4 +1648,27 @@
<eat-comment />
<public type="attr" name="textCursorDrawable" id="0x01010362" />
<public type="attr" name="resizeMode" />
+
+<!-- ===============================================================
+ Resources added in version 13 of the platform (Ice Cream Sandwich)
+ =============================================================== -->
+ <eat-comment />
+ <public type="attr" name="state_hovered" />
+ <public type="attr" name="state_drag_can_accept" />
+ <public type="attr" name="state_drag_hovered" />
+ <public type="attr" name="stopWithTask" />
+
+ <public type="style" name="Theme.Holo.Light.NoActionBar" />
+ <public type="style" name="TextAppearance.SuggestionHighlight" />
+ <public type="style" name="Theme.Holo.SplitActionBarWhenNarrow" />
+ <public type="style" name="Theme.Holo.Light.SplitActionBarWhenNarrow" />
+
+ <public type="attr" name="textSuggestionsWindowStyle" />
+ <public type="attr" name="textEditSuggestionsBottomWindowLayout" />
+ <public type="attr" name="textEditSuggestionsTopWindowLayout" />
+ <public type="attr" name="textEditSuggestionItemLayout" />
+ <public type="attr" name="horizontalDirection" />
+
+ <public type="attr" name="fullBackupAgent" />
+
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 247f5fd..ccb3518 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -506,6 +506,13 @@
tasks to the foreground and background. Malicious applications can force
themselves to the front without your control.</string>
+ <!-- Title of an application permission, allowing an application to remove/kill tasks -->
+ <string name="permlab_removeTasks">stop running applications</string>
+ <!-- Description of an application permission, allowing an application to remove/kill tasks -->
+ <string name="permdesc_removeTasks">Allows an application to remove
+ tasks and kill their applications. Malicious applications can disrupt
+ the behavior of other applications.</string>
+
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_setDebugApp">enable application debugging</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
@@ -624,6 +631,11 @@
<string name="permdesc_backup">Allows the application to control the system\'s backup and restore mechanism. Not for use by normal applications.</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permlab_confirm_full_backup">confirm a full backup or restore operation</string>
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permdesc_confirm_full_backup">Allows the application to launch the full backup confirmation UI. Not to be used by any application.</string>
+
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_internalSystemWindow">display unauthorized windows</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permdesc_internalSystemWindow">Allows the creation of
@@ -1301,6 +1313,14 @@
with Near Field Communication (NFC) tags, cards, and readers.</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permlab_vpn">intercept and modify all network traffic</string>
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permdesc_vpn">Allows an application to intercept and
+ inspect all network traffic, for example to establish a VPN connection.
+ Malicious applications may monitor, redirect, or modify network packets
+ without your knowledge.</string>
+
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_disableKeyguard">disable keylock</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permdesc_disableKeyguard">Allows an application to disable
@@ -1832,7 +1852,7 @@
<!-- Do not translate. WebView User Agent string -->
<string name="web_user_agent" translatable="false">Mozilla/5.0 (Linux; U; <xliff:g id="x">Android %s</xliff:g>)
- AppleWebKit/534.13 (KHTML, like Gecko) Version/4.0 <xliff:g id="mobile">%s</xliff:g>Safari/534.13</string>
+ AppleWebKit/534.16 (KHTML, like Gecko) Version/4.0 <xliff:g id="mobile">%s</xliff:g>Safari/534.16</string>
<!-- Do not translate. WebView User Agent targeted content -->
<string name="web_user_agent_target_content" translatable="false">"Mobile "</string>
@@ -2655,12 +2675,6 @@
<string-array translatable="false" name="carrier_properties">
</string-array>
- <!-- Title for the selected state of a CompoundButton. -->
- <string name="accessibility_compound_button_selected">checked</string>
-
- <!-- Title for the unselected state of a CompoundButton. -->
- <string name="accessibility_compound_button_unselected">not checked</string>
-
<string name="grant_credentials_permission_message_header">The following one or more applications request permission to access your account, now and in the future.</string>
<string name="grant_credentials_permission_message_footer">Do you want to allow this request?</string>
<string name="grant_permissions_header_text">Access Request</string>
@@ -2822,6 +2836,13 @@
<!-- Description of the button to decrement the NumberPicker value. [CHAR LIMIT=NONE] -->
<string name="number_picker_decrement_button">Decrement</string>
+ <!-- Content description for the action bar "home" affordance. [CHAR LIMIT=NONE] -->
+ <string name="action_bar_home_description">Navigate home</string>
+ <!-- Content description for the action bar "up" affordance. [CHAR LIMIT=NONE] -->
+ <string name="action_bar_up_description">Navigate up</string>
+ <!-- Content description for the action menu overflow button. [CHAR LIMIT=NONE] -->
+ <string name="action_menu_overflow_description">More options</string>
+
<!-- Storage description for internal storage. [CHAR LIMIT=NONE] -->
<string name="storage_internal">Internal Storage</string>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 08a3024..26b5d95 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -217,7 +217,6 @@
<style name="TextAppearance.StatusBar">
<item name="android:textAppearance">?android:attr/textAppearanceSmall</item>
- <item name="android:textColor">?android:attr/textColorPrimary</item>
</style>
<style name="TextAppearance.StatusBar.Ticker">
</style>
@@ -226,15 +225,13 @@
</style>
<style name="TextAppearance.StatusBar.Icon">
- <item name="android:textStyle">bold</item>
</style>
<style name="TextAppearance.StatusBar.EventContent">
- <item name="android:textAppearance">?android:attr/textAppearanceSmall</item>
- <item name="android:textColor">?android:attr/textColorPrimaryInverse</item>
+ <item name="android:textColor">#ff999999</item>
+ <item name="android:textSize">14sp</item>
</style>
<style name="TextAppearance.StatusBar.EventContent.Title">
- <item name="android:textSize">18sp</item>
- <item name="android:textStyle">bold</item>
+ <item name="android:textColor">?android:attr/textColorPrimary</item>
</style>
<style name="TextAppearance.StatusBar.EventContent.Info">
<item name="android:textAppearance">?android:attr/textAppearanceLarge</item>
@@ -426,6 +423,9 @@
<item name="android:textEditNoPasteWindowLayout">?android:attr/textEditNoPasteWindowLayout</item>
<item name="android:textEditSidePasteWindowLayout">?android:attr/textEditSidePasteWindowLayout</item>
<item name="android:textEditSideNoPasteWindowLayout">?android:attr/textEditSideNoPasteWindowLayout</item>
+ <item name="android:textEditSuggestionsBottomWindowLayout">?android:attr/textEditSuggestionsBottomWindowLayout</item>
+ <item name="android:textEditSuggestionsTopWindowLayout">?android:attr/textEditSuggestionsTopWindowLayout</item>
+ <item name="android:textEditSuggestionItemLayout">?android:attr/textEditSuggestionItemLayout</item>
<item name="android:textCursorDrawable">?android:attr/textCursorDrawable</item>
</style>
@@ -599,11 +599,10 @@
<item name="android:divider">@android:drawable/divider_horizontal_bright_opaque</item>
</style>
- <style name="Widget.ListView.Menu">
+ <style name="Widget.ListView.Menu" parent="Widget.Holo.ListView">
<item name="android:cacheColorHint">@null</item>
<item name="android:scrollbars">vertical</item>
<item name="android:fadingEdge">none</item>
- <item name="listSelector">@android:drawable/menu_selector</item>
<!-- Light background for the list in menus, so the divider for bright themes -->
<item name="android:divider">@android:drawable/divider_horizontal_dark</item>
</style>
@@ -874,6 +873,13 @@
<item name="android:textSize">30sp</item>
</style>
+ <!-- @hide -->
+ <style name="TextAppearance.SuggestionHighlight">
+ <item name="android:textSize">18sp</item>
+ <item name="android:textColor">@android:color/suggestion_highlight_text</item>
+ <item name="android:textStyle">bold</item>
+ </style>
+
<!-- Preference Styles -->
<style name="Preference">
@@ -1047,6 +1053,17 @@
<item name="windowExitAnimation">@android:anim/fade_out</item>
</style>
+ <!-- Style for the popup window that contains text suggestions. -->
+ <style name="Widget.TextSuggestions">
+ <item name="android:popupAnimationStyle">@android:style/Animation.TextSuggestions</item>
+ </style>
+
+ <!-- Animation effects when showing/hiding the text suggestions popup window. -->
+ <style name="Animation.TextSuggestions">
+ <item name="windowEnterAnimation">@android:anim/fade_in</item>
+ <item name="windowExitAnimation">@android:anim/fade_out</item>
+ </style>
+
<style name="Widget.ActionBar">
<item name="android:background">@android:drawable/action_bar_background</item>
<item name="android:displayOptions">useLogo|showHome|showTitle</item>
@@ -1061,6 +1078,7 @@
<item name="android:progressBarStyle">@android:style/Widget.ProgressBar.Horizontal</item>
<item name="android:indeterminateProgressStyle">@android:style/Widget.ProgressBar.Small</item>
<item name="android:homeLayout">@android:layout/action_bar_home</item>
+ <item name="android:embeddedTabs">@android:bool/action_bar_embed_tabs</item>
</style>
<style name="Widget.ActionMode">
@@ -1096,7 +1114,7 @@
<style name="Widget.ActionButton.Overflow">
<item name="android:src">@drawable/ic_menu_more</item>
- <item name="android:contentDescription">@string/more_item_label</item>
+ <item name="android:contentDescription">@string/action_menu_overflow_description</item>
</style>
<style name="Widget.ActionButton.CloseMode">
@@ -1214,8 +1232,10 @@
<item name="android:textColor">?textColorPrimary</item>
</style>
+ <!-- This style is for smaller screens; values-xlarge defines a version
+ for larger screens. -->
<style name="TextAppearance.Holo.Widget.TabWidget">
- <item name="android:textSize">18sp</item>
+ <item name="android:textSize">14sp</item>
<item name="android:textStyle">normal</item>
<item name="android:textColor">@android:color/tab_indicator_text</item>
</style>
@@ -1408,6 +1428,11 @@
<item name="android:minWidth">64dip</item>
</style>
+ <style name="Widget.Holo.StackView">
+ <item name="android:resOutColor">#ff6699ff</item>
+ <item name="android:clickColor">#886699ff</item>
+ </style>
+
<style name="Widget.Holo.Button.Borderless">
<item name="android:background">?android:attr/selectableItemBackground</item>
</style>
@@ -1462,6 +1487,9 @@
<style name="Widget.Holo.TextSelectHandle" parent="Widget.TextSelectHandle">
</style>
+ <style name="Widget.Holo.TextSuggestions" parent="Widget.TextSuggestions">
+ </style>
+
<style name="Widget.Holo.AbsListView" parent="Widget.AbsListView">
</style>
@@ -1668,6 +1696,9 @@
<item name="android:button">@android:drawable/btn_star_holo_dark</item>
</style>
+ <!-- The holo style for smaller screens actually uses the non-holo layout,
+ which is more compact. values-xlarge defines an alternative version
+ for the real holo look on a large screen. -->
<style name="Widget.Holo.TabWidget" parent="Widget.TabWidget">
<item name="android:textAppearance">@style/TextAppearance.Holo.Widget.TabWidget</item>
<item name="android:tabStripLeft">@null</item>
@@ -1748,6 +1779,7 @@
<item name="android:background">?android:attr/selectableItemBackground</item>
<item name="android:paddingLeft">16dip</item>
<item name="android:paddingRight">16dip</item>
+ <item name="android:contentDescription">@string/action_menu_overflow_description</item>
</style>
<style name="Widget.Holo.ActionButton.TextButton" parent="Widget.Holo.ButtonBar.Button">
@@ -2082,6 +2114,7 @@
<item name="android:src">@android:drawable/ic_menu_moreoverflow_holo_light</item>
<item name="android:paddingLeft">16dip</item>
<item name="android:paddingRight">16dip</item>
+ <item name="android:contentDescription">@string/action_menu_overflow_description</item>
</style>
<style name="Widget.Holo.Light.ActionBarView_TabView" parent="Widget.Holo.ActionBarView_TabView">
@@ -2183,8 +2216,16 @@
<style name="Widget.Holo.PreferenceFrameLayout">
<item name="android:borderTop">0dip</item>
- <item name="android:borderBottom">48dip</item>
- <item name="android:borderLeft">32dip</item>
- <item name="android:borderRight">32dip</item>
+ <item name="android:borderBottom">0dip</item>
+ <item name="android:borderLeft">0dip</item>
+ <item name="android:borderRight">0dip</item>
+ </style>
+
+ <!-- Pointer styles -->
+ <style name="Pointer">
+ <item name="android:pointerIconArrow">@android:drawable/pointer_arrow_icon</item>
+ <item name="android:pointerIconSpotHover">@android:drawable/pointer_spot_hover_icon</item>
+ <item name="android:pointerIconSpotTouch">@android:drawable/pointer_spot_touch_icon</item>
+ <item name="android:pointerIconSpotAnchor">@android:drawable/pointer_spot_anchor_icon</item>
</style>
</resources>
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index 2f434ec..0a614b2 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -180,6 +180,10 @@
<item name="textEditNoPasteWindowLayout">@android:layout/text_edit_no_paste_window</item>
<item name="textEditSidePasteWindowLayout">@android:layout/text_edit_side_paste_window</item>
<item name="textEditSideNoPasteWindowLayout">@android:layout/text_edit_side_no_paste_window</item>
+ <item name="textSuggestionsWindowStyle">@android:style/Widget.TextSuggestions</item>
+ <item name="textEditSuggestionsBottomWindowLayout">@android:layout/text_edit_suggestions_bottom_window</item>
+ <item name="textEditSuggestionsTopWindowLayout">@android:layout/text_edit_suggestions_top_window</item>
+ <item name="textEditSuggestionItemLayout">@android:layout/text_edit_suggestion_item</item>
<item name="textCursorDrawable">@null</item>
<!-- Widget styles -->
@@ -272,7 +276,7 @@
<item name="actionModeStyle">@style/Widget.ActionMode</item>
<item name="actionModeCloseButtonStyle">@style/Widget.ActionButton.CloseMode</item>
<item name="actionBarStyle">@android:style/Widget.ActionBar</item>
- <item name="actionBarSize">56dip</item>
+ <item name="actionBarSize">@dimen/action_bar_default_height</item>
<item name="actionModePopupWindowStyle">?android:attr/popupWindowStyle</item>
<item name="actionMenuTextAppearance">?android:attr/textAppearanceMedium</item>
<item name="actionMenuTextColor">?android:attr/textColorPrimary</item>
@@ -321,6 +325,8 @@
<item name="fastScrollOverlayPosition">floating</item>
<item name="fastScrollTextColor">@android:color/primary_text_dark</item>
+ <!-- Pointer style -->
+ <item name="pointerStyle">@android:style/Pointer</item>
</style>
<!-- Variant of the default (dark) theme with no title bar -->
@@ -675,7 +681,7 @@
and a few custom attributes. -->
<style name="Theme.Holo.InputMethod" parent="Theme.Holo.Panel">
<item name="android:windowAnimationStyle">@android:style/Animation.InputMethod</item>
- <item name="android:imeFullscreenBackground">@android:drawable/input_method_fullscreen_background</item>
+ <item name="android:imeFullscreenBackground">@android:drawable/input_method_fullscreen_background_holo</item>
<item name="android:imeExtractEnterAnimation">@android:anim/input_method_extract_enter</item>
<item name="android:imeExtractExitAnimation">@android:anim/input_method_extract_exit</item>
</style>
@@ -706,10 +712,10 @@
<!-- Menu Themes -->
<eat-comment />
- <style name="Theme.IconMenu">
+ <style name="Theme.IconMenu" parent="Theme.Holo">
<!-- Menu/item attributes -->
<item name="android:itemTextAppearance">@android:style/TextAppearance.Widget.IconMenu.Item</item>
- <item name="android:itemBackground">@android:drawable/menu_selector</item>
+ <item name="android:itemBackground">?android:attr/selectableItemBackground</item>
<item name="android:itemIconDisabledAlpha">?android:attr/disabledAlpha</item>
<item name="android:horizontalDivider">@android:drawable/divider_horizontal_dark</item>
<item name="android:verticalDivider">@android:drawable/divider_vertical_dark</item>
@@ -718,7 +724,7 @@
<item name="android:background">@null</item>
</style>
- <style name="Theme.ExpandedMenu">
+ <style name="Theme.ExpandedMenu" parent="Theme.Holo">
<!-- Menu/item attributes -->
<item name="android:itemTextAppearance">?android:attr/textAppearanceLarge</item>
<item name="android:listViewStyle">@android:style/Widget.ListView.Menu</item>
@@ -931,6 +937,7 @@
<item name="textSelectHandleRight">@android:drawable/text_select_handle_right</item>
<item name="textSelectHandle">@android:drawable/text_select_handle_middle</item>
<item name="textSelectHandleWindowStyle">@android:style/Widget.Holo.TextSelectHandle</item>
+ <item name="textSuggestionsWindowStyle">@android:style/Widget.Holo.TextSuggestions</item>
<item name="textCursorDrawable">@android:drawable/text_cursor_holo_dark</item>
<!-- Widget styles -->
@@ -986,6 +993,7 @@
<item name="quickContactBadgeStyleSmallWindowLarge">@android:style/Widget.Holo.QuickContactBadgeSmall.WindowLarge</item>
<item name="listPopupWindowStyle">@android:style/Widget.Holo.ListPopupWindow</item>
<item name="popupMenuStyle">@android:style/Widget.Holo.PopupMenu</item>
+ <item name="stackViewStyle">@android:style/Widget.Holo.StackView</item>
<!-- Preference styles -->
<item name="preferenceScreenStyle">@android:style/Preference.Holo.PreferenceScreen</item>
@@ -1015,7 +1023,7 @@
<item name="actionModeStyle">@style/Widget.Holo.ActionMode</item>
<item name="actionModeCloseButtonStyle">@style/Widget.Holo.ActionButton.CloseMode</item>
<item name="actionBarStyle">@android:style/Widget.Holo.ActionBar</item>
- <item name="actionBarSize">56dip</item>
+ <item name="actionBarSize">@dimen/action_bar_default_height</item>
<item name="actionModePopupWindowStyle">@android:style/Widget.Holo.PopupWindow.ActionMode</item>
<item name="actionModeCutDrawable">@android:drawable/ic_menu_cut_holo_dark</item>
@@ -1216,6 +1224,7 @@
<item name="textSelectHandleRight">@android:drawable/text_select_handle_right</item>
<item name="textSelectHandle">@android:drawable/text_select_handle_middle</item>
<item name="textSelectHandleWindowStyle">@android:style/Widget.Holo.TextSelectHandle</item>
+ <item name="textSuggestionsWindowStyle">@android:style/Widget.Holo.TextSuggestions</item>
<item name="textCursorDrawable">@android:drawable/text_cursor_holo_light</item>
<!-- Widget styles -->
@@ -1271,7 +1280,8 @@
<item name="quickContactBadgeStyleSmallWindowLarge">@android:style/Widget.Holo.QuickContactBadgeSmall.WindowLarge</item>
<item name="listPopupWindowStyle">@android:style/Widget.Holo.Light.ListPopupWindow</item>
<item name="popupMenuStyle">@android:style/Widget.Holo.Light.PopupMenu</item>
-
+ <item name="stackViewStyle">@android:style/Widget.Holo.StackView</item>
+
<!-- Preference styles -->
<item name="preferenceScreenStyle">@android:style/Preference.Holo.PreferenceScreen</item>
<item name="preferenceCategoryStyle">@android:style/Preference.Holo.Category</item>
@@ -1300,7 +1310,7 @@
<item name="actionModeStyle">@style/Widget.Holo.Light.ActionMode</item>
<item name="actionModeCloseButtonStyle">@style/Widget.Holo.Light.ActionButton.CloseMode</item>
<item name="actionBarStyle">@android:style/Widget.Holo.Light.ActionBar</item>
- <item name="actionBarSize">56dip</item>
+ <item name="actionBarSize">@dimen/action_bar_default_height</item>
<item name="actionModePopupWindowStyle">@android:style/Widget.Holo.Light.PopupWindow.ActionMode</item>
<item name="actionModeCutDrawable">@android:drawable/ic_menu_cut_holo_light</item>
@@ -1546,4 +1556,17 @@
<item name="android:windowNoTitle">true</item>
</style>
+ <!-- Variant of the holographic (dark) theme with an action bar that
+ splits across the top and bottom of the activity when constrained
+ for horizontal space. -->
+ <style name="Theme.Holo.SplitActionBarWhenNarrow">
+ <item name="android:windowSplitActionBar">@android:bool/split_action_bar_is_narrow</item>
+ </style>
+
+ <!-- Variant of the holographic (light) theme with an action bar that
+ splits across the top and bottom of the activity when constrained
+ for horizontal space. -->
+ <style name="Theme.Holo.Light.SplitActionBarWhenNarrow">
+ <item name="android:windowSplitActionBar">@android:bool/split_action_bar_is_narrow</item>
+ </style>
</resources>
diff --git a/core/tests/coretests/Android.mk b/core/tests/coretests/Android.mk
index b496805..b02d904 100644
--- a/core/tests/coretests/Android.mk
+++ b/core/tests/coretests/Android.mk
@@ -12,7 +12,7 @@ LOCAL_SRC_FILES := \
$(call all-java-files-under, EnabledTestApp/src)
LOCAL_DX_FLAGS := --core-library
-LOCAL_STATIC_JAVA_LIBRARIES := core-tests-supportlib android-common frameworks-core-util-lib
+LOCAL_STATIC_JAVA_LIBRARIES := core-tests android-common frameworks-core-util-lib
LOCAL_JAVA_LIBRARIES := android.test.runner
LOCAL_PACKAGE_NAME := FrameworksCoreTests
diff --git a/core/tests/coretests/src/android/app/activity/LocalProvider.java b/core/tests/coretests/src/android/app/activity/LocalProvider.java
index 085e622..d6a10c2 100644
--- a/core/tests/coretests/src/android/app/activity/LocalProvider.java
+++ b/core/tests/coretests/src/android/app/activity/LocalProvider.java
@@ -23,7 +23,6 @@ import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
-import android.util.Config;
import android.util.Log;
/** Simple test provider that runs in the local process. */
@@ -106,7 +105,7 @@ public class LocalProvider extends ContentProvider {
null, null, sort);
if (ret == null) {
- if (Config.LOGD) Log.d(TAG, "Alarms.query: failed");
+ if (false) Log.d(TAG, "Alarms.query: failed");
} else {
ret.setNotificationUri(getContext().getContentResolver(), url);
}
@@ -145,7 +144,7 @@ public class LocalProvider extends ContentProvider {
"Cannot update URL: " + url);
}
}
- if (Config.LOGD) Log.d(TAG, "*** notifyChange() rowId: " + rowId);
+ if (false) Log.d(TAG, "*** notifyChange() rowId: " + rowId);
getContext().getContentResolver().notifyChange(url, null);
return count;
}
diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java
index 39258ae..5ef8d11 100644
--- a/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java
+++ b/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java
@@ -31,9 +31,11 @@ import android.test.suitebuilder.annotation.MediumTest;
import android.test.suitebuilder.annotation.SmallTest;
import android.test.suitebuilder.annotation.Suppress;
import android.util.Log;
+import android.util.Pair;
import java.io.File;
import java.util.ArrayList;
+import java.util.List;
public class SQLiteDatabaseTest extends AndroidTestCase {
private static final String TAG = "DatabaseGeneralTest";
@@ -892,6 +894,49 @@ public class SQLiteDatabaseTest extends AndroidTestCase {
c.close();
}
+ @SmallTest
+ public void testAttachDb() {
+ String newDb = "/sdcard/mydata.db";
+ File f = new File(newDb);
+ if (f.exists()) {
+ f.delete();
+ }
+ assertFalse(f.exists());
+ SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(newDb, null);
+ db.execSQL("create table test1 (i int);");
+ db.execSQL("insert into test1 values(1);");
+ db.execSQL("insert into test1 values(11);");
+ Cursor c = null;
+ try {
+ c = db.rawQuery("select * from test1", null);
+ int count = c.getCount();
+ Log.i(TAG, "count: " + count);
+ assertEquals(2, count);
+ } finally {
+ c.close();
+ db.close();
+ c = null;
+ }
+
+ mDatabase.execSQL("attach database ? as newDb" , new String[]{newDb});
+ Cursor c1 = null;
+ try {
+ c1 = mDatabase.rawQuery("select * from newDb.test1", null);
+ assertEquals(2, c1.getCount());
+ } catch (Exception e) {
+ fail("unexpected exception: " + e.getMessage());
+ } finally {
+ if (c1 != null) {
+ c1.close();
+ }
+ }
+ List<Pair<String, String>> dbs = mDatabase.getAttachedDbs();
+ for (Pair<String, String> p: dbs) {
+ Log.i(TAG, "attached dbs: " + p.first + " : " + p.second);
+ }
+ assertEquals(2, dbs.size());
+ }
+
/**
* http://b/issue?id=2943028
* SQLiteOpenHelper maintains a Singleton even if it is in bad state.
diff --git a/core/tests/coretests/src/android/net/NetworkStatsTest.java b/core/tests/coretests/src/android/net/NetworkStatsTest.java
new file mode 100644
index 0000000..45719c2
--- /dev/null
+++ b/core/tests/coretests/src/android/net/NetworkStatsTest.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2011 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.net;
+
+import android.os.SystemClock;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import junit.framework.TestCase;
+
+@SmallTest
+public class NetworkStatsTest extends TestCase {
+
+ private static final String TEST_IFACE = "test0";
+
+ public void testFindIndex() throws Exception {
+ final NetworkStats stats = new NetworkStats.Builder(SystemClock.elapsedRealtime(), 3)
+ .addEntry(TEST_IFACE, 100, 1024, 0)
+ .addEntry(TEST_IFACE, 101, 0, 1024)
+ .addEntry(TEST_IFACE, 102, 1024, 1024).build();
+
+ assertEquals(2, stats.findIndex(TEST_IFACE, 102));
+ assertEquals(2, stats.findIndex(TEST_IFACE, 102));
+ assertEquals(0, stats.findIndex(TEST_IFACE, 100));
+ assertEquals(-1, stats.findIndex(TEST_IFACE, 6));
+ }
+
+ public void testSubtractIdenticalData() throws Exception {
+ final NetworkStats before = new NetworkStats.Builder(SystemClock.elapsedRealtime(), 2)
+ .addEntry(TEST_IFACE, 100, 1024, 0)
+ .addEntry(TEST_IFACE, 101, 0, 1024).build();
+
+ final NetworkStats after = new NetworkStats.Builder(SystemClock.elapsedRealtime(), 2)
+ .addEntry(TEST_IFACE, 100, 1024, 0)
+ .addEntry(TEST_IFACE, 101, 0, 1024).build();
+
+ final NetworkStats result = after.subtract(before);
+
+ assertEquals(0, result.rx[0]);
+ assertEquals(0, result.tx[0]);
+ assertEquals(0, result.rx[1]);
+ assertEquals(0, result.tx[1]);
+ }
+
+ public void testSubtractIdenticalRows() throws Exception {
+ final NetworkStats before = new NetworkStats.Builder(SystemClock.elapsedRealtime(), 2)
+ .addEntry(TEST_IFACE, 100, 1024, 0)
+ .addEntry(TEST_IFACE, 101, 0, 1024).build();
+
+ final NetworkStats after = new NetworkStats.Builder(SystemClock.elapsedRealtime(), 2)
+ .addEntry(TEST_IFACE, 100, 1025, 2)
+ .addEntry(TEST_IFACE, 101, 3, 1028).build();
+
+ final NetworkStats result = after.subtract(before);
+
+ // expect delta between measurements
+ assertEquals(1, result.rx[0]);
+ assertEquals(2, result.tx[0]);
+ assertEquals(3, result.rx[1]);
+ assertEquals(4, result.tx[1]);
+ }
+
+ public void testSubtractNewRows() throws Exception {
+ final NetworkStats before = new NetworkStats.Builder(SystemClock.elapsedRealtime(), 2)
+ .addEntry(TEST_IFACE, 100, 1024, 0)
+ .addEntry(TEST_IFACE, 101, 0, 1024).build();
+
+ final NetworkStats after = new NetworkStats.Builder(SystemClock.elapsedRealtime(), 3)
+ .addEntry(TEST_IFACE, 100, 1024, 0)
+ .addEntry(TEST_IFACE, 101, 0, 1024)
+ .addEntry(TEST_IFACE, 102, 1024, 1024).build();
+
+ final NetworkStats result = after.subtract(before);
+
+ // its okay to have new rows
+ assertEquals(0, result.rx[0]);
+ assertEquals(0, result.tx[0]);
+ assertEquals(0, result.rx[1]);
+ assertEquals(0, result.tx[1]);
+ assertEquals(1024, result.rx[2]);
+ assertEquals(1024, result.tx[2]);
+ }
+
+}
diff --git a/core/tests/coretests/src/android/os/MemoryFileTest.java b/core/tests/coretests/src/android/os/MemoryFileTest.java
index e627bb4..82af662 100644
--- a/core/tests/coretests/src/android/os/MemoryFileTest.java
+++ b/core/tests/coretests/src/android/os/MemoryFileTest.java
@@ -20,13 +20,11 @@ import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.LargeTest;
import android.test.suitebuilder.annotation.SmallTest;
-import java.io.File;
-import java.io.FileDescriptor;
-import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
public class MemoryFileTest extends AndroidTestCase {
@@ -101,6 +99,25 @@ public class MemoryFileTest extends AndroidTestCase {
file.close();
}
+ // http://code.google.com/p/android/issues/detail?id=11415
+ public void testOutputStreamAdvances() throws IOException {
+ MemoryFile file = new MemoryFile("MemoryFileTest", 10);
+
+ OutputStream os = file.getOutputStream();
+ os.write(new byte[] { 1, 2, 3, 4, 5 });
+ os.write(new byte[] { -1, -1, 6, 7, 8, -1 }, 2, 3);
+ os.write(9);
+ try {
+ os.write(new byte[] { -1, -1 });
+ fail();
+ } catch (IndexOutOfBoundsException expected) {
+ }
+
+ byte[] copy = new byte[file.length()];
+ file.readBytes(copy, 0, 0, file.length());
+ assertEquals("[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]", Arrays.toString(copy));
+ }
+
// Tests for the IndexOutOfBoundsException cases in read().
private void readIndexOutOfBoundsException(int offset, int count, String msg)
diff --git a/core/tests/coretests/src/android/text/TextUtilsTest.java b/core/tests/coretests/src/android/text/TextUtilsTest.java
index 63dd0cc..d494c5d 100644
--- a/core/tests/coretests/src/android/text/TextUtilsTest.java
+++ b/core/tests/coretests/src/android/text/TextUtilsTest.java
@@ -16,29 +16,21 @@
package android.text;
-import android.graphics.Paint;
+import com.google.android.collect.Lists;
+
+import android.test.MoreAsserts;
import android.os.Parcel;
import android.test.suitebuilder.annotation.LargeTest;
import android.test.suitebuilder.annotation.SmallTest;
-import android.text.Spannable;
-import android.text.SpannableString;
-import android.text.Spanned;
-import android.text.SpannedString;
-import android.text.TextPaint;
-import android.text.TextUtils;
import android.text.style.StyleSpan;
import android.text.util.Rfc822Token;
import android.text.util.Rfc822Tokenizer;
-import android.test.MoreAsserts;
-import com.google.android.collect.Lists;
-import com.google.android.collect.Maps;
+import java.util.ArrayList;
+import java.util.List;
import junit.framework.TestCase;
-import java.util.List;
-import java.util.Map;
-
/**
* TextUtilsTest tests {@link TextUtils}.
*/
@@ -417,6 +409,7 @@ public class TextUtilsTest extends TestCase {
return mString.charAt(off);
}
+ @Override
public String toString() {
return mString.toString();
}
@@ -425,4 +418,104 @@ public class TextUtilsTest extends TestCase {
return new Wrapper(mString.subSequence(start, end));
}
}
+
+ @LargeTest
+ public void testRemoveEmptySpans() {
+ MockSpanned spanned = new MockSpanned();
+
+ spanned.test();
+ spanned.addSpan().test();
+ spanned.addSpan().test();
+ spanned.addSpan().test();
+ spanned.addEmptySpan().test();
+ spanned.addSpan().test();
+ spanned.addEmptySpan().test();
+ spanned.addEmptySpan().test();
+ spanned.addSpan().test();
+
+ spanned.clear();
+ spanned.addEmptySpan().test();
+ spanned.addEmptySpan().test();
+ spanned.addEmptySpan().test();
+ spanned.addSpan().test();
+ spanned.addEmptySpan().test();
+ spanned.addSpan().test();
+
+ spanned.clear();
+ spanned.addSpan().test();
+ spanned.addEmptySpan().test();
+ spanned.addSpan().test();
+ spanned.addEmptySpan().test();
+ spanned.addSpan().test();
+ spanned.addSpan().test();
+ }
+
+ protected static class MockSpanned implements Spanned {
+
+ private List<Object> allSpans = new ArrayList<Object>();
+ private List<Object> nonEmptySpans = new ArrayList<Object>();
+
+ public void clear() {
+ allSpans.clear();
+ nonEmptySpans.clear();
+ }
+
+ public MockSpanned addSpan() {
+ Object o = new Object();
+ allSpans.add(o);
+ nonEmptySpans.add(o);
+ return this;
+ }
+
+ public MockSpanned addEmptySpan() {
+ Object o = new Object();
+ allSpans.add(o);
+ return this;
+ }
+
+ public void test() {
+ Object[] nonEmpty = TextUtils.removeEmptySpans(allSpans.toArray(), this, Object.class);
+ assertEquals("Mismatched array size", nonEmptySpans.size(), nonEmpty.length);
+ for (int i=0; i<nonEmpty.length; i++) {
+ assertEquals("Span differ", nonEmptySpans.get(i), nonEmpty[i]);
+ }
+ }
+
+ public char charAt(int arg0) {
+ return 0;
+ }
+
+ public int length() {
+ return 0;
+ }
+
+ public CharSequence subSequence(int arg0, int arg1) {
+ return null;
+ }
+
+ @Override
+ public <T> T[] getSpans(int start, int end, Class<T> type) {
+ return null;
+ }
+
+ @Override
+ public int getSpanStart(Object tag) {
+ return 0;
+ }
+
+ @Override
+ public int getSpanEnd(Object tag) {
+ return nonEmptySpans.contains(tag) ? 1 : 0;
+ }
+
+ @Override
+ public int getSpanFlags(Object tag) {
+ return 0;
+ }
+
+ @Override
+ public int nextSpanTransition(int start, int limit, Class type) {
+ return 0;
+ }
+ }
}
diff --git a/core/tests/coretests/src/android/view/menu/MenuLayoutLandscapeTest.java b/core/tests/coretests/src/android/view/menu/MenuLayoutLandscapeTest.java
index d9bf860..9347b27 100644
--- a/core/tests/coretests/src/android/view/menu/MenuLayoutLandscapeTest.java
+++ b/core/tests/coretests/src/android/view/menu/MenuLayoutLandscapeTest.java
@@ -59,6 +59,8 @@ public class MenuLayoutLandscapeTest extends ActivityInstrumentationTestCase<Men
private void assertLayout(Integer... expectedLayout) {
toggleMenu();
+ /* TODO These need to be rewritten to account for presenters that an activity
+ * does not have access to.
IconMenuView iconMenuView = ((IconMenuView) mActivity.getMenuView(MenuBuilder.TYPE_ICON));
int[] layout = iconMenuView.getLayout();
int layoutNumRows = iconMenuView.getLayoutNumRows();
@@ -70,6 +72,7 @@ public class MenuLayoutLandscapeTest extends ActivityInstrumentationTestCase<Men
assertEquals("Col mismatch on row " + row, expectedLayout[row].intValue(),
layout[row]);
}
+ */
}
public void test1ShortItem() {
diff --git a/core/tests/coretests/src/android/view/menu/MenuLayoutPortraitTest.java b/core/tests/coretests/src/android/view/menu/MenuLayoutPortraitTest.java
index ad746b0..b053699 100644
--- a/core/tests/coretests/src/android/view/menu/MenuLayoutPortraitTest.java
+++ b/core/tests/coretests/src/android/view/menu/MenuLayoutPortraitTest.java
@@ -58,6 +58,8 @@ public class MenuLayoutPortraitTest extends ActivityInstrumentationTestCase<Menu
private void assertLayout(Integer... expectedLayout) {
toggleMenu();
+ /* TODO These need to be rewritten to account for presenters that an activity
+ * does not have access to.
IconMenuView iconMenuView = ((IconMenuView) mActivity.getMenuView(MenuBuilder.TYPE_ICON));
int[] layout = iconMenuView.getLayout();
int layoutNumRows = iconMenuView.getLayoutNumRows();
@@ -69,6 +71,7 @@ public class MenuLayoutPortraitTest extends ActivityInstrumentationTestCase<Menu
assertEquals("Col mismatch on row " + row, expectedLayout[row].intValue(),
layout[row]);
}
+ */
}
public void test1ShortItem() {
diff --git a/core/tests/coretests/src/android/view/menu/MenuScenario.java b/core/tests/coretests/src/android/view/menu/MenuScenario.java
index b0b8802..668aec4 100644
--- a/core/tests/coretests/src/android/view/menu/MenuScenario.java
+++ b/core/tests/coretests/src/android/view/menu/MenuScenario.java
@@ -16,16 +16,12 @@
package android.view.menu;
-import android.util.ListScenario;
-import com.android.internal.view.menu.MenuBuilder;
-import com.android.internal.view.menu.MenuBuilder.MenuAdapter;
-
import android.app.Activity;
import android.os.Bundle;
+import android.util.ListScenario;
import android.util.SparseArray;
import android.view.Menu;
import android.view.MenuItem;
-import android.view.View;
/**
* Utility base class for creating various Menu scenarios. Configurable by the
@@ -36,7 +32,6 @@ public class MenuScenario extends Activity implements MenuItem.OnMenuItemClickLi
private Menu mMenu;
private MenuItem[] mItems;
private boolean[] mWasItemClicked;
- private MenuAdapter[] mMenuAdapters = new MenuAdapter[MenuBuilder.NUM_TYPES];
@Override
protected void onCreate(Bundle icicle) {
@@ -149,39 +144,6 @@ public class MenuScenario extends Activity implements MenuItem.OnMenuItemClickLi
return -1;
}
- /**
- * @see MenuBuilder#getMenuAdapter(int)
- */
- public MenuAdapter getMenuAdapter(int menuType) {
- if (mMenuAdapters[menuType] == null) {
- mMenuAdapters[menuType] = ((MenuBuilder) mMenu).getMenuAdapter(menuType);
- }
-
- return mMenuAdapters[menuType];
- }
-
- /**
- * Gets a menu view. Call this after you're sure it has been shown,
- * otherwise it may not have the proper layout_* attributes set.
- *
- * @param menuType The type of menu.
- * @return The MenuView for that type.
- */
- public View getMenuView(int menuType) {
- return ((MenuBuilder) mMenu).getMenuView(menuType, null);
- }
-
- /**
- * Gets the menu item view for a given position.
- *
- * @param menuType The type of menu.
- * @param position The position of the item.
- * @return The menu item view for the given item in the given menu type.
- */
- public View getItemView(int menuType, int position) {
- return getMenuAdapter(menuType).getView(position, null, null);
- }
-
public static class Params {
// Using as data structure, so no m prefix
private boolean shouldShowMenu = true;
diff --git a/core/tests/coretests/src/android/view/menu/MenuWith1ItemTest.java b/core/tests/coretests/src/android/view/menu/MenuWith1ItemTest.java
index 4e71053..82ad858 100644
--- a/core/tests/coretests/src/android/view/menu/MenuWith1ItemTest.java
+++ b/core/tests/coretests/src/android/view/menu/MenuWith1ItemTest.java
@@ -61,6 +61,9 @@ public class MenuWith1ItemTest extends ActivityInstrumentationTestCase<MenuWith1
@LargeTest
public void testTouchModeTransfersRemovesFocus() throws Exception {
+ /* TODO These need to be rewritten to account for presenters that an activity
+ * does not have access to.
+
// open menu, move around to give it focus
sendKeys(KeyEvent.KEYCODE_MENU, KeyEvent.KEYCODE_DPAD_LEFT);
final View menuItem = mActivity.getItemView(MenuBuilder.TYPE_ICON, 0);
@@ -80,5 +83,6 @@ public class MenuWith1ItemTest extends ActivityInstrumentationTestCase<MenuWith1
sendKeys(KeyEvent.KEYCODE_MENU);
assertTrue("menuItem.isInTouchMode()", menuItem.isInTouchMode());
assertFalse("menuItem.isFocused()", menuItem.isFocused());
+ */
}
}
diff --git a/core/tests/coretests/src/android/widget/focus/RequestFocusTest.java b/core/tests/coretests/src/android/widget/focus/RequestFocusTest.java
index 477831e..909a8c9 100644
--- a/core/tests/coretests/src/android/widget/focus/RequestFocusTest.java
+++ b/core/tests/coretests/src/android/widget/focus/RequestFocusTest.java
@@ -90,7 +90,7 @@ public class RequestFocusTest extends ActivityInstrumentationTestCase<RequestFoc
fail("requestFocus from wrong thread should raise exception.");
} catch (AndroidRuntimeException e) {
// Expected. The actual exception is not public, so we can't catch it.
- assertEquals("android.view.ViewRoot$CalledFromWrongThreadException",
+ assertEquals("android.view.ViewAncestor$CalledFromWrongThreadException",
e.getClass().getName());
}
}
diff --git a/core/tests/coretests/src/android/widget/listview/ListViewHeight.java b/core/tests/coretests/src/android/widget/listview/ListViewHeight.java
index 64f280a..5cf3ff6 100644
--- a/core/tests/coretests/src/android/widget/listview/ListViewHeight.java
+++ b/core/tests/coretests/src/android/widget/listview/ListViewHeight.java
@@ -88,7 +88,7 @@ public class ListViewHeight extends Activity {
// Clicking this button will remove the list adapter and hide the outer enclosing view.
// We have to climb all the way to the top because the bug (not checking visibility)
- // only occurs at the very outer loop of ViewRoot.performTraversals and in the case of
+ // only occurs at the very outer loop of ViewAncestor.performTraversals and in the case of
// an Activity, this means you have to crawl all the way out to the root view.
// In the search manager, it's sufficient to simply show/hide the outer search manager
// view to trigger the same bug.
diff --git a/core/tests/systemproperties/Android.mk b/core/tests/systemproperties/Android.mk
index 05216e0..9f01a28 100644
--- a/core/tests/systemproperties/Android.mk
+++ b/core/tests/systemproperties/Android.mk
@@ -9,7 +9,7 @@ LOCAL_SRC_FILES := \
$(call all-java-files-under, src)
LOCAL_DX_FLAGS := --core-library
-LOCAL_STATIC_JAVA_LIBRARIES := core-tests-supportlib android-common frameworks-core-util-lib
+LOCAL_STATIC_JAVA_LIBRARIES := core-tests android-common frameworks-core-util-lib
LOCAL_JAVA_LIBRARIES := android.test.runner
LOCAL_PACKAGE_NAME := FrameworksCoreSystemPropertiesTests
diff --git a/core/tests/systemproperties/AndroidManifest.xml b/core/tests/systemproperties/AndroidManifest.xml
index ad0abf4..1608788 100644
--- a/core/tests/systemproperties/AndroidManifest.xml
+++ b/core/tests/systemproperties/AndroidManifest.xml
@@ -24,7 +24,7 @@
</application>
<instrumentation android:name="android.test.InstrumentationTestRunner"
- android:targetPackage="com.android.frameworks.coretests"
- android:label="Frameworks Core Tests" />
+ android:targetPackage="com.android.frameworks.coretests.systemproperties"
+ android:label="Frameworks SystemProperties Core Tests" />
</manifest>
diff --git a/core/tests/systemproperties/run_core_systemproperties_test.sh b/core/tests/systemproperties/run_core_systemproperties_test.sh
index 48880f3..d39adbb 100755
--- a/core/tests/systemproperties/run_core_systemproperties_test.sh
+++ b/core/tests/systemproperties/run_core_systemproperties_test.sh
@@ -16,6 +16,9 @@ fi
if [[ $rebuild == true ]]; then
make -j4 FrameworksCoreSystemPropertiesTests
TESTAPP=${ANDROID_PRODUCT_OUT}/data/app/FrameworksCoreSystemPropertiesTests.apk
+ COMMAND="adb install -r $TESTAPP"
+ echo $COMMAND
+ $COMMAND
fi
adb shell am instrument -w -e class android.os.SystemPropertiesTest com.android.frameworks.coretests.systemproperties/android.test.InstrumentationTestRunner