summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--api/current.txt2
-rwxr-xr-xcore/java/android/animation/ValueAnimator.java2
-rw-r--r--core/java/android/app/Activity.java5
-rw-r--r--core/java/android/app/ActivityOptions.java12
-rw-r--r--core/java/android/app/ActivityThread.java8
-rw-r--r--core/java/android/os/Trace.java1
-rw-r--r--core/java/android/server/BluetoothAdapterStateMachine.java58
-rwxr-xr-xcore/java/android/server/BluetoothService.java12
-rw-r--r--core/java/android/view/AccessibilityIterators.java352
-rw-r--r--core/java/android/view/Choreographer.java33
-rw-r--r--core/java/android/view/View.java175
-rw-r--r--core/java/android/view/ViewTreeObserver.java206
-rw-r--r--core/java/android/view/accessibility/AccessibilityEvent.java36
-rw-r--r--core/java/android/view/accessibility/AccessibilityNodeInfo.java4
-rw-r--r--core/java/android/widget/AbsListView.java114
-rw-r--r--core/java/android/widget/AccessibilityIterators.java219
-rw-r--r--core/java/android/widget/ActivityChooserModel.java546
-rw-r--r--core/java/android/widget/TextView.java93
-rw-r--r--core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java103
-rw-r--r--core/jni/Android.mk1
-rw-r--r--core/jni/android/graphics/TextLayoutCache.cpp44
-rw-r--r--core/jni/android/graphics/TextLayoutCache.h5
-rw-r--r--core/jni/android_os_Power.cpp11
-rw-r--r--core/res/res/layout-sw600dp/keyguard_screen_sim_pin_portrait.xml3
-rw-r--r--core/res/res/layout-sw600dp/keyguard_screen_tab_unlock.xml8
-rw-r--r--core/res/res/layout-sw600dp/keyguard_screen_tab_unlock_land.xml7
-rw-r--r--core/res/res/layout/keyguard_screen_sim_pin_portrait.xml3
-rw-r--r--core/res/res/layout/keyguard_screen_sim_puk_landscape.xml4
-rw-r--r--core/res/res/layout/keyguard_screen_tab_unlock.xml3
-rw-r--r--core/res/res/layout/keyguard_screen_tab_unlock_land.xml5
-rw-r--r--core/res/res/values-sw600dp-land/arrays.xml4
-rwxr-xr-xcore/res/res/values/attrs.xml11
-rw-r--r--data/fonts/Android.mk19
-rw-r--r--data/fonts/DroidSansDevanagari-Regular.ttfbin0 -> 123804 bytes
-rw-r--r--data/fonts/DroidSansTamil-Bold.ttfbin0 -> 36448 bytes
-rw-r--r--data/fonts/DroidSansTamil-Regular.ttfbin0 -> 36308 bytes
-rw-r--r--data/fonts/fallback_fonts-ja.xml5
-rw-r--r--data/fonts/fallback_fonts.xml5
-rw-r--r--data/fonts/fonts.mk1
-rw-r--r--packages/SystemUI/res/drawable-hdpi/navbar_search_bg_scrim.9.pngbin0 -> 21892 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/navbar_search_bg_scrim.9.pngbin0 -> 11004 bytes
-rw-r--r--packages/SystemUI/res/drawable-xhdpi/navbar_search_bg_scrim.9.pngbin0 -> 34907 bytes
-rw-r--r--packages/SystemUI/res/drawable/navbar_search_bg_scrim.pngbin7955 -> 0 bytes
-rw-r--r--packages/SystemUI/res/layout-land/status_bar_search_panel.xml7
-rw-r--r--packages/SystemUI/res/layout-port/status_bar_search_panel.xml7
-rw-r--r--packages/SystemUI/res/layout-sw600dp/status_bar_search_panel.xml23
-rw-r--r--packages/SystemUI/res/layout-sw720dp/status_bar_search_panel.xml74
-rw-r--r--packages/SystemUI/res/values-sw600dp/dimens.xml14
-rw-r--r--packages/SystemUI/res/values/dimens.xml12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/DelegateViewHelper.java39
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java4
-rw-r--r--policy/src/com/android/internal/policy/impl/KeyguardViewManager.java1
-rw-r--r--policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java7
-rw-r--r--services/java/com/android/server/am/ActivityManagerService.java8
-rw-r--r--services/java/com/android/server/am/ActivityRecord.java15
-rwxr-xr-xservices/java/com/android/server/am/ActivityStack.java49
-rw-r--r--services/java/com/android/server/am/TaskRecord.java4
-rw-r--r--telephony/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java10
59 files changed, 1873 insertions, 523 deletions
diff --git a/api/current.txt b/api/current.txt
index 848d563..13c972f 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -25088,6 +25088,7 @@ package android.view.accessibility {
method public void appendRecord(android.view.accessibility.AccessibilityRecord);
method public int describeContents();
method public static java.lang.String eventTypeToString(int);
+ method public int getAction();
method public long getEventTime();
method public int getEventType();
method public int getMovementGranularity();
@@ -25098,6 +25099,7 @@ package android.view.accessibility {
method public static android.view.accessibility.AccessibilityEvent obtain(int);
method public static android.view.accessibility.AccessibilityEvent obtain(android.view.accessibility.AccessibilityEvent);
method public static android.view.accessibility.AccessibilityEvent obtain();
+ method public void setAction(int);
method public void setEventTime(long);
method public void setEventType(int);
method public void setMovementGranularity(int);
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index 326f27c..f3a442a 100755
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -910,7 +910,7 @@ public class ValueAnimator extends Animator {
animationHandler.mPendingAnimations.add(this);
if (mStartDelay == 0) {
// This sets the initial value of the animation, prior to actually starting it running
- setCurrentPlayTime(getCurrentPlayTime());
+ setCurrentPlayTime(0);
mPlayingState = STOPPED;
mRunning = true;
notifyStartListeners();
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 29d96fe..781eea5 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -2540,11 +2540,10 @@ public class Activity extends ContextThemeWrapper
if (item.getItemId() == android.R.id.home && mActionBar != null &&
(mActionBar.getDisplayOptions() & ActionBar.DISPLAY_HOME_AS_UP) != 0) {
if (mParent == null) {
- onNavigateUp();
+ return onNavigateUp();
} else {
- mParent.onNavigateUpFromChild(this);
+ return mParent.onNavigateUpFromChild(this);
}
- return true;
}
return false;
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index b730581..3d0b7d8 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -147,12 +147,17 @@ public class ActivityOptions {
* activity is scaled from a small originating area of the screen to
* its final full representation.
*
+ * <p>If the Intent this is being used with has not set its
+ * {@link android.content.Intent#setSourceBounds Intent.setSourceBounds},
+ * those bounds will be filled in for you based on the initial
+ * bounds passed in here.
+ *
* @param source The View that the new activity is animating from. This
* defines the coordinate space for <var>startX</var> and <var>startY</var>.
* @param startX The x starting location of the new activity, relative to <var>source</var>.
* @param startY The y starting location of the activity, relative to <var>source</var>.
* @param startWidth The initial width of the new activity.
- * @param startWidth The initial height of the new activity.
+ * @param startHeight The initial height of the new activity.
* @return Returns a new ActivityOptions object that you can use to
* supply these options as the options Bundle when starting an activity.
*/
@@ -175,6 +180,11 @@ public class ActivityOptions {
* is scaled from a given position to the new activity window that is
* being started.
*
+ * <p>If the Intent this is being used with has not set its
+ * {@link android.content.Intent#setSourceBounds Intent.setSourceBounds},
+ * those bounds will be filled in for you based on the initial
+ * thumbnail location and size provided here.
+ *
* @param source The View that this thumbnail is animating from. This
* defines the coordinate space for <var>startX</var> and <var>startY</var>.
* @param thumbnail The bitmap that will be shown as the initial thumbnail
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index b55ee26..314f5c2 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -136,7 +136,7 @@ public final class ActivityThread {
/** @hide */
public static final boolean DEBUG_BROADCAST = false;
private static final boolean DEBUG_RESULTS = false;
- private static final boolean DEBUG_BACKUP = true;
+ private static final boolean DEBUG_BACKUP = false;
private static final boolean DEBUG_CONFIGURATION = false;
private static final boolean DEBUG_SERVICE = false;
private static final boolean DEBUG_MEMORY_TRIM = false;
@@ -1172,10 +1172,10 @@ public final class ActivityThread {
case DUMP_PROVIDER: return "DUMP_PROVIDER";
}
}
- return "(unknown)";
+ return Integer.toString(code);
}
public void handleMessage(Message msg) {
- if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + msg.what);
+ if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
switch (msg.what) {
case LAUNCH_ACTIVITY: {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
@@ -1378,7 +1378,7 @@ public final class ActivityThread {
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
}
- if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + msg.what);
+ if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what));
}
private void maybeSnapshot() {
diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java
index 05acd63..ac9ee26 100644
--- a/core/java/android/os/Trace.java
+++ b/core/java/android/os/Trace.java
@@ -37,6 +37,7 @@ public final class Trace {
public static final long TRACE_TAG_WINDOW_MANAGER = 1L << 5;
public static final long TRACE_TAG_ACTIVITY_MANAGER = 1L << 6;
public static final long TRACE_TAG_SYNC_MANAGER = 1L << 7;
+ public static final long TRACE_TAG_AUDIO = 1L << 8;
private static final long sEnabledTags = nativeGetEnabledTags();
diff --git a/core/java/android/server/BluetoothAdapterStateMachine.java b/core/java/android/server/BluetoothAdapterStateMachine.java
index f543de9..2a994b2 100644
--- a/core/java/android/server/BluetoothAdapterStateMachine.java
+++ b/core/java/android/server/BluetoothAdapterStateMachine.java
@@ -39,7 +39,7 @@ import java.io.PrintWriter;
* (BluetootOn)<----------------------<-
* | ^ -------------------->- |
* | | | |
- * TURN_OFF | | SCAN_MODE_CHANGED m1 | | USER_TURN_ON
+ * USER_TURN_OFF | | SCAN_MODE_CHANGED m1 | | USER_TURN_ON
* AIRPLANE_MODE_ON | | | |
* V | | |
* (Switching) (PerProcessState)
@@ -121,8 +121,10 @@ final class BluetoothAdapterStateMachine extends StateMachine {
private static final int DEVICES_DISCONNECT_TIMEOUT = 103;
// Prepare Bluetooth timeout happens
private static final int PREPARE_BLUETOOTH_TIMEOUT = 104;
- // Bluetooth Powerdown timeout happens
- private static final int POWER_DOWN_TIMEOUT = 105;
+ // Bluetooth turn off wait timeout happens
+ private static final int TURN_OFF_TIMEOUT = 105;
+ // Bluetooth device power off wait timeout happens
+ private static final int POWER_DOWN_TIMEOUT = 106;
private Context mContext;
private BluetoothService mBluetoothService;
@@ -137,13 +139,17 @@ final class BluetoothAdapterStateMachine extends StateMachine {
// this is the BluetoothAdapter state that reported externally
private int mPublicState;
+ // When turning off, broadcast STATE_OFF in the last HotOff state
+ // This is because we do HotOff -> PowerOff -> HotOff for USER_TURN_OFF
+ private boolean mDelayBroadcastStateOff;
// timeout value waiting for all the devices to be disconnected
private static final int DEVICES_DISCONNECT_TIMEOUT_TIME = 3000;
private static final int PREPARE_BLUETOOTH_TIMEOUT_TIME = 10000;
- private static final int POWER_DOWN_TIMEOUT_TIME = 5000;
+ private static final int TURN_OFF_TIMEOUT_TIME = 5000;
+ private static final int POWER_DOWN_TIMEOUT_TIME = 20;
BluetoothAdapterStateMachine(Context context, BluetoothService bluetoothService,
BluetoothAdapter bluetoothAdapter) {
@@ -168,6 +174,7 @@ final class BluetoothAdapterStateMachine extends StateMachine {
setInitialState(mPowerOff);
mPublicState = BluetoothAdapter.STATE_OFF;
+ mDelayBroadcastStateOff = false;
}
/**
@@ -315,6 +322,10 @@ final class BluetoothAdapterStateMachine extends StateMachine {
case SERVICE_RECORD_LOADED:
removeMessages(PREPARE_BLUETOOTH_TIMEOUT);
transitionTo(mHotOff);
+ if (mDelayBroadcastStateOff) {
+ broadcastState(BluetoothAdapter.STATE_OFF);
+ mDelayBroadcastStateOff = false;
+ }
break;
case PREPARE_BLUETOOTH_TIMEOUT:
Log.e(TAG, "Bluetooth adapter SDP failed to load");
@@ -373,8 +384,17 @@ final class BluetoothAdapterStateMachine extends StateMachine {
case AIRPLANE_MODE_ON:
case TURN_COLD:
shutoffBluetooth();
+ // we cannot go to power off state yet, we need wait for the Bluetooth
+ // device power off. Unfortunately the stack does not give a event back
+ // so we wait a little bit here
+ sendMessageDelayed(POWER_DOWN_TIMEOUT,
+ POWER_DOWN_TIMEOUT_TIME);
+ break;
+ case POWER_DOWN_TIMEOUT:
transitionTo(mPowerOff);
- broadcastState(BluetoothAdapter.STATE_OFF);
+ if (!mDelayBroadcastStateOff) {
+ broadcastState(BluetoothAdapter.STATE_OFF);
+ }
break;
case AIRPLANE_MODE_OFF:
if (getBluetoothPersistedSetting()) {
@@ -402,6 +422,9 @@ final class BluetoothAdapterStateMachine extends StateMachine {
recoverStateMachine(TURN_HOT, null);
}
break;
+ case TURN_HOT:
+ deferMessage(message);
+ break;
default:
return NOT_HANDLED;
}
@@ -436,15 +459,17 @@ final class BluetoothAdapterStateMachine extends StateMachine {
}
break;
case POWER_STATE_CHANGED:
- removeMessages(POWER_DOWN_TIMEOUT);
+ removeMessages(TURN_OFF_TIMEOUT);
if (!((Boolean) message.obj)) {
if (mPublicState == BluetoothAdapter.STATE_TURNING_OFF) {
transitionTo(mHotOff);
- finishSwitchingOff();
+ mBluetoothService.finishDisable();
+ mBluetoothService.cleanupAfterFinishDisable();
deferMessage(obtainMessage(TURN_COLD));
if (mContext.getResources().getBoolean
(com.android.internal.R.bool.config_bluetooth_adapter_quick_switch)) {
deferMessage(obtainMessage(TURN_HOT));
+ mDelayBroadcastStateOff = true;
}
}
} else {
@@ -461,7 +486,7 @@ final class BluetoothAdapterStateMachine extends StateMachine {
case ALL_DEVICES_DISCONNECTED:
removeMessages(DEVICES_DISCONNECT_TIMEOUT);
mBluetoothService.switchConnectable(false);
- sendMessageDelayed(POWER_DOWN_TIMEOUT, POWER_DOWN_TIMEOUT_TIME);
+ sendMessageDelayed(TURN_OFF_TIMEOUT, TURN_OFF_TIMEOUT_TIME);
break;
case DEVICES_DISCONNECT_TIMEOUT:
sendMessage(ALL_DEVICES_DISCONNECTED);
@@ -473,7 +498,7 @@ final class BluetoothAdapterStateMachine extends StateMachine {
deferMessage(obtainMessage(TURN_HOT));
}
break;
- case POWER_DOWN_TIMEOUT:
+ case TURN_OFF_TIMEOUT:
transitionTo(mHotOff);
finishSwitchingOff();
// reset the hardware for error recovery
@@ -536,7 +561,7 @@ final class BluetoothAdapterStateMachine extends StateMachine {
DEVICES_DISCONNECT_TIMEOUT_TIME);
} else {
mBluetoothService.switchConnectable(false);
- sendMessageDelayed(POWER_DOWN_TIMEOUT, POWER_DOWN_TIMEOUT_TIME);
+ sendMessageDelayed(TURN_OFF_TIMEOUT, TURN_OFF_TIMEOUT_TIME);
}
// we turn all the way to PowerOff with AIRPLANE_MODE_ON
@@ -610,13 +635,12 @@ final class BluetoothAdapterStateMachine extends StateMachine {
}
break;
case POWER_STATE_CHANGED:
- removeMessages(POWER_DOWN_TIMEOUT);
+ removeMessages(TURN_OFF_TIMEOUT);
if (!((Boolean) message.obj)) {
transitionTo(mHotOff);
- deferMessage(obtainMessage(TURN_COLD));
- if (mContext.getResources().getBoolean
+ if (!mContext.getResources().getBoolean
(com.android.internal.R.bool.config_bluetooth_adapter_quick_switch)) {
- deferMessage(obtainMessage(TURN_HOT));
+ deferMessage(obtainMessage(TURN_COLD));
}
} else {
if (!isTurningOn) {
@@ -629,7 +653,7 @@ final class BluetoothAdapterStateMachine extends StateMachine {
}
}
break;
- case POWER_DOWN_TIMEOUT:
+ case TURN_OFF_TIMEOUT:
transitionTo(mHotOff);
Log.e(TAG, "Power-down timed out, resetting...");
deferMessage(obtainMessage(TURN_COLD));
@@ -676,12 +700,12 @@ final class BluetoothAdapterStateMachine extends StateMachine {
perProcessCallback(false, (IBluetoothStateChangeCallback)message.obj);
if (mBluetoothService.isApplicationStateChangeTrackerEmpty()) {
mBluetoothService.switchConnectable(false);
- sendMessageDelayed(POWER_DOWN_TIMEOUT, POWER_DOWN_TIMEOUT_TIME);
+ sendMessageDelayed(TURN_OFF_TIMEOUT, TURN_OFF_TIMEOUT_TIME);
}
break;
case AIRPLANE_MODE_ON:
mBluetoothService.switchConnectable(false);
- sendMessageDelayed(POWER_DOWN_TIMEOUT, POWER_DOWN_TIMEOUT_TIME);
+ sendMessageDelayed(TURN_OFF_TIMEOUT, TURN_OFF_TIMEOUT_TIME);
allProcessesCallback(false);
// we turn all the way to PowerOff with AIRPLANE_MODE_ON
deferMessage(obtainMessage(AIRPLANE_MODE_ON));
diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java
index a420734..3cf207f 100755
--- a/core/java/android/server/BluetoothService.java
+++ b/core/java/android/server/BluetoothService.java
@@ -526,6 +526,12 @@ public class BluetoothService extends IBluetooth.Stub {
return false;
}
switchConnectable(false);
+
+ // Bluetooth stack needs a small delay here before adding
+ // SDP records, otherwise dbus stalls for over 30 seconds 1 out of 50 runs
+ try {
+ Thread.sleep(20);
+ } catch (InterruptedException e) {}
updateSdpRecords();
return true;
}
@@ -593,6 +599,12 @@ public class BluetoothService extends IBluetooth.Stub {
// Add SDP records for profiles maintained by Android userspace
addReservedSdpRecords(uuids);
+ // Bluetooth stack need some a small delay here before adding more
+ // SDP records, otherwise dbus stalls for over 30 seconds 1 out of 50 runs
+ try {
+ Thread.sleep(20);
+ } catch (InterruptedException e) {}
+
if (R.getBoolean(com.android.internal.R.bool.config_bluetooth_default_profiles)) {
// Enable profiles maintained by Bluez userspace.
setBluetoothTetheringNative(true, BluetoothPanProfileHandler.NAP_ROLE,
diff --git a/core/java/android/view/AccessibilityIterators.java b/core/java/android/view/AccessibilityIterators.java
new file mode 100644
index 0000000..386c866
--- /dev/null
+++ b/core/java/android/view/AccessibilityIterators.java
@@ -0,0 +1,352 @@
+/*
+ * 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.view;
+
+import android.content.ComponentCallbacks;
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
+
+import java.text.BreakIterator;
+import java.util.Locale;
+
+/**
+ * This class contains the implementation of text segment iterators
+ * for accessibility support.
+ *
+ * Note: Such iterators are needed in the view package since we want
+ * to be able to iterator over content description of any view.
+ *
+ * @hide
+ */
+public final class AccessibilityIterators {
+
+ /**
+ * @hide
+ */
+ public static interface TextSegmentIterator {
+ public int[] following(int current);
+ public int[] preceding(int current);
+ }
+
+ /**
+ * @hide
+ */
+ public static abstract class AbstractTextSegmentIterator implements TextSegmentIterator {
+ protected static final int DONE = -1;
+
+ protected String mText;
+
+ private final int[] mSegment = new int[2];
+
+ public void initialize(String text) {
+ mText = text;
+ }
+
+ protected int[] getRange(int start, int end) {
+ if (start < 0 || end < 0 || start == end) {
+ return null;
+ }
+ mSegment[0] = start;
+ mSegment[1] = end;
+ return mSegment;
+ }
+ }
+
+ static class CharacterTextSegmentIterator extends AbstractTextSegmentIterator
+ implements ComponentCallbacks {
+ private static CharacterTextSegmentIterator sInstance;
+
+ private final Context mAppContext;
+
+ protected BreakIterator mImpl;
+
+ public static CharacterTextSegmentIterator getInstance(Context context) {
+ if (sInstance == null) {
+ sInstance = new CharacterTextSegmentIterator(context);
+ }
+ return sInstance;
+ }
+
+ private CharacterTextSegmentIterator(Context context) {
+ mAppContext = context.getApplicationContext();
+ Locale locale = mAppContext.getResources().getConfiguration().locale;
+ onLocaleChanged(locale);
+ ViewRootImpl.addConfigCallback(this);
+ }
+
+ @Override
+ public void initialize(String text) {
+ super.initialize(text);
+ mImpl.setText(text);
+ }
+
+ @Override
+ public int[] following(int offset) {
+ final int textLegth = mText.length();
+ if (textLegth <= 0) {
+ return null;
+ }
+ if (offset >= textLegth) {
+ return null;
+ }
+ int start = -1;
+ if (offset < 0) {
+ offset = 0;
+ if (mImpl.isBoundary(offset)) {
+ start = offset;
+ }
+ }
+ if (start < 0) {
+ start = mImpl.following(offset);
+ }
+ if (start < 0) {
+ return null;
+ }
+ final int end = mImpl.following(start);
+ return getRange(start, end);
+ }
+
+ @Override
+ public int[] preceding(int offset) {
+ final int textLegth = mText.length();
+ if (textLegth <= 0) {
+ return null;
+ }
+ if (offset <= 0) {
+ return null;
+ }
+ int end = -1;
+ if (offset > mText.length()) {
+ offset = mText.length();
+ if (mImpl.isBoundary(offset)) {
+ end = offset;
+ }
+ }
+ if (end < 0) {
+ end = mImpl.preceding(offset);
+ }
+ if (end < 0) {
+ return null;
+ }
+ final int start = mImpl.preceding(end);
+ return getRange(start, end);
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ Configuration oldConfig = mAppContext.getResources().getConfiguration();
+ final int changed = oldConfig.diff(newConfig);
+ if ((changed & ActivityInfo.CONFIG_LOCALE) != 0) {
+ Locale locale = newConfig.locale;
+ onLocaleChanged(locale);
+ }
+ }
+
+ @Override
+ public void onLowMemory() {
+ /* ignore */
+ }
+
+ protected void onLocaleChanged(Locale locale) {
+ mImpl = BreakIterator.getCharacterInstance(locale);
+ }
+ }
+
+ static class WordTextSegmentIterator extends CharacterTextSegmentIterator {
+ private static WordTextSegmentIterator sInstance;
+
+ public static WordTextSegmentIterator getInstance(Context context) {
+ if (sInstance == null) {
+ sInstance = new WordTextSegmentIterator(context);
+ }
+ return sInstance;
+ }
+
+ private WordTextSegmentIterator(Context context) {
+ super(context);
+ }
+
+ @Override
+ protected void onLocaleChanged(Locale locale) {
+ mImpl = BreakIterator.getWordInstance(locale);
+ }
+
+ @Override
+ public int[] following(int offset) {
+ final int textLegth = mText.length();
+ if (textLegth <= 0) {
+ return null;
+ }
+ if (offset >= mText.length()) {
+ return null;
+ }
+ int start = -1;
+ if (offset < 0) {
+ offset = 0;
+ if (mImpl.isBoundary(offset) && isLetterOrDigit(offset)) {
+ start = offset;
+ }
+ }
+ if (start < 0) {
+ while ((offset = mImpl.following(offset)) != DONE) {
+ if (isLetterOrDigit(offset)) {
+ start = offset;
+ break;
+ }
+ }
+ }
+ if (start < 0) {
+ return null;
+ }
+ final int end = mImpl.following(start);
+ return getRange(start, end);
+ }
+
+ @Override
+ public int[] preceding(int offset) {
+ final int textLegth = mText.length();
+ if (textLegth <= 0) {
+ return null;
+ }
+ if (offset <= 0) {
+ return null;
+ }
+ int end = -1;
+ if (offset > mText.length()) {
+ offset = mText.length();
+ if (mImpl.isBoundary(offset) && offset > 0 && isLetterOrDigit(offset - 1)) {
+ end = offset;
+ }
+ }
+ if (end < 0) {
+ while ((offset = mImpl.preceding(offset)) != DONE) {
+ if (offset > 0 && isLetterOrDigit(offset - 1)) {
+ end = offset;
+ break;
+ }
+ }
+ }
+ if (end < 0) {
+ return null;
+ }
+ final int start = mImpl.preceding(end);
+ return getRange(start, end);
+ }
+
+ private boolean isLetterOrDigit(int index) {
+ if (index >= 0 && index < mText.length()) {
+ final int codePoint = mText.codePointAt(index);
+ return Character.isLetterOrDigit(codePoint);
+ }
+ return false;
+ }
+ }
+
+ static class ParagraphTextSegmentIterator extends AbstractTextSegmentIterator {
+ private static ParagraphTextSegmentIterator sInstance;
+
+ public static ParagraphTextSegmentIterator getInstance() {
+ if (sInstance == null) {
+ sInstance = new ParagraphTextSegmentIterator();
+ }
+ return sInstance;
+ }
+
+ @Override
+ public int[] following(int offset) {
+ final int textLength = mText.length();
+ if (textLength <= 0) {
+ return null;
+ }
+ if (offset >= textLength) {
+ return null;
+ }
+ int start = -1;
+ if (offset < 0) {
+ start = 0;
+ } else {
+ for (int i = offset + 1; i < textLength; i++) {
+ if (mText.charAt(i) == '\n') {
+ start = i;
+ break;
+ }
+ }
+ }
+ while (start < textLength && mText.charAt(start) == '\n') {
+ start++;
+ }
+ if (start < 0) {
+ return null;
+ }
+ int end = start;
+ for (int i = end + 1; i < textLength; i++) {
+ end = i;
+ if (mText.charAt(i) == '\n') {
+ break;
+ }
+ }
+ while (end < textLength && mText.charAt(end) == '\n') {
+ end++;
+ }
+ return getRange(start, end);
+ }
+
+ @Override
+ public int[] preceding(int offset) {
+ final int textLength = mText.length();
+ if (textLength <= 0) {
+ return null;
+ }
+ if (offset <= 0) {
+ return null;
+ }
+ int end = -1;
+ if (offset > mText.length()) {
+ end = mText.length();
+ } else {
+ if (offset > 0 && mText.charAt(offset - 1) == '\n') {
+ offset--;
+ }
+ for (int i = offset - 1; i >= 0; i--) {
+ if (i > 0 && mText.charAt(i - 1) == '\n') {
+ end = i;
+ break;
+ }
+ }
+ }
+ if (end <= 0) {
+ return null;
+ }
+ int start = end;
+ while (start > 0 && mText.charAt(start - 1) == '\n') {
+ start--;
+ }
+ if (start == 0 && mText.charAt(start) == '\n') {
+ return null;
+ }
+ for (int i = start - 1; i >= 0; i--) {
+ start = i;
+ if (start > 0 && mText.charAt(i - 1) == '\n') {
+ break;
+ }
+ }
+ start = Math.max(0, start);
+ return getRange(start, end);
+ }
+ }
+}
diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java
index b319cd5..825f351 100644
--- a/core/java/android/view/Choreographer.java
+++ b/core/java/android/view/Choreographer.java
@@ -92,6 +92,7 @@ public final class Choreographer {
private boolean mFrameScheduled;
private boolean mCallbacksRunning;
private long mLastFrameTimeNanos;
+ private long mFrameIntervalNanos;
/**
* Callback type: Input callback. Runs first.
@@ -116,6 +117,8 @@ public final class Choreographer {
mHandler = new FrameHandler(looper);
mDisplayEventReceiver = USE_VSYNC ? new FrameDisplayEventReceiver(looper) : null;
mLastFrameTimeNanos = Long.MIN_VALUE;
+ mFrameIntervalNanos = (long)(1000000000 /
+ new Display(Display.DEFAULT_DISPLAY, null).getRefreshRate());
mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1];
for (int i = 0; i <= CALLBACK_LAST; i++) {
@@ -343,17 +346,37 @@ public final class Choreographer {
}
void doFrame(long timestampNanos, int frame) {
+ final long startNanos;
synchronized (mLock) {
if (!mFrameScheduled) {
return; // no work to do
}
- mFrameScheduled = false;
- mLastFrameTimeNanos = timestampNanos;
- }
- final long startNanos;
- if (DEBUG) {
startNanos = System.nanoTime();
+ final long jitterNanos = startNanos - timestampNanos;
+ if (jitterNanos >= mFrameIntervalNanos) {
+ final long lastFrameOffset = jitterNanos % mFrameIntervalNanos;
+ if (DEBUG) {
+ Log.d(TAG, "Missed vsync by " + (jitterNanos * 0.000001f) + " ms "
+ + "which is more than the frame interval of "
+ + (mFrameIntervalNanos * 0.000001f) + " ms! "
+ + "Setting frame time to " + (lastFrameOffset * 0.000001f)
+ + " ms in the past.");
+ }
+ timestampNanos = startNanos - lastFrameOffset;
+ }
+
+ if (timestampNanos < mLastFrameTimeNanos) {
+ if (DEBUG) {
+ Log.d(TAG, "Frame time appears to be going backwards. May be due to a "
+ + "previously skipped frame. Waiting for next vsync");
+ }
+ scheduleVsyncLocked();
+ return;
+ }
+
+ mFrameScheduled = false;
+ mLastFrameTimeNanos = timestampNanos;
}
doCallbacks(Choreographer.CALLBACK_INPUT);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 5299d58..2972774 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -47,7 +47,6 @@ import android.os.Parcelable;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.SystemProperties;
-import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.FloatProperty;
import android.util.LocaleUtil;
@@ -60,6 +59,10 @@ import android.util.Property;
import android.util.SparseArray;
import android.util.TypedValue;
import android.view.ContextMenu.ContextMenuInfo;
+import android.view.AccessibilityIterators.TextSegmentIterator;
+import android.view.AccessibilityIterators.CharacterTextSegmentIterator;
+import android.view.AccessibilityIterators.WordTextSegmentIterator;
+import android.view.AccessibilityIterators.ParagraphTextSegmentIterator;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityEventSource;
import android.view.accessibility.AccessibilityManager;
@@ -1524,7 +1527,8 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
| AccessibilityEvent.TYPE_VIEW_HOVER_EXIT
| AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED
| AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED
- | AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED;
+ | AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED
+ | AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY;
/**
* Temporary Rect currently for use in setBackground(). This will probably
@@ -1590,6 +1594,11 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
int mAccessibilityViewId = NO_ID;
/**
+ * @hide
+ */
+ private int mAccessibilityCursorPosition = -1;
+
+ /**
* The view's tag.
* {@hide}
*
@@ -4703,8 +4712,11 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
}
}
- info.addAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS);
- info.addAction(AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS);
+ if (!isAccessibilityFocused()) {
+ info.addAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS);
+ } else {
+ info.addAction(AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS);
+ }
if (isClickable()) {
info.addAction(AccessibilityNodeInfo.ACTION_CLICK);
@@ -4714,11 +4726,12 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
info.addAction(AccessibilityNodeInfo.ACTION_LONG_CLICK);
}
- if (getContentDescription() != null) {
+ if (mContentDescription != null && mContentDescription.length() > 0) {
info.addAction(AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY);
info.addAction(AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY);
info.setMovementGranularities(AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER
- | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD);
+ | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD
+ | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH);
}
}
@@ -5949,7 +5962,8 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
outViews.add(this);
}
} else if ((flags & FIND_VIEWS_WITH_CONTENT_DESCRIPTION) != 0
- && !TextUtils.isEmpty(searched) && !TextUtils.isEmpty(mContentDescription)) {
+ && (searched != null && searched.length() > 0)
+ && (mContentDescription != null && mContentDescription.length() > 0)) {
String searchedLowerCase = searched.toString().toLowerCase();
String contentDescriptionLowerCase = mContentDescription.toString().toLowerCase();
if (contentDescriptionLowerCase.contains(searchedLowerCase)) {
@@ -6050,6 +6064,10 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
invalidate();
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
notifyAccessibilityStateChanged();
+
+ // Clear the text navigation state.
+ setAccessibilityCursorPosition(-1);
+
// Try to move accessibility focus to the input focus.
View rootView = getRootView();
if (rootView != null) {
@@ -6447,9 +6465,10 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
* possible accessibility actions look at {@link AccessibilityNodeInfo}.
*
* @param action The action to perform.
+ * @param arguments Optional action arguments.
* @return Whether the action was performed.
*/
- public boolean performAccessibilityAction(int action, Bundle args) {
+ public boolean performAccessibilityAction(int action, Bundle arguments) {
switch (action) {
case AccessibilityNodeInfo.ACTION_CLICK: {
if (isClickable()) {
@@ -6498,10 +6517,150 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
return true;
}
} break;
+ case AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY: {
+ if (arguments != null) {
+ final int granularity = arguments.getInt(
+ AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT);
+ return nextAtGranularity(granularity);
+ }
+ } break;
+ case AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY: {
+ if (arguments != null) {
+ final int granularity = arguments.getInt(
+ AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT);
+ return previousAtGranularity(granularity);
+ }
+ } break;
}
return false;
}
+ private boolean nextAtGranularity(int granularity) {
+ CharSequence text = getIterableTextForAccessibility();
+ if (text != null && text.length() > 0) {
+ return false;
+ }
+ TextSegmentIterator iterator = getIteratorForGranularity(granularity);
+ if (iterator == null) {
+ return false;
+ }
+ final int current = getAccessibilityCursorPosition();
+ final int[] range = iterator.following(current);
+ if (range == null) {
+ setAccessibilityCursorPosition(-1);
+ return false;
+ }
+ final int start = range[0];
+ final int end = range[1];
+ setAccessibilityCursorPosition(start);
+ sendViewTextTraversedAtGranularityEvent(
+ AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY,
+ granularity, start, end);
+ return true;
+ }
+
+ private boolean previousAtGranularity(int granularity) {
+ CharSequence text = getIterableTextForAccessibility();
+ if (text != null && text.length() > 0) {
+ return false;
+ }
+ TextSegmentIterator iterator = getIteratorForGranularity(granularity);
+ if (iterator == null) {
+ return false;
+ }
+ final int selectionStart = getAccessibilityCursorPosition();
+ final int current = selectionStart >= 0 ? selectionStart : text.length() + 1;
+ final int[] range = iterator.preceding(current);
+ if (range == null) {
+ setAccessibilityCursorPosition(-1);
+ return false;
+ }
+ final int start = range[0];
+ final int end = range[1];
+ setAccessibilityCursorPosition(end);
+ sendViewTextTraversedAtGranularityEvent(
+ AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY,
+ granularity, start, end);
+ return true;
+ }
+
+ /**
+ * Gets the text reported for accessibility purposes.
+ *
+ * @return The accessibility text.
+ *
+ * @hide
+ */
+ public CharSequence getIterableTextForAccessibility() {
+ return mContentDescription;
+ }
+
+ /**
+ * @hide
+ */
+ public int getAccessibilityCursorPosition() {
+ return mAccessibilityCursorPosition;
+ }
+
+ /**
+ * @hide
+ */
+ public void setAccessibilityCursorPosition(int position) {
+ mAccessibilityCursorPosition = position;
+ }
+
+ private void sendViewTextTraversedAtGranularityEvent(int action, int granularity,
+ int fromIndex, int toIndex) {
+ if (mParent == null) {
+ return;
+ }
+ AccessibilityEvent event = AccessibilityEvent.obtain(
+ AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY);
+ onInitializeAccessibilityEvent(event);
+ onPopulateAccessibilityEvent(event);
+ event.setFromIndex(fromIndex);
+ event.setToIndex(toIndex);
+ event.setAction(action);
+ event.setMovementGranularity(granularity);
+ mParent.requestSendAccessibilityEvent(this, event);
+ }
+
+ /**
+ * @hide
+ */
+ public TextSegmentIterator getIteratorForGranularity(int granularity) {
+ switch (granularity) {
+ case AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER: {
+ CharSequence text = getIterableTextForAccessibility();
+ if (text != null && text.length() > 0) {
+ CharacterTextSegmentIterator iterator =
+ CharacterTextSegmentIterator.getInstance(mContext);
+ iterator.initialize(text.toString());
+ return iterator;
+ }
+ } break;
+ case AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD: {
+ CharSequence text = getIterableTextForAccessibility();
+ if (text != null && text.length() > 0) {
+ WordTextSegmentIterator iterator =
+ WordTextSegmentIterator.getInstance(mContext);
+ iterator.initialize(text.toString());
+ return iterator;
+ }
+ } break;
+ case AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH: {
+ CharSequence text = getIterableTextForAccessibility();
+ if (text != null && text.length() > 0) {
+ ParagraphTextSegmentIterator iterator =
+ ParagraphTextSegmentIterator.getInstance();
+ iterator.initialize(text.toString());
+ return iterator;
+ }
+ } break;
+ }
+ return null;
+ }
+
/**
* @hide
*/
diff --git a/core/java/android/view/ViewTreeObserver.java b/core/java/android/view/ViewTreeObserver.java
index 1c5d436..6a8a60a 100644
--- a/core/java/android/view/ViewTreeObserver.java
+++ b/core/java/android/view/ViewTreeObserver.java
@@ -20,7 +20,6 @@ import android.graphics.Rect;
import android.graphics.Region;
import java.util.ArrayList;
-import java.util.concurrent.CopyOnWriteArrayList;
/**
* A view tree observer is used to register listeners that can be notified of global
@@ -32,12 +31,12 @@ import java.util.concurrent.CopyOnWriteArrayList;
* for more information.
*/
public final class ViewTreeObserver {
- private CopyOnWriteArrayList<OnGlobalFocusChangeListener> mOnGlobalFocusListeners;
- private CopyOnWriteArrayList<OnGlobalLayoutListener> mOnGlobalLayoutListeners;
- private CopyOnWriteArrayList<OnTouchModeChangeListener> mOnTouchModeChangeListeners;
- private CopyOnWriteArrayList<OnComputeInternalInsetsListener> mOnComputeInternalInsetsListeners;
- private CopyOnWriteArrayList<OnScrollChangedListener> mOnScrollChangedListeners;
- private ArrayList<OnPreDrawListener> mOnPreDrawListeners;
+ private CopyOnWriteArray<OnGlobalFocusChangeListener> mOnGlobalFocusListeners;
+ private CopyOnWriteArray<OnGlobalLayoutListener> mOnGlobalLayoutListeners;
+ private CopyOnWriteArray<OnTouchModeChangeListener> mOnTouchModeChangeListeners;
+ private CopyOnWriteArray<OnComputeInternalInsetsListener> mOnComputeInternalInsetsListeners;
+ private CopyOnWriteArray<OnScrollChangedListener> mOnScrollChangedListeners;
+ private CopyOnWriteArray<OnPreDrawListener> mOnPreDrawListeners;
private ArrayList<OnDrawListener> mOnDrawListeners;
private boolean mAlive = true;
@@ -147,7 +146,7 @@ public final class ViewTreeObserver {
* windows behind it should be placed.
*/
public final Rect contentInsets = new Rect();
-
+
/**
* Offsets from the frame of the window at which windows behind it
* are visible.
@@ -166,13 +165,13 @@ public final class ViewTreeObserver {
* can be touched.
*/
public static final int TOUCHABLE_INSETS_FRAME = 0;
-
+
/**
* Option for {@link #setTouchableInsets(int)}: the area inside of
* the content insets can be touched.
*/
public static final int TOUCHABLE_INSETS_CONTENT = 1;
-
+
/**
* Option for {@link #setTouchableInsets(int)}: the area inside of
* the visible insets can be touched.
@@ -195,7 +194,7 @@ public final class ViewTreeObserver {
}
int mTouchableInsets;
-
+
void reset() {
contentInsets.setEmpty();
visibleInsets.setEmpty();
@@ -231,7 +230,7 @@ public final class ViewTreeObserver {
mTouchableInsets = other.mTouchableInsets;
}
}
-
+
/**
* Interface definition for a callback to be invoked when layout has
* completed and the client can compute its interior insets.
@@ -328,7 +327,7 @@ public final class ViewTreeObserver {
checkIsAlive();
if (mOnGlobalFocusListeners == null) {
- mOnGlobalFocusListeners = new CopyOnWriteArrayList<OnGlobalFocusChangeListener>();
+ mOnGlobalFocusListeners = new CopyOnWriteArray<OnGlobalFocusChangeListener>();
}
mOnGlobalFocusListeners.add(listener);
@@ -363,7 +362,7 @@ public final class ViewTreeObserver {
checkIsAlive();
if (mOnGlobalLayoutListeners == null) {
- mOnGlobalLayoutListeners = new CopyOnWriteArrayList<OnGlobalLayoutListener>();
+ mOnGlobalLayoutListeners = new CopyOnWriteArray<OnGlobalLayoutListener>();
}
mOnGlobalLayoutListeners.add(listener);
@@ -413,7 +412,7 @@ public final class ViewTreeObserver {
checkIsAlive();
if (mOnPreDrawListeners == null) {
- mOnPreDrawListeners = new ArrayList<OnPreDrawListener>();
+ mOnPreDrawListeners = new CopyOnWriteArray<OnPreDrawListener>();
}
mOnPreDrawListeners.add(listener);
@@ -485,7 +484,7 @@ public final class ViewTreeObserver {
checkIsAlive();
if (mOnScrollChangedListeners == null) {
- mOnScrollChangedListeners = new CopyOnWriteArrayList<OnScrollChangedListener>();
+ mOnScrollChangedListeners = new CopyOnWriteArray<OnScrollChangedListener>();
}
mOnScrollChangedListeners.add(listener);
@@ -519,7 +518,7 @@ public final class ViewTreeObserver {
checkIsAlive();
if (mOnTouchModeChangeListeners == null) {
- mOnTouchModeChangeListeners = new CopyOnWriteArrayList<OnTouchModeChangeListener>();
+ mOnTouchModeChangeListeners = new CopyOnWriteArray<OnTouchModeChangeListener>();
}
mOnTouchModeChangeListeners.add(listener);
@@ -558,7 +557,7 @@ public final class ViewTreeObserver {
if (mOnComputeInternalInsetsListeners == null) {
mOnComputeInternalInsetsListeners =
- new CopyOnWriteArrayList<OnComputeInternalInsetsListener>();
+ new CopyOnWriteArray<OnComputeInternalInsetsListener>();
}
mOnComputeInternalInsetsListeners.add(listener);
@@ -622,10 +621,16 @@ public final class ViewTreeObserver {
// perform the dispatching. The iterator is a safe guard against listeners that
// could mutate the list by calling the various add/remove methods. This prevents
// the array from being modified while we iterate it.
- final CopyOnWriteArrayList<OnGlobalFocusChangeListener> listeners = mOnGlobalFocusListeners;
+ final CopyOnWriteArray<OnGlobalFocusChangeListener> listeners = mOnGlobalFocusListeners;
if (listeners != null && listeners.size() > 0) {
- for (OnGlobalFocusChangeListener listener : listeners) {
- listener.onGlobalFocusChanged(oldFocus, newFocus);
+ CopyOnWriteArray.Access<OnGlobalFocusChangeListener> access = listeners.start();
+ try {
+ int count = access.size();
+ for (int i = 0; i < count; i++) {
+ access.get(i).onGlobalFocusChanged(oldFocus, newFocus);
+ }
+ } finally {
+ listeners.end();
}
}
}
@@ -640,10 +645,16 @@ public final class ViewTreeObserver {
// perform the dispatching. The iterator is a safe guard against listeners that
// could mutate the list by calling the various add/remove methods. This prevents
// the array from being modified while we iterate it.
- final CopyOnWriteArrayList<OnGlobalLayoutListener> listeners = mOnGlobalLayoutListeners;
+ final CopyOnWriteArray<OnGlobalLayoutListener> listeners = mOnGlobalLayoutListeners;
if (listeners != null && listeners.size() > 0) {
- for (OnGlobalLayoutListener listener : listeners) {
- listener.onGlobalLayout();
+ CopyOnWriteArray.Access<OnGlobalLayoutListener> access = listeners.start();
+ try {
+ int count = access.size();
+ for (int i = 0; i < count; i++) {
+ access.get(i).onGlobalLayout();
+ }
+ } finally {
+ listeners.end();
}
}
}
@@ -658,17 +669,17 @@ public final class ViewTreeObserver {
*/
@SuppressWarnings("unchecked")
public final boolean dispatchOnPreDraw() {
- // NOTE: we *must* clone the listener list to perform the dispatching.
- // The clone is a safe guard against listeners that
- // could mutate the list by calling the various add/remove methods. This prevents
- // the array from being modified while we process it.
boolean cancelDraw = false;
- if (mOnPreDrawListeners != null && mOnPreDrawListeners.size() > 0) {
- final ArrayList<OnPreDrawListener> listeners =
- (ArrayList<OnPreDrawListener>) mOnPreDrawListeners.clone();
- int numListeners = listeners.size();
- for (int i = 0; i < numListeners; ++i) {
- cancelDraw |= !(listeners.get(i).onPreDraw());
+ final CopyOnWriteArray<OnPreDrawListener> listeners = mOnPreDrawListeners;
+ if (listeners != null && listeners.size() > 0) {
+ CopyOnWriteArray.Access<OnPreDrawListener> access = listeners.start();
+ try {
+ int count = access.size();
+ for (int i = 0; i < count; i++) {
+ cancelDraw |= !(access.get(i).onPreDraw());
+ }
+ } finally {
+ listeners.end();
}
}
return cancelDraw;
@@ -693,11 +704,17 @@ public final class ViewTreeObserver {
* @param inTouchMode True if the touch mode is now enabled, false otherwise.
*/
final void dispatchOnTouchModeChanged(boolean inTouchMode) {
- final CopyOnWriteArrayList<OnTouchModeChangeListener> listeners =
+ final CopyOnWriteArray<OnTouchModeChangeListener> listeners =
mOnTouchModeChangeListeners;
if (listeners != null && listeners.size() > 0) {
- for (OnTouchModeChangeListener listener : listeners) {
- listener.onTouchModeChanged(inTouchMode);
+ CopyOnWriteArray.Access<OnTouchModeChangeListener> access = listeners.start();
+ try {
+ int count = access.size();
+ for (int i = 0; i < count; i++) {
+ access.get(i).onTouchModeChanged(inTouchMode);
+ }
+ } finally {
+ listeners.end();
}
}
}
@@ -710,10 +727,16 @@ public final class ViewTreeObserver {
// perform the dispatching. The iterator is a safe guard against listeners that
// could mutate the list by calling the various add/remove methods. This prevents
// the array from being modified while we iterate it.
- final CopyOnWriteArrayList<OnScrollChangedListener> listeners = mOnScrollChangedListeners;
+ final CopyOnWriteArray<OnScrollChangedListener> listeners = mOnScrollChangedListeners;
if (listeners != null && listeners.size() > 0) {
- for (OnScrollChangedListener listener : listeners) {
- listener.onScrollChanged();
+ CopyOnWriteArray.Access<OnScrollChangedListener> access = listeners.start();
+ try {
+ int count = access.size();
+ for (int i = 0; i < count; i++) {
+ access.get(i).onScrollChanged();
+ }
+ } finally {
+ listeners.end();
}
}
}
@@ -722,11 +745,11 @@ public final class ViewTreeObserver {
* Returns whether there are listeners for computing internal insets.
*/
final boolean hasComputeInternalInsetsListeners() {
- final CopyOnWriteArrayList<OnComputeInternalInsetsListener> listeners =
+ final CopyOnWriteArray<OnComputeInternalInsetsListener> listeners =
mOnComputeInternalInsetsListeners;
return (listeners != null && listeners.size() > 0);
}
-
+
/**
* Calls all listeners to compute the current insets.
*/
@@ -735,12 +758,105 @@ public final class ViewTreeObserver {
// perform the dispatching. The iterator is a safe guard against listeners that
// could mutate the list by calling the various add/remove methods. This prevents
// the array from being modified while we iterate it.
- final CopyOnWriteArrayList<OnComputeInternalInsetsListener> listeners =
+ final CopyOnWriteArray<OnComputeInternalInsetsListener> listeners =
mOnComputeInternalInsetsListeners;
if (listeners != null && listeners.size() > 0) {
- for (OnComputeInternalInsetsListener listener : listeners) {
- listener.onComputeInternalInsets(inoutInfo);
+ CopyOnWriteArray.Access<OnComputeInternalInsetsListener> access = listeners.start();
+ try {
+ int count = access.size();
+ for (int i = 0; i < count; i++) {
+ access.get(i).onComputeInternalInsets(inoutInfo);
+ }
+ } finally {
+ listeners.end();
+ }
+ }
+ }
+
+ /**
+ * Copy on write array. This array is not thread safe, and only one loop can
+ * iterate over this array at any given time. This class avoids allocations
+ * until a concurrent modification happens.
+ *
+ * Usage:
+ *
+ * CopyOnWriteArray.Access<MyData> access = array.start();
+ * try {
+ * for (int i = 0; i < access.size(); i++) {
+ * MyData d = access.get(i);
+ * }
+ * } finally {
+ * access.end();
+ * }
+ */
+ static class CopyOnWriteArray<T> {
+ private ArrayList<T> mData = new ArrayList<T>();
+ private ArrayList<T> mDataCopy;
+
+ private final Access<T> mAccess = new Access<T>();
+
+ private boolean mStart;
+
+ static class Access<T> {
+ private ArrayList<T> mData;
+ private int mSize;
+
+ T get(int index) {
+ return mData.get(index);
}
+
+ int size() {
+ return mSize;
+ }
+ }
+
+ CopyOnWriteArray() {
+ }
+
+ private ArrayList<T> getArray() {
+ if (mStart) {
+ if (mDataCopy == null) mDataCopy = new ArrayList<T>(mData);
+ return mDataCopy;
+ }
+ return mData;
+ }
+
+ Access<T> start() {
+ if (mStart) throw new IllegalStateException("Iteration already started");
+ mStart = true;
+ mDataCopy = null;
+ mAccess.mData = mData;
+ mAccess.mSize = mData.size();
+ return mAccess;
+ }
+
+ void end() {
+ if (!mStart) throw new IllegalStateException("Iteration not started");
+ mStart = false;
+ if (mDataCopy != null) {
+ mData = mDataCopy;
+ }
+ mDataCopy = null;
+ }
+
+ int size() {
+ return getArray().size();
+ }
+
+ void add(T item) {
+ getArray().add(item);
+ }
+
+ void addAll(CopyOnWriteArray<T> array) {
+ getArray().addAll(array.mData);
+ }
+
+ void remove(T item) {
+ getArray().remove(item);
+ }
+
+ void clear() {
+ getArray().clear();
}
}
}
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index f70ffa9..1a2a194 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -236,12 +236,19 @@ import java.util.List;
* <li>{@link #getClassName()} - The class name of the source.</li>
* <li>{@link #getPackageName()} - The package name of the source.</li>
* <li>{@link #getEventTime()} - The event time.</li>
- * <li>{@link #getText()} - The text of the current text at the movement granularity.</li>
+ * <li>{@link #getMovementGranularity()} - Sets the granularity at which a view's text
+ * was traversed.</li>
+ * <li>{@link #getText()} - The text of the source's sub-tree.</li>
+ * <li>{@link #getFromIndex()} - The start of the next/previous text at the specified granularity
+ * - inclusive.</li>
+ * <li>{@link #getToIndex()} - The end of the next/previous text at the specified granularity
+ * - exclusive.</li>
* <li>{@link #isPassword()} - Whether the source is password.</li>
* <li>{@link #isEnabled()} - Whether the source is enabled.</li>
* <li>{@link #getContentDescription()} - The content description of the source.</li>
* <li>{@link #getMovementGranularity()} - Sets the granularity at which a view's text
* was traversed.</li>
+ * <li>{@link #getAction()} - Gets traversal action which specifies the direction.</li>
* </ul>
* </p>
* <p>
@@ -635,6 +642,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
private CharSequence mPackageName;
private long mEventTime;
int mMovementGranularity;
+ int mAction;
private final ArrayList<AccessibilityRecord> mRecords = new ArrayList<AccessibilityRecord>();
@@ -653,6 +661,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
super.init(event);
mEventType = event.mEventType;
mMovementGranularity = event.mMovementGranularity;
+ mAction = event.mAction;
mEventTime = event.mEventTime;
mPackageName = event.mPackageName;
}
@@ -791,6 +800,27 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
}
/**
+ * Sets the performed action that triggered this event.
+ *
+ * @param action The action.
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ public void setAction(int action) {
+ enforceNotSealed();
+ mAction = action;
+ }
+
+ /**
+ * Gets the performed action that triggered this event.
+ *
+ * @return The action.
+ */
+ public int getAction() {
+ return mAction;
+ }
+
+ /**
* Returns a cached instance if such is available or a new one is
* instantiated with its type property set.
*
@@ -879,6 +909,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
super.clear();
mEventType = 0;
mMovementGranularity = 0;
+ mAction = 0;
mPackageName = null;
mEventTime = 0;
while (!mRecords.isEmpty()) {
@@ -896,6 +927,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
mSealed = (parcel.readInt() == 1);
mEventType = parcel.readInt();
mMovementGranularity = parcel.readInt();
+ mAction = parcel.readInt();
mPackageName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
mEventTime = parcel.readLong();
mConnectionId = parcel.readInt();
@@ -947,6 +979,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
parcel.writeInt(isSealed() ? 1 : 0);
parcel.writeInt(mEventType);
parcel.writeInt(mMovementGranularity);
+ parcel.writeInt(mAction);
TextUtils.writeToParcel(mPackageName, parcel, 0);
parcel.writeLong(mEventTime);
parcel.writeInt(mConnectionId);
@@ -1004,6 +1037,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
builder.append("; EventTime: ").append(mEventTime);
builder.append("; PackageName: ").append(mPackageName);
builder.append("; MovementGranularity: ").append(mMovementGranularity);
+ builder.append("; Action: ").append(mAction);
builder.append(super.toString());
if (DEBUG) {
builder.append("\n");
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index c0696a9..fef24e2 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -102,12 +102,12 @@ public class AccessibilityNodeInfo implements Parcelable {
public static final int ACTION_CLEAR_SELECTION = 0x00000008;
/**
- * Action that long clicks on the node info.
+ * Action that clicks on the node info.
*/
public static final int ACTION_CLICK = 0x00000010;
/**
- * Action that clicks on the node.
+ * Action that long clicks on the node.
*/
public static final int ACTION_LONG_CLICK = 0x00000020;
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 449870e..a3b613b 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -3985,7 +3985,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
}
class PositionScroller implements Runnable {
- private static final int SCROLL_DURATION = 400;
+ private static final int SCROLL_DURATION = 200;
private static final int MOVE_DOWN_POS = 1;
private static final int MOVE_UP_POS = 2;
@@ -4006,21 +4006,35 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
mExtraScroll = ViewConfiguration.get(mContext).getScaledFadingEdgeLength();
}
- void start(int position) {
+ void start(final int position) {
stop();
+ final int childCount = getChildCount();
+ if (childCount == 0) {
+ // Can't scroll without children.
+ if (mDataChanged) {
+ // But we might have something in a minute.
+ post(new Runnable() {
+ @Override public void run() {
+ start(position);
+ }
+ });
+ }
+ return;
+ }
+
final int firstPos = mFirstPosition;
- final int lastPos = firstPos + getChildCount() - 1;
+ final int lastPos = firstPos + childCount - 1;
int viewTravelCount;
- if (position <= firstPos) {
+ if (position < firstPos) {
viewTravelCount = firstPos - position + 1;
mMode = MOVE_UP_POS;
- } else if (position >= lastPos) {
+ } else if (position > lastPos) {
viewTravelCount = position - lastPos + 1;
mMode = MOVE_DOWN_POS;
} else {
- // Already on screen, nothing to do
+ scrollToVisible(position, INVALID_POSITION, SCROLL_DURATION);
return;
}
@@ -4036,7 +4050,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
postOnAnimation(this);
}
- void start(int position, int boundPosition) {
+ void start(final int position, final int boundPosition) {
stop();
if (boundPosition == INVALID_POSITION) {
@@ -4044,11 +4058,25 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
return;
}
+ final int childCount = getChildCount();
+ if (childCount == 0) {
+ // Can't scroll without children.
+ if (mDataChanged) {
+ // But we might have something in a minute.
+ post(new Runnable() {
+ @Override public void run() {
+ start(position, boundPosition);
+ }
+ });
+ }
+ return;
+ }
+
final int firstPos = mFirstPosition;
- final int lastPos = firstPos + getChildCount() - 1;
+ final int lastPos = firstPos + childCount - 1;
int viewTravelCount;
- if (position <= firstPos) {
+ if (position < firstPos) {
final int boundPosFromLast = lastPos - boundPosition;
if (boundPosFromLast < 1) {
// Moving would shift our bound position off the screen. Abort.
@@ -4064,7 +4092,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
viewTravelCount = posTravel;
mMode = MOVE_UP_POS;
}
- } else if (position >= lastPos) {
+ } else if (position > lastPos) {
final int boundPosFromFirst = boundPosition - firstPos;
if (boundPosFromFirst < 1) {
// Moving would shift our bound position off the screen. Abort.
@@ -4081,7 +4109,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
mMode = MOVE_DOWN_POS;
}
} else {
- // Already on screen, nothing to do
+ scrollToVisible(position, boundPosition, SCROLL_DURATION);
return;
}
@@ -4137,6 +4165,60 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
postOnAnimation(this);
}
+ /**
+ * Scroll such that targetPos is in the visible padded region without scrolling
+ * boundPos out of view. Assumes targetPos is onscreen.
+ */
+ void scrollToVisible(int targetPos, int boundPos, int duration) {
+ final int firstPos = mFirstPosition;
+ final int childCount = getChildCount();
+ final int lastPos = firstPos + childCount - 1;
+ final int paddedTop = mListPadding.top;
+ final int paddedBottom = getHeight() - mListPadding.bottom;
+
+ if (targetPos < firstPos || targetPos > lastPos) {
+ Log.w(TAG, "scrollToVisible called with targetPos " + targetPos +
+ " not visible [" + firstPos + ", " + lastPos + "]");
+ }
+ if (boundPos < firstPos || boundPos > lastPos) {
+ // boundPos doesn't matter, it's already offscreen.
+ boundPos = INVALID_POSITION;
+ }
+
+ final View targetChild = getChildAt(targetPos - firstPos);
+ final int targetTop = targetChild.getTop();
+ final int targetBottom = targetChild.getBottom();
+ int scrollBy = 0;
+
+ if (targetBottom > paddedBottom) {
+ scrollBy = targetBottom - paddedBottom;
+ }
+ if (targetTop < paddedTop) {
+ scrollBy = targetTop - paddedTop;
+ }
+
+ if (scrollBy == 0) {
+ return;
+ }
+
+ if (boundPos >= 0) {
+ final View boundChild = getChildAt(boundPos - firstPos);
+ final int boundTop = boundChild.getTop();
+ final int boundBottom = boundChild.getBottom();
+ final int absScroll = Math.abs(scrollBy);
+
+ if (scrollBy < 0 && boundBottom + absScroll > paddedBottom) {
+ // Don't scroll the bound view off the bottom of the screen.
+ scrollBy = Math.max(0, boundBottom - paddedBottom);
+ } else if (scrollBy > 0 && boundTop - absScroll < paddedTop) {
+ // Don't scroll the bound view off the top of the screen.
+ scrollBy = Math.min(0, boundTop - paddedTop);
+ }
+ }
+
+ smoothScrollBy(scrollBy, duration);
+ }
+
void stop() {
removeCallbacks(this);
}
@@ -6217,6 +6299,16 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
removeDetachedView(scrapPile.remove(size--), false);
}
}
+
+ if (mTransientStateViews != null) {
+ for (int i = 0; i < mTransientStateViews.size(); i++) {
+ final View v = mTransientStateViews.valueAt(i);
+ if (!v.hasTransientState()) {
+ mTransientStateViews.removeAt(i);
+ i--;
+ }
+ }
+ }
}
/**
diff --git a/core/java/android/widget/AccessibilityIterators.java b/core/java/android/widget/AccessibilityIterators.java
new file mode 100644
index 0000000..e800e8d
--- /dev/null
+++ b/core/java/android/widget/AccessibilityIterators.java
@@ -0,0 +1,219 @@
+/*
+ * 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.widget;
+
+import android.graphics.Rect;
+import android.text.Layout;
+import android.text.Spannable;
+import android.view.AccessibilityIterators.AbstractTextSegmentIterator;
+
+/**
+ * This class contains the implementation of text segment iterators
+ * for accessibility support.
+ */
+final class AccessibilityIterators {
+
+ static class LineTextSegmentIterator extends AbstractTextSegmentIterator {
+ private static LineTextSegmentIterator sLineInstance;
+
+ protected static final int DIRECTION_START = -1;
+ protected static final int DIRECTION_END = 1;
+
+ protected Layout mLayout;
+
+ public static LineTextSegmentIterator getInstance() {
+ if (sLineInstance == null) {
+ sLineInstance = new LineTextSegmentIterator();
+ }
+ return sLineInstance;
+ }
+
+ public void initialize(Spannable text, Layout layout) {
+ mText = text.toString();
+ mLayout = layout;
+ }
+
+ @Override
+ public int[] following(int offset) {
+ final int textLegth = mText.length();
+ if (textLegth <= 0) {
+ return null;
+ }
+ if (offset >= mText.length()) {
+ return null;
+ }
+ int nextLine = -1;
+ if (offset < 0) {
+ nextLine = mLayout.getLineForOffset(0);
+ } else {
+ final int currentLine = mLayout.getLineForOffset(offset);
+ if (currentLine < mLayout.getLineCount() - 1) {
+ nextLine = currentLine + 1;
+ }
+ }
+ if (nextLine < 0) {
+ return null;
+ }
+ final int start = getLineEdgeIndex(nextLine, DIRECTION_START);
+ final int end = getLineEdgeIndex(nextLine, DIRECTION_END) + 1;
+ return getRange(start, end);
+ }
+
+ @Override
+ public int[] preceding(int offset) {
+ final int textLegth = mText.length();
+ if (textLegth <= 0) {
+ return null;
+ }
+ if (offset <= 0) {
+ return null;
+ }
+ int previousLine = -1;
+ if (offset > mText.length()) {
+ previousLine = mLayout.getLineForOffset(mText.length());
+ } else {
+ final int currentLine = mLayout.getLineForOffset(offset - 1);
+ if (currentLine > 0) {
+ previousLine = currentLine - 1;
+ }
+ }
+ if (previousLine < 0) {
+ return null;
+ }
+ final int start = getLineEdgeIndex(previousLine, DIRECTION_START);
+ final int end = getLineEdgeIndex(previousLine, DIRECTION_END) + 1;
+ return getRange(start, end);
+ }
+
+ protected int getLineEdgeIndex(int lineNumber, int direction) {
+ final int paragraphDirection = mLayout.getParagraphDirection(lineNumber);
+ if (direction * paragraphDirection < 0) {
+ return mLayout.getLineStart(lineNumber);
+ } else {
+ return mLayout.getLineEnd(lineNumber) - 1;
+ }
+ }
+ }
+
+ static class PageTextSegmentIterator extends LineTextSegmentIterator {
+ private static PageTextSegmentIterator sPageInstance;
+
+ private TextView mView;
+
+ private final Rect mTempRect = new Rect();
+
+ public static PageTextSegmentIterator getInstance() {
+ if (sPageInstance == null) {
+ sPageInstance = new PageTextSegmentIterator();
+ }
+ return sPageInstance;
+ }
+
+ public void initialize(TextView view) {
+ super.initialize((Spannable) view.getIterableTextForAccessibility(), view.getLayout());
+ mView = view;
+ }
+
+ @Override
+ public int[] following(int offset) {
+ final int textLegth = mText.length();
+ if (textLegth <= 0) {
+ return null;
+ }
+ if (offset >= mText.length()) {
+ return null;
+ }
+ if (!mView.getGlobalVisibleRect(mTempRect)) {
+ return null;
+ }
+
+ final int currentLine = mLayout.getLineForOffset(offset);
+ final int currentLineTop = mLayout.getLineTop(currentLine);
+ final int pageHeight = mTempRect.height() - mView.getTotalPaddingTop()
+ - mView.getTotalPaddingBottom();
+
+ final int nextPageStartLine;
+ final int nextPageEndLine;
+ if (offset < 0) {
+ nextPageStartLine = currentLine;
+ final int nextPageEndY = currentLineTop + pageHeight;
+ nextPageEndLine = mLayout.getLineForVertical(nextPageEndY);
+ } else {
+ final int nextPageStartY = currentLineTop + pageHeight;
+ nextPageStartLine = mLayout.getLineForVertical(nextPageStartY) + 1;
+ if (mLayout.getLineTop(nextPageStartLine) <= nextPageStartY) {
+ return null;
+ }
+ final int nextPageEndY = nextPageStartY + pageHeight;
+ nextPageEndLine = mLayout.getLineForVertical(nextPageEndY);
+ }
+
+ final int start = getLineEdgeIndex(nextPageStartLine, DIRECTION_START);
+ final int end = getLineEdgeIndex(nextPageEndLine, DIRECTION_END) + 1;
+
+ return getRange(start, end);
+ }
+
+ @Override
+ public int[] preceding(int offset) {
+ final int textLegth = mText.length();
+ if (textLegth <= 0) {
+ return null;
+ }
+ if (offset <= 0) {
+ return null;
+ }
+ if (!mView.getGlobalVisibleRect(mTempRect)) {
+ return null;
+ }
+
+ final int currentLine = mLayout.getLineForOffset(offset);
+ final int currentLineTop = mLayout.getLineTop(currentLine);
+ final int pageHeight = mTempRect.height() - mView.getTotalPaddingTop()
+ - mView.getTotalPaddingBottom();
+
+ final int previousPageStartLine;
+ final int previousPageEndLine;
+ if (offset > mText.length()) {
+ final int prevousPageStartY = mLayout.getHeight() - pageHeight;
+ if (prevousPageStartY < 0) {
+ return null;
+ }
+ previousPageStartLine = mLayout.getLineForVertical(prevousPageStartY);
+ previousPageEndLine = mLayout.getLineCount() - 1;
+ } else {
+ final int prevousPageStartY;
+ if (offset == mText.length()) {
+ prevousPageStartY = mLayout.getHeight() - 2 * pageHeight;
+ } else {
+ prevousPageStartY = currentLineTop - 2 * pageHeight;
+ }
+ if (prevousPageStartY < 0) {
+ return null;
+ }
+ previousPageStartLine = mLayout.getLineForVertical(prevousPageStartY);
+ final int previousPageEndY = prevousPageStartY + pageHeight;
+ previousPageEndLine = mLayout.getLineForVertical(previousPageEndY) - 1;
+ }
+
+ final int start = getLineEdgeIndex(previousPageStartLine, DIRECTION_START);
+ final int end = getLineEdgeIndex(previousPageEndLine, DIRECTION_END) + 1;
+
+ return getRange(start, end);
+ }
+ }
+}
diff --git a/core/java/android/widget/ActivityChooserModel.java b/core/java/android/widget/ActivityChooserModel.java
index fba8d3a..c6104bc 100644
--- a/core/java/android/widget/ActivityChooserModel.java
+++ b/core/java/android/widget/ActivityChooserModel.java
@@ -23,7 +23,6 @@ import android.content.pm.ResolveInfo;
import android.database.DataSetObservable;
import android.database.DataSetObserver;
import android.os.AsyncTask;
-import android.os.Handler;
import android.text.TextUtils;
import android.util.Log;
import android.util.Xml;
@@ -42,10 +41,8 @@ import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
-import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
-import java.util.Set;
/**
* <p>
@@ -239,7 +236,7 @@ public class ActivityChooserModel extends DataSetObservable {
/**
* List of activities that can handle the current intent.
*/
- private final List<ActivityResolveInfo> mActivites = new ArrayList<ActivityResolveInfo>();
+ private final List<ActivityResolveInfo> mActivities = new ArrayList<ActivityResolveInfo>();
/**
* List with historical choice records.
@@ -278,18 +275,18 @@ public class ActivityChooserModel extends DataSetObservable {
/**
* Flag whether choice history can be read. In general many clients can
- * share the same data model and {@link #readHistoricalData()} may be called
+ * share the same data model and {@link #readHistoricalDataIfNeeded()} may be called
* by arbitrary of them any number of times. Therefore, this class guarantees
* that the very first read succeeds and subsequent reads can be performed
- * only after a call to {@link #persistHistoricalData()} followed by change
+ * only after a call to {@link #persistHistoricalDataIfNeeded()} followed by change
* of the share records.
*/
private boolean mCanReadHistoricalData = true;
/**
* Flag whether the choice history was read. This is used to enforce that
- * before calling {@link #persistHistoricalData()} a call to
- * {@link #persistHistoricalData()} has been made. This aims to avoid a
+ * before calling {@link #persistHistoricalDataIfNeeded()} a call to
+ * {@link #persistHistoricalDataIfNeeded()} has been made. This aims to avoid a
* scenario in which a choice history file exits, it is not read yet and
* it is overwritten. Note that always all historical records are read in
* full and the file is rewritten. This is necessary since we need to
@@ -299,16 +296,16 @@ public class ActivityChooserModel extends DataSetObservable {
/**
* Flag whether the choice records have changed. In general many clients can
- * share the same data model and {@link #persistHistoricalData()} may be called
+ * share the same data model and {@link #persistHistoricalDataIfNeeded()} may be called
* by arbitrary of them any number of times. Therefore, this class guarantees
* that choice history will be persisted only if it has changed.
*/
private boolean mHistoricalRecordsChanged = true;
/**
- * Hander for scheduling work on client tread.
+ * Flag whether to reload the activities for the current intent.
*/
- private final Handler mHandler = new Handler();
+ private boolean mReloadActivities = false;
/**
* Policy for controlling how the model handles chosen activities.
@@ -346,7 +343,6 @@ public class ActivityChooserModel extends DataSetObservable {
dataModel = new ActivityChooserModel(context, historyFileName);
sDataModelRegistry.put(historyFileName, dataModel);
}
- dataModel.readHistoricalData();
return dataModel;
}
}
@@ -383,7 +379,8 @@ public class ActivityChooserModel extends DataSetObservable {
return;
}
mIntent = intent;
- loadActivitiesLocked();
+ mReloadActivities = true;
+ ensureConsistentState();
}
}
@@ -407,7 +404,8 @@ public class ActivityChooserModel extends DataSetObservable {
*/
public int getActivityCount() {
synchronized (mInstanceLock) {
- return mActivites.size();
+ ensureConsistentState();
+ return mActivities.size();
}
}
@@ -421,7 +419,8 @@ public class ActivityChooserModel extends DataSetObservable {
*/
public ResolveInfo getActivity(int index) {
synchronized (mInstanceLock) {
- return mActivites.get(index).resolveInfo;
+ ensureConsistentState();
+ return mActivities.get(index).resolveInfo;
}
}
@@ -433,15 +432,18 @@ public class ActivityChooserModel extends DataSetObservable {
* @return The index if found, -1 otherwise.
*/
public int getActivityIndex(ResolveInfo activity) {
- List<ActivityResolveInfo> activities = mActivites;
- final int activityCount = activities.size();
- for (int i = 0; i < activityCount; i++) {
- ActivityResolveInfo currentActivity = activities.get(i);
- if (currentActivity.resolveInfo == activity) {
- return i;
+ synchronized (mInstanceLock) {
+ ensureConsistentState();
+ List<ActivityResolveInfo> activities = mActivities;
+ final int activityCount = activities.size();
+ for (int i = 0; i < activityCount; i++) {
+ ActivityResolveInfo currentActivity = activities.get(i);
+ if (currentActivity.resolveInfo == activity) {
+ return i;
+ }
}
+ return INVALID_INDEX;
}
- return INVALID_INDEX;
}
/**
@@ -462,30 +464,34 @@ public class ActivityChooserModel extends DataSetObservable {
* @see OnChooseActivityListener
*/
public Intent chooseActivity(int index) {
- ActivityResolveInfo chosenActivity = mActivites.get(index);
+ synchronized (mInstanceLock) {
+ ensureConsistentState();
- ComponentName chosenName = new ComponentName(
- chosenActivity.resolveInfo.activityInfo.packageName,
- chosenActivity.resolveInfo.activityInfo.name);
+ ActivityResolveInfo chosenActivity = mActivities.get(index);
- Intent choiceIntent = new Intent(mIntent);
- choiceIntent.setComponent(chosenName);
+ ComponentName chosenName = new ComponentName(
+ chosenActivity.resolveInfo.activityInfo.packageName,
+ chosenActivity.resolveInfo.activityInfo.name);
- if (mActivityChoserModelPolicy != null) {
- // Do not allow the policy to change the intent.
- Intent choiceIntentCopy = new Intent(choiceIntent);
- final boolean handled = mActivityChoserModelPolicy.onChooseActivity(this,
- choiceIntentCopy);
- if (handled) {
- return null;
+ Intent choiceIntent = new Intent(mIntent);
+ choiceIntent.setComponent(chosenName);
+
+ if (mActivityChoserModelPolicy != null) {
+ // Do not allow the policy to change the intent.
+ Intent choiceIntentCopy = new Intent(choiceIntent);
+ final boolean handled = mActivityChoserModelPolicy.onChooseActivity(this,
+ choiceIntentCopy);
+ if (handled) {
+ return null;
+ }
}
- }
- HistoricalRecord historicalRecord = new HistoricalRecord(chosenName,
- System.currentTimeMillis(), DEFAULT_HISTORICAL_RECORD_WEIGHT);
- addHisoricalRecord(historicalRecord);
+ HistoricalRecord historicalRecord = new HistoricalRecord(chosenName,
+ System.currentTimeMillis(), DEFAULT_HISTORICAL_RECORD_WEIGHT);
+ addHisoricalRecord(historicalRecord);
- return choiceIntent;
+ return choiceIntent;
+ }
}
/**
@@ -494,7 +500,9 @@ public class ActivityChooserModel extends DataSetObservable {
* @param listener The listener.
*/
public void setOnChooseActivityListener(OnChooseActivityListener listener) {
- mActivityChoserModelPolicy = listener;
+ synchronized (mInstanceLock) {
+ mActivityChoserModelPolicy = listener;
+ }
}
/**
@@ -508,8 +516,9 @@ public class ActivityChooserModel extends DataSetObservable {
*/
public ResolveInfo getDefaultActivity() {
synchronized (mInstanceLock) {
- if (!mActivites.isEmpty()) {
- return mActivites.get(0).resolveInfo;
+ ensureConsistentState();
+ if (!mActivities.isEmpty()) {
+ return mActivities.get(0).resolveInfo;
}
}
return null;
@@ -526,72 +535,50 @@ public class ActivityChooserModel extends DataSetObservable {
* @param index The index of the activity to set as default.
*/
public void setDefaultActivity(int index) {
- ActivityResolveInfo newDefaultActivity = mActivites.get(index);
- ActivityResolveInfo oldDefaultActivity = mActivites.get(0);
-
- final float weight;
- if (oldDefaultActivity != null) {
- // Add a record with weight enough to boost the chosen at the top.
- weight = oldDefaultActivity.weight - newDefaultActivity.weight
- + DEFAULT_ACTIVITY_INFLATION;
- } else {
- weight = DEFAULT_HISTORICAL_RECORD_WEIGHT;
- }
-
- ComponentName defaultName = new ComponentName(
- newDefaultActivity.resolveInfo.activityInfo.packageName,
- newDefaultActivity.resolveInfo.activityInfo.name);
- HistoricalRecord historicalRecord = new HistoricalRecord(defaultName,
- System.currentTimeMillis(), weight);
- addHisoricalRecord(historicalRecord);
- }
-
- /**
- * Reads the history data from the backing file if the latter
- * was provided. Calling this method more than once before a call
- * to {@link #persistHistoricalData()} has been made has no effect.
- * <p>
- * <strong>Note:</strong> Historical data is read asynchronously and
- * as soon as the reading is completed any registered
- * {@link DataSetObserver}s will be notified. Also no historical
- * data is read until this method is invoked.
- * <p>
- */
- private void readHistoricalData() {
synchronized (mInstanceLock) {
- if (!mCanReadHistoricalData || !mHistoricalRecordsChanged) {
- return;
- }
- mCanReadHistoricalData = false;
- mReadShareHistoryCalled = true;
- if (!TextUtils.isEmpty(mHistoryFileName)) {
- AsyncTask.SERIAL_EXECUTOR.execute(new HistoryLoader());
+ ensureConsistentState();
+
+ ActivityResolveInfo newDefaultActivity = mActivities.get(index);
+ ActivityResolveInfo oldDefaultActivity = mActivities.get(0);
+
+ final float weight;
+ if (oldDefaultActivity != null) {
+ // Add a record with weight enough to boost the chosen at the top.
+ weight = oldDefaultActivity.weight - newDefaultActivity.weight
+ + DEFAULT_ACTIVITY_INFLATION;
+ } else {
+ weight = DEFAULT_HISTORICAL_RECORD_WEIGHT;
}
+
+ ComponentName defaultName = new ComponentName(
+ newDefaultActivity.resolveInfo.activityInfo.packageName,
+ newDefaultActivity.resolveInfo.activityInfo.name);
+ HistoricalRecord historicalRecord = new HistoricalRecord(defaultName,
+ System.currentTimeMillis(), weight);
+ addHisoricalRecord(historicalRecord);
}
}
/**
* Persists the history data to the backing file if the latter
- * was provided. Calling this method before a call to {@link #readHistoricalData()}
+ * was provided. Calling this method before a call to {@link #readHistoricalDataIfNeeded()}
* throws an exception. Calling this method more than one without choosing an
* activity has not effect.
*
* @throws IllegalStateException If this method is called before a call to
- * {@link #readHistoricalData()}.
+ * {@link #readHistoricalDataIfNeeded()}.
*/
- private void persistHistoricalData() {
- synchronized (mInstanceLock) {
- if (!mReadShareHistoryCalled) {
- throw new IllegalStateException("No preceding call to #readHistoricalData");
- }
- if (!mHistoricalRecordsChanged) {
- return;
- }
- mHistoricalRecordsChanged = false;
- mCanReadHistoricalData = true;
- if (!TextUtils.isEmpty(mHistoryFileName)) {
- AsyncTask.SERIAL_EXECUTOR.execute(new HistoryPersister());
- }
+ private void persistHistoricalDataIfNeeded() {
+ if (!mReadShareHistoryCalled) {
+ throw new IllegalStateException("No preceding call to #readHistoricalData");
+ }
+ if (!mHistoricalRecordsChanged) {
+ return;
+ }
+ mHistoricalRecordsChanged = false;
+ if (!TextUtils.isEmpty(mHistoryFileName)) {
+ new PersistHistoryAsyncTask().executeOnExecutor(AsyncTask.SERIAL_EXECUTOR,
+ new ArrayList<HistoricalRecord>(mHistoricalRecords), mHistoryFileName);
}
}
@@ -608,21 +595,7 @@ public class ActivityChooserModel extends DataSetObservable {
return;
}
mActivitySorter = activitySorter;
- sortActivities();
- }
- }
-
- /**
- * Sorts the activities based on history and an intent. If
- * a sorter is not specified this a default implementation is used.
- *
- * @see #setActivitySorter(ActivitySorter)
- */
- private void sortActivities() {
- synchronized (mInstanceLock) {
- if (mActivitySorter != null && !mActivites.isEmpty()) {
- mActivitySorter.sort(mIntent, mActivites,
- Collections.unmodifiableList(mHistoricalRecords));
+ if (sortActivitiesIfNeeded()) {
notifyChanged();
}
}
@@ -647,8 +620,10 @@ public class ActivityChooserModel extends DataSetObservable {
return;
}
mHistoryMaxSize = historyMaxSize;
- pruneExcessiveHistoricalRecordsLocked();
- sortActivities();
+ pruneExcessiveHistoricalRecordsIfNeeded();
+ if (sortActivitiesIfNeeded()) {
+ notifyChanged();
+ }
}
}
@@ -670,6 +645,7 @@ public class ActivityChooserModel extends DataSetObservable {
*/
public int getHistorySize() {
synchronized (mInstanceLock) {
+ ensureConsistentState();
return mHistoricalRecords.size();
}
}
@@ -681,82 +657,110 @@ public class ActivityChooserModel extends DataSetObservable {
}
/**
- * Adds a historical record.
- *
- * @param historicalRecord The record to add.
- * @return True if the record was added.
+ * Ensures the model is in a consistent state which is the
+ * activities for the current intent have been loaded, the
+ * most recent history has been read, and the activities
+ * are sorted.
*/
- private boolean addHisoricalRecord(HistoricalRecord historicalRecord) {
- synchronized (mInstanceLock) {
- final boolean added = mHistoricalRecords.add(historicalRecord);
- if (added) {
- mHistoricalRecordsChanged = true;
- pruneExcessiveHistoricalRecordsLocked();
- persistHistoricalData();
- sortActivities();
- }
- return added;
+ private void ensureConsistentState() {
+ boolean stateChanged = loadActivitiesIfNeeded();
+ stateChanged |= readHistoricalDataIfNeeded();
+ pruneExcessiveHistoricalRecordsIfNeeded();
+ if (stateChanged) {
+ sortActivitiesIfNeeded();
+ notifyChanged();
}
}
/**
- * Prunes older excessive records to guarantee {@link #mHistoryMaxSize}.
+ * Sorts the activities if necessary which is if there is a
+ * sorter, there are some activities to sort, and there is some
+ * historical data.
+ *
+ * @return Whether sorting was performed.
*/
- private void pruneExcessiveHistoricalRecordsLocked() {
- List<HistoricalRecord> choiceRecords = mHistoricalRecords;
- final int pruneCount = choiceRecords.size() - mHistoryMaxSize;
- if (pruneCount <= 0) {
- return;
- }
- mHistoricalRecordsChanged = true;
- for (int i = 0; i < pruneCount; i++) {
- HistoricalRecord prunedRecord = choiceRecords.remove(0);
- if (DEBUG) {
- Log.i(LOG_TAG, "Pruned: " + prunedRecord);
- }
+ private boolean sortActivitiesIfNeeded() {
+ if (mActivitySorter != null && mIntent != null
+ && !mActivities.isEmpty() && !mHistoricalRecords.isEmpty()) {
+ mActivitySorter.sort(mIntent, mActivities,
+ Collections.unmodifiableList(mHistoricalRecords));
+ return true;
}
+ return false;
}
/**
- * Loads the activities.
+ * Loads the activities for the current intent if needed which is
+ * if they are not already loaded for the current intent.
+ *
+ * @return Whether loading was performed.
*/
- private void loadActivitiesLocked() {
- mActivites.clear();
- if (mIntent != null) {
- List<ResolveInfo> resolveInfos =
- mContext.getPackageManager().queryIntentActivities(mIntent, 0);
+ private boolean loadActivitiesIfNeeded() {
+ if (mReloadActivities && mIntent != null) {
+ mReloadActivities = false;
+ mActivities.clear();
+ List<ResolveInfo> resolveInfos = mContext.getPackageManager()
+ .queryIntentActivities(mIntent, 0);
final int resolveInfoCount = resolveInfos.size();
for (int i = 0; i < resolveInfoCount; i++) {
ResolveInfo resolveInfo = resolveInfos.get(i);
- mActivites.add(new ActivityResolveInfo(resolveInfo));
+ mActivities.add(new ActivityResolveInfo(resolveInfo));
}
- sortActivities();
- } else {
- notifyChanged();
+ return true;
}
+ return false;
}
/**
- * Prunes historical records for a package that goes away.
+ * Reads the historical data if necessary which is it has
+ * changed, there is a history file, and there is not persist
+ * in progress.
*
- * @param packageName The name of the package that goes away.
+ * @return Whether reading was performed.
*/
- private void pruneHistoricalRecordsForPackageLocked(String packageName) {
- boolean recordsRemoved = false;
-
- List<HistoricalRecord> historicalRecords = mHistoricalRecords;
- for (int i = 0; i < historicalRecords.size(); i++) {
- HistoricalRecord historicalRecord = historicalRecords.get(i);
- String recordPackageName = historicalRecord.activity.getPackageName();
- if (recordPackageName.equals(packageName)) {
- historicalRecords.remove(historicalRecord);
- recordsRemoved = true;
- }
+ private boolean readHistoricalDataIfNeeded() {
+ if (mCanReadHistoricalData && mHistoricalRecordsChanged &&
+ !TextUtils.isEmpty(mHistoryFileName)) {
+ mCanReadHistoricalData = false;
+ mReadShareHistoryCalled = true;
+ readHistoricalDataImpl();
+ return true;
}
+ return false;
+ }
- if (recordsRemoved) {
+ /**
+ * Adds a historical record.
+ *
+ * @param historicalRecord The record to add.
+ * @return True if the record was added.
+ */
+ private boolean addHisoricalRecord(HistoricalRecord historicalRecord) {
+ final boolean added = mHistoricalRecords.add(historicalRecord);
+ if (added) {
mHistoricalRecordsChanged = true;
- sortActivities();
+ pruneExcessiveHistoricalRecordsIfNeeded();
+ persistHistoricalDataIfNeeded();
+ sortActivitiesIfNeeded();
+ notifyChanged();
+ }
+ return added;
+ }
+
+ /**
+ * Prunes older excessive records to guarantee maxHistorySize.
+ */
+ private void pruneExcessiveHistoricalRecordsIfNeeded() {
+ final int pruneCount = mHistoricalRecords.size() - mHistoryMaxSize;
+ if (pruneCount <= 0) {
+ return;
+ }
+ mHistoricalRecordsChanged = true;
+ for (int i = 0; i < pruneCount; i++) {
+ HistoricalRecord prunedRecord = mHistoricalRecords.remove(0);
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Pruned: " + prunedRecord);
+ }
}
}
@@ -974,112 +978,72 @@ public class ActivityChooserModel extends DataSetObservable {
/**
* Command for reading the historical records from a file off the UI thread.
*/
- private final class HistoryLoader implements Runnable {
-
- public void run() {
- FileInputStream fis = null;
- try {
- fis = mContext.openFileInput(mHistoryFileName);
- } catch (FileNotFoundException fnfe) {
- if (DEBUG) {
- Log.i(LOG_TAG, "Could not open historical records file: " + mHistoryFileName);
- }
- return;
+ private void readHistoricalDataImpl() {
+ FileInputStream fis = null;
+ try {
+ fis = mContext.openFileInput(mHistoryFileName);
+ } catch (FileNotFoundException fnfe) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Could not open historical records file: " + mHistoryFileName);
}
- try {
- XmlPullParser parser = Xml.newPullParser();
- parser.setInput(fis, null);
-
- int type = XmlPullParser.START_DOCUMENT;
- while (type != XmlPullParser.END_DOCUMENT && type != XmlPullParser.START_TAG) {
- type = parser.next();
- }
-
- if (!TAG_HISTORICAL_RECORDS.equals(parser.getName())) {
- throw new XmlPullParserException("Share records file does not start with "
- + TAG_HISTORICAL_RECORDS + " tag.");
- }
-
- List<HistoricalRecord> readRecords = new ArrayList<HistoricalRecord>();
+ return;
+ }
+ try {
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(fis, null);
- while (true) {
- type = parser.next();
- if (type == XmlPullParser.END_DOCUMENT) {
- break;
- }
- if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
- continue;
- }
- String nodeName = parser.getName();
- if (!TAG_HISTORICAL_RECORD.equals(nodeName)) {
- throw new XmlPullParserException("Share records file not well-formed.");
- }
+ int type = XmlPullParser.START_DOCUMENT;
+ while (type != XmlPullParser.END_DOCUMENT && type != XmlPullParser.START_TAG) {
+ type = parser.next();
+ }
- String activity = parser.getAttributeValue(null, ATTRIBUTE_ACTIVITY);
- final long time =
- Long.parseLong(parser.getAttributeValue(null, ATTRIBUTE_TIME));
- final float weight =
- Float.parseFloat(parser.getAttributeValue(null, ATTRIBUTE_WEIGHT));
+ if (!TAG_HISTORICAL_RECORDS.equals(parser.getName())) {
+ throw new XmlPullParserException("Share records file does not start with "
+ + TAG_HISTORICAL_RECORDS + " tag.");
+ }
- HistoricalRecord readRecord = new HistoricalRecord(activity, time,
- weight);
- readRecords.add(readRecord);
+ List<HistoricalRecord> historicalRecords = mHistoricalRecords;
+ historicalRecords.clear();
- if (DEBUG) {
- Log.i(LOG_TAG, "Read " + readRecord.toString());
- }
+ while (true) {
+ type = parser.next();
+ if (type == XmlPullParser.END_DOCUMENT) {
+ break;
}
-
- if (DEBUG) {
- Log.i(LOG_TAG, "Read " + readRecords.size() + " historical records.");
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+ String nodeName = parser.getName();
+ if (!TAG_HISTORICAL_RECORD.equals(nodeName)) {
+ throw new XmlPullParserException("Share records file not well-formed.");
}
- synchronized (mInstanceLock) {
- Set<HistoricalRecord> uniqueShareRecords =
- new LinkedHashSet<HistoricalRecord>(readRecords);
-
- // Make sure no duplicates. Example: Read a file with
- // one record, add one record, persist the two records,
- // add a record, read the persisted records - the
- // read two records should not be added again.
- List<HistoricalRecord> historicalRecords = mHistoricalRecords;
- final int historicalRecordsCount = historicalRecords.size();
- for (int i = historicalRecordsCount - 1; i >= 0; i--) {
- HistoricalRecord historicalRecord = historicalRecords.get(i);
- uniqueShareRecords.add(historicalRecord);
- }
-
- if (historicalRecords.size() == uniqueShareRecords.size()) {
- return;
- }
+ String activity = parser.getAttributeValue(null, ATTRIBUTE_ACTIVITY);
+ final long time =
+ Long.parseLong(parser.getAttributeValue(null, ATTRIBUTE_TIME));
+ final float weight =
+ Float.parseFloat(parser.getAttributeValue(null, ATTRIBUTE_WEIGHT));
+ HistoricalRecord readRecord = new HistoricalRecord(activity, time, weight);
+ historicalRecords.add(readRecord);
- // Make sure the oldest records go to the end.
- historicalRecords.clear();
- historicalRecords.addAll(uniqueShareRecords);
-
- mHistoricalRecordsChanged = true;
-
- // Do this on the client thread since the client may be on the UI
- // thread, wait for data changes which happen during sorting, and
- // perform UI modification based on the data change.
- mHandler.post(new Runnable() {
- public void run() {
- pruneExcessiveHistoricalRecordsLocked();
- sortActivities();
- }
- });
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Read " + readRecord.toString());
}
- } catch (XmlPullParserException xppe) {
- Log.e(LOG_TAG, "Error reading historical recrod file: " + mHistoryFileName, xppe);
- } catch (IOException ioe) {
- Log.e(LOG_TAG, "Error reading historical recrod file: " + mHistoryFileName, ioe);
- } finally {
- if (fis != null) {
- try {
- fis.close();
- } catch (IOException ioe) {
- /* ignore */
- }
+ }
+
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Read " + historicalRecords.size() + " historical records.");
+ }
+ } catch (XmlPullParserException xppe) {
+ Log.e(LOG_TAG, "Error reading historical recrod file: " + mHistoryFileName, xppe);
+ } catch (IOException ioe) {
+ Log.e(LOG_TAG, "Error reading historical recrod file: " + mHistoryFileName, ioe);
+ } finally {
+ if (fis != null) {
+ try {
+ fis.close();
+ } catch (IOException ioe) {
+ /* ignore */
}
}
}
@@ -1088,21 +1052,21 @@ public class ActivityChooserModel extends DataSetObservable {
/**
* Command for persisting the historical records to a file off the UI thread.
*/
- private final class HistoryPersister implements Runnable {
+ private final class PersistHistoryAsyncTask extends AsyncTask<Object, Void, Void> {
- public void run() {
- FileOutputStream fos = null;
- List<HistoricalRecord> records = null;
+ @Override
+ @SuppressWarnings("unchecked")
+ public Void doInBackground(Object... args) {
+ List<HistoricalRecord> historicalRecords = (List<HistoricalRecord>) args[0];
+ String hostoryFileName = (String) args[1];
- synchronized (mInstanceLock) {
- records = new ArrayList<HistoricalRecord>(mHistoricalRecords);
- }
+ FileOutputStream fos = null;
try {
- fos = mContext.openFileOutput(mHistoryFileName, Context.MODE_PRIVATE);
+ fos = mContext.openFileOutput(hostoryFileName, Context.MODE_PRIVATE);
} catch (FileNotFoundException fnfe) {
- Log.e(LOG_TAG, "Error writing historical recrod file: " + mHistoryFileName, fnfe);
- return;
+ Log.e(LOG_TAG, "Error writing historical recrod file: " + hostoryFileName, fnfe);
+ return null;
}
XmlSerializer serializer = Xml.newSerializer();
@@ -1112,11 +1076,12 @@ public class ActivityChooserModel extends DataSetObservable {
serializer.startDocument("UTF-8", true);
serializer.startTag(null, TAG_HISTORICAL_RECORDS);
- final int recordCount = records.size();
+ final int recordCount = historicalRecords.size();
for (int i = 0; i < recordCount; i++) {
- HistoricalRecord record = records.remove(0);
+ HistoricalRecord record = historicalRecords.remove(0);
serializer.startTag(null, TAG_HISTORICAL_RECORD);
- serializer.attribute(null, ATTRIBUTE_ACTIVITY, record.activity.flattenToString());
+ serializer.attribute(null, ATTRIBUTE_ACTIVITY,
+ record.activity.flattenToString());
serializer.attribute(null, ATTRIBUTE_TIME, String.valueOf(record.time));
serializer.attribute(null, ATTRIBUTE_WEIGHT, String.valueOf(record.weight));
serializer.endTag(null, TAG_HISTORICAL_RECORD);
@@ -1138,6 +1103,7 @@ public class ActivityChooserModel extends DataSetObservable {
} catch (IOException ioe) {
Log.e(LOG_TAG, "Error writing historical recrod file: " + mHistoryFileName, ioe);
} finally {
+ mCanReadHistoricalData = true;
if (fos != null) {
try {
fos.close();
@@ -1146,6 +1112,7 @@ public class ActivityChooserModel extends DataSetObservable {
}
}
}
+ return null;
}
}
@@ -1155,33 +1122,8 @@ public class ActivityChooserModel extends DataSetObservable {
private final class DataModelPackageMonitor extends PackageMonitor {
@Override
- public void onPackageAdded(String packageName, int uid) {
- synchronized (mInstanceLock) {
- loadActivitiesLocked();
- }
- }
-
- @Override
- public void onPackageAppeared(String packageName, int reason) {
- synchronized (mInstanceLock) {
- loadActivitiesLocked();
- }
- }
-
- @Override
- public void onPackageRemoved(String packageName, int uid) {
- synchronized (mInstanceLock) {
- pruneHistoricalRecordsForPackageLocked(packageName);
- loadActivitiesLocked();
- }
- }
-
- @Override
- public void onPackageDisappeared(String packageName, int reason) {
- synchronized (mInstanceLock) {
- pruneHistoricalRecordsForPackageLocked(packageName);
- loadActivitiesLocked();
- }
+ public void onSomePackagesChanged() {
+ mReloadActivities = true;
}
}
}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 8c81343..f2334ae 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -26,7 +26,6 @@ import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.graphics.Canvas;
-import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
@@ -91,6 +90,7 @@ import android.util.AttributeSet;
import android.util.FloatMath;
import android.util.Log;
import android.util.TypedValue;
+import android.view.AccessibilityIterators.TextSegmentIterator;
import android.view.ActionMode;
import android.view.DragEvent;
import android.view.Gravity;
@@ -7712,6 +7712,17 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
if (!isPassword) {
info.setText(getTextForAccessibility());
}
+
+ if (TextUtils.isEmpty(getContentDescription())
+ && !TextUtils.isEmpty(mText)) {
+ info.addAction(AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY);
+ info.addAction(AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY);
+ info.setMovementGranularities(AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER
+ | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD
+ | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE
+ | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH
+ | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE);
+ }
}
@Override
@@ -7726,12 +7737,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
/**
- * Gets the text reported for accessibility purposes. It is the
- * text if not empty or the hint.
+ * Gets the text reported for accessibility purposes.
*
* @return The accessibility text.
+ *
+ * @hide
*/
- private CharSequence getTextForAccessibility() {
+ public CharSequence getTextForAccessibility() {
CharSequence text = getText();
if (TextUtils.isEmpty(text)) {
text = getHint();
@@ -8287,6 +8299,79 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
/**
+ * @hide
+ */
+ @Override
+ public CharSequence getIterableTextForAccessibility() {
+ if (getContentDescription() == null) {
+ if (!(mText instanceof Spannable)) {
+ setText(mText, BufferType.SPANNABLE);
+ }
+ return mText;
+ }
+ return super.getIterableTextForAccessibility();
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public TextSegmentIterator getIteratorForGranularity(int granularity) {
+ switch (granularity) {
+ case AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE: {
+ Spannable text = (Spannable) getIterableTextForAccessibility();
+ if (!TextUtils.isEmpty(text) && getLayout() != null) {
+ AccessibilityIterators.LineTextSegmentIterator iterator =
+ AccessibilityIterators.LineTextSegmentIterator.getInstance();
+ iterator.initialize(text, getLayout());
+ return iterator;
+ }
+ } break;
+ case AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE: {
+ Spannable text = (Spannable) getIterableTextForAccessibility();
+ if (!TextUtils.isEmpty(text) && getLayout() != null) {
+ AccessibilityIterators.PageTextSegmentIterator iterator =
+ AccessibilityIterators.PageTextSegmentIterator.getInstance();
+ iterator.initialize(this);
+ return iterator;
+ }
+ } break;
+ }
+ return super.getIteratorForGranularity(granularity);
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public int getAccessibilityCursorPosition() {
+ if (TextUtils.isEmpty(getContentDescription())) {
+ return getSelectionEnd();
+ } else {
+ return super.getAccessibilityCursorPosition();
+ }
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public void setAccessibilityCursorPosition(int index) {
+ if (getAccessibilityCursorPosition() == index) {
+ return;
+ }
+ if (TextUtils.isEmpty(getContentDescription())) {
+ if (index >= 0) {
+ Selection.setSelection((Spannable) mText, index);
+ } else {
+ Selection.removeSelection((Spannable) mText);
+ }
+ } else {
+ super.setAccessibilityCursorPosition(index);
+ }
+ }
+
+ /**
* User interface state that is stored by TextView for implementing
* {@link View#onSaveInstanceState}.
*/
diff --git a/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java b/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java
index 624dea8..a74ecd3 100644
--- a/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java
+++ b/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java
@@ -32,6 +32,7 @@ import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
+import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
@@ -99,8 +100,11 @@ public class MultiWaveView extends View {
private float mTapRadius;
private float mWaveCenterX;
private float mWaveCenterY;
- private float mVerticalOffset;
+ private int mMaxTargetHeight;
+ private int mMaxTargetWidth;
private float mHorizontalOffset;
+ private float mVerticalOffset;
+
private float mOuterRadius = 0.0f;
private float mHitRadius = 0.0f;
private float mSnapMargin = 0.0f;
@@ -142,6 +146,9 @@ public class MultiWaveView extends View {
private int mTargetDescriptionsResourceId;
private int mDirectionDescriptionsResourceId;
private boolean mAlwaysTrackFinger;
+ private int mHorizontalInset;
+ private int mVerticalInset;
+ private int mGravity = Gravity.TOP;
public MultiWaveView(Context context) {
this(context, null);
@@ -153,10 +160,9 @@ public class MultiWaveView extends View {
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MultiWaveView);
mOuterRadius = a.getDimension(R.styleable.MultiWaveView_outerRadius, mOuterRadius);
- mHorizontalOffset = a.getDimension(R.styleable.MultiWaveView_horizontalOffset,
- mHorizontalOffset);
- mVerticalOffset = a.getDimension(R.styleable.MultiWaveView_verticalOffset,
- mVerticalOffset);
+// mHorizontalOffset = a.getDimension(R.styleable.MultiWaveView_horizontalOffset,
+// mHorizontalOffset);
+// mVerticalOffset = a.getDimension(R.styleable.MultiWaveView_verticalOffset, mVerticalOffset);
mHitRadius = a.getDimension(R.styleable.MultiWaveView_hitRadius, mHitRadius);
mSnapMargin = a.getDimension(R.styleable.MultiWaveView_snapMargin, mSnapMargin);
mVibrationDuration = a.getInt(R.styleable.MultiWaveView_vibrationDuration,
@@ -169,6 +175,7 @@ public class MultiWaveView extends View {
mOuterRing = new TargetDrawable(res,
a.peekValue(R.styleable.MultiWaveView_waveDrawable).resourceId);
mAlwaysTrackFinger = a.getBoolean(R.styleable.MultiWaveView_alwaysTrackFinger, false);
+ mGravity = a.getInt(R.styleable.MultiWaveView_gravity, Gravity.TOP);
// Read chevron animation drawables
final int chevrons[] = { R.styleable.MultiWaveView_leftChevronDrawable,
@@ -231,16 +238,16 @@ public class MultiWaveView extends View {
@Override
protected int getSuggestedMinimumWidth() {
- // View should be large enough to contain the background + target drawable on either edge
- return mOuterRing.getWidth()
- + (mTargetDrawables.size() > 0 ? (mTargetDrawables.get(0).getWidth()/2) : 0);
+ // View should be large enough to contain the background + handle and
+ // target drawable on either edge.
+ return mOuterRing.getWidth() + mMaxTargetWidth;
}
@Override
protected int getSuggestedMinimumHeight() {
- // View should be large enough to contain the unlock ring + target drawable on either edge
- return mOuterRing.getHeight()
- + (mTargetDrawables.size() > 0 ? (mTargetDrawables.get(0).getHeight()/2) : 0);
+ // View should be large enough to contain the unlock ring + target and
+ // target drawable on either edge
+ return mOuterRing.getHeight() + mMaxTargetHeight;
}
private int resolveMeasured(int measureSpec, int desired)
@@ -265,9 +272,10 @@ public class MultiWaveView extends View {
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int minimumWidth = getSuggestedMinimumWidth();
final int minimumHeight = getSuggestedMinimumHeight();
- int viewWidth = resolveMeasured(widthMeasureSpec, minimumWidth);
- int viewHeight = resolveMeasured(heightMeasureSpec, minimumHeight);
- setMeasuredDimension(viewWidth, viewHeight);
+ int computedWidth = resolveMeasured(widthMeasureSpec, minimumWidth);
+ int computedHeight = resolveMeasured(heightMeasureSpec, minimumHeight);
+ setupGravity((computedWidth - minimumWidth), (computedHeight - minimumHeight));
+ setMeasuredDimension(computedWidth, computedHeight);
}
private void switchToState(int state, float x, float y) {
@@ -521,14 +529,25 @@ public class MultiWaveView extends View {
TypedArray array = res.obtainTypedArray(resourceId);
int count = array.length();
ArrayList<TargetDrawable> targetDrawables = new ArrayList<TargetDrawable>(count);
+ int maxWidth = mHandleDrawable.getWidth();
+ int maxHeight = mHandleDrawable.getHeight();
for (int i = 0; i < count; i++) {
TypedValue value = array.peekValue(i);
- targetDrawables.add(new TargetDrawable(res, value != null ? value.resourceId : 0));
+ TargetDrawable target= new TargetDrawable(res, value != null ? value.resourceId : 0);
+ targetDrawables.add(target);
+ maxWidth = Math.max(maxWidth, target.getWidth());
+ maxHeight = Math.max(maxHeight, target.getHeight());
}
- array.recycle();
mTargetResourceId = resourceId;
mTargetDrawables = targetDrawables;
- updateTargetPositions();
+ if (mMaxTargetWidth != maxWidth || mMaxTargetHeight != maxHeight) {
+ mMaxTargetWidth = maxWidth;
+ mMaxTargetHeight = maxHeight;
+ requestLayout(); // required to resize layout and call updateTargetPositions()
+ } else {
+ updateTargetPositions();
+ }
+ array.recycle();
}
/**
@@ -638,23 +657,27 @@ public class MultiWaveView extends View {
boolean handled = false;
switch (action) {
case MotionEvent.ACTION_DOWN:
+ if (DEBUG) Log.v(TAG, "*** DOWN ***");
handleDown(event);
handled = true;
break;
case MotionEvent.ACTION_MOVE:
+ if (DEBUG) Log.v(TAG, "*** MOVE ***");
handleMove(event);
handled = true;
break;
case MotionEvent.ACTION_UP:
+ if (DEBUG) Log.v(TAG, "*** UP ***");
handleMove(event);
handleUp(event);
handled = true;
break;
case MotionEvent.ACTION_CANCEL:
- handleMove(event);
+ if (DEBUG) Log.v(TAG, "*** CANCEL ***");
+ // handleMove(event);
handleCancel(event);
handled = true;
break;
@@ -795,6 +818,11 @@ public class MultiWaveView extends View {
}
mGrabbedState = newState;
if (mOnTriggerListener != null) {
+ if (newState == OnTriggerListener.NO_HANDLE) {
+ mOnTriggerListener.onReleased(this, OnTriggerListener.CENTER_HANDLE);
+ } else {
+ mOnTriggerListener.onGrabbed(this, OnTriggerListener.CENTER_HANDLE);
+ }
mOnTriggerListener.onGrabbedStateChange(this, mGrabbedState);
}
}
@@ -832,13 +860,45 @@ public class MultiWaveView extends View {
moveHandleTo(centerX, centerY, false);
}
+ private void setupGravity(int dx, int dy) {
+ final int layoutDirection = getResolvedLayoutDirection();
+ final int absoluteGravity = Gravity.getAbsoluteGravity(mGravity, layoutDirection);
+
+ switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
+ case Gravity.LEFT:
+ mHorizontalInset = 0;
+ break;
+ case Gravity.RIGHT:
+ mHorizontalInset = dx;
+ break;
+ case Gravity.CENTER_HORIZONTAL:
+ default:
+ mHorizontalInset = dx / 2;
+ break;
+ }
+ switch (absoluteGravity & Gravity.VERTICAL_GRAVITY_MASK) {
+ case Gravity.TOP:
+ mVerticalInset = 0;
+ break;
+ case Gravity.BOTTOM:
+ mVerticalInset = dy;
+ break;
+ case Gravity.CENTER_VERTICAL:
+ default:
+ mVerticalInset = dy / 2;
+ break;
+ }
+ }
+
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
final int width = right - left;
final int height = bottom - top;
- float newWaveCenterX = mHorizontalOffset + Math.max(width, mOuterRing.getWidth() ) / 2;
- float newWaveCenterY = mVerticalOffset + Math.max(height, mOuterRing.getHeight()) / 2;
+ float newWaveCenterX = mHorizontalOffset + mHorizontalInset
+ + Math.max(width, mMaxTargetWidth + mOuterRing.getWidth()) / 2;
+ float newWaveCenterY = mVerticalOffset + mVerticalInset
+ + Math.max(height, + mMaxTargetHeight + mOuterRing.getHeight()) / 2;
if (newWaveCenterX != mWaveCenterX || newWaveCenterY != mWaveCenterY) {
if (mWaveCenterX == 0 && mWaveCenterY == 0) {
performInitialLayout(newWaveCenterX, newWaveCenterY);
@@ -848,9 +908,8 @@ public class MultiWaveView extends View {
mOuterRing.setX(mWaveCenterX);
mOuterRing.setY(Math.max(mWaveCenterY, mWaveCenterY));
-
- updateTargetPositions();
}
+ updateTargetPositions();
if (DEBUG) dump();
}
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 523b2d5..cd0959b 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -218,6 +218,7 @@ LOCAL_SHARED_LIBRARIES := \
libusbhost \
libharfbuzz \
libz \
+ libsuspend \
ifeq ($(USE_OPENGL_RENDERER),true)
LOCAL_SHARED_LIBRARIES += libhwui
diff --git a/core/jni/android/graphics/TextLayoutCache.cpp b/core/jni/android/graphics/TextLayoutCache.cpp
index 2c38893..16f377d 100644
--- a/core/jni/android/graphics/TextLayoutCache.cpp
+++ b/core/jni/android/graphics/TextLayoutCache.cpp
@@ -34,8 +34,9 @@ namespace android {
#define TYPE_FACE_HEBREW_REGULAR "/system/fonts/DroidSansHebrew-Regular.ttf"
#define TYPE_FACE_HEBREW_BOLD "/system/fonts/DroidSansHebrew-Bold.ttf"
#define TYPEFACE_BENGALI "/system/fonts/Lohit-Bengali.ttf"
-#define TYPEFACE_DEVANAGARI "/system/fonts/Lohit-Devanagari.ttf"
-#define TYPEFACE_TAMIL "/system/fonts/Lohit-Tamil.ttf"
+#define TYPEFACE_DEVANAGARI_REGULAR "/system/fonts/DroidSansDevanagari-Regular.ttf"
+#define TYPEFACE_TAMIL_REGULAR "/system/fonts/DroidSansTamil-Regular.ttf"
+#define TYPEFACE_TAMIL_BOLD "/system/fonts/DroidSansTamil-Bold.ttf"
#define TYPEFACE_THAI "/system/fonts/DroidSansThai.ttf"
ANDROID_SINGLETON_STATIC_INSTANCE(TextLayoutEngine);
@@ -337,8 +338,9 @@ TextLayoutShaper::TextLayoutShaper() : mShaperItemGlyphArraySize(0) {
mHebrewBoldTypeface = NULL;
mBengaliTypeface = NULL;
mThaiTypeface = NULL;
- mDevanagariTypeface = NULL;
- mTamilTypeface = NULL;
+ mDevanagariRegularTypeface = NULL;
+ mTamilRegularTypeface = NULL;
+ mTamilBoldTypeface = NULL;
mFontRec.klass = &harfbuzzSkiaClass;
mFontRec.userData = 0;
@@ -364,8 +366,9 @@ TextLayoutShaper::~TextLayoutShaper() {
SkSafeUnref(mHebrewBoldTypeface);
SkSafeUnref(mBengaliTypeface);
SkSafeUnref(mThaiTypeface);
- SkSafeUnref(mDevanagariTypeface);
- SkSafeUnref(mTamilTypeface);
+ SkSafeUnref(mDevanagariRegularTypeface);
+ SkSafeUnref(mTamilRegularTypeface);
+ SkSafeUnref(mTamilBoldTypeface);
deleteShaperItemGlyphArrays();
}
@@ -801,17 +804,38 @@ SkTypeface* TextLayoutShaper::typefaceForUnichar(const SkPaint* paint, SkTypefac
break;
case HB_Script_Devanagari:
- typeface = getCachedTypeface(&mDevanagariTypeface, TYPEFACE_DEVANAGARI);
+ typeface = getCachedTypeface(&mDevanagariRegularTypeface, TYPEFACE_DEVANAGARI_REGULAR);
#if DEBUG_GLYPHS
- ALOGD("Using Devanagari Typeface");
+ ALOGD("Using Devanagari Regular Typeface");
#endif
break;
case HB_Script_Tamil:
- typeface = getCachedTypeface(&mTamilTypeface, TYPEFACE_TAMIL);
+ if (typeface) {
+ switch (typeface->style()) {
+ case SkTypeface::kBold:
+ case SkTypeface::kBoldItalic:
+ typeface = getCachedTypeface(&mTamilBoldTypeface, TYPEFACE_TAMIL_BOLD);
#if DEBUG_GLYPHS
- ALOGD("Using Tamil Typeface");
+ ALOGD("Using Tamil Bold Typeface");
#endif
+ break;
+
+ case SkTypeface::kNormal:
+ case SkTypeface::kItalic:
+ default:
+ typeface = getCachedTypeface(&mTamilRegularTypeface, TYPEFACE_TAMIL_REGULAR);
+#if DEBUG_GLYPHS
+ ALOGD("Using Tamil Regular Typeface");
+#endif
+ break;
+ }
+ } else {
+ typeface = getCachedTypeface(&mTamilRegularTypeface, TYPEFACE_TAMIL_REGULAR);
+#if DEBUG_GLYPHS
+ ALOGD("Using Tamil Regular Typeface");
+#endif
+ }
break;
default:
diff --git a/core/jni/android/graphics/TextLayoutCache.h b/core/jni/android/graphics/TextLayoutCache.h
index 7d7caac..0be61c6 100644
--- a/core/jni/android/graphics/TextLayoutCache.h
+++ b/core/jni/android/graphics/TextLayoutCache.h
@@ -194,8 +194,9 @@ private:
SkTypeface* mHebrewBoldTypeface;
SkTypeface* mBengaliTypeface;
SkTypeface* mThaiTypeface;
- SkTypeface* mDevanagariTypeface;
- SkTypeface* mTamilTypeface;
+ SkTypeface* mDevanagariRegularTypeface;
+ SkTypeface* mTamilRegularTypeface;
+ SkTypeface* mTamilBoldTypeface;
/**
* Cache of Harfbuzz faces
diff --git a/core/jni/android_os_Power.cpp b/core/jni/android_os_Power.cpp
index 48845f6..a201d8b 100644
--- a/core/jni/android_os_Power.cpp
+++ b/core/jni/android_os_Power.cpp
@@ -24,6 +24,7 @@
#include <hardware/power.h>
#include <hardware_legacy/power.h>
#include <cutils/android_reboot.h>
+#include <suspend/autosuspend.h>
static struct power_module *sPowerModule;
@@ -70,8 +71,14 @@ setLastUserActivityTimeout(JNIEnv *env, jobject clazz, jlong timeMS)
static int
setScreenState(JNIEnv *env, jobject clazz, jboolean on)
{
- if (sPowerModule)
- sPowerModule->setInteractive(sPowerModule, on);
+ if (on) {
+ autosuspend_disable();
+ sPowerModule->setInteractive(sPowerModule, true);
+ } else {
+ sPowerModule->setInteractive(sPowerModule, false);
+ autosuspend_enable();
+ }
+
return 0;
}
diff --git a/core/res/res/layout-sw600dp/keyguard_screen_sim_pin_portrait.xml b/core/res/res/layout-sw600dp/keyguard_screen_sim_pin_portrait.xml
index d8bea56..1be4462 100644
--- a/core/res/res/layout-sw600dp/keyguard_screen_sim_pin_portrait.xml
+++ b/core/res/res/layout-sw600dp/keyguard_screen_sim_pin_portrait.xml
@@ -57,12 +57,13 @@
android:background="@android:drawable/edit_text">
<!-- displays dots as user enters pin -->
- <TextView android:id="@+id/pinDisplay"
+ <EditText android:id="@+id/pinDisplay"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
android:maxLines="1"
android:textAppearance="?android:attr/textAppearanceLargeInverse"
+ android:textColor="@android:color/primary_text_holo_light"
android:textStyle="bold"
android:inputType="textPassword"
/>
diff --git a/core/res/res/layout-sw600dp/keyguard_screen_tab_unlock.xml b/core/res/res/layout-sw600dp/keyguard_screen_tab_unlock.xml
index 73dadb4..66cf98d 100644
--- a/core/res/res/layout-sw600dp/keyguard_screen_tab_unlock.xml
+++ b/core/res/res/layout-sw600dp/keyguard_screen_tab_unlock.xml
@@ -85,10 +85,10 @@
<com.android.internal.widget.multiwaveview.MultiWaveView
android:id="@+id/unlock_widget"
android:orientation="horizontal"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_alignParentBottom="true"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
android:layout_gravity="center"
+ android:gravity="center"
android:targetDrawables="@array/lockscreen_targets_with_camera"
android:targetDescriptions="@array/lockscreen_target_descriptions_with_camera"
@@ -99,8 +99,6 @@
android:snapMargin="@dimen/multiwaveview_snap_margin"
android:hitRadius="@dimen/multiwaveview_hit_radius"
android:rightChevronDrawable="@drawable/ic_lockscreen_chevron_right"
- android:horizontalOffset="0dip"
- android:verticalOffset="60dip"
android:feedbackCount="3"
android:vibrationDuration="20"
/>
diff --git a/core/res/res/layout-sw600dp/keyguard_screen_tab_unlock_land.xml b/core/res/res/layout-sw600dp/keyguard_screen_tab_unlock_land.xml
index 10b1ace..65b442b 100644
--- a/core/res/res/layout-sw600dp/keyguard_screen_tab_unlock_land.xml
+++ b/core/res/res/layout-sw600dp/keyguard_screen_tab_unlock_land.xml
@@ -84,10 +84,11 @@
<com.android.internal.widget.multiwaveview.MultiWaveView
android:id="@+id/unlock_widget"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
android:layout_rowSpan="7"
android:layout_gravity="center_vertical|center_horizontal"
+ android:gravity="center"
android:targetDrawables="@array/lockscreen_targets_with_camera"
android:targetDescriptions="@array/lockscreen_target_descriptions_with_camera"
@@ -100,8 +101,6 @@
android:rightChevronDrawable="@drawable/ic_lockscreen_chevron_right"
android:feedbackCount="3"
android:vibrationDuration="20"
- android:horizontalOffset="0dip"
- android:verticalOffset="0dip"
/>
<!-- emergency call button shown when sim is PUKd and tab_selector is hidden -->
diff --git a/core/res/res/layout/keyguard_screen_sim_pin_portrait.xml b/core/res/res/layout/keyguard_screen_sim_pin_portrait.xml
index 6e8a645..9ca351c 100644
--- a/core/res/res/layout/keyguard_screen_sim_pin_portrait.xml
+++ b/core/res/res/layout/keyguard_screen_sim_pin_portrait.xml
@@ -57,12 +57,13 @@
android:background="@android:drawable/edit_text">
<!-- displays dots as user enters pin -->
- <TextView android:id="@+id/pinDisplay"
+ <EditText android:id="@+id/pinDisplay"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
android:maxLines="1"
android:textAppearance="?android:attr/textAppearanceLargeInverse"
+ android:textColor="@android:color/primary_text_holo_light"
android:textStyle="bold"
android:inputType="textPassword"
/>
diff --git a/core/res/res/layout/keyguard_screen_sim_puk_landscape.xml b/core/res/res/layout/keyguard_screen_sim_puk_landscape.xml
index 722dc26..56e6426 100644
--- a/core/res/res/layout/keyguard_screen_sim_puk_landscape.xml
+++ b/core/res/res/layout/keyguard_screen_sim_puk_landscape.xml
@@ -122,14 +122,16 @@
android:background="@android:drawable/edit_text">
<!-- displays dots as user enters new pin -->
- <TextView android:id="@+id/pinDisplay"
+ <EditText android:id="@+id/pinDisplay"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
android:maxLines="1"
android:textAppearance="?android:attr/textAppearanceLargeInverse"
+ android:textColor="@android:color/primary_text_holo_light"
android:textStyle="bold"
android:inputType="textPassword"
+ android:hint="@android:string/keyguard_password_enter_pin_prompt"
/>
<ImageButton android:id="@+id/pinDel"
diff --git a/core/res/res/layout/keyguard_screen_tab_unlock.xml b/core/res/res/layout/keyguard_screen_tab_unlock.xml
index 0ec8f75..3fd3023 100644
--- a/core/res/res/layout/keyguard_screen_tab_unlock.xml
+++ b/core/res/res/layout/keyguard_screen_tab_unlock.xml
@@ -129,6 +129,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentBottom="true"
+ android:gravity="top"
android:targetDrawables="@array/lockscreen_targets_with_camera"
android:targetDescriptions="@array/lockscreen_target_descriptions_with_camera"
@@ -139,8 +140,6 @@
android:snapMargin="@dimen/multiwaveview_snap_margin"
android:hitRadius="@dimen/multiwaveview_hit_radius"
android:rightChevronDrawable="@drawable/ic_lockscreen_chevron_right"
- android:horizontalOffset="0dip"
- android:verticalOffset="60dip"
android:feedbackCount="3"
android:vibrationDuration="20"
/>
diff --git a/core/res/res/layout/keyguard_screen_tab_unlock_land.xml b/core/res/res/layout/keyguard_screen_tab_unlock_land.xml
index 294f91e..cd03c10 100644
--- a/core/res/res/layout/keyguard_screen_tab_unlock_land.xml
+++ b/core/res/res/layout/keyguard_screen_tab_unlock_land.xml
@@ -131,9 +131,10 @@
<!-- Column 2 -->
<com.android.internal.widget.multiwaveview.MultiWaveView
android:id="@+id/unlock_widget"
- android:layout_width="200dip"
+ android:layout_width="302dip"
android:layout_height="match_parent"
android:layout_rowSpan="7"
+ android:gravity="center"
android:targetDrawables="@array/lockscreen_targets_with_camera"
android:targetDescriptions="@array/lockscreen_target_descriptions_with_camera"
@@ -146,8 +147,6 @@
android:topChevronDrawable="@drawable/ic_lockscreen_chevron_up"
android:feedbackCount="3"
android:vibrationDuration="20"
- android:horizontalOffset="0dip"
- android:verticalOffset="0dip"
/>
<!-- Music transport control -->
diff --git a/core/res/res/values-sw600dp-land/arrays.xml b/core/res/res/values-sw600dp-land/arrays.xml
index 6304bc0..6a09cf8 100644
--- a/core/res/res/values-sw600dp-land/arrays.xml
+++ b/core/res/res/values-sw600dp-land/arrays.xml
@@ -57,14 +57,14 @@
<array name="lockscreen_targets_with_camera">
<item>@drawable/ic_lockscreen_unlock</item>
- <item>@null</item>
+ <item>@drawable/ic_lockscreen_search</item>
<item>@drawable/ic_lockscreen_camera</item>
<item>@null</item>
</array>
<array name="lockscreen_target_descriptions_with_camera">
<item>@string/description_target_unlock</item>
- <item>@null</item>
+ <item>@string/description_target_search</item>
<item>@string/description_target_camera</item>
<item>@null</item>
</array>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 9fa666e..484de0d 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3107,7 +3107,7 @@
<attr name="singleLine" format="boolean" />
<!-- Specifies whether the widget is enabled. The interpretation of the enabled state varies by subclass.
For example, a non-enabled EditText prevents the user from editing the contained text, and
- a non-enabled Button prevents the user from tapping the button.
+ a non-enabled Button prevents the user from tapping the button.
The appearance of enabled and non-enabled widgets may differ, if the drawables referenced
from evaluating state_enabled differ. -->
<attr name="enabled" format="boolean" />
@@ -5378,12 +5378,17 @@
<!-- Number of waves/chevrons to show in animation. -->
<attr name="feedbackCount" format="integer" />
- <!-- Used to shift center of pattern vertically. -->
+ <!-- {@deprecated Not used by the framework. Use android:gravity instead}
+ Used to shift center of pattern vertically. -->
<attr name="verticalOffset" format="dimension" />
- <!-- Used to shift center of pattern horizontally. -->
+ <!-- {@deprecated Not used by the framework. Use android:gravity instead}
+ Used to shift center of pattern horizontally. -->
<attr name="horizontalOffset" format="dimension" />
+ <!-- How the items in this layout should be positioned -->
+ <attr name="gravity" />
+
<!-- Used when the handle shouldn't wait to be hit before following the finger -->
<attr name="alwaysTrackFinger" format="boolean" />
</declare-styleable>
diff --git a/data/fonts/Android.mk b/data/fonts/Android.mk
index 8ad15b6..13f2480 100644
--- a/data/fonts/Android.mk
+++ b/data/fonts/Android.mk
@@ -65,6 +65,22 @@ LOCAL_MODULE_PATH := $(TARGET_OUT)/fonts
include $(BUILD_PREBUILT)
include $(CLEAR_VARS)
+LOCAL_MODULE := DroidSansTamil-Regular.ttf
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_PATH := $(TARGET_OUT)/fonts
+include $(BUILD_PREBUILT)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := DroidSansTamil-Bold.ttf
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_PATH := $(TARGET_OUT)/fonts
+include $(BUILD_PREBUILT)
+
+include $(CLEAR_VARS)
LOCAL_MODULE := MTLmr3m.ttf
LOCAL_SRC_FILES := $(LOCAL_MODULE)
LOCAL_MODULE_CLASS := ETC
@@ -85,6 +101,8 @@ extra_font_files := \
DroidSans.ttf \
DroidSans-Bold.ttf \
DroidSansEthiopic-Regular.ttf \
+ DroidSansTamil-Regular.ttf \
+ DroidSansTamil-Bold.ttf \
MTLmr3m.ttf \
fallback_fonts-ja.xml
endif # SMALLER_FONT_FOOTPRINT
@@ -126,6 +144,7 @@ font_src_files := \
Roboto-BoldItalic.ttf \
DroidNaskh-Regular.ttf \
DroidNaskh-Regular-SystemUI.ttf \
+ DroidSansDevanagari-Regular.ttf \
DroidSansHebrew-Regular.ttf \
DroidSansHebrew-Bold.ttf \
DroidSansThai.ttf \
diff --git a/data/fonts/DroidSansDevanagari-Regular.ttf b/data/fonts/DroidSansDevanagari-Regular.ttf
new file mode 100644
index 0000000..45e15e6
--- /dev/null
+++ b/data/fonts/DroidSansDevanagari-Regular.ttf
Binary files differ
diff --git a/data/fonts/DroidSansTamil-Bold.ttf b/data/fonts/DroidSansTamil-Bold.ttf
new file mode 100644
index 0000000..8ad0085
--- /dev/null
+++ b/data/fonts/DroidSansTamil-Bold.ttf
Binary files differ
diff --git a/data/fonts/DroidSansTamil-Regular.ttf b/data/fonts/DroidSansTamil-Regular.ttf
new file mode 100644
index 0000000..4b8f536
--- /dev/null
+++ b/data/fonts/DroidSansTamil-Regular.ttf
Binary files differ
diff --git a/data/fonts/fallback_fonts-ja.xml b/data/fonts/fallback_fonts-ja.xml
index a182e31..be53d1d 100644
--- a/data/fonts/fallback_fonts-ja.xml
+++ b/data/fonts/fallback_fonts-ja.xml
@@ -65,7 +65,7 @@
</family>
<family>
<fileset>
- <file>Lohit-Devanagari.ttf</file>
+ <file>DroidSansDevanagari-Regular.ttf</file>
</fileset>
</family>
<family>
@@ -75,7 +75,8 @@
</family>
<family>
<fileset>
- <file>Lohit-Tamil.ttf</file>
+ <file>DroidSansTamil-Regular.ttf</file>
+ <file>DroidSansTamil-Bold.ttf</file>
</fileset>
</family>
<family>
diff --git a/data/fonts/fallback_fonts.xml b/data/fonts/fallback_fonts.xml
index 69d223e..80fffa9 100644
--- a/data/fonts/fallback_fonts.xml
+++ b/data/fonts/fallback_fonts.xml
@@ -65,7 +65,7 @@
</family>
<family>
<fileset>
- <file>Lohit-Devanagari.ttf</file>
+ <file>DroidSansDevanagari-Regular.ttf</file>
</fileset>
</family>
<family>
@@ -75,7 +75,8 @@
</family>
<family>
<fileset>
- <file>Lohit-Tamil.ttf</file>
+ <file>DroidSansTamil-Regular.ttf</file>
+ <file>DroidSansTamil-Bold.ttf</file>
</fileset>
</family>
<family>
diff --git a/data/fonts/fonts.mk b/data/fonts/fonts.mk
index 0eec616..7a84df6 100644
--- a/data/fonts/fonts.mk
+++ b/data/fonts/fonts.mk
@@ -26,6 +26,7 @@ PRODUCT_PACKAGES := \
Roboto-BoldItalic.ttf \
DroidNaskh-Regular.ttf \
DroidNaskh-Regular-SystemUI.ttf \
+ DroidSansDevanagari-Regular.ttf \
DroidSansHebrew-Regular.ttf \
DroidSansHebrew-Bold.ttf \
DroidSansThai.ttf \
diff --git a/packages/SystemUI/res/drawable-hdpi/navbar_search_bg_scrim.9.png b/packages/SystemUI/res/drawable-hdpi/navbar_search_bg_scrim.9.png
new file mode 100644
index 0000000..4c163a2
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/navbar_search_bg_scrim.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/navbar_search_bg_scrim.9.png b/packages/SystemUI/res/drawable-mdpi/navbar_search_bg_scrim.9.png
new file mode 100644
index 0000000..21c5abd
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/navbar_search_bg_scrim.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/navbar_search_bg_scrim.9.png b/packages/SystemUI/res/drawable-xhdpi/navbar_search_bg_scrim.9.png
new file mode 100644
index 0000000..7874c63
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/navbar_search_bg_scrim.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable/navbar_search_bg_scrim.png b/packages/SystemUI/res/drawable/navbar_search_bg_scrim.png
deleted file mode 100644
index d595ed2..0000000
--- a/packages/SystemUI/res/drawable/navbar_search_bg_scrim.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/layout-land/status_bar_search_panel.xml b/packages/SystemUI/res/layout-land/status_bar_search_panel.xml
index 2adee33..5d4d989 100644
--- a/packages/SystemUI/res/layout-land/status_bar_search_panel.xml
+++ b/packages/SystemUI/res/layout-land/status_bar_search_panel.xml
@@ -34,17 +34,18 @@
<RelativeLayout
android:id="@+id/search_panel_container"
- android:layout_width="230dip"
+ android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_alignParentRight="true">
<com.android.internal.widget.multiwaveview.MultiWaveView
android:id="@+id/multi_wave_view"
android:orientation="vertical"
- android:layout_width="match_parent"
+ android:layout_width="@dimen/navbar_search_panel_height"
android:layout_height="match_parent"
android:layout_alignParentBottom="true"
android:background="@drawable/navbar_search_bg_scrim"
+ android:gravity="left"
prvandroid:targetDrawables="@array/navbar_search_targets"
prvandroid:targetDescriptions="@array/navbar_search_target_descriptions"
@@ -54,8 +55,6 @@
prvandroid:outerRadius="@dimen/navbar_search_target_placement_radius"
prvandroid:snapMargin="@dimen/navbar_search_snap_margin"
prvandroid:hitRadius="@dimen/navbar_search_hit_radius"
- prvandroid:verticalOffset="0dip"
- prvandroid:horizontalOffset="60dip"
prvandroid:feedbackCount="0"
prvandroid:vibrationDuration="0"
prvandroid:alwaysTrackFinger="true"/>
diff --git a/packages/SystemUI/res/layout-port/status_bar_search_panel.xml b/packages/SystemUI/res/layout-port/status_bar_search_panel.xml
index 463fa04..2486b75 100644
--- a/packages/SystemUI/res/layout-port/status_bar_search_panel.xml
+++ b/packages/SystemUI/res/layout-port/status_bar_search_panel.xml
@@ -35,16 +35,17 @@
<RelativeLayout
android:id="@+id/search_panel_container"
android:layout_width="match_parent"
- android:layout_height="230dip"
+ android:layout_height="wrap_content"
android:layout_alignParentBottom="true">
<com.android.internal.widget.multiwaveview.MultiWaveView
android:id="@+id/multi_wave_view"
android:orientation="horizontal"
android:layout_width="match_parent"
- android:layout_height="match_parent"
+ android:layout_height="@dimen/navbar_search_panel_height"
android:layout_alignParentBottom="true"
android:background="@drawable/navbar_search_bg_scrim"
+ android:gravity="top"
prvandroid:targetDrawables="@array/navbar_search_targets"
prvandroid:targetDescriptions="@array/navbar_search_target_descriptions"
@@ -54,8 +55,6 @@
prvandroid:outerRadius="@dimen/navbar_search_target_placement_radius"
prvandroid:snapMargin="@dimen/navbar_search_snap_margin"
prvandroid:hitRadius="@dimen/navbar_search_hit_radius"
- prvandroid:horizontalOffset="0dip"
- prvandroid:verticalOffset="60dip"
prvandroid:feedbackCount="0"
prvandroid:vibrationDuration="0"
prvandroid:alwaysTrackFinger="true"/>
diff --git a/packages/SystemUI/res/layout-sw600dp/status_bar_search_panel.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_search_panel.xml
index 6e33e27..4b2fbc7 100644
--- a/packages/SystemUI/res/layout-sw600dp/status_bar_search_panel.xml
+++ b/packages/SystemUI/res/layout-sw600dp/status_bar_search_panel.xml
@@ -29,23 +29,32 @@
<RelativeLayout
android:id="@+id/search_bg_protect"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginBottom="0dip">
+ android:layout_height="wrap_content">
<RelativeLayout
android:id="@+id/search_panel_container"
android:layout_width="wrap_content"
- android:layout_height="230dip"
+ android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
- android:layout_alignParentLeft="true">
+ android:layout_centerHorizontal="true">
+
+ <View
+ android:layout_width="0dip"
+ android:layout_height="0dip"
+ android:layout_alignTop="@id/multi_wave_view"
+ android:layout_alignLeft="@id/multi_wave_view"
+ android:layout_alignRight="@id/multi_wave_view"
+ android:layout_alignBottom="@id/multi_wave_view"
+ android:layout_marginBottom="@dimen/navigation_bar_size"
+ android:background="@drawable/navbar_search_bg_scrim"/>
<com.android.internal.widget.multiwaveview.MultiWaveView
android:id="@+id/multi_wave_view"
android:orientation="horizontal"
android:layout_width="wrap_content"
- android:layout_height="match_parent"
+ android:layout_height="@dimen/navbar_search_panel_height"
android:layout_alignParentBottom="true"
- android:background="@drawable/navbar_search_bg_scrim"
+ android:gravity="top"
prvandroid:targetDrawables="@array/navbar_search_targets"
prvandroid:targetDescriptions="@array/navbar_search_target_descriptions"
@@ -55,8 +64,6 @@
prvandroid:outerRadius="@dimen/navbar_search_target_placement_radius"
prvandroid:snapMargin="@dimen/navbar_search_snap_margin"
prvandroid:hitRadius="@dimen/navbar_search_hit_radius"
- prvandroid:horizontalOffset="0dip"
- prvandroid:verticalOffset="60dip"
prvandroid:feedbackCount="0"
prvandroid:vibrationDuration="0"
prvandroid:alwaysTrackFinger="true"/>
diff --git a/packages/SystemUI/res/layout-sw720dp/status_bar_search_panel.xml b/packages/SystemUI/res/layout-sw720dp/status_bar_search_panel.xml
new file mode 100644
index 0000000..b4872c7
--- /dev/null
+++ b/packages/SystemUI/res/layout-sw720dp/status_bar_search_panel.xml
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* apps/common/assets/default/default/skins/StatusBar.xml
+**
+** Copyright 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.
+*/
+-->
+
+<com.android.systemui.SearchPanelView
+ xmlns:prvandroid="http://schemas.android.com/apk/prv/res/android"
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/search_panel_container"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:paddingBottom="0dip">
+
+ <RelativeLayout
+ android:id="@+id/search_bg_protect"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <RelativeLayout
+ android:id="@+id/search_panel_container"
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/navbar_search_panel_height"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentLeft="true">
+
+ <View
+ android:layout_width="0dip"
+ android:layout_height="0dip"
+ android:layout_alignTop="@id/multi_wave_view"
+ android:layout_alignLeft="@id/multi_wave_view"
+ android:layout_alignRight="@id/multi_wave_view"
+ android:layout_alignBottom="@id/multi_wave_view"
+ android:layout_marginBottom="@dimen/navigation_bar_size"
+ android:background="@drawable/navbar_search_bg_scrim"/>
+
+ <com.android.internal.widget.multiwaveview.MultiWaveView
+ android:id="@+id/multi_wave_view"
+ android:orientation="horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_alignParentBottom="true"
+
+ prvandroid:targetDrawables="@array/navbar_search_targets"
+ prvandroid:targetDescriptions="@array/navbar_search_target_descriptions"
+ prvandroid:directionDescriptions="@array/navbar_search_direction_descriptions"
+ prvandroid:handleDrawable="@drawable/navbar_search_handle"
+ prvandroid:waveDrawable="@drawable/navbar_search_outerring"
+ prvandroid:outerRadius="@dimen/navbar_search_target_placement_radius"
+ prvandroid:snapMargin="@dimen/navbar_search_snap_margin"
+ prvandroid:hitRadius="@dimen/navbar_search_hit_radius"
+ prvandroid:feedbackCount="0"
+ prvandroid:vibrationDuration="0"
+ prvandroid:alwaysTrackFinger="true"/>
+
+ </RelativeLayout>
+
+ </RelativeLayout>
+
+</com.android.systemui.SearchPanelView>
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index 43ae557..50a61b1 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -19,11 +19,21 @@
<!-- The width of the notification panel window -->
<dimen name="notification_panel_width">446dp</dimen>
- <!-- Layout parameters for the notification panel -->
- <dimen name="notification_panel_margin_bottom">192dp</dimen>
+ <!-- Layout parameters for the notification panel -->
+ <dimen name="notification_panel_margin_bottom">192dp</dimen>
<dimen name="notification_panel_margin_left">0dp</dimen>
<!-- Gravity for the notification panel -->
<!-- 0x33 = center_horizontal|top -->
<integer name="notification_panel_layout_gravity">0x31</integer>
+
+ <!-- Default target placement radius for navigation bar search target -->
+ <dimen name="navbar_search_target_placement_radius">182dip</dimen>
+
+ <!-- Diameter of outer shape drawable shown in navbar search-->
+ <dimen name="navbar_search_outerring_diameter">364dp</dimen>
+
+ <!-- Height of search panel including navigation bar height -->
+ <dimen name="navbar_search_panel_height">300dip</dimen>
+
</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index e92dbc5..276d74b 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -116,13 +116,19 @@
<dimen name="navbar_search_hit_radius">60dip</dimen>
<!-- Diameter of outer shape drawable shown in navbar search-->
- <dimen name="navbar_search_outerring_diameter">300dip</dimen>
+ <dimen name="navbar_search_outerring_diameter">270dp</dimen>
+
+ <!-- Threshold for swipe-up gesture to activate search dialog -->
+ <dimen name="navbar_search_up_threshhold">20dip</dimen>
+
+ <!-- Height of search panel including navigation bar height -->
+ <dimen name="navbar_search_panel_height">230dip</dimen>
<!-- Height of the draggable handle at the bottom of the phone notification panel -->
<dimen name="close_handle_height">34dp</dimen>
- <!-- Layout parameters for the notification panel -->
- <dimen name="notification_panel_margin_bottom">0dp</dimen>
+ <!-- Layout parameters for the notification panel -->
+ <dimen name="notification_panel_margin_bottom">0dp</dimen>
<dimen name="notification_panel_margin_left">0dp</dimen>
<!-- Gravity for the notification panel -->
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DelegateViewHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/DelegateViewHelper.java
index f725724..e074a80 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/DelegateViewHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/DelegateViewHelper.java
@@ -16,22 +16,28 @@
package com.android.systemui.statusbar;
+import android.util.Slog;
import android.view.MotionEvent;
import android.view.Surface;
-import android.view.VelocityTracker;
import android.view.View;
+import com.android.systemui.R;
+
public class DelegateViewHelper {
- private static final int VELOCITY_THRESHOLD = 1000;
- private VelocityTracker mVelocityTracker;
private View mDelegateView;
private View mSourceView;
private BaseStatusBar mBar;
private int[] mTempPoint = new int[2];
+ private float[] mDownPoint = new float[2];
private int mOrientation;
+ private float mTriggerThreshhold;
public DelegateViewHelper(View sourceView) {
mSourceView = sourceView;
+ if (mSourceView != null) {
+ mTriggerThreshhold = mSourceView.getContext().getResources()
+ .getDimension(R.dimen.navbar_search_up_threshhold);
+ }
}
public void setDelegateView(View view) {
@@ -49,30 +55,25 @@ public class DelegateViewHelper {
public boolean onInterceptTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
- mVelocityTracker = VelocityTracker.obtain();
- break;
- case MotionEvent.ACTION_CANCEL:
- case MotionEvent.ACTION_UP:
- mVelocityTracker.recycle();
- mVelocityTracker = null;
+ mDownPoint[0] = event.getX();
+ mDownPoint[1] = event.getY();
break;
}
- if (mVelocityTracker != null) {
- if (mDelegateView != null && mDelegateView.getVisibility() != View.VISIBLE) {
- mVelocityTracker.addMovement(event);
- mVelocityTracker.computeCurrentVelocity(1000);
+ if (mDelegateView != null) {
+ if (mDelegateView.getVisibility() != View.VISIBLE && event.getAction() != MotionEvent.ACTION_CANCEL) {
final boolean isVertical = (mOrientation == Surface.ROTATION_90
|| mOrientation == Surface.ROTATION_270);
- float velocity = isVertical ? - mVelocityTracker.getXVelocity()
- : - mVelocityTracker.getYVelocity();
- if (velocity > VELOCITY_THRESHOLD) {
- if (mDelegateView != null && mDelegateView.getVisibility() != View.VISIBLE) {
+ final int historySize = event.getHistorySize();
+ for (int k = 0; k < historySize + 1; k++) {
+ float x = k < historySize ? event.getHistoricalX(k) : event.getX();
+ float y = k < historySize ? event.getHistoricalY(k) : event.getY();
+ final float distance = isVertical ? (mDownPoint[0] - x) : (mDownPoint[1] - y);
+ if (distance > mTriggerThreshhold) {
mBar.showSearchPanel();
+ break;
}
}
}
- }
- if (mDelegateView != null) {
mSourceView.getLocationOnScreen(mTempPoint);
float deltaX = mTempPoint[0];
float deltaY = mTempPoint[1];
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index c1d3c57..1561e27 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -451,7 +451,7 @@ public class PhoneStatusBar extends BaseStatusBar {
@Override
protected void updateSearchPanel() {
super.updateSearchPanel();
- mSearchPanelView.setStatusBarView(mStatusBarView);
+ mSearchPanelView.setStatusBarView(mNavigationBarView);
mNavigationBarView.setDelegateView(mSearchPanelView);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
index 4ad90ca..a394596 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
@@ -1077,8 +1077,8 @@ public class TabletStatusBar extends BaseStatusBar implements
mTicker.halt();
}
}
- if ((diff & (StatusBarManager.DISABLE_RECENT
- | StatusBarManager.DISABLE_BACK
+ if ((diff & (StatusBarManager.DISABLE_RECENT
+ | StatusBarManager.DISABLE_BACK
| StatusBarManager.DISABLE_HOME)) != 0) {
setNavigationVisibility(state);
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardViewManager.java b/policy/src/com/android/internal/policy/impl/KeyguardViewManager.java
index 7100e89..7f432bf 100644
--- a/policy/src/com/android/internal/policy/impl/KeyguardViewManager.java
+++ b/policy/src/com/android/internal/policy/impl/KeyguardViewManager.java
@@ -181,6 +181,7 @@ public class KeyguardViewManager implements KeyguardWindowController {
( View.STATUS_BAR_DISABLE_BACK
| View.STATUS_BAR_DISABLE_HOME
);
+ Log.v(TAG, "KGVM: Set visibility on " + mKeyguardHost + " to " + visFlags);
mKeyguardHost.setSystemUiVisibility(visFlags);
mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams);
diff --git a/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java b/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java
index 2408d7f..8320b1d 100644
--- a/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java
+++ b/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java
@@ -717,9 +717,10 @@ public class LockPatternKeyguardView extends KeyguardViewBase {
@Override
public void onClockVisibilityChanged() {
- int visFlags = getSystemUiVisibility() & ~View.STATUS_BAR_DISABLE_CLOCK;
- setSystemUiVisibility(visFlags
- | (mUpdateMonitor.isClockVisible() ? View.STATUS_BAR_DISABLE_CLOCK : 0));
+ int visFlags = (getSystemUiVisibility() & ~View.STATUS_BAR_DISABLE_CLOCK)
+ | (mUpdateMonitor.isClockVisible() ? View.STATUS_BAR_DISABLE_CLOCK : 0);
+ Log.v(TAG, "Set visibility on " + this + " to " + visFlags);
+ setSystemUiVisibility(visFlags);
}
// We need to stop the biometric unlock when a phone call comes in
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 4b40107..1f7aeb0 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -14508,9 +14508,11 @@ public final class ActivityManagerService extends ActivityManagerNative
// activities causes more harm than good.
if (curLevel >= ComponentCallbacks2.TRIM_MEMORY_COMPLETE
&& app != mHomeProcess && app != mPreviousProcess) {
+ // Need to do this on its own message because the stack may not
+ // be in a consistent state at this point.
// For these apps we will also finish their activities
// to help them free memory.
- mMainStack.destroyActivitiesLocked(app, false, "trim");
+ mMainStack.scheduleDestroyActivities(app, false, "trim");
}
}
}
@@ -14582,7 +14584,9 @@ public final class ActivityManagerService extends ActivityManagerNative
}
if (mAlwaysFinishActivities) {
- mMainStack.destroyActivitiesLocked(null, false, "always-finish");
+ // Need to do this on its own message because the stack may not
+ // be in a consistent state at this point.
+ mMainStack.scheduleDestroyActivities(null, false, "always-finish");
}
}
diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java
index cce8e7a..97bfd6f 100644
--- a/services/java/com/android/server/am/ActivityRecord.java
+++ b/services/java/com/android/server/am/ActivityRecord.java
@@ -29,6 +29,7 @@ import android.content.pm.ApplicationInfo;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.graphics.Bitmap;
+import android.graphics.Rect;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
@@ -562,12 +563,26 @@ final class ActivityRecord {
service.mWindowManager.overridePendingAppTransitionScaleUp(
pendingOptions.getStartX(), pendingOptions.getStartY(),
pendingOptions.getStartWidth(), pendingOptions.getStartHeight());
+ if (intent.getSourceBounds() == null) {
+ intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
+ pendingOptions.getStartY(),
+ pendingOptions.getStartX()+pendingOptions.getStartWidth(),
+ pendingOptions.getStartY()+pendingOptions.getStartHeight()));
+ }
break;
case ActivityOptions.ANIM_THUMBNAIL:
service.mWindowManager.overridePendingAppTransitionThumb(
pendingOptions.getThumbnail(),
pendingOptions.getStartX(), pendingOptions.getStartY(),
pendingOptions.getOnAnimationStartListener());
+ if (intent.getSourceBounds() == null) {
+ intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
+ pendingOptions.getStartY(),
+ pendingOptions.getStartX()
+ + pendingOptions.getThumbnail().getWidth(),
+ pendingOptions.getStartY()
+ + pendingOptions.getThumbnail().getHeight()));
+ }
break;
}
pendingOptions = null;
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index 25fae83..1e14f5b 100755
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -45,7 +45,6 @@ import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
-import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
@@ -289,7 +288,19 @@ final class ActivityStack {
static final int RESUME_TOP_ACTIVITY_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 6;
static final int LAUNCH_TICK_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 7;
static final int STOP_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 8;
-
+ static final int DESTROY_ACTIVITIES_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 9;
+
+ static class ScheduleDestroyArgs {
+ final ProcessRecord mOwner;
+ final boolean mOomAdj;
+ final String mReason;
+ ScheduleDestroyArgs(ProcessRecord owner, boolean oomAdj, String reason) {
+ mOwner = owner;
+ mOomAdj = oomAdj;
+ mReason = reason;
+ }
+ }
+
final Handler mHandler = new Handler() {
//public Handler() {
// if (localLOGV) Slog.v(TAG, "Handler started!");
@@ -384,6 +395,12 @@ final class ActivityStack {
}
}
} break;
+ case DESTROY_ACTIVITIES_MSG: {
+ ScheduleDestroyArgs args = (ScheduleDestroyArgs)msg.obj;
+ synchronized (mService) {
+ destroyActivitiesLocked(args.mOwner, args.mOomAdj, args.mReason);
+ }
+ }
}
}
};
@@ -3821,19 +3838,39 @@ final class ActivityStack {
r.connections = null;
}
}
-
+
+ final void scheduleDestroyActivities(ProcessRecord owner, boolean oomAdj, String reason) {
+ Message msg = mHandler.obtainMessage(DESTROY_ACTIVITIES_MSG);
+ msg.obj = new ScheduleDestroyArgs(owner, oomAdj, reason);
+ mHandler.sendMessage(msg);
+ }
+
final void destroyActivitiesLocked(ProcessRecord owner, boolean oomAdj, String reason) {
+ boolean lastIsOpaque = false;
for (int i=mHistory.size()-1; i>=0; i--) {
ActivityRecord r = mHistory.get(i);
+ if (r.finishing) {
+ continue;
+ }
+ if (r.fullscreen) {
+ lastIsOpaque = true;
+ }
if (owner != null && r.app != owner) {
continue;
}
+ if (!lastIsOpaque) {
+ continue;
+ }
// We can destroy this one if we have its icicle saved and
// it is not in the process of pausing/stopping/finishing.
- if (r.app != null && r.haveState && !r.visible && r.stopped && !r.finishing
+ if (r.app != null && r != mResumedActivity && r != mPausingActivity
+ && r.haveState && !r.visible && r.stopped
&& r.state != ActivityState.DESTROYING
&& r.state != ActivityState.DESTROYED) {
- destroyActivityLocked(r, true, oomAdj, "trim");
+ if (DEBUG_SWITCH) Slog.v(TAG, "Destroying " + r + " in state " + r.state
+ + " resumed=" + mResumedActivity
+ + " pausing=" + mPausingActivity);
+ destroyActivityLocked(r, true, oomAdj, reason);
}
}
}
@@ -3847,7 +3884,7 @@ final class ActivityStack {
final boolean destroyActivityLocked(ActivityRecord r,
boolean removeFromApp, boolean oomAdj, String reason) {
if (DEBUG_SWITCH) Slog.v(
- TAG, "Removing activity: token=" + r
+ TAG, "Removing activity from " + reason + ": token=" + r
+ ", app=" + (r.app != null ? r.app.processName : "(null)"));
EventLog.writeEvent(EventLogTags.AM_DESTROY_ACTIVITY,
System.identityHashCode(r),
diff --git a/services/java/com/android/server/am/TaskRecord.java b/services/java/com/android/server/am/TaskRecord.java
index e3ebcc6..4b4a89d 100644
--- a/services/java/com/android/server/am/TaskRecord.java
+++ b/services/java/com/android/server/am/TaskRecord.java
@@ -63,9 +63,10 @@ class TaskRecord extends ThumbnailHolder {
// If this Intent has a selector, we want to clear it for the
// recent task since it is not relevant if the user later wants
// to re-launch the app.
- if (_intent.getSelector() != null) {
+ if (_intent.getSelector() != null || _intent.getSourceBounds() != null) {
_intent = new Intent(_intent);
_intent.setSelector(null);
+ _intent.setSourceBounds(null);
}
}
intent = _intent;
@@ -78,6 +79,7 @@ class TaskRecord extends ThumbnailHolder {
Intent targetIntent = new Intent(_intent);
targetIntent.setComponent(targetComponent);
targetIntent.setSelector(null);
+ targetIntent.setSourceBounds(null);
intent = targetIntent;
realActivity = targetComponent;
origActivity = _intent.getComponent();
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java
index 297c2ac..0a40c75 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java
@@ -343,10 +343,16 @@ public class CdmaLteServiceStateTracker extends CdmaServiceStateTracker {
// new ERI text
if (ss.getState() == ServiceState.STATE_IN_SERVICE) {
eriText = phone.getCdmaEriText();
+ } else if (ss.getState() == ServiceState.STATE_POWER_OFF) {
+ eriText = phone.mIccRecords.getServiceProviderName();
+ if (TextUtils.isEmpty(eriText)) {
+ // Sets operator alpha property by retrieving from
+ // build-time system property
+ eriText = SystemProperties.get("ro.cdma.home.operator.alpha");
+ }
} else {
// Note that ServiceState.STATE_OUT_OF_SERVICE is valid used
- // for
- // mRegistrationState 0,2,3 and 4
+ // for mRegistrationState 0,2,3 and 4
eriText = phone.getContext()
.getText(com.android.internal.R.string.roamingTextSearching).toString();
}