summaryrefslogtreecommitdiffstats
path: root/core/java/android
diff options
context:
space:
mode:
Diffstat (limited to 'core/java/android')
-rw-r--r--core/java/android/app/FragmentBreadCrumbs.java64
-rw-r--r--core/java/android/app/LauncherActivity.java11
-rw-r--r--core/java/android/appwidget/AppWidgetManager.java8
-rw-r--r--core/java/android/content/SyncManager.java17
-rw-r--r--core/java/android/os/FileUtils.java14
-rw-r--r--core/java/android/os/UserManager.java2
-rw-r--r--core/java/android/provider/Settings.java7
-rw-r--r--core/java/android/service/dreams/Sandman.java122
-rw-r--r--core/java/android/view/View.java50
-rw-r--r--core/java/android/view/ViewGroup.java94
-rw-r--r--core/java/android/view/textservice/TextServicesManager.java6
-rw-r--r--core/java/android/webkit/AccessibilityInjector.java136
-rw-r--r--core/java/android/webkit/WebView.java1
-rw-r--r--core/java/android/webkit/WebViewClassic.java55
-rw-r--r--core/java/android/widget/CheckedTextView.java15
-rw-r--r--core/java/android/widget/FrameLayout.java5
-rw-r--r--core/java/android/widget/RelativeLayout.java5
-rw-r--r--core/java/android/widget/ScrollView.java2
-rw-r--r--core/java/android/widget/SimpleExpandableListAdapter.java52
-rw-r--r--core/java/android/widget/TableRow.java5
-rw-r--r--core/java/android/widget/TextView.java40
21 files changed, 550 insertions, 161 deletions
diff --git a/core/java/android/app/FragmentBreadCrumbs.java b/core/java/android/app/FragmentBreadCrumbs.java
index df64035..b810b89 100644
--- a/core/java/android/app/FragmentBreadCrumbs.java
+++ b/core/java/android/app/FragmentBreadCrumbs.java
@@ -19,7 +19,9 @@ package android.app;
import android.animation.LayoutTransition;
import android.app.FragmentManager.BackStackEntry;
import android.content.Context;
+import android.content.res.TypedArray;
import android.util.AttributeSet;
+import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -51,7 +53,11 @@ public class FragmentBreadCrumbs extends ViewGroup
private OnClickListener mParentClickListener;
private OnBreadCrumbClickListener mOnBreadCrumbClickListener;
-
+
+ private int mGravity;
+
+ private static final int DEFAULT_GRAVITY = Gravity.START | Gravity.CENTER_VERTICAL;
+
/**
* Interface to intercept clicks on the bread crumbs.
*/
@@ -80,6 +86,14 @@ public class FragmentBreadCrumbs extends ViewGroup
public FragmentBreadCrumbs(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
+
+ TypedArray a = context.obtainStyledAttributes(attrs,
+ com.android.internal.R.styleable.FragmentBreadCrumbs, defStyle, 0);
+
+ mGravity = a.getInt(com.android.internal.R.styleable.FragmentBreadCrumbs_gravity,
+ DEFAULT_GRAVITY);
+
+ a.recycle();
}
/**
@@ -159,16 +173,50 @@ public class FragmentBreadCrumbs extends ViewGroup
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
- // Eventually we should implement our own layout of the views,
- // rather than relying on a linear layout.
+ // Eventually we should implement our own layout of the views, rather than relying on
+ // a single linear layout.
final int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- final View child = getChildAt(i);
+ if (childCount == 0) {
+ return;
+ }
+
+ final View child = getChildAt(0);
- int childRight = mPaddingLeft + child.getMeasuredWidth() - mPaddingRight;
- int childBottom = mPaddingTop + child.getMeasuredHeight() - mPaddingBottom;
- child.layout(mPaddingLeft, mPaddingTop, childRight, childBottom);
+ final int childTop = mPaddingTop;
+ final int childBottom = mPaddingTop + child.getMeasuredHeight() - mPaddingBottom;
+
+ int childLeft;
+ int childRight;
+
+ final int layoutDirection = getLayoutDirection();
+ final int horizontalGravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
+ switch (Gravity.getAbsoluteGravity(horizontalGravity, layoutDirection)) {
+ case Gravity.RIGHT:
+ childRight = mRight - mLeft - mPaddingRight;
+ childLeft = childRight - child.getMeasuredWidth();
+ break;
+
+ case Gravity.CENTER_HORIZONTAL:
+ childLeft = mPaddingLeft + (mRight - mLeft - child.getMeasuredWidth()) / 2;
+ childRight = childLeft + child.getMeasuredWidth();
+ break;
+
+ case Gravity.LEFT:
+ default:
+ childLeft = mPaddingLeft;
+ childRight = childLeft + child.getMeasuredWidth();
+ break;
}
+
+ if (childLeft < mPaddingLeft) {
+ childLeft = mPaddingLeft;
+ }
+
+ if (childRight > mRight - mLeft - mPaddingRight) {
+ childRight = mRight - mLeft - mPaddingRight;
+ }
+
+ child.layout(childLeft, childTop, childRight, childBottom);
}
@Override
diff --git a/core/java/android/app/LauncherActivity.java b/core/java/android/app/LauncherActivity.java
index 8eb9ba4..96c7246 100644
--- a/core/java/android/app/LauncherActivity.java
+++ b/core/java/android/app/LauncherActivity.java
@@ -439,14 +439,21 @@ public abstract class LauncherActivity extends ListActivity {
protected List<ResolveInfo> onQueryPackageManager(Intent queryIntent) {
return mPackageManager.queryIntentActivities(queryIntent, /* no flags */ 0);
}
-
+
+ /**
+ * @hide
+ */
+ protected void onSortResultList(List<ResolveInfo> results) {
+ Collections.sort(results, new ResolveInfo.DisplayNameComparator(mPackageManager));
+ }
+
/**
* Perform the query to determine which results to show and return a list of them.
*/
public List<ListItem> makeListItems() {
// Load all matching activities and sort correctly
List<ResolveInfo> list = onQueryPackageManager(mIntent);
- Collections.sort(list, new ResolveInfo.DisplayNameComparator(mPackageManager));
+ onSortResultList(list);
ArrayList<ListItem> result = new ArrayList<ListItem>(list.size());
int listSize = list.size();
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index 888955c..2af65b9 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -448,6 +448,10 @@ public class AppWidgetManager {
* and outside of the handler.
* This method will only work when called from the uid that owns the AppWidget provider.
*
+ * <p>
+ * This method will be ignored if a widget has not received a full update via
+ * {@link #updateAppWidget(int[], RemoteViews)}.
+ *
* @param appWidgetIds The AppWidget instances for which to set the RemoteViews.
* @param views The RemoteViews object containing the incremental update / command.
*/
@@ -476,6 +480,10 @@ public class AppWidgetManager {
* and outside of the handler.
* This method will only work when called from the uid that owns the AppWidget provider.
*
+ * <p>
+ * This method will be ignored if a widget has not received a full update via
+ * {@link #updateAppWidget(int[], RemoteViews)}.
+ *
* @param appWidgetId The AppWidget instance for which to set the RemoteViews.
* @param views The RemoteViews object containing the incremental update / command.
*/
diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java
index e0e2995..1e4ad76 100644
--- a/core/java/android/content/SyncManager.java
+++ b/core/java/android/content/SyncManager.java
@@ -243,6 +243,10 @@ public class SyncManager {
public void updateRunningAccounts() {
mRunningAccounts = AccountManagerService.getSingleton().getRunningAccounts();
+ if (mBootCompleted) {
+ doDatabaseCleanup();
+ }
+
for (ActiveSyncContext currentSyncContext : mActiveSyncContexts) {
if (!containsAccountAndUser(mRunningAccounts,
currentSyncContext.mSyncOperation.account,
@@ -258,6 +262,13 @@ public class SyncManager {
sendCheckAlarmsMessage();
}
+ private void doDatabaseCleanup() {
+ for (UserInfo user : mUserManager.getUsers()) {
+ Account[] accountsForUser = AccountManagerService.getSingleton().getAccounts(user.id);
+ mSyncStorageEngine.doDatabaseCleanup(accountsForUser, user.id);
+ }
+ }
+
private BroadcastReceiver mConnectivityIntentReceiver =
new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
@@ -891,12 +902,10 @@ public class SyncManager {
updateRunningAccounts();
- final Account[] accounts = AccountManagerService.getSingleton().getAccounts(userId);
- mSyncStorageEngine.doDatabaseCleanup(accounts, userId);
-
mSyncQueue.addPendingOperations(userId);
// Schedule sync for any accounts under started user
+ final Account[] accounts = AccountManagerService.getSingleton().getAccounts(userId);
for (Account account : accounts) {
scheduleSync(account, userId, null, null, 0 /* no delay */,
true /* onlyThoseWithUnknownSyncableState */);
@@ -1619,6 +1628,8 @@ public class SyncManager {
public void onBootCompleted() {
mBootCompleted = true;
+ doDatabaseCleanup();
+
if (mReadyToRunLatch != null) {
mReadyToRunLatch.countDown();
}
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index 0941d71..2bec1c1 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -16,6 +16,7 @@
package android.os;
+import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
@@ -91,7 +92,7 @@ public class FileUtils {
}
return result;
}
-
+
/**
* Copy data from a source stream to destFile.
* Return true if succeed, return false if failed.
@@ -143,12 +144,16 @@ public class FileUtils {
*/
public static String readTextFile(File file, int max, String ellipsis) throws IOException {
InputStream input = new FileInputStream(file);
+ // wrapping a BufferedInputStream around it because when reading /proc with unbuffered
+ // input stream, bytes read not equal to buffer size is not necessarily the correct
+ // indication for EOF; but it is true for BufferedInputStream due to its implementation.
+ BufferedInputStream bis = new BufferedInputStream(input);
try {
long size = file.length();
if (max > 0 || (size > 0 && max == 0)) { // "head" mode: read the first N bytes
if (size > 0 && (max == 0 || size < max)) max = (int) size;
byte[] data = new byte[max + 1];
- int length = input.read(data);
+ int length = bis.read(data);
if (length <= 0) return "";
if (length <= max) return new String(data, 0, length);
if (ellipsis == null) return new String(data, 0, max);
@@ -161,7 +166,7 @@ public class FileUtils {
if (last != null) rolled = true;
byte[] tmp = last; last = data; data = tmp;
if (data == null) data = new byte[-max];
- len = input.read(data);
+ len = bis.read(data);
} while (len == data.length);
if (last == null && len <= 0) return "";
@@ -178,12 +183,13 @@ public class FileUtils {
int len;
byte[] data = new byte[1024];
do {
- len = input.read(data);
+ len = bis.read(data);
if (len > 0) contents.write(data, 0, len);
} while (len == data.length);
return contents.toString();
}
} finally {
+ bis.close();
input.close();
}
}
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 2739cac..898c766 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -320,6 +320,8 @@ public class UserManager {
* @return a value greater than or equal to 1
*/
public static int getMaxSupportedUsers() {
+ // Don't allow multiple users on certain builds
+ if (android.os.Build.ID.startsWith("JVP")) return 1;
return SystemProperties.getInt("fw.max_users",
Resources.getSystem().getInteger(R.integer.config_multiuserMaximumUsers));
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 00ea873..8897039 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -386,10 +386,9 @@ public final class Settings {
/**
* Activity Action: Show settings to allow configuration of application
- * development-related settings.
- * <p>
- * In some cases, a matching Activity may not exist, so ensure you safeguard
- * against this.
+ * development-related settings. As of
+ * {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1} this action is
+ * a required part of the platform.
* <p>
* Input: Nothing.
* <p>
diff --git a/core/java/android/service/dreams/Sandman.java b/core/java/android/service/dreams/Sandman.java
new file mode 100644
index 0000000..70142ce
--- /dev/null
+++ b/core/java/android/service/dreams/Sandman.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2012 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.service.dreams;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.Slog;
+
+/**
+ * Internal helper for launching dreams to ensure consistency between the
+ * <code>UiModeManagerService</code> system service and the <code>Somnambulator</code> activity.
+ *
+ * @hide
+ */
+public final class Sandman {
+ private static final String TAG = "Sandman";
+
+ private static final int DEFAULT_SCREENSAVER_ENABLED = 1;
+ private static final int DEFAULT_SCREENSAVER_ACTIVATED_ON_DOCK = 1;
+
+ // The component name of a special dock app that merely launches a dream.
+ // We don't want to launch this app when docked because it causes an unnecessary
+ // activity transition. We just want to start the dream.
+ private static final ComponentName SOMNAMBULATOR_COMPONENT =
+ new ComponentName("com.android.systemui", "com.android.systemui.Somnambulator");
+
+
+ // The sandman is eternal. No one instantiates him.
+ private Sandman() {
+ }
+
+ /**
+ * Returns true if the specified dock app intent should be started.
+ * False if we should dream instead, if appropriate.
+ */
+ public static boolean shouldStartDockApp(Context context, Intent intent) {
+ ComponentName name = intent.resolveActivity(context.getPackageManager());
+ return name != null && !name.equals(SOMNAMBULATOR_COMPONENT);
+ }
+
+ /**
+ * Starts a dream manually.
+ */
+ public static void startDreamByUserRequest(Context context) {
+ startDream(context, false);
+ }
+
+ /**
+ * Starts a dream when docked if the system has been configured to do so,
+ * otherwise does nothing.
+ */
+ public static void startDreamWhenDockedIfAppropriate(Context context) {
+ if (!isScreenSaverEnabled(context)
+ || !isScreenSaverActivatedOnDock(context)) {
+ Slog.i(TAG, "Dreams currently disabled for docks.");
+ return;
+ }
+
+ startDream(context, true);
+ }
+
+ private static void startDream(Context context, boolean docked) {
+ try {
+ IDreamManager dreamManagerService = IDreamManager.Stub.asInterface(
+ ServiceManager.getService(DreamService.DREAM_SERVICE));
+ if (dreamManagerService != null && !dreamManagerService.isDreaming()) {
+ if (docked) {
+ Slog.i(TAG, "Activating dream while docked.");
+
+ // Wake up.
+ // The power manager will wake up the system automatically when it starts
+ // receiving power from a dock but there is a race between that happening
+ // and the UI mode manager starting a dream. We want the system to already
+ // be awake by the time this happens. Otherwise the dream may not start.
+ PowerManager powerManager =
+ (PowerManager)context.getSystemService(Context.POWER_SERVICE);
+ powerManager.wakeUp(SystemClock.uptimeMillis());
+ } else {
+ Slog.i(TAG, "Activating dream by user request.");
+ }
+
+ // Dream.
+ dreamManagerService.dream();
+ }
+ } catch (RemoteException ex) {
+ Slog.e(TAG, "Could not start dream when docked.", ex);
+ }
+ }
+
+ private static boolean isScreenSaverEnabled(Context context) {
+ return Settings.Secure.getIntForUser(context.getContentResolver(),
+ Settings.Secure.SCREENSAVER_ENABLED, DEFAULT_SCREENSAVER_ENABLED,
+ UserHandle.USER_CURRENT) != 0;
+ }
+
+ private static boolean isScreenSaverActivatedOnDock(Context context) {
+ return Settings.Secure.getIntForUser(context.getContentResolver(),
+ Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK,
+ DEFAULT_SCREENSAVER_ACTIVATED_ON_DOCK, UserHandle.USER_CURRENT) != 0;
+ }
+}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 22243b1..946965b 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -3666,15 +3666,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
// Padding from the background drawable is stored at this point in mUserPaddingLeftInitial
// and mUserPaddingRightInitial) so drawable padding will be used as ultimate default if
// defined.
- if (startPaddingDefined) {
- mUserPaddingLeftInitial = startPadding;
- } else if (leftPaddingDefined) {
+ if (leftPaddingDefined) {
mUserPaddingLeftInitial = leftPadding;
}
- if (endPaddingDefined) {
- mUserPaddingRightInitial = endPadding;
- }
- else if (rightPaddingDefined) {
+ if (rightPaddingDefined) {
mUserPaddingRightInitial = rightPadding;
}
}
@@ -5214,11 +5209,19 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*/
@RemotableViewMethod
public void setContentDescription(CharSequence contentDescription) {
+ if (mContentDescription == null) {
+ if (contentDescription == null) {
+ return;
+ }
+ } else if (mContentDescription.equals(contentDescription)) {
+ return;
+ }
mContentDescription = contentDescription;
final boolean nonEmptyDesc = contentDescription != null && contentDescription.length() > 0;
if (nonEmptyDesc && getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
}
+ notifyAccessibilityStateChanged();
}
/**
@@ -11551,8 +11554,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
/**
* Resolve all RTL related properties.
+ *
+ * @hide
*/
- void resolveRtlPropertiesIfNeeded() {
+ public void resolveRtlPropertiesIfNeeded() {
if (!needRtlPropertiesResolution()) return;
// Order is important here: LayoutDirection MUST be resolved first
@@ -11576,8 +11581,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
onRtlPropertiesChanged(getLayoutDirection());
}
- // Reset resolution of all RTL related properties.
- void resetRtlProperties() {
+ /**
+ * Reset resolution of all RTL related properties.
+ *
+ * @hide
+ */
+ public void resetRtlProperties() {
resetResolvedLayoutDirection();
resetResolvedTextDirection();
resetResolvedTextAlignment();
@@ -14187,7 +14196,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*
* @hide
*/
- public void resolveDrawables() {
+ protected void resolveDrawables() {
if (mBackground != null) {
mBackground.setLayoutDirection(getLayoutDirection());
}
@@ -14210,7 +14219,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
public void onResolveDrawables(int layoutDirection) {
}
- private void resetResolvedDrawables() {
+ /**
+ * @hide
+ */
+ protected void resetResolvedDrawables() {
mPrivateFlags2 &= ~PFLAG2_DRAWABLE_RESOLVED;
}
@@ -14796,14 +14808,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
if (isRtlCompatibilityMode()) {
mPaddingLeft = mUserPaddingLeftInitial;
mPaddingRight = mUserPaddingRightInitial;
+ return;
+ }
+ if (isLayoutRtl()) {
+ mPaddingLeft = (mUserPaddingEnd >= 0) ? mUserPaddingEnd : mUserPaddingLeftInitial;
+ mPaddingRight = (mUserPaddingStart >= 0) ? mUserPaddingStart : mUserPaddingRightInitial;
} else {
- if (isLayoutRtl()) {
- mPaddingLeft = mUserPaddingRightInitial;
- mPaddingRight = mUserPaddingLeftInitial;
- } else {
- mPaddingLeft = mUserPaddingLeftInitial;
- mPaddingRight = mUserPaddingRightInitial;
- }
+ mPaddingLeft = (mUserPaddingStart >= 0) ? mUserPaddingStart : mUserPaddingLeftInitial;
+ mPaddingRight = (mUserPaddingEnd >= 0) ? mUserPaddingEnd : mUserPaddingRightInitial;
}
}
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 6436059..9ce7df9 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -4817,6 +4817,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
int parentWidthMeasureSpec, int widthUsed,
int parentHeightMeasureSpec, int heightUsed) {
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
+ final int layoutDirection = getLayoutDirection();
+ lp.resolveLayoutDirection(layoutDirection);
final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
@@ -5261,6 +5263,21 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
* @hide
*/
@Override
+ public void resolveRtlPropertiesIfNeeded() {
+ super.resolveRtlPropertiesIfNeeded();
+ int count = getChildCount();
+ for (int i = 0; i < count; i++) {
+ final View child = getChildAt(i);
+ if (child.isLayoutDirectionInherited()) {
+ child.resolveRtlPropertiesIfNeeded();
+ }
+ }
+ }
+
+ /**
+ * @hide
+ */
+ @Override
public boolean resolveLayoutDirection() {
final boolean result = super.resolveLayoutDirection();
if (result) {
@@ -5315,6 +5332,51 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
* @hide
*/
@Override
+ public void resolvePadding() {
+ super.resolvePadding();
+ int count = getChildCount();
+ for (int i = 0; i < count; i++) {
+ final View child = getChildAt(i);
+ if (child.isLayoutDirectionInherited()) {
+ child.resolvePadding();
+ }
+ }
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ protected void resolveDrawables() {
+ super.resolveDrawables();
+ int count = getChildCount();
+ for (int i = 0; i < count; i++) {
+ final View child = getChildAt(i);
+ if (child.isLayoutDirectionInherited()) {
+ child.resolveDrawables();
+ }
+ }
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public void resetRtlProperties() {
+ super.resetRtlProperties();
+ int count = getChildCount();
+ for (int i = 0; i < count; i++) {
+ final View child = getChildAt(i);
+ if (child.isLayoutDirectionInherited()) {
+ child.resetRtlProperties();
+ }
+ }
+ }
+
+ /**
+ * @hide
+ */
+ @Override
public void resetResolvedLayoutDirection() {
super.resetResolvedLayoutDirection();
@@ -5360,6 +5422,38 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
}
/**
+ * @hide
+ */
+ @Override
+ public void resetResolvedPadding() {
+ super.resetResolvedPadding();
+
+ int count = getChildCount();
+ for (int i = 0; i < count; i++) {
+ final View child = getChildAt(i);
+ if (child.isLayoutDirectionInherited()) {
+ child.resetResolvedPadding();
+ }
+ }
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ protected void resetResolvedDrawables() {
+ super.resetResolvedDrawables();
+
+ int count = getChildCount();
+ for (int i = 0; i < count; i++) {
+ final View child = getChildAt(i);
+ if (child.isLayoutDirectionInherited()) {
+ child.resetResolvedDrawables();
+ }
+ }
+ }
+
+ /**
* 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
diff --git a/core/java/android/view/textservice/TextServicesManager.java b/core/java/android/view/textservice/TextServicesManager.java
index 81b36db..e0e19b9 100644
--- a/core/java/android/view/textservice/TextServicesManager.java
+++ b/core/java/android/view/textservice/TextServicesManager.java
@@ -217,6 +217,12 @@ public final class TextServicesManager {
public SpellCheckerSubtype getCurrentSpellCheckerSubtype(
boolean allowImplicitlySelectedSubtype) {
try {
+ if (sService == null) {
+ // TODO: This is a workaround. Needs to investigate why sService could be null
+ // here.
+ Log.e(TAG, "sService is null.");
+ return null;
+ }
// Passing null as a locale until we support multiple enabled spell checker subtypes.
return sService.getCurrentSpellCheckerSubtype(null, allowImplicitlySelectedSubtype);
} catch (RemoteException e) {
diff --git a/core/java/android/webkit/AccessibilityInjector.java b/core/java/android/webkit/AccessibilityInjector.java
index d6576e1..fe5cad4 100644
--- a/core/java/android/webkit/AccessibilityInjector.java
+++ b/core/java/android/webkit/AccessibilityInjector.java
@@ -96,11 +96,32 @@ class AccessibilityInjector {
// Template for JavaScript that performs AndroidVox actions.
private static final String ACCESSIBILITY_ANDROIDVOX_TEMPLATE =
- "cvox.AndroidVox.performAction('%1s')";
+ "(function() {" +
+ " if ((typeof(cvox) != 'undefined')" +
+ " && (cvox != null)" +
+ " && (typeof(cvox.ChromeVox) != 'undefined')" +
+ " && (cvox.ChromeVox != null)" +
+ " && (typeof(cvox.AndroidVox) != 'undefined')" +
+ " && (cvox.AndroidVox != null)" +
+ " && cvox.ChromeVox.isActive) {" +
+ " return cvox.AndroidVox.performAction('%1s');" +
+ " } else {" +
+ " return false;" +
+ " }" +
+ "})()";
// JS code used to shut down an active AndroidVox instance.
private static final String TOGGLE_CVOX_TEMPLATE =
- "javascript:(function() { cvox.ChromeVox.host.activateOrDeactivateChromeVox(%b); })();";
+ "javascript:(function() {" +
+ " if ((typeof(cvox) != 'undefined')" +
+ " && (cvox != null)" +
+ " && (typeof(cvox.ChromeVox) != 'undefined')" +
+ " && (cvox.ChromeVox != null)" +
+ " && (typeof(cvox.ChromeVox.host) != 'undefined')" +
+ " && (cvox.ChromeVox.host != null)) {" +
+ " cvox.ChromeVox.host.activateOrDeactivateChromeVox(%b);" +
+ " }" +
+ "})();";
/**
* Creates an instance of the AccessibilityInjector based on
@@ -117,33 +138,60 @@ class AccessibilityInjector {
}
/**
+ * If JavaScript is enabled, pauses or resumes AndroidVox.
+ *
+ * @param enabled Whether feedback should be enabled.
+ */
+ public void toggleAccessibilityFeedback(boolean enabled) {
+ if (!isAccessibilityEnabled() || !isJavaScriptEnabled()) {
+ return;
+ }
+
+ toggleAndroidVox(enabled);
+
+ if (!enabled && (mTextToSpeech != null)) {
+ mTextToSpeech.stop();
+ }
+ }
+
+ /**
* Attempts to load scripting interfaces for accessibility.
* <p>
- * This should be called when the window is attached.
- * </p>
+ * This should only be called before a page loads.
*/
- public void addAccessibilityApisIfNecessary() {
+ private void addAccessibilityApisIfNecessary() {
if (!isAccessibilityEnabled() || !isJavaScriptEnabled()) {
return;
}
addTtsApis();
addCallbackApis();
- toggleAndroidVox(true);
}
/**
* Attempts to unload scripting interfaces for accessibility.
* <p>
- * This should be called when the window is detached.
- * </p>
+ * This should only be called before a page loads.
*/
- public void removeAccessibilityApisIfNecessary() {
- toggleAndroidVox(false);
+ private void removeAccessibilityApisIfNecessary() {
removeTtsApis();
removeCallbackApis();
}
+ /**
+ * Destroys this accessibility injector.
+ */
+ public void destroy() {
+ if (mTextToSpeech != null) {
+ mTextToSpeech.shutdown();
+ mTextToSpeech = null;
+ }
+
+ if (mCallback != null) {
+ mCallback = null;
+ }
+ }
+
private void toggleAndroidVox(boolean state) {
if (!mAccessibilityScriptInjected) {
return;
@@ -502,7 +550,12 @@ class AccessibilityInjector {
* settings.
*/
private boolean isJavaScriptEnabled() {
- return mWebView.getSettings().getJavaScriptEnabled();
+ final WebSettings settings = mWebView.getSettings();
+ if (settings == null) {
+ return false;
+ }
+
+ return settings.getJavaScriptEnabled();
}
/**
@@ -717,7 +770,7 @@ class AccessibilityInjector {
private final String mInterfaceName;
private boolean mResult = false;
- private long mResultId = -1;
+ private int mResultId = -1;
private CallbackHandler(String interfaceName) {
mInterfaceName = interfaceName;
@@ -769,28 +822,46 @@ class AccessibilityInjector {
* @return Whether the result was received.
*/
private boolean waitForResultTimedLocked(int resultId) {
- if (DEBUG)
- Log.d(TAG, "Waiting for CVOX result...");
- long waitTimeMillis = RESULT_TIMEOUT;
final long startTimeMillis = SystemClock.uptimeMillis();
+
+ if (DEBUG)
+ Log.d(TAG, "Waiting for CVOX result with ID " + resultId + "...");
+
while (true) {
+ // Fail if we received a callback from the future.
+ if (mResultId > resultId) {
+ if (DEBUG)
+ Log.w(TAG, "Aborted CVOX result");
+ return false;
+ }
+
+ final long elapsedTimeMillis = (SystemClock.uptimeMillis() - startTimeMillis);
+
+ // Succeed if we received the callback we were expecting.
+ if (DEBUG)
+ Log.w(TAG, "Check " + mResultId + " versus expected " + resultId);
+ if (mResultId == resultId) {
+ if (DEBUG)
+ Log.w(TAG, "Received CVOX result after " + elapsedTimeMillis + " ms");
+ return true;
+ }
+
+ final long waitTimeMillis = (RESULT_TIMEOUT - elapsedTimeMillis);
+
+ // Fail if we've already exceeded the timeout.
+ if (waitTimeMillis <= 0) {
+ if (DEBUG)
+ Log.w(TAG, "Timed out while waiting for CVOX result");
+ return false;
+ }
+
try {
- if (mResultId == resultId) {
- return true;
- }
- if (mResultId > resultId) {
- return false;
- }
- final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
- waitTimeMillis = RESULT_TIMEOUT - elapsedTimeMillis;
- if (waitTimeMillis <= 0) {
- return false;
- }
+ if (DEBUG)
+ Log.w(TAG, "Start waiting...");
mResultLock.wait(waitTimeMillis);
} catch (InterruptedException ie) {
if (DEBUG)
- Log.w(TAG, "Timed out while waiting for CVOX result");
- /* ignore */
+ Log.w(TAG, "Interrupted while waiting for CVOX result");
}
}
}
@@ -805,10 +876,12 @@ class AccessibilityInjector {
@JavascriptInterface
@SuppressWarnings("unused")
public void onResult(String id, String result) {
- final long resultId;
+ if (DEBUG)
+ Log.w(TAG, "Saw CVOX result of '" + result + "' for ID " + id);
+ final int resultId;
try {
- resultId = Long.parseLong(id);
+ resultId = Integer.parseInt(id);
} catch (NumberFormatException e) {
return;
}
@@ -817,6 +890,9 @@ class AccessibilityInjector {
if (resultId > mResultId) {
mResult = Boolean.parseBoolean(result);
mResultId = resultId;
+ } else {
+ if (DEBUG)
+ Log.w(TAG, "Result with ID " + resultId + " was stale vesus " + mResultId);
}
mResultLock.notifyAll();
}
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 4202a7f..6df7820 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -1480,7 +1480,6 @@ public class WebView extends AbsoluteLayout
*
* @param listener an implementation of WebView.PictureListener
* @deprecated This method is now obsolete.
- * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}
*/
@Deprecated
public void setPictureListener(PictureListener listener) {
diff --git a/core/java/android/webkit/WebViewClassic.java b/core/java/android/webkit/WebViewClassic.java
index d68511c..0f8966e 100644
--- a/core/java/android/webkit/WebViewClassic.java
+++ b/core/java/android/webkit/WebViewClassic.java
@@ -2132,6 +2132,10 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
private void destroyJava() {
mCallbackProxy.blockMessages();
+ if (mAccessibilityInjector != null) {
+ mAccessibilityInjector.destroy();
+ mAccessibilityInjector = null;
+ }
if (mWebViewCore != null) {
// Tell WebViewCore to destroy itself
synchronized (this) {
@@ -3967,8 +3971,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
// null, and that will be the case
mWebView.setCertificate(null);
- // reset the flag since we set to true in if need after
- // loading is see onPageFinished(Url)
if (isAccessibilityInjectionEnabled()) {
getAccessibilityInjector().onPageStarted(url);
}
@@ -5185,7 +5187,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
if (cm.hasPrimaryClip()) {
Point cursorPoint = new Point(contentToViewX(mSelectCursorBase.x),
contentToViewY(mSelectCursorBase.y));
- Point cursorTop = calculateCaretTop();
+ Point cursorTop = calculateBaseCaretTop();
cursorTop.set(contentToViewX(cursorTop.x),
contentToViewY(cursorTop.y));
@@ -5229,17 +5231,22 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
return scale;
}
+ private Point calculateBaseCaretTop() {
+ return calculateCaretTop(mSelectCursorBase, mSelectCursorBaseTextQuad);
+ }
+
+ private Point calculateDraggingCaretTop() {
+ return calculateCaretTop(mSelectDraggingCursor, mSelectDraggingTextQuad);
+ }
+
/**
* Assuming arbitrary shape of a quadralateral forming text bounds, this
* calculates the top of a caret.
*/
- private Point calculateCaretTop() {
- float scale = scaleAlongSegment(mSelectCursorBase.x, mSelectCursorBase.y,
- mSelectCursorBaseTextQuad.p4, mSelectCursorBaseTextQuad.p3);
- int x = Math.round(scaleCoordinate(scale,
- mSelectCursorBaseTextQuad.p1.x, mSelectCursorBaseTextQuad.p2.x));
- int y = Math.round(scaleCoordinate(scale,
- mSelectCursorBaseTextQuad.p1.y, mSelectCursorBaseTextQuad.p2.y));
+ private static Point calculateCaretTop(Point base, QuadF quad) {
+ float scale = scaleAlongSegment(base.x, base.y, quad.p4, quad.p3);
+ int x = Math.round(scaleCoordinate(scale, quad.p1.x, quad.p2.x));
+ int y = Math.round(scaleCoordinate(scale, quad.p1.y, quad.p2.y));
return new Point(x, y);
}
@@ -5269,12 +5276,20 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
return true;
}
- private void updateWebkitSelection() {
+ private void updateWebkitSelection(boolean isSnapped) {
int handleId = (mSelectDraggingCursor == mSelectCursorBase)
? HANDLE_ID_BASE : HANDLE_ID_EXTENT;
+ int x = mSelectDraggingCursor.x;
+ int y = mSelectDraggingCursor.y;
+ if (isSnapped) {
+ // "center" the cursor in the snapping quad
+ Point top = calculateDraggingCaretTop();
+ x = Math.round((top.x + x) / 2);
+ y = Math.round((top.y + y) / 2);
+ }
mWebViewCore.removeMessages(EventHub.SELECT_TEXT);
mWebViewCore.sendMessageAtFrontOfQueue(EventHub.SELECT_TEXT,
- mSelectDraggingCursor.x, mSelectDraggingCursor.y, (Integer)handleId);
+ x, y, (Integer)handleId);
}
private void resetCaretTimer() {
@@ -5384,7 +5399,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
if (mWebView.hasWindowFocus()) setActive(true);
if (isAccessibilityInjectionEnabled()) {
- getAccessibilityInjector().addAccessibilityApisIfNecessary();
+ getAccessibilityInjector().toggleAccessibilityFeedback(true);
}
updateHwAccelerated();
@@ -5397,11 +5412,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
if (mWebView.hasWindowFocus()) setActive(false);
if (isAccessibilityInjectionEnabled()) {
- getAccessibilityInjector().removeAccessibilityApisIfNecessary();
- } else {
- // Ensure the injector is cleared if we're detaching from the window
- // and accessibility is disabled.
- mAccessibilityInjector = null;
+ getAccessibilityInjector().toggleAccessibilityFeedback(false);
}
updateHwAccelerated();
@@ -5616,7 +5627,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
Math.max(0, mEditTextContentBounds.top - buffer),
mEditTextContentBounds.right + buffer,
mEditTextContentBounds.bottom + buffer);
- Point caretTop = calculateCaretTop();
+ Point caretTop = calculateBaseCaretTop();
if (visibleRect.width() < mEditTextContentBounds.width()) {
// The whole edit won't fit in the width, so use the caret rect
if (mSelectCursorBase.x < caretTop.x) {
@@ -5947,10 +5958,12 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
} else {
endScrollEdit();
}
+ boolean snapped = false;
if (inCursorText || (mIsEditingText && !inEditBounds)) {
snapDraggingCursor();
+ snapped = true;
}
- updateWebkitSelection();
+ updateWebkitSelection(snapped);
if (!inCursorText && mIsEditingText && inEditBounds) {
// Visually snap even if we have moved the handle.
snapDraggingCursor();
@@ -6277,7 +6290,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
int oldX = mSelectDraggingCursor.x;
int oldY = mSelectDraggingCursor.y;
mSelectDraggingCursor.set(selectionX, selectionY);
- updateWebkitSelection();
+ updateWebkitSelection(false);
scrollEditText(scrollX, scrollY);
mSelectDraggingCursor.set(oldX, oldY);
}
diff --git a/core/java/android/widget/CheckedTextView.java b/core/java/android/widget/CheckedTextView.java
index e74e37c..de8b80d 100644
--- a/core/java/android/widget/CheckedTextView.java
+++ b/core/java/android/widget/CheckedTextView.java
@@ -188,10 +188,11 @@ public class CheckedTextView extends TextView implements Checkable {
resetPaddingToInitialValues();
int newPadding = (mCheckMarkDrawable != null) ?
mCheckMarkWidth + mBasePadding : mBasePadding;
- mNeedRequestlayout |= (mPaddingRight != newPadding);
if (isLayoutRtl()) {
+ mNeedRequestlayout |= (mPaddingLeft != newPadding);
mPaddingLeft = newPadding;
} else {
+ mNeedRequestlayout |= (mPaddingRight != newPadding);
mPaddingRight = newPadding;
}
if (mNeedRequestlayout) {
@@ -200,18 +201,6 @@ public class CheckedTextView extends TextView implements Checkable {
}
}
- @Override
- public void setPadding(int left, int top, int right, int bottom) {
- super.setPadding(left, top, right, bottom);
- setBasePadding(isLayoutRtl());
- }
-
- @Override
- public void setPaddingRelative(int start, int top, int end, int bottom) {
- super.setPaddingRelative(start, top, end, bottom);
- setBasePadding(isLayoutRtl());
- }
-
private void setBasePadding(boolean isLayoutRtl) {
if (isLayoutRtl) {
mBasePadding = mPaddingLeft;
diff --git a/core/java/android/widget/FrameLayout.java b/core/java/android/widget/FrameLayout.java
index 00cd604..45f30df 100644
--- a/core/java/android/widget/FrameLayout.java
+++ b/core/java/android/widget/FrameLayout.java
@@ -304,11 +304,16 @@ public class FrameLayout extends ViewGroup {
int maxWidth = 0;
int childState = 0;
+ final int layoutDirection = getLayoutDirection();
+
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (mMeasureAllChildren || child.getVisibility() != GONE) {
measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
+ // measureChildWithMargins() has triggered layout params resolution, so no need
+ // to do it now
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+
maxWidth = Math.max(maxWidth,
child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
maxHeight = Math.max(maxHeight,
diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java
index 4ca405b..ace26f3 100644
--- a/core/java/android/widget/RelativeLayout.java
+++ b/core/java/android/widget/RelativeLayout.java
@@ -414,12 +414,15 @@ public class RelativeLayout extends ViewGroup {
final boolean isWrapContentWidth = widthMode != MeasureSpec.EXACTLY;
final boolean isWrapContentHeight = heightMode != MeasureSpec.EXACTLY;
+ final int layoutDirection = getLayoutDirection();
+
View[] views = mSortedHorizontalChildren;
int count = views.length;
for (int i = 0; i < count; i++) {
View child = views[i];
if (child.getVisibility() != GONE) {
LayoutParams params = (LayoutParams) child.getLayoutParams();
+ params.resolveLayoutDirection(layoutDirection);
applyHorizontalSizeRules(params, myWidth);
measureChildHorizontal(child, params, myWidth, myHeight);
@@ -483,8 +486,6 @@ public class RelativeLayout extends ViewGroup {
}
}
- final int layoutDirection = getLayoutDirection();
-
if (isWrapContentWidth) {
// Width already has left padding in it since it was calculated by looking at
// the right of each child view
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index bc41931..1def89f 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -329,11 +329,13 @@ public class ScrollView extends FrameLayout {
return;
}
+ final int layoutDirection = getLayoutDirection();
if (getChildCount() > 0) {
final View child = getChildAt(0);
int height = getMeasuredHeight();
if (child.getMeasuredHeight() < height) {
final FrameLayout.LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ lp.resolveLayoutDirection(layoutDirection);
int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
mPaddingLeft + mPaddingRight, lp.width);
diff --git a/core/java/android/widget/SimpleExpandableListAdapter.java b/core/java/android/widget/SimpleExpandableListAdapter.java
index f514374..015c169 100644
--- a/core/java/android/widget/SimpleExpandableListAdapter.java
+++ b/core/java/android/widget/SimpleExpandableListAdapter.java
@@ -38,8 +38,6 @@ import java.util.Map;
*/
public class SimpleExpandableListAdapter extends BaseExpandableListAdapter {
private List<? extends Map<String, ?>> mGroupData;
- // Keeps track of if a group is currently expanded or not
- private boolean[] mIsGroupExpanded;
private int mExpandedGroupLayout;
private int mCollapsedGroupLayout;
private String[] mGroupFrom;
@@ -198,8 +196,6 @@ public class SimpleExpandableListAdapter extends BaseExpandableListAdapter {
int childLayout, int lastChildLayout, String[] childFrom,
int[] childTo) {
mGroupData = groupData;
- // Initially all groups are not expanded
- mIsGroupExpanded = new boolean[groupData.size()];
mExpandedGroupLayout = expandedGroupLayout;
mCollapsedGroupLayout = collapsedGroupLayout;
mGroupFrom = groupFrom;
@@ -302,52 +298,4 @@ public class SimpleExpandableListAdapter extends BaseExpandableListAdapter {
return true;
}
- /**
- * {@inheritDoc}
- * @return 1 for the last child in a group, 0 for the other children.
- */
- @Override
- public int getChildType(int groupPosition, int childPosition) {
- final int childrenInGroup = getChildrenCount(groupPosition);
- return childPosition == childrenInGroup - 1 ? 1 : 0;
- }
-
- /**
- * {@inheritDoc}
- * @return 2, one type for the last child in a group, one for the other children.
- */
- @Override
- public int getChildTypeCount() {
- return 2;
- }
-
- /**
- * {@inheritDoc}
- * @return 1 for an expanded group view, 0 for a collapsed one.
- */
- @Override
- public int getGroupType(int groupPosition) {
- return mIsGroupExpanded[groupPosition] ? 1 : 0;
- }
-
- /**
- * {@inheritDoc}
- * @return 2, one for a collapsed group view, one for an expanded one.
- */
- @Override
- public int getGroupTypeCount() {
- return 2;
- }
-
- /** {@inheritDoc} */
- @Override
- public void onGroupCollapsed(int groupPosition) {
- mIsGroupExpanded[groupPosition] = false;
- }
-
- /** {@inheritDoc} */
- @Override
- public void onGroupExpanded(int groupPosition) {
- mIsGroupExpanded[groupPosition] = true;
- }
}
diff --git a/core/java/android/widget/TableRow.java b/core/java/android/widget/TableRow.java
index db3853f..3f8f9dae 100644
--- a/core/java/android/widget/TableRow.java
+++ b/core/java/android/widget/TableRow.java
@@ -192,7 +192,9 @@ public class TableRow extends LinearLayout {
int widthMeasureSpec, int totalWidth,
int heightMeasureSpec, int totalHeight) {
if (mConstrainedColumnWidths != null) {
+ final int layoutDirection = getLayoutDirection();
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ lp.resolveLayoutDirection(layoutDirection);
int measureMode = MeasureSpec.EXACTLY;
int columnWidth = 0;
@@ -226,7 +228,6 @@ public class TableRow extends LinearLayout {
final int childWidth = child.getMeasuredWidth();
lp.mOffset[LayoutParams.LOCATION_NEXT] = columnWidth - childWidth;
- final int layoutDirection = getLayoutDirection();
final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
case Gravity.LEFT:
@@ -292,11 +293,13 @@ public class TableRow extends LinearLayout {
}
final int[] columnWidths = mColumnWidths;
+ final int layoutDirection = getLayoutDirection();
for (int i = 0; i < numColumns; i++) {
final View child = getVirtualChildAt(i);
if (child != null && child.getVisibility() != GONE) {
final LayoutParams layoutParams = (LayoutParams) child.getLayoutParams();
+ layoutParams.resolveLayoutDirection(layoutDirection);
if (layoutParams.span == 1) {
int spec;
switch (layoutParams.width) {
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 410a0ca..b3c679c 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -33,6 +33,7 @@ import android.graphics.RectF;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.inputmethodservice.ExtractEditText;
+import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
@@ -132,6 +133,7 @@ import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Locale;
+import java.util.concurrent.locks.ReentrantLock;
/**
* Displays text to the user and optionally allows them to edit it. A TextView
@@ -378,6 +380,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
private InputFilter[] mFilters = NO_FILTERS;
+ private volatile Locale mCurrentTextServicesLocaleCache;
+ private final ReentrantLock mCurrentTextServicesLocaleLock = new ReentrantLock();
+
// It is possible to have a selection even when mEditor is null (programmatically set, like when
// a link is pressed). These highlight-related fields do not go in mEditor.
int mHighlightColor = 0x6633B5E5;
@@ -7675,13 +7680,43 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
/**
* This is a temporary method. Future versions may support multi-locale text.
+ * Caveat: This method may not return the latest text services locale, but this should be
+ * acceptable and it's more important to make this method asynchronous.
*
* @return The locale that should be used for a word iterator and a spell checker
* in this TextView, based on the current spell checker settings,
* the current IME's locale, or the system default locale.
* @hide
*/
+ // TODO: Support multi-locale
+ // TODO: Update the text services locale immediately after the keyboard locale is switched
+ // by catching intent of keyboard switch event
public Locale getTextServicesLocale() {
+ if (mCurrentTextServicesLocaleCache == null) {
+ // If there is no cached text services locale, just return the default locale.
+ mCurrentTextServicesLocaleCache = Locale.getDefault();
+ }
+ // Start fetching the text services locale asynchronously.
+ updateTextServicesLocaleAsync();
+ return mCurrentTextServicesLocaleCache;
+ }
+
+ private void updateTextServicesLocaleAsync() {
+ AsyncTask.execute(new Runnable() {
+ @Override
+ public void run() {
+ if (mCurrentTextServicesLocaleLock.tryLock()) {
+ try {
+ updateTextServicesLocaleLocked();
+ } finally {
+ mCurrentTextServicesLocaleLock.unlock();
+ }
+ }
+ }
+ });
+ }
+
+ private void updateTextServicesLocaleLocked() {
Locale locale = Locale.getDefault();
final TextServicesManager textServicesManager = (TextServicesManager)
mContext.getSystemService(Context.TEXT_SERVICES_MANAGER_SERVICE);
@@ -7689,7 +7724,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
if (subtype != null) {
locale = SpellCheckerSubtype.constructLocaleFromString(subtype.getLocale());
}
- return locale;
+ mCurrentTextServicesLocaleCache = locale;
}
void onLocaleChanged() {
@@ -8279,6 +8314,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
}
+ /**
+ * @hide
+ */
protected void resetResolvedDrawables() {
mResolvedDrawables = false;
}