summaryrefslogtreecommitdiffstats
path: root/core/java
diff options
context:
space:
mode:
Diffstat (limited to 'core/java')
-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.java30
-rw-r--r--core/java/android/os/UEventObserver.java46
-rw-r--r--core/java/android/provider/Settings.java7
-rw-r--r--core/java/android/service/dreams/DreamService.java17
-rw-r--r--core/java/android/service/dreams/Sandman.java122
-rw-r--r--core/java/android/view/View.java8
-rw-r--r--core/java/android/view/ViewGroup.java2
-rw-r--r--core/java/android/view/textservice/TextServicesManager.java6
-rw-r--r--core/java/android/webkit/AccessibilityInjector.java29
-rw-r--r--core/java/android/webkit/WebView.java1
-rw-r--r--core/java/android/webkit/WebViewClassic.java41
-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.java37
-rw-r--r--core/java/com/android/internal/widget/ActionBarContextView.java2
-rw-r--r--core/java/com/android/internal/widget/ActionBarView.java13
21 files changed, 338 insertions, 111 deletions
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 93c9526..1e4ad76 100644
--- a/core/java/android/content/SyncManager.java
+++ b/core/java/android/content/SyncManager.java
@@ -219,20 +219,13 @@ public class SyncManager {
// Use this as a random offset to seed all periodic syncs
private int mSyncRandomOffsetMillis;
- private UserManager mUserManager;
+ private final UserManager mUserManager;
private static final long SYNC_ALARM_TIMEOUT_MIN = 30 * 1000; // 30 seconds
private static final long SYNC_ALARM_TIMEOUT_MAX = 2 * 60 * 60 * 1000; // two hours
- private UserManager getUserManager() {
- if (mUserManager == null) {
- mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
- }
- return mUserManager;
- }
-
private List<UserInfo> getAllUsers() {
- return getUserManager().getUsers();
+ return mUserManager.getUsers();
}
private boolean containsAccountAndUser(AccountAndUser[] accounts, Account account, int userId) {
@@ -250,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,
@@ -265,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) {
@@ -337,6 +341,7 @@ public class SyncManager {
// Initialize the SyncStorageEngine first, before registering observers
// and creating threads and so on; it may fail if the disk is full.
mContext = context;
+
SyncStorageEngine.init(context);
mSyncStorageEngine = SyncStorageEngine.getSingleton();
mSyncStorageEngine.setOnSyncRequestListener(new OnSyncRequestListener() {
@@ -402,6 +407,7 @@ public class SyncManager {
mNotificationMgr = null;
}
mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+ mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
// This WakeLock is used to ensure that we stay awake between the time that we receive
// a sync alarm notification and when we finish processing it. We need to do this
@@ -896,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 */);
@@ -1624,6 +1628,8 @@ public class SyncManager {
public void onBootCompleted() {
mBootCompleted = true;
+ doDatabaseCleanup();
+
if (mReadyToRunLatch != null) {
mReadyToRunLatch.countDown();
}
diff --git a/core/java/android/os/UEventObserver.java b/core/java/android/os/UEventObserver.java
index d33382b..9dbfd50 100644
--- a/core/java/android/os/UEventObserver.java
+++ b/core/java/android/os/UEventObserver.java
@@ -16,6 +16,8 @@
package android.os;
+import android.util.Log;
+
import java.util.ArrayList;
import java.util.HashMap;
@@ -37,14 +39,20 @@ import java.util.HashMap;
* @hide
*/
public abstract class UEventObserver {
+ private static final String TAG = "UEventObserver";
+ private static final boolean DEBUG = false;
+
private static UEventThread sThread;
- private static native void native_setup();
- private static native int next_event(byte[] buffer);
+ private static native void nativeSetup();
+ private static native String nativeWaitForNextEvent();
+ private static native void nativeAddMatch(String match);
+ private static native void nativeRemoveMatch(String match);
public UEventObserver() {
}
+ @Override
protected void finalize() throws Throwable {
try {
stopObserving();
@@ -78,10 +86,18 @@ public abstract class UEventObserver {
* This method can be called multiple times to register multiple matches.
* Only one call to stopObserving is required even with multiple registered
* matches.
- * @param match A substring of the UEvent to match. Use "" to match all
- * UEvent's
+ *
+ * @param match A substring of the UEvent to match. Try to be as specific
+ * as possible to avoid incurring unintended additional cost from processing
+ * irrelevant messages. Netlink messages can be moderately high bandwidth and
+ * are expensive to parse. For example, some devices may send one netlink message
+ * for each vsync period.
*/
public final void startObserving(String match) {
+ if (match == null || match.isEmpty()) {
+ throw new IllegalArgumentException("match substring must be non-empty");
+ }
+
final UEventThread t = getThread();
t.addObserver(match, this);
}
@@ -117,7 +133,7 @@ public abstract class UEventObserver {
while (offset < length) {
int equals = message.indexOf('=', offset);
- int at = message.indexOf(0, offset);
+ int at = message.indexOf('\0', offset);
if (at < 0) break;
if (equals > offset && equals < at) {
@@ -158,15 +174,17 @@ public abstract class UEventObserver {
super("UEventObserver");
}
+ @Override
public void run() {
- native_setup();
+ nativeSetup();
- byte[] buffer = new byte[1024];
- int len;
while (true) {
- len = next_event(buffer);
- if (len > 0) {
- sendEvent(new String(buffer, 0, len));
+ String message = nativeWaitForNextEvent();
+ if (message != null) {
+ if (DEBUG) {
+ Log.d(TAG, message);
+ }
+ sendEvent(message);
}
}
}
@@ -176,7 +194,7 @@ public abstract class UEventObserver {
final int N = mKeysAndObservers.size();
for (int i = 0; i < N; i += 2) {
final String key = (String)mKeysAndObservers.get(i);
- if (message.indexOf(key) != -1) {
+ if (message.contains(key)) {
final UEventObserver observer =
(UEventObserver)mKeysAndObservers.get(i + 1);
mTempObserversToSignal.add(observer);
@@ -199,6 +217,7 @@ public abstract class UEventObserver {
synchronized (mKeysAndObservers) {
mKeysAndObservers.add(match);
mKeysAndObservers.add(observer);
+ nativeAddMatch(match);
}
}
@@ -208,7 +227,8 @@ public abstract class UEventObserver {
for (int i = 0; i < mKeysAndObservers.size(); ) {
if (mKeysAndObservers.get(i + 1) == observer) {
mKeysAndObservers.remove(i + 1);
- mKeysAndObservers.remove(i);
+ final String match = (String)mKeysAndObservers.remove(i);
+ nativeRemoveMatch(match);
} else {
i += 2;
}
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/DreamService.java b/core/java/android/service/dreams/DreamService.java
index f865455..c72b714 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -37,6 +37,7 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
+import android.view.WindowManagerGlobal;
import android.view.WindowManager.LayoutParams;
import android.view.accessibility.AccessibilityEvent;
@@ -510,8 +511,12 @@ public class DreamService extends Service implements Window.Callback {
@Override
public void onDestroy() {
if (mDebug) Slog.v(TAG, "onDestroy()");
- super.onDestroy();
// hook for subclasses
+
+ // Just in case destroy came in before detach, let's take care of that now
+ detach();
+
+ super.onDestroy();
}
// end public api
@@ -521,13 +526,13 @@ public class DreamService extends Service implements Window.Callback {
}
/**
- * Called when the Dream is about to be unbound and destroyed.
+ * Called by DreamController.stopDream() when the Dream is about to be unbound and destroyed.
*
* Must run on mHandler.
*/
private final void detach() {
if (mWindow == null) {
- Slog.e(TAG, "detach() called when not attached");
+ // already detached!
return;
}
@@ -540,7 +545,11 @@ public class DreamService extends Service implements Window.Callback {
if (mDebug) Slog.v(TAG, "detach(): Removing window from window manager");
try {
- mWindowManager.removeView(mWindow.getDecorView());
+ // force our window to be removed synchronously
+ mWindowManager.removeViewImmediate(mWindow.getDecorView());
+ // the following will print a log message if it finds any other leaked windows
+ WindowManagerGlobal.getInstance().closeAll(mWindowToken,
+ this.getClass().getName(), "Dream");
} catch (Throwable t) {
Slog.w(TAG, "Crashed removing window view", t);
}
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..608bdd7 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -5214,11 +5214,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();
}
/**
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 6436059..0661992 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
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..357a16e 100644
--- a/core/java/android/webkit/AccessibilityInjector.java
+++ b/core/java/android/webkit/AccessibilityInjector.java
@@ -96,11 +96,26 @@ 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')"+
+ " && (typeof(cvox.ChromeVox) != 'undefined')" +
+ " && (typeof(cvox.AndroidVox) != 'undefined')" +
+ " && 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')"+
+ " && (typeof(cvox.ChromeVox) != 'undefined')" +
+ " && (typeof(cvox.ChromeVox.host) != 'undefined')) {" +
+ " cvox.ChromeVox.host.activateOrDeactivateChromeVox(%b);" +
+ " }" +
+ "})();";
/**
* Creates an instance of the AccessibilityInjector based on
@@ -776,20 +791,26 @@ class AccessibilityInjector {
while (true) {
try {
if (mResultId == resultId) {
+ if (DEBUG)
+ Log.w(TAG, "Received CVOX result");
return true;
}
if (mResultId > resultId) {
+ if (DEBUG)
+ Log.w(TAG, "Obsolete CVOX result");
return false;
}
final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
waitTimeMillis = RESULT_TIMEOUT - elapsedTimeMillis;
if (waitTimeMillis <= 0) {
+ if (DEBUG)
+ Log.w(TAG, "Timed out while waiting for CVOX result");
return false;
}
mResultLock.wait(waitTimeMillis);
} catch (InterruptedException ie) {
if (DEBUG)
- Log.w(TAG, "Timed out while waiting for CVOX result");
+ Log.w(TAG, "Interrupted while waiting for CVOX result");
/* ignore */
}
}
@@ -805,6 +826,8 @@ class AccessibilityInjector {
@JavascriptInterface
@SuppressWarnings("unused")
public void onResult(String id, String result) {
+ if (DEBUG)
+ Log.w(TAG, "Saw CVOX result of '" + result + "'");
final long resultId;
try {
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..7d0d0ba 100644
--- a/core/java/android/webkit/WebViewClassic.java
+++ b/core/java/android/webkit/WebViewClassic.java
@@ -5185,7 +5185,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 +5229,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 +5274,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() {
@@ -5616,7 +5629,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 +5960,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 +6292,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/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..958b669 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() {
diff --git a/core/java/com/android/internal/widget/ActionBarContextView.java b/core/java/com/android/internal/widget/ActionBarContextView.java
index 8bc1081..43c63b6 100644
--- a/core/java/com/android/internal/widget/ActionBarContextView.java
+++ b/core/java/com/android/internal/widget/ActionBarContextView.java
@@ -343,9 +343,11 @@ public class ActionBarContextView extends AbsActionBarView implements AnimatorLi
final int height = maxHeight - verticalPadding;
final int childSpecHeight = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST);
+ final int layoutDirection = getLayoutDirection();
if (mClose != null) {
availableWidth = measureChildView(mClose, availableWidth, childSpecHeight, 0);
MarginLayoutParams lp = (MarginLayoutParams) mClose.getLayoutParams();
+ lp.resolveLayoutDirection(layoutDirection);
availableWidth -= lp.leftMargin + lp.rightMargin;
}
diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java
index 5a7f10c..d8b3d2f 100644
--- a/core/java/com/android/internal/widget/ActionBarView.java
+++ b/core/java/com/android/internal/widget/ActionBarView.java
@@ -946,6 +946,9 @@ public class ActionBarView extends AbsActionBarView {
final ActionBar.LayoutParams ablp = lp instanceof ActionBar.LayoutParams ?
(ActionBar.LayoutParams) lp : null;
+ final int layoutDirection = getLayoutDirection();
+ lp.resolveLayoutDirection(layoutDirection);
+
int horizontalMargin = 0;
int verticalMargin = 0;
if (ablp != null) {
@@ -1096,9 +1099,9 @@ public class ActionBarView extends AbsActionBarView {
customView = mCustomNavView;
}
if (customView != null) {
- final int resolvedLayoutDirection = getLayoutDirection();
ViewGroup.LayoutParams lp = customView.getLayoutParams();
- lp.resolveLayoutDirection(resolvedLayoutDirection);
+ final int layoutDirection = getLayoutDirection();
+ lp.resolveLayoutDirection(layoutDirection);
final ActionBar.LayoutParams ablp = lp instanceof ActionBar.LayoutParams ?
(ActionBar.LayoutParams) lp : null;
final int gravity = ablp != null ? ablp.gravity : DEFAULT_CUSTOM_GRAVITY;
@@ -1139,7 +1142,7 @@ public class ActionBarView extends AbsActionBarView {
}
int xpos = 0;
- switch (Gravity.getAbsoluteGravity(hgravity, resolvedLayoutDirection)) {
+ switch (Gravity.getAbsoluteGravity(hgravity, layoutDirection)) {
case Gravity.CENTER_HORIZONTAL:
xpos = ((mRight - mLeft) - navWidth) / 2;
break;
@@ -1336,11 +1339,15 @@ public class ActionBarView extends AbsActionBarView {
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
measureChildWithMargins(mUpView, widthMeasureSpec, 0, heightMeasureSpec, 0);
+ // measureChildWithMargins() has triggered layout params resolution, so no need
+ // to do it now
final LayoutParams upLp = (LayoutParams) mUpView.getLayoutParams();
mUpWidth = upLp.leftMargin + mUpView.getMeasuredWidth() + upLp.rightMargin;
int width = mUpView.getVisibility() == GONE ? 0 : mUpWidth;
int height = upLp.topMargin + mUpView.getMeasuredHeight() + upLp.bottomMargin;
measureChildWithMargins(mIconView, widthMeasureSpec, width, heightMeasureSpec, 0);
+ // measureChildWithMargins() has triggered layout params resolution, so no need
+ // to do it now
final LayoutParams iconLp = (LayoutParams) mIconView.getLayoutParams();
width += iconLp.leftMargin + mIconView.getMeasuredWidth() + iconLp.rightMargin;
height = Math.max(height,