summaryrefslogtreecommitdiffstats
path: root/core/java
diff options
context:
space:
mode:
Diffstat (limited to 'core/java')
-rw-r--r--core/java/android/app/backup/BackupAgent.java11
-rw-r--r--core/java/android/net/NetworkStats.java117
-rw-r--r--core/java/android/net/NetworkStatsHistory.java138
-rw-r--r--core/java/android/net/http/AndroidHttpClient.java2
-rw-r--r--core/java/android/provider/ContactsContract.java23
-rwxr-xr-xcore/java/android/server/BluetoothService.java1
-rw-r--r--core/java/android/speech/tts/AudioPlaybackHandler.java31
-rw-r--r--core/java/android/speech/tts/SynthesisMessageParams.java5
-rw-r--r--core/java/android/view/ActionProvider.java37
-rw-r--r--core/java/android/view/MenuInflater.java34
-rw-r--r--core/java/android/webkit/WebView.java55
-rw-r--r--core/java/android/widget/ActivityChooserModel.java63
-rw-r--r--core/java/android/widget/ActivityChooserView.java311
-rw-r--r--core/java/android/widget/CalendarView.java75
-rw-r--r--core/java/android/widget/DatePicker.java95
-rw-r--r--core/java/android/widget/ShareActionProvider.java105
-rw-r--r--core/java/android/widget/TextView.java524
-rw-r--r--core/java/android/widget/TimePicker.java28
-rw-r--r--core/java/com/android/internal/view/menu/MenuBuilder.java10
-rw-r--r--core/java/com/android/internal/view/menu/MenuItemImpl.java10
-rw-r--r--core/java/com/android/server/NetworkManagementSocketTagger.java56
21 files changed, 1197 insertions, 534 deletions
diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java
index dce0a97..436fdf8 100644
--- a/core/java/android/app/backup/BackupAgent.java
+++ b/core/java/android/app/backup/BackupAgent.java
@@ -28,6 +28,7 @@ import android.os.RemoteException;
import android.util.Log;
import java.io.File;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.util.HashSet;
import java.util.LinkedList;
@@ -533,6 +534,16 @@ public abstract class BackupAgent extends ContextWrapper {
Log.d(TAG, "onBackup (" + BackupAgent.this.getClass().getName() + ") threw", ex);
throw ex;
} finally {
+ // Send the EOD marker indicating that there is no more data
+ // forthcoming from this agent.
+ try {
+ FileOutputStream out = new FileOutputStream(data.getFileDescriptor());
+ byte[] buf = new byte[4];
+ out.write(buf);
+ } catch (IOException e) {
+ Log.e(TAG, "Unable to finalize backup stream!");
+ }
+
Binder.restoreCallingIdentity(ident);
try {
callbackBinder.opComplete(token);
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index a4c66e4..fbff7d8 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -43,31 +43,19 @@ public class NetworkStats implements Parcelable {
/** {@link #tag} value for without tag. */
public static final int TAG_NONE = 0;
- // TODO: move public fields to Entry accessors, then undeprecate
- // TODO: refactor rx/tx to rxBytes/txBytes
-
/**
* {@link SystemClock#elapsedRealtime()} timestamp when this data was
* generated.
*/
- @Deprecated
- public final long elapsedRealtime;
- @Deprecated
- public int size;
- @Deprecated
- public String[] iface;
- @Deprecated
- public int[] uid;
- @Deprecated
- public int[] tag;
- @Deprecated
- public long[] rx;
- @Deprecated
- public long[] rxPackets;
- @Deprecated
- public long[] tx;
- @Deprecated
- public long[] txPackets;
+ private final long elapsedRealtime;
+ private int size;
+ private String[] iface;
+ private int[] uid;
+ private int[] tag;
+ private long[] rxBytes;
+ private long[] rxPackets;
+ private long[] txBytes;
+ private long[] txPackets;
public static class Entry {
public String iface;
@@ -77,6 +65,20 @@ public class NetworkStats implements Parcelable {
public long rxPackets;
public long txBytes;
public long txPackets;
+
+ public Entry() {
+ }
+
+ public Entry(String iface, int uid, int tag, long rxBytes, long rxPackets, long txBytes,
+ long txPackets) {
+ this.iface = iface;
+ this.uid = uid;
+ this.tag = tag;
+ this.rxBytes = rxBytes;
+ this.rxPackets = rxPackets;
+ this.txBytes = txBytes;
+ this.txPackets = txPackets;
+ }
}
public NetworkStats(long elapsedRealtime, int initialSize) {
@@ -85,9 +87,9 @@ public class NetworkStats implements Parcelable {
this.iface = new String[initialSize];
this.uid = new int[initialSize];
this.tag = new int[initialSize];
- this.rx = new long[initialSize];
+ this.rxBytes = new long[initialSize];
this.rxPackets = new long[initialSize];
- this.tx = new long[initialSize];
+ this.txBytes = new long[initialSize];
this.txPackets = new long[initialSize];
}
@@ -97,23 +99,15 @@ public class NetworkStats implements Parcelable {
iface = parcel.createStringArray();
uid = parcel.createIntArray();
tag = parcel.createIntArray();
- rx = parcel.createLongArray();
+ rxBytes = parcel.createLongArray();
rxPackets = parcel.createLongArray();
- tx = parcel.createLongArray();
+ txBytes = parcel.createLongArray();
txPackets = parcel.createLongArray();
}
- /**
- * Add new stats entry with given values.
- */
- public NetworkStats addEntry(String iface, int uid, int tag, long rx, long tx) {
- final Entry entry = new Entry();
- entry.iface = iface;
- entry.uid = uid;
- entry.tag = tag;
- entry.rxBytes = rx;
- entry.txBytes = tx;
- return addValues(entry);
+ public NetworkStats addValues(String iface, int uid, int tag, long rxBytes, long rxPackets,
+ long txBytes, long txPackets) {
+ return addValues(new Entry(iface, uid, tag, rxBytes, rxPackets, txBytes, txPackets));
}
/**
@@ -126,18 +120,18 @@ public class NetworkStats implements Parcelable {
iface = Arrays.copyOf(iface, newLength);
uid = Arrays.copyOf(uid, newLength);
tag = Arrays.copyOf(tag, newLength);
- rx = Arrays.copyOf(rx, newLength);
+ rxBytes = Arrays.copyOf(rxBytes, newLength);
rxPackets = Arrays.copyOf(rxPackets, newLength);
- tx = Arrays.copyOf(tx, newLength);
+ txBytes = Arrays.copyOf(txBytes, newLength);
txPackets = Arrays.copyOf(txPackets, newLength);
}
iface[size] = entry.iface;
uid[size] = entry.uid;
tag[size] = entry.tag;
- rx[size] = entry.rxBytes;
+ rxBytes[size] = entry.rxBytes;
rxPackets[size] = entry.rxPackets;
- tx[size] = entry.txBytes;
+ txBytes[size] = entry.txBytes;
txPackets[size] = entry.txPackets;
size++;
@@ -152,9 +146,9 @@ public class NetworkStats implements Parcelable {
entry.iface = iface[i];
entry.uid = uid[i];
entry.tag = tag[i];
- entry.rxBytes = rx[i];
+ entry.rxBytes = rxBytes[i];
entry.rxPackets = rxPackets[i];
- entry.txBytes = tx[i];
+ entry.txBytes = txBytes[i];
entry.txPackets = txPackets[i];
return entry;
}
@@ -167,20 +161,31 @@ public class NetworkStats implements Parcelable {
return size;
}
+ // @VisibleForTesting
+ public int internalSize() {
+ return iface.length;
+ }
+
+ public NetworkStats combineValues(String iface, int uid, int tag, long rxBytes, long rxPackets,
+ long txBytes, long txPackets) {
+ return combineValues(new Entry(iface, uid, tag, rxBytes, rxPackets, txBytes, txPackets));
+ }
+
/**
* Combine given values with an existing row, or create a new row if
* {@link #findIndex(String, int, int)} is unable to find match. Can also be
* used to subtract values from existing rows.
*/
- public NetworkStats combineEntry(String iface, int uid, int tag, long rx, long tx) {
- // TODO: extent to accept rxPackets/txPackets
- final int i = findIndex(iface, uid, tag);
+ public NetworkStats combineValues(Entry entry) {
+ final int i = findIndex(entry.iface, entry.uid, entry.tag);
if (i == -1) {
// only create new entry when positive contribution
- addEntry(iface, uid, tag, rx, tx);
+ addValues(entry);
} else {
- this.rx[i] += rx;
- this.tx[i] += tx;
+ rxBytes[i] += entry.rxBytes;
+ rxPackets[i] += entry.rxPackets;
+ txBytes[i] += entry.txBytes;
+ txPackets[i] += entry.txPackets;
}
return this;
}
@@ -280,15 +285,15 @@ public class NetworkStats implements Parcelable {
final int j = value.findIndex(entry.iface, entry.uid, entry.tag);
if (j == -1) {
// newly appearing row, return entire value
- entry.rxBytes = rx[i];
+ entry.rxBytes = rxBytes[i];
entry.rxPackets = rxPackets[i];
- entry.txBytes = tx[i];
+ entry.txBytes = txBytes[i];
entry.txPackets = txPackets[i];
} else {
// existing row, subtract remote value
- entry.rxBytes = rx[i] - value.rx[j];
+ entry.rxBytes = rxBytes[i] - value.rxBytes[j];
entry.rxPackets = rxPackets[i] - value.rxPackets[j];
- entry.txBytes = tx[i] - value.tx[j];
+ entry.txBytes = txBytes[i] - value.txBytes[j];
entry.txPackets = txPackets[i] - value.txPackets[j];
if (enforceMonotonic
&& (entry.rxBytes < 0 || entry.rxPackets < 0 || entry.txBytes < 0
@@ -321,9 +326,9 @@ public class NetworkStats implements Parcelable {
pw.print(" iface="); pw.print(iface[i]);
pw.print(" uid="); pw.print(uid[i]);
pw.print(" tag="); pw.print(tag[i]);
- pw.print(" rxBytes="); pw.print(rx[i]);
+ pw.print(" rxBytes="); pw.print(rxBytes[i]);
pw.print(" rxPackets="); pw.print(rxPackets[i]);
- pw.print(" txBytes="); pw.print(tx[i]);
+ pw.print(" txBytes="); pw.print(txBytes[i]);
pw.print(" txPackets="); pw.println(txPackets[i]);
}
}
@@ -347,9 +352,9 @@ public class NetworkStats implements Parcelable {
dest.writeStringArray(iface);
dest.writeIntArray(uid);
dest.writeIntArray(tag);
- dest.writeLongArray(rx);
+ dest.writeLongArray(rxBytes);
dest.writeLongArray(rxPackets);
- dest.writeLongArray(tx);
+ dest.writeLongArray(txBytes);
dest.writeLongArray(txPackets);
}
diff --git a/core/java/android/net/NetworkStatsHistory.java b/core/java/android/net/NetworkStatsHistory.java
index dd2945c..8bd1738 100644
--- a/core/java/android/net/NetworkStatsHistory.java
+++ b/core/java/android/net/NetworkStatsHistory.java
@@ -43,13 +43,20 @@ public class NetworkStatsHistory implements Parcelable {
private static final int VERSION_INIT = 1;
// TODO: teach about varint encoding to use less disk space
-
- public final long bucketDuration;
-
- public int bucketCount;
- public long[] bucketStart;
- public long[] rx;
- public long[] tx;
+ // TODO: extend to record rxPackets/txPackets
+
+ private final long bucketDuration;
+ private int bucketCount;
+ private long[] bucketStart;
+ private long[] rxBytes;
+ private long[] txBytes;
+
+ public static class Entry {
+ public long bucketStart;
+ public long bucketDuration;
+ public long rxBytes;
+ public long txBytes;
+ }
public NetworkStatsHistory(long bucketDuration) {
this(bucketDuration, 10);
@@ -58,16 +65,16 @@ public class NetworkStatsHistory implements Parcelable {
public NetworkStatsHistory(long bucketDuration, int initialSize) {
this.bucketDuration = bucketDuration;
bucketStart = new long[initialSize];
- rx = new long[initialSize];
- tx = new long[initialSize];
+ rxBytes = new long[initialSize];
+ txBytes = new long[initialSize];
bucketCount = 0;
}
public NetworkStatsHistory(Parcel in) {
bucketDuration = in.readLong();
bucketStart = readLongArray(in);
- rx = in.createLongArray();
- tx = in.createLongArray();
+ rxBytes = in.createLongArray();
+ txBytes = in.createLongArray();
bucketCount = bucketStart.length;
}
@@ -75,8 +82,8 @@ public class NetworkStatsHistory implements Parcelable {
public void writeToParcel(Parcel out, int flags) {
out.writeLong(bucketDuration);
writeLongArray(out, bucketStart, bucketCount);
- writeLongArray(out, rx, bucketCount);
- writeLongArray(out, tx, bucketCount);
+ writeLongArray(out, rxBytes, bucketCount);
+ writeLongArray(out, txBytes, bucketCount);
}
public NetworkStatsHistory(DataInputStream in) throws IOException {
@@ -85,8 +92,8 @@ public class NetworkStatsHistory implements Parcelable {
case VERSION_INIT: {
bucketDuration = in.readLong();
bucketStart = readLongArray(in);
- rx = readLongArray(in);
- tx = readLongArray(in);
+ rxBytes = readLongArray(in);
+ txBytes = readLongArray(in);
bucketCount = bucketStart.length;
break;
}
@@ -100,8 +107,8 @@ public class NetworkStatsHistory implements Parcelable {
out.writeInt(VERSION_INIT);
out.writeLong(bucketDuration);
writeLongArray(out, bucketStart, bucketCount);
- writeLongArray(out, rx, bucketCount);
- writeLongArray(out, tx, bucketCount);
+ writeLongArray(out, rxBytes, bucketCount);
+ writeLongArray(out, txBytes, bucketCount);
}
/** {@inheritDoc} */
@@ -109,6 +116,42 @@ public class NetworkStatsHistory implements Parcelable {
return 0;
}
+ public int size() {
+ return bucketCount;
+ }
+
+ public long getBucketDuration() {
+ return bucketDuration;
+ }
+
+ public long getStart() {
+ if (bucketCount > 0) {
+ return bucketStart[0];
+ } else {
+ return Long.MAX_VALUE;
+ }
+ }
+
+ public long getEnd() {
+ if (bucketCount > 0) {
+ return bucketStart[bucketCount - 1] + bucketDuration;
+ } else {
+ return Long.MIN_VALUE;
+ }
+ }
+
+ /**
+ * Return specific stats entry.
+ */
+ public Entry getValues(int i, Entry recycle) {
+ final Entry entry = recycle != null ? recycle : new Entry();
+ entry.bucketStart = bucketStart[i];
+ entry.bucketDuration = bucketDuration;
+ entry.rxBytes = rxBytes[i];
+ entry.txBytes = txBytes[i];
+ return entry;
+ }
+
/**
* Record that data traffic occurred in the given time range. Will
* distribute across internal buckets, creating new buckets as needed.
@@ -135,8 +178,8 @@ public class NetworkStatsHistory implements Parcelable {
final long overlap = Math.min(curEnd, end) - Math.max(curStart, start);
if (overlap > 0) {
- this.rx[i] += rx * overlap / duration;
- this.tx[i] += tx * overlap / duration;
+ this.rxBytes[i] += rx * overlap / duration;
+ this.txBytes[i] += tx * overlap / duration;
}
}
}
@@ -149,7 +192,7 @@ public class NetworkStatsHistory implements Parcelable {
for (int i = 0; i < input.bucketCount; i++) {
final long start = input.bucketStart[i];
final long end = start + input.bucketDuration;
- recordData(start, end, input.rx[i], input.tx[i]);
+ recordData(start, end, input.rxBytes[i], input.txBytes[i]);
}
}
@@ -179,8 +222,8 @@ public class NetworkStatsHistory implements Parcelable {
if (bucketCount >= bucketStart.length) {
final int newLength = Math.max(bucketStart.length, 10) * 3 / 2;
bucketStart = Arrays.copyOf(bucketStart, newLength);
- rx = Arrays.copyOf(rx, newLength);
- tx = Arrays.copyOf(tx, newLength);
+ rxBytes = Arrays.copyOf(rxBytes, newLength);
+ txBytes = Arrays.copyOf(txBytes, newLength);
}
// create gap when inserting bucket in middle
@@ -189,13 +232,13 @@ public class NetworkStatsHistory implements Parcelable {
final int length = bucketCount - index;
System.arraycopy(bucketStart, index, bucketStart, dstPos, length);
- System.arraycopy(rx, index, rx, dstPos, length);
- System.arraycopy(tx, index, tx, dstPos, length);
+ System.arraycopy(rxBytes, index, rxBytes, dstPos, length);
+ System.arraycopy(txBytes, index, txBytes, dstPos, length);
}
bucketStart[index] = start;
- rx[index] = 0;
- tx[index] = 0;
+ rxBytes[index] = 0;
+ txBytes[index] = 0;
bucketCount++;
}
@@ -216,8 +259,8 @@ public class NetworkStatsHistory implements Parcelable {
if (i > 0) {
final int length = bucketStart.length;
bucketStart = Arrays.copyOfRange(bucketStart, i, length);
- rx = Arrays.copyOfRange(rx, i, length);
- tx = Arrays.copyOfRange(tx, i, length);
+ rxBytes = Arrays.copyOfRange(rxBytes, i, length);
+ txBytes = Arrays.copyOfRange(txBytes, i, length);
bucketCount -= i;
}
}
@@ -226,9 +269,20 @@ public class NetworkStatsHistory implements Parcelable {
* Return interpolated data usage across the requested range. Interpolates
* across buckets, so values may be rounded slightly.
*/
- public long[] getTotalData(long start, long end, long[] outTotal) {
- long rx = 0;
- long tx = 0;
+ public Entry getValues(long start, long end, Entry recycle) {
+ return getValues(start, end, Long.MAX_VALUE, recycle);
+ }
+
+ /**
+ * Return interpolated data usage across the requested range. Interpolates
+ * across buckets, so values may be rounded slightly.
+ */
+ public Entry getValues(long start, long end, long now, Entry recycle) {
+ final Entry entry = recycle != null ? recycle : new Entry();
+ entry.bucketStart = start;
+ entry.bucketDuration = end - start;
+ entry.rxBytes = 0;
+ entry.txBytes = 0;
for (int i = bucketCount - 1; i >= 0; i--) {
final long curStart = bucketStart[i];
@@ -239,19 +293,19 @@ public class NetworkStatsHistory implements Parcelable {
// bucket is newer than record; keep looking
if (curStart > end) continue;
+ // include full value for active buckets, otherwise only fractional
+ final boolean activeBucket = curStart < now && curEnd > now;
final long overlap = Math.min(curEnd, end) - Math.max(curStart, start);
- if (overlap > 0) {
- rx += this.rx[i] * overlap / bucketDuration;
- tx += this.tx[i] * overlap / bucketDuration;
+ if (activeBucket || overlap == bucketDuration) {
+ entry.rxBytes += rxBytes[i];
+ entry.txBytes += txBytes[i];
+ } else if (overlap > 0) {
+ entry.rxBytes += rxBytes[i] * overlap / bucketDuration;
+ entry.txBytes += txBytes[i] * overlap / bucketDuration;
}
}
- if (outTotal == null || outTotal.length != 2) {
- outTotal = new long[2];
- }
- outTotal[0] = rx;
- outTotal[1] = tx;
- return outTotal;
+ return entry;
}
/**
@@ -292,8 +346,8 @@ public class NetworkStatsHistory implements Parcelable {
for (int i = start; i < bucketCount; i++) {
pw.print(prefix);
pw.print(" bucketStart="); pw.print(bucketStart[i]);
- pw.print(" rx="); pw.print(rx[i]);
- pw.print(" tx="); pw.println(tx[i]);
+ pw.print(" rxBytes="); pw.print(rxBytes[i]);
+ pw.print(" txBytes="); pw.println(txBytes[i]);
}
}
diff --git a/core/java/android/net/http/AndroidHttpClient.java b/core/java/android/net/http/AndroidHttpClient.java
index 641a576..c534e58 100644
--- a/core/java/android/net/http/AndroidHttpClient.java
+++ b/core/java/android/net/http/AndroidHttpClient.java
@@ -65,7 +65,7 @@ import android.util.Base64;
import android.util.Log;
/**
- * Subclass of the Apache {@link DefaultHttpClient} that is configured with
+ * Implementation of the Apache {@link DefaultHttpClient} that is configured with
* reasonable default settings and registered schemes for Android, and
* also lets the user add {@link HttpRequestInterceptor} classes.
* Don't create this directly, use the {@link #newInstance} factory method.
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index ec67683..36b3ca0 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -17,6 +17,7 @@
package android.provider;
import android.accounts.Account;
+import android.app.Activity;
import android.content.ContentProviderClient;
import android.content.ContentProviderOperation;
import android.content.ContentResolver;
@@ -6251,6 +6252,28 @@ public final class ContactsContract {
public static final String NOTES = "notes";
/**
+ * The Activity action to open the group in the source app (e.g.
+ * {@link Intent#ACTION_VIEW}). Can be NULL if the group does not have a dedicated viewer.
+ * This is used in conjunction with {@link #ACTION_URI}: In order to show an "Open in
+ * (sourceapp)"-button, both of these fields must be set
+ * <p>
+ * Type: TEXT
+ */
+ public static final String ACTION = "action";
+
+
+ /**
+ * Uri to open the group in the source app.
+ * Can be NULL if the group does not have a dedicated viewer.
+ * This is used in conjunction with {@link #ACTION}: In order to show an "Open in
+ * (sourceapp)"-button, both of these fields must be set
+ * <p>
+ * Type: TEXT
+ */
+ public static final String ACTION_URI = "action_uri";
+
+
+ /**
* The ID of this group if it is a System Group, i.e. a group that has a special meaning
* to the sync adapter, null otherwise.
* <P>Type: TEXT</P>
diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java
index b23e3ce..ff16c18 100755
--- a/core/java/android/server/BluetoothService.java
+++ b/core/java/android/server/BluetoothService.java
@@ -2403,6 +2403,7 @@ public class BluetoothService extends IBluetooth.Stub {
convertToAdapterState(state));
intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_CONNECTION_STATE,
convertToAdapterState(prevState));
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
mContext.sendBroadcast(intent, BLUETOOTH_PERM);
Log.d(TAG, "CONNECTION_STATE_CHANGE: " + device + ": "
+ prevState + " -> " + state);
diff --git a/core/java/android/speech/tts/AudioPlaybackHandler.java b/core/java/android/speech/tts/AudioPlaybackHandler.java
index 8ef4295..1210941 100644
--- a/core/java/android/speech/tts/AudioPlaybackHandler.java
+++ b/core/java/android/speech/tts/AudioPlaybackHandler.java
@@ -384,7 +384,7 @@ class AudioPlaybackHandler {
}
count += written;
}
-
+ param.mBytesWritten += count;
param.mLogger.onPlaybackStart();
}
@@ -396,14 +396,16 @@ class AudioPlaybackHandler {
params.mLogger.onWriteData();
}
- // Flush all remaining data to the audio track, stop it and release
- // all it's resources.
+ // Wait for the audio track to stop playing, and then release it's resources.
private void handleSynthesisDone(SynthesisMessageParams params) {
if (DBG) Log.d(TAG, "handleSynthesisDone()");
final AudioTrack audioTrack = params.getAudioTrack();
try {
if (audioTrack != null) {
+ if (DBG) Log.d(TAG, "Waiting for audio track to complete : " +
+ audioTrack.hashCode());
+ blockUntilDone(params);
if (DBG) Log.d(TAG, "Releasing audio track [" + audioTrack.hashCode() + "]");
// The last call to AudioTrack.write( ) will return only after
// all data from the audioTrack has been sent to the mixer, so
@@ -417,6 +419,18 @@ class AudioPlaybackHandler {
}
}
+ private static void blockUntilDone(SynthesisMessageParams params) {
+ if (params.mAudioTrack == null || params.mBytesWritten <= 0) {
+ return;
+ }
+
+ final AudioTrack track = params.mAudioTrack;
+ final int bytesPerFrame = getBytesPerFrame(params.mAudioFormat);
+ final int lengthInBytes = params.mBytesWritten;
+
+ blockUntilDone(track, bytesPerFrame, lengthInBytes);
+ }
+
private void handleSynthesisCompleteDataAvailable(MessageParams msg) {
final SynthesisMessageParams params = (SynthesisMessageParams) msg;
if (DBG) Log.d(TAG, "completeAudioAvailable(" + params + ")");
@@ -455,13 +469,18 @@ class AudioPlaybackHandler {
}
- private static void blockUntilDone(AudioTrack audioTrack, int bytesPerFrame, int length) {
- int lengthInFrames = length / bytesPerFrame;
+ private static void blockUntilDone(AudioTrack audioTrack, int bytesPerFrame,
+ int lengthInBytes) {
+ int lengthInFrames = lengthInBytes / bytesPerFrame;
int currentPosition = 0;
while ((currentPosition = audioTrack.getPlaybackHeadPosition()) < lengthInFrames) {
+ if (audioTrack.getPlayState() != AudioTrack.PLAYSTATE_PLAYING) {
+ break;
+ }
+
long estimatedTimeMs = ((lengthInFrames - currentPosition) * 1000) /
audioTrack.getSampleRate();
- audioTrack.getPlayState();
+
if (DBG) Log.d(TAG, "About to sleep for : " + estimatedTimeMs + " ms," +
" Playback position : " + currentPosition);
try {
diff --git a/core/java/android/speech/tts/SynthesisMessageParams.java b/core/java/android/speech/tts/SynthesisMessageParams.java
index caf02ef..ffe70e2 100644
--- a/core/java/android/speech/tts/SynthesisMessageParams.java
+++ b/core/java/android/speech/tts/SynthesisMessageParams.java
@@ -32,7 +32,9 @@ final class SynthesisMessageParams extends MessageParams {
final float mPan;
final EventLogger mLogger;
- public volatile AudioTrack mAudioTrack;
+ volatile AudioTrack mAudioTrack;
+ // Not volatile, accessed only from the synthesis thread.
+ int mBytesWritten;
private final LinkedList<ListEntry> mDataBufferList = new LinkedList<ListEntry>();
@@ -52,6 +54,7 @@ final class SynthesisMessageParams extends MessageParams {
// initially null.
mAudioTrack = null;
+ mBytesWritten = 0;
}
@Override
diff --git a/core/java/android/view/ActionProvider.java b/core/java/android/view/ActionProvider.java
index 6491da0..5601dc5 100644
--- a/core/java/android/view/ActionProvider.java
+++ b/core/java/android/view/ActionProvider.java
@@ -28,7 +28,9 @@ import android.content.Context;
* {@link android.app.ActionBar} as a substitute for the menu item when the item is
* displayed as an action item. Also the provider is responsible for performing a
* default action if a menu item placed on the overflow menu of the ActionBar is
- * selected and none of the menu item callbacks has handled the selection.
+ * selected and none of the menu item callbacks has handled the selection. For this
+ * case the provider can also optionally provide a sub-menu for accomplishing the
+ * task at hand.
* </p>
* <p>
* There are two ways for using an action provider for creating and handling of action views:
@@ -76,7 +78,7 @@ public abstract class ActionProvider {
* Performs an optional default action.
* <p>
* For the case of an action provider placed in a menu item not shown as an action this
- * method is invoked if none of the callbacks for processing menu selection has handled
+ * method is invoked if previous callbacks for processing menu selection has handled
* the event.
* </p>
* <p>
@@ -104,11 +106,36 @@ public abstract class ActionProvider {
* </ul>
* </p>
* <p>
- * The default implementation does not perform any action.
+ * The default implementation does not perform any action and returns false.
* </p>
+ */
+ public boolean onPerformDefaultAction() {
+ return false;
+ }
+
+ /**
+ * Determines if this ActionProvider has a submenu associated with it.
+ *
+ * <p>Associated submenus will be shown when an action view is not. This
+ * provider instance will receive a call to {@link #onPrepareSubMenu(SubMenu)}
+ * after the call to {@link #onPerformDefaultAction()} and before a submenu is
+ * displayed to the user.
+ *
+ * @return true if the item backed by this provider should have an associated submenu
+ */
+ public boolean hasSubMenu() {
+ return false;
+ }
+
+ /**
+ * Called to prepare an associated submenu for the menu item backed by this ActionProvider.
+ *
+ * <p>if {@link #hasSubMenu()} returns true, this method will be called when the
+ * menu item is selected to prepare the submenu for presentation to the user. Apps
+ * may use this to create or alter submenu content right before display.
*
- * @param actionView A view created by {@link #onCreateActionView()}.
+ * @param subMenu Submenu that will be displayed
*/
- public void onPerformDefaultAction(View actionView) {
+ public void onPrepareSubMenu(SubMenu subMenu) {
}
}
diff --git a/core/java/android/view/MenuInflater.java b/core/java/android/view/MenuInflater.java
index a7f0cba..c46e43a 100644
--- a/core/java/android/view/MenuInflater.java
+++ b/core/java/android/view/MenuInflater.java
@@ -166,7 +166,12 @@ public class MenuInflater {
// Add the item if it hasn't been added (if the item was
// a submenu, it would have been added already)
if (!menuState.hasAddedItem()) {
- menuState.addItem();
+ if (menuState.itemActionProvider != null &&
+ menuState.itemActionProvider.hasSubMenu()) {
+ menuState.addSubMenuItem();
+ } else {
+ menuState.addItem();
+ }
}
} else if (tagName.equals(XML_MENU)) {
reachedEndOfMenu = true;
@@ -270,6 +275,8 @@ public class MenuInflater {
private String itemListenerMethodName;
+ private ActionProvider itemActionProvider;
+
private static final int defaultGroupId = NO_ID;
private static final int defaultItemId = NO_ID;
private static final int defaultItemCategory = 0;
@@ -347,6 +354,19 @@ public class MenuInflater {
itemActionViewClassName = a.getString(com.android.internal.R.styleable.MenuItem_actionViewClass);
itemActionProviderClassName = a.getString(com.android.internal.R.styleable.MenuItem_actionProviderClass);
+ final boolean hasActionProvider = itemActionProviderClassName != null;
+ if (hasActionProvider && itemActionViewLayout == 0 && itemActionViewClassName == null) {
+ itemActionProvider = newInstance(itemActionProviderClassName,
+ ACTION_PROVIDER_CONSTRUCTOR_SIGNATURE,
+ mActionProviderConstructorArguments);
+ } else {
+ if (hasActionProvider) {
+ Log.w(LOG_TAG, "Ignoring attribute 'actionProviderClass'."
+ + " Action view already specified.");
+ }
+ itemActionProvider = null;
+ }
+
a.recycle();
itemAdded = false;
@@ -406,16 +426,8 @@ public class MenuInflater {
+ " Action view already specified.");
}
}
- if (itemActionProviderClassName != null) {
- if (!actionViewSpecified) {
- ActionProvider actionProvider = newInstance(itemActionProviderClassName,
- ACTION_PROVIDER_CONSTRUCTOR_SIGNATURE,
- mActionProviderConstructorArguments);
- item.setActionProvider(actionProvider);
- } else {
- Log.w(LOG_TAG, "Ignoring attribute 'itemActionProviderClass'."
- + " Action view already specified.");
- }
+ if (itemActionProvider != null) {
+ item.setActionProvider(itemActionProvider);
}
}
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 7ba86a5..3ae10fe 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -9089,6 +9089,52 @@ public class WebView extends AbsoluteLayout
}
}
+ /**
+ * Begin collecting per-tile profiling data
+ *
+ * @hide only used by profiling tests
+ */
+ public void tileProfilingStart() {
+ nativeTileProfilingStart();
+ }
+ /**
+ * Return per-tile profiling data
+ *
+ * @hide only used by profiling tests
+ */
+ public float tileProfilingStop() {
+ return nativeTileProfilingStop();
+ }
+
+ /** @hide only used by profiling tests */
+ public void tileProfilingClear() {
+ nativeTileProfilingClear();
+ }
+ /** @hide only used by profiling tests */
+ public int tileProfilingNumFrames() {
+ return nativeTileProfilingNumFrames();
+ }
+ /** @hide only used by profiling tests */
+ public int tileProfilingNumTilesInFrame(int frame) {
+ return nativeTileProfilingNumTilesInFrame(frame);
+ }
+ /** @hide only used by profiling tests */
+ public int tileProfilingGetX(int frame, int tile) {
+ return nativeTileProfilingGetX(frame, tile);
+ }
+ /** @hide only used by profiling tests */
+ public int tileProfilingGetY(int frame, int tile) {
+ return nativeTileProfilingGetY(frame, tile);
+ }
+ /** @hide only used by profiling tests */
+ public boolean tileProfilingGetReady(int frame, int tile) {
+ return nativeTileProfilingGetReady(frame, tile);
+ }
+ /** @hide only used by profiling tests */
+ public int tileProfilingGetLevel(int frame, int tile) {
+ return nativeTileProfilingGetLevel(frame, tile);
+ }
+
private native int nativeCacheHitFramePointer();
private native boolean nativeCacheHitIsPlugin();
private native Rect nativeCacheHitNodeBounds();
@@ -9211,6 +9257,15 @@ public class WebView extends AbsoluteLayout
private native void nativeStopGL();
private native Rect nativeSubtractLayers(Rect content);
private native int nativeTextGeneration();
+ private native void nativeTileProfilingStart();
+ private native float nativeTileProfilingStop();
+ private native void nativeTileProfilingClear();
+ private native int nativeTileProfilingNumFrames();
+ private native int nativeTileProfilingNumTilesInFrame(int frame);
+ private native int nativeTileProfilingGetX(int frame, int tile);
+ private native int nativeTileProfilingGetY(int frame, int tile);
+ private native boolean nativeTileProfilingGetReady(int frame, int tile);
+ private native int nativeTileProfilingGetLevel(int frame, int tile);
// Never call this version except by updateCachedTextfield(String) -
// we always want to pass in our generation number.
private native void nativeUpdateCachedTextfield(String updatedText,
diff --git a/core/java/android/widget/ActivityChooserModel.java b/core/java/android/widget/ActivityChooserModel.java
index 83f80ff..32c44d8 100644
--- a/core/java/android/widget/ActivityChooserModel.java
+++ b/core/java/android/widget/ActivityChooserModel.java
@@ -126,7 +126,7 @@ public class ActivityChooserModel extends DataSetObservable {
*/
// This cannot be done by a simple comparator since an Activity weight
// is computed from history. Note that Activity implements Comparable.
- public void sort(Intent intent, List<Activity> activities,
+ public void sort(Intent intent, List<ActivityResolveInfo> activities,
List<HistoricalRecord> historicalRecords);
}
@@ -215,7 +215,7 @@ public class ActivityChooserModel extends DataSetObservable {
/**
* List of activities that can handle the current intent.
*/
- private final List<Activity> mActivitys = new ArrayList<Activity>();
+ private final List<ActivityResolveInfo> mActivites = new ArrayList<ActivityResolveInfo>();
/**
* List with historical choice records.
@@ -311,9 +311,6 @@ public class ActivityChooserModel extends DataSetObservable {
* @return The model.
*/
public static ActivityChooserModel get(Context context, String historyFileName) {
- if (historyFileName == null) {
- return new ActivityChooserModel(context, historyFileName);
- }
synchronized (sRegistryLock) {
ActivityChooserModel dataModel = sDataModelRegistry.get(historyFileName);
if (dataModel == null) {
@@ -380,7 +377,7 @@ public class ActivityChooserModel extends DataSetObservable {
*/
public int getActivityCount() {
synchronized (mInstanceLock) {
- return mActivitys.size();
+ return mActivites.size();
}
}
@@ -389,12 +386,12 @@ public class ActivityChooserModel extends DataSetObservable {
*
* @return The activity.
*
- * @see Activity
+ * @see ActivityResolveInfo
* @see #setIntent(Intent)
*/
public ResolveInfo getActivity(int index) {
synchronized (mInstanceLock) {
- return mActivitys.get(index).resolveInfo;
+ return mActivites.get(index).resolveInfo;
}
}
@@ -406,10 +403,10 @@ public class ActivityChooserModel extends DataSetObservable {
* @return The index if found, -1 otherwise.
*/
public int getActivityIndex(ResolveInfo activity) {
- List<Activity> activities = mActivitys;
+ List<ActivityResolveInfo> activities = mActivites;
final int activityCount = activities.size();
for (int i = 0; i < activityCount; i++) {
- Activity currentActivity = activities.get(i);
+ ActivityResolveInfo currentActivity = activities.get(i);
if (currentActivity.resolveInfo == activity) {
return i;
}
@@ -433,8 +430,8 @@ public class ActivityChooserModel extends DataSetObservable {
* @see HistoricalRecord
*/
public Intent chooseActivity(int index) {
- Activity chosenActivity = mActivitys.get(index);
- Activity defaultActivity = mActivitys.get(0);
+ ActivityResolveInfo chosenActivity = mActivites.get(index);
+ ActivityResolveInfo defaultActivity = mActivites.get(0);
ComponentName chosenName = new ComponentName(
chosenActivity.resolveInfo.activityInfo.packageName,
@@ -460,8 +457,8 @@ public class ActivityChooserModel extends DataSetObservable {
*/
public ResolveInfo getDefaultActivity() {
synchronized (mInstanceLock) {
- if (!mActivitys.isEmpty()) {
- return mActivitys.get(0).resolveInfo;
+ if (!mActivites.isEmpty()) {
+ return mActivites.get(0).resolveInfo;
}
}
return null;
@@ -478,8 +475,8 @@ public class ActivityChooserModel extends DataSetObservable {
* @param index The index of the activity to set as default.
*/
public void setDefaultActivity(int index) {
- Activity newDefaultActivity = mActivitys.get(index);
- Activity oldDefaultActivity = mActivitys.get(0);
+ ActivityResolveInfo newDefaultActivity = mActivites.get(index);
+ ActivityResolveInfo oldDefaultActivity = mActivites.get(0);
final float weight;
if (oldDefaultActivity != null) {
@@ -572,8 +569,8 @@ public class ActivityChooserModel extends DataSetObservable {
*/
private void sortActivities() {
synchronized (mInstanceLock) {
- if (mActivitySorter != null && !mActivitys.isEmpty()) {
- mActivitySorter.sort(mIntent, mActivitys,
+ if (mActivitySorter != null && !mActivites.isEmpty()) {
+ mActivitySorter.sort(mIntent, mActivites,
Collections.unmodifiableList(mHistoricalRecords));
notifyChanged();
}
@@ -661,14 +658,14 @@ public class ActivityChooserModel extends DataSetObservable {
* Loads the activities.
*/
private void loadActivitiesLocked() {
- mActivitys.clear();
+ mActivites.clear();
if (mIntent != null) {
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);
- mActivitys.add(new Activity(resolveInfo));
+ mActivites.add(new ActivityResolveInfo(resolveInfo));
}
sortActivities();
} else {
@@ -797,7 +794,7 @@ public class ActivityChooserModel extends DataSetObservable {
/**
* Represents an activity.
*/
- public final class Activity implements Comparable<Activity> {
+ public final class ActivityResolveInfo implements Comparable<ActivityResolveInfo> {
/**
* The {@link ResolveInfo} of the activity.
@@ -814,7 +811,7 @@ public class ActivityChooserModel extends DataSetObservable {
*
* @param resolveInfo activity {@link ResolveInfo}.
*/
- public Activity(ResolveInfo resolveInfo) {
+ public ActivityResolveInfo(ResolveInfo resolveInfo) {
this.resolveInfo = resolveInfo;
}
@@ -834,14 +831,14 @@ public class ActivityChooserModel extends DataSetObservable {
if (getClass() != obj.getClass()) {
return false;
}
- Activity other = (Activity) obj;
+ ActivityResolveInfo other = (ActivityResolveInfo) obj;
if (Float.floatToIntBits(weight) != Float.floatToIntBits(other.weight)) {
return false;
}
return true;
}
- public int compareTo(Activity another) {
+ public int compareTo(ActivityResolveInfo another) {
return Float.floatToIntBits(another.weight) - Float.floatToIntBits(weight);
}
@@ -862,18 +859,18 @@ public class ActivityChooserModel extends DataSetObservable {
private final class DefaultSorter implements ActivitySorter {
private static final float WEIGHT_DECAY_COEFFICIENT = 0.95f;
- private final Map<String, Activity> mPackageNameToActivityMap =
- new HashMap<String, Activity>();
+ private final Map<String, ActivityResolveInfo> mPackageNameToActivityMap =
+ new HashMap<String, ActivityResolveInfo>();
- public void sort(Intent intent, List<Activity> activities,
+ public void sort(Intent intent, List<ActivityResolveInfo> activities,
List<HistoricalRecord> historicalRecords) {
- Map<String, Activity> packageNameToActivityMap =
+ Map<String, ActivityResolveInfo> packageNameToActivityMap =
mPackageNameToActivityMap;
packageNameToActivityMap.clear();
final int activityCount = activities.size();
for (int i = 0; i < activityCount; i++) {
- Activity activity = activities.get(i);
+ ActivityResolveInfo activity = activities.get(i);
activity.weight = 0.0f;
String packageName = activity.resolveInfo.activityInfo.packageName;
packageNameToActivityMap.put(packageName, activity);
@@ -884,9 +881,11 @@ public class ActivityChooserModel extends DataSetObservable {
for (int i = lastShareIndex; i >= 0; i--) {
HistoricalRecord historicalRecord = historicalRecords.get(i);
String packageName = historicalRecord.activity.getPackageName();
- Activity activity = packageNameToActivityMap.get(packageName);
- activity.weight += historicalRecord.weight * nextRecordWeight;
- nextRecordWeight = nextRecordWeight * WEIGHT_DECAY_COEFFICIENT;
+ ActivityResolveInfo activity = packageNameToActivityMap.get(packageName);
+ if (activity != null) {
+ activity.weight += historicalRecord.weight * nextRecordWeight;
+ nextRecordWeight = nextRecordWeight * WEIGHT_DECAY_COEFFICIENT;
+ }
}
Collections.sort(activities);
diff --git a/core/java/android/widget/ActivityChooserView.java b/core/java/android/widget/ActivityChooserView.java
index 2fe8162..f500b39 100644
--- a/core/java/android/widget/ActivityChooserView.java
+++ b/core/java/android/widget/ActivityChooserView.java
@@ -16,10 +16,7 @@
package android.widget;
-import android.app.AlertDialog;
-import android.app.AlertDialog.Builder;
import android.content.Context;
-import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
@@ -27,12 +24,20 @@ import android.content.res.TypedArray;
import android.database.DataSetObserver;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
-import android.os.Debug;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.ActivityChooserModel;
import android.widget.ActivityChooserModel.ActivityChooserModelClient;
+import android.widget.AdapterView;
+import android.widget.BaseAdapter;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.ListPopupWindow;
+import android.widget.PopupWindow;
+import android.widget.TextView;
import com.android.internal.R;
@@ -56,11 +61,6 @@ import com.android.internal.R;
* </li>
* </ul>
* </p>
- * </p>
- * This view is backed by a {@link ActivityChooserModel}. Calling {@link #showPopup()}
- * while this view is attached to the view hierarchy will show a popup with
- * activities while if the view is not attached it will show a dialog.
- * </p>
*
* @hide
*/
@@ -92,29 +92,21 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod
private final ImageButton mDefaultActionButton;
/**
- * The header for handlers list.
+ * Observer for the model data.
*/
- private final View mListHeaderView;
+ private final DataSetObserver mModelDataSetOberver = new DataSetObserver() {
- /**
- * The footer for handlers list.
- */
- private final View mListFooterView;
-
- /**
- * The title of the header view.
- */
- private TextView mListHeaderViewTitle;
-
- /**
- * The title for expanding the activities list.
- */
- private final String mListHeaderViewTitleSelectDefault;
-
- /**
- * The title if no activity exist.
- */
- private final String mListHeaderViewTitleNoActivities;
+ @Override
+ public void onChanged() {
+ super.onChanged();
+ mAdapter.notifyDataSetChanged();
+ }
+ @Override
+ public void onInvalidated() {
+ super.onInvalidated();
+ mAdapter.notifyDataSetInvalidated();
+ }
+ };
/**
* Popup window for showing the activity overflow list.
@@ -122,11 +114,6 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod
private ListPopupWindow mListPopupWindow;
/**
- * Alert dialog for showing the activity overflow list.
- */
- private AlertDialog mAlertDialog;
-
- /**
* Listener for the dismissal of the popup/alert.
*/
private PopupWindow.OnDismissListener mOnDismissListener;
@@ -147,16 +134,6 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod
private boolean mIsAttachedToWindow;
/**
- * Flag whether this view is showing an alert dialog.
- */
- private boolean mIsShowingAlertDialog;
-
- /**
- * Flag whether this view is showing a popup window.
- */
- private boolean mIsShowingPopuWindow;
-
- /**
* Create a new instance.
*
* @param context The application environment.
@@ -195,8 +172,7 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod
Drawable expandActivityOverflowButtonDrawable = attributesArray.getDrawable(
R.styleable.ActivityChooserView_expandActivityOverflowButtonDrawable);
- LayoutInflater inflater = (LayoutInflater) context.getSystemService(
- Context.LAYOUT_INFLATER_SERVICE);
+ LayoutInflater inflater = LayoutInflater.from(mContext);
inflater.inflate(R.layout.activity_chooser_view, this, true);
mCallbacks = new Callbacks();
@@ -211,15 +187,6 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod
mExpandActivityOverflowButton.setOnClickListener(mCallbacks);
mExpandActivityOverflowButton.setBackgroundDrawable(expandActivityOverflowButtonDrawable);
- mListHeaderView = inflater.inflate(R.layout.activity_chooser_list_header, null);
- mListFooterView = inflater.inflate(R.layout.activity_chooser_list_footer, null);
-
- mListHeaderViewTitle = (TextView) mListHeaderView.findViewById(R.id.title);
- mListHeaderViewTitleSelectDefault = context.getString(
- R.string.activity_chooser_view_select_default);
- mListHeaderViewTitleNoActivities = context.getString(
- R.string.activity_chooser_view_no_activities);
-
mAdapter = new ActivityChooserViewAdapter();
mAdapter.registerDataSetObserver(new DataSetObserver() {
@Override
@@ -262,7 +229,7 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod
* @return True if the popup was shown, false if already showing.
*/
public boolean showPopup() {
- if (isShowingPopup()) {
+ if (isShowingPopup() || !mIsAttachedToWindow) {
return false;
}
mIsSelectingDefaultActivity = false;
@@ -276,38 +243,29 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod
* @param maxActivityCount The max number of activities to display.
*/
private void showPopupUnchecked(int maxActivityCount) {
- mAdapter.setMaxActivityCount(maxActivityCount);
- if (mIsSelectingDefaultActivity) {
- if (mAdapter.getActivityCount() > 0) {
- mListHeaderViewTitle.setText(mListHeaderViewTitleSelectDefault);
- } else {
- mListHeaderViewTitle.setText(mListHeaderViewTitleNoActivities);
- }
- mAdapter.setHeaderView(mListHeaderView);
- } else {
- mAdapter.setHeaderView(null);
+ if (mAdapter.getDataModel() == null) {
+ throw new IllegalStateException("No data model. Did you call #setDataModel?");
}
- if (mAdapter.getActivityCount() > maxActivityCount + 1) {
- mAdapter.setFooterView(mListFooterView);
+ mAdapter.setMaxActivityCount(maxActivityCount);
+
+ final int activityCount = mAdapter.getActivityCount();
+ if (maxActivityCount != ActivityChooserViewAdapter.MAX_ACTIVITY_COUNT_UNLIMITED
+ && activityCount > maxActivityCount + 1) {
+ mAdapter.setShowFooterView(true);
} else {
- mAdapter.setFooterView(null);
+ mAdapter.setShowFooterView(false);
}
- if (!mIsAttachedToWindow || mIsShowingAlertDialog) {
- AlertDialog alertDialog = getAlertDilalog();
- if (!alertDialog.isShowing()) {
- alertDialog.setCustomTitle(this);
- alertDialog.show();
- mIsShowingAlertDialog = true;
- }
- } else {
- ListPopupWindow popupWindow = getListPopupWindow();
- if (!popupWindow.isShowing()) {
- popupWindow.setContentWidth(mAdapter.measureContentWidth());
- popupWindow.show();
- mIsShowingPopuWindow = true;
+ ListPopupWindow popupWindow = getListPopupWindow();
+ if (!popupWindow.isShowing()) {
+ if (mIsSelectingDefaultActivity) {
+ mAdapter.setShowDefaultActivity(true);
+ } else {
+ mAdapter.setShowDefaultActivity(false);
}
+ popupWindow.setContentWidth(mAdapter.measureContentWidth());
+ popupWindow.show();
}
}
@@ -317,12 +275,7 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod
* @return True if dismissed, false if already dismissed.
*/
public boolean dismissPopup() {
- if (!isShowingPopup()) {
- return false;
- }
- if (mIsShowingAlertDialog) {
- getAlertDilalog().dismiss();
- } else if (mIsShowingPopuWindow) {
+ if (isShowingPopup()) {
getListPopupWindow().dismiss();
}
return true;
@@ -334,12 +287,7 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod
* @return True if the popup is shown.
*/
public boolean isShowingPopup() {
- if (mIsShowingAlertDialog) {
- return getAlertDilalog().isShowing();
- } else if (mIsShowingPopuWindow) {
- return getListPopupWindow().isShowing();
- }
- return false;
+ return getListPopupWindow().isShowing();
}
@Override
@@ -347,6 +295,7 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod
super.onAttachedToWindow();
ActivityChooserModel dataModel = mAdapter.getDataModel();
if (dataModel != null) {
+ dataModel.registerObserver(mModelDataSetOberver);
dataModel.readHistoricalData();
}
mIsAttachedToWindow = true;
@@ -357,6 +306,7 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod
super.onDetachedFromWindow();
ActivityChooserModel dataModel = mAdapter.getDataModel();
if (dataModel != null) {
+ dataModel.unregisterObserver(mModelDataSetOberver);
dataModel.persistHistoricalData();
}
mIsAttachedToWindow = false;
@@ -371,13 +321,11 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- mActivityChooserContent.layout(left, top, right, bottom);
- if (mIsShowingPopuWindow) {
- if (isShown()) {
- showPopupUnchecked(mAdapter.getMaxActivityCount());
- } else {
- dismissPopup();
- }
+ mActivityChooserContent.layout(0, 0, right - left, bottom - top);
+ if (getListPopupWindow().isShowing()) {
+ showPopupUnchecked(mAdapter.getMaxActivityCount());
+ } else {
+ dismissPopup();
}
}
@@ -429,22 +377,6 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod
}
/**
- * Gets the alert dialog which is lazily initialized.
- *
- * @return The popup.
- */
- private AlertDialog getAlertDilalog() {
- if (mAlertDialog == null) {
- Builder builder = new Builder(getContext());
- builder.setAdapter(mAdapter, null);
- mAlertDialog = builder.create();
- mAlertDialog.getListView().setOnItemClickListener(mCallbacks);
- mAlertDialog.setOnDismissListener(mCallbacks);
- }
- return mAlertDialog;
- }
-
- /**
* Updates the buttons state.
*/
private void updateButtons() {
@@ -469,24 +401,23 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod
* Interface implementation to avoid publishing them in the APIs.
*/
private class Callbacks implements AdapterView.OnItemClickListener,
- View.OnClickListener, View.OnLongClickListener, PopupWindow.OnDismissListener,
- DialogInterface.OnDismissListener {
+ View.OnClickListener, View.OnLongClickListener, PopupWindow.OnDismissListener {
// AdapterView#OnItemClickListener
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
ActivityChooserViewAdapter adapter = (ActivityChooserViewAdapter) parent.getAdapter();
final int itemViewType = adapter.getItemViewType(position);
switch (itemViewType) {
- case ActivityChooserViewAdapter.ITEM_VIEW_TYPE_HEADER: {
- /* do nothing */
- } break;
case ActivityChooserViewAdapter.ITEM_VIEW_TYPE_FOOTER: {
showPopupUnchecked(ActivityChooserViewAdapter.MAX_ACTIVITY_COUNT_UNLIMITED);
} break;
case ActivityChooserViewAdapter.ITEM_VIEW_TYPE_ACTIVITY: {
dismissPopup();
if (mIsSelectingDefaultActivity) {
- mAdapter.getDataModel().setDefaultActivity(position);
+ // The item at position zero is the default already.
+ if (position > 0) {
+ mAdapter.getDataModel().setDefaultActivity(position);
+ }
} else {
// The first item in the model is default action => adjust index
Intent launchIntent = mAdapter.getDataModel().chooseActivity(position + 1);
@@ -530,16 +461,6 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod
// PopUpWindow.OnDismissListener#onDismiss
public void onDismiss() {
- mIsShowingPopuWindow = false;
- notifyOnDismissListener();
- }
-
- // DialogInterface.OnDismissListener#onDismiss
- @Override
- public void onDismiss(DialogInterface dialog) {
- mIsShowingAlertDialog = false;
- AlertDialog alertDialog = (AlertDialog) dialog;
- alertDialog.setCustomTitle(null);
notifyOnDismissListener();
}
@@ -559,59 +480,35 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod
public static final int MAX_ACTIVITY_COUNT_DEFAULT = 4;
- private static final int ITEM_VIEW_TYPE_HEADER = 0;
+ private static final int ITEM_VIEW_TYPE_ACTIVITY = 0;
- private static final int ITEM_VIEW_TYPE_ACTIVITY = 1;
-
- private static final int ITEM_VIEW_TYPE_FOOTER = 2;
+ private static final int ITEM_VIEW_TYPE_FOOTER = 1;
private static final int ITEM_VIEW_TYPE_COUNT = 3;
- private final DataSetObserver mDataSetOberver = new DataSetObserver() {
-
- @Override
- public void onChanged() {
- super.onChanged();
- notifyDataSetChanged();
- }
- @Override
- public void onInvalidated() {
- super.onInvalidated();
- notifyDataSetInvalidated();
- }
- };
-
private ActivityChooserModel mDataModel;
private int mMaxActivityCount = MAX_ACTIVITY_COUNT_DEFAULT;
- private ResolveInfo mDefaultActivity;
+ private boolean mShowDefaultActivity;
- private View mHeaderView;
-
- private View mFooterView;
+ private boolean mShowFooterView;
public void setDataModel(ActivityChooserModel dataModel) {
+ ActivityChooserModel oldDataModel = mAdapter.getDataModel();
+ if (oldDataModel != null && isShown()) {
+ oldDataModel.unregisterObserver(mModelDataSetOberver);
+ }
mDataModel = dataModel;
- mDataModel.registerObserver(mDataSetOberver);
- notifyDataSetChanged();
- }
-
- @Override
- public void notifyDataSetChanged() {
- if (mDataModel.getActivityCount() > 0) {
- mDefaultActivity = mDataModel.getDefaultActivity();
- } else {
- mDefaultActivity = null;
+ if (dataModel != null && isShown()) {
+ dataModel.registerObserver(mModelDataSetOberver);
}
- super.notifyDataSetChanged();
+ notifyDataSetChanged();
}
@Override
public int getItemViewType(int position) {
- if (mHeaderView != null && position == 0) {
- return ITEM_VIEW_TYPE_HEADER;
- } else if (mFooterView != null && position == getCount() - 1) {
+ if (mShowFooterView && position == getCount() - 1) {
return ITEM_VIEW_TYPE_FOOTER;
} else {
return ITEM_VIEW_TYPE_ACTIVITY;
@@ -626,14 +523,11 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod
public int getCount() {
int count = 0;
int activityCount = mDataModel.getActivityCount();
- if (activityCount > 0) {
+ if (!mShowDefaultActivity && mDataModel.getDefaultActivity() != null) {
activityCount--;
}
count = Math.min(activityCount, mMaxActivityCount);
- if (mHeaderView != null) {
- count++;
- }
- if (mFooterView != null) {
+ if (mShowFooterView) {
count++;
}
return count;
@@ -642,16 +536,13 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod
public Object getItem(int position) {
final int itemViewType = getItemViewType(position);
switch (itemViewType) {
- case ITEM_VIEW_TYPE_HEADER:
- return mHeaderView;
case ITEM_VIEW_TYPE_FOOTER:
- return mFooterView;
+ return null;
case ITEM_VIEW_TYPE_ACTIVITY:
- int targetIndex = (mHeaderView == null) ? position : position - 1;
- if (mDefaultActivity != null) {
- targetIndex++;
+ if (!mShowDefaultActivity && mDataModel.getDefaultActivity() != null) {
+ position++;
}
- return mDataModel.getActivity(targetIndex);
+ return mDataModel.getActivity(position);
default:
throw new IllegalArgumentException();
}
@@ -661,27 +552,19 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod
return position;
}
- @Override
- public boolean isEnabled(int position) {
- final int itemViewType = getItemViewType(position);
- switch (itemViewType) {
- case ITEM_VIEW_TYPE_HEADER:
- return false;
- case ITEM_VIEW_TYPE_FOOTER:
- case ITEM_VIEW_TYPE_ACTIVITY:
- return true;
- default:
- throw new IllegalArgumentException();
- }
- }
-
public View getView(int position, View convertView, ViewGroup parent) {
final int itemViewType = getItemViewType(position);
switch (itemViewType) {
- case ITEM_VIEW_TYPE_HEADER:
- return mHeaderView;
case ITEM_VIEW_TYPE_FOOTER:
- return mFooterView;
+ if (convertView == null || convertView.getId() != ITEM_VIEW_TYPE_FOOTER) {
+ convertView = LayoutInflater.from(getContext()).inflate(
+ R.layout.activity_chooser_view_list_item, parent, false);
+ convertView.setId(ITEM_VIEW_TYPE_FOOTER);
+ TextView titleView = (TextView) convertView.findViewById(R.id.title);
+ titleView.setText(mContext.getString(
+ R.string.activity_chooser_view_see_all));
+ }
+ return convertView;
case ITEM_VIEW_TYPE_ACTIVITY:
if (convertView == null || convertView.getId() != R.id.list_item) {
convertView = LayoutInflater.from(getContext()).inflate(
@@ -695,6 +578,12 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod
// Set the title.
TextView titleView = (TextView) convertView.findViewById(R.id.title);
titleView.setText(activity.loadLabel(packageManager));
+ // Highlight the default.
+ if (mShowDefaultActivity && position == 0) {
+ convertView.setActivated(true);
+ } else {
+ convertView.setActivated(false);
+ }
return convertView;
default:
throw new IllegalArgumentException();
@@ -702,7 +591,7 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod
}
public int measureContentWidth() {
- // The user may have specified some of the target not to be show but we
+ // The user may have specified some of the target not to be shown but we
// want to measure all of them since after expansion they should fit.
final int oldMaxActivityCount = mMaxActivityCount;
mMaxActivityCount = MAX_ACTIVITY_COUNT_UNLIMITED;
@@ -733,19 +622,12 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod
}
public ResolveInfo getDefaultActivity() {
- return mDefaultActivity;
- }
-
- public void setHeaderView(View headerView) {
- if (mHeaderView != headerView) {
- mHeaderView = headerView;
- notifyDataSetChanged();
- }
+ return mDataModel.getDefaultActivity();
}
- public void setFooterView(View footerView) {
- if (mFooterView != footerView) {
- mFooterView = footerView;
+ public void setShowFooterView(boolean showFooterView) {
+ if (mShowFooterView != showFooterView) {
+ mShowFooterView = showFooterView;
notifyDataSetChanged();
}
}
@@ -761,5 +643,12 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod
public ActivityChooserModel getDataModel() {
return mDataModel;
}
+
+ public void setShowDefaultActivity(boolean showDefaultActivity) {
+ if (mShowDefaultActivity != showDefaultActivity) {
+ mShowDefaultActivity = showDefaultActivity;
+ notifyDataSetChanged();
+ }
+ }
}
}
diff --git a/core/java/android/widget/CalendarView.java b/core/java/android/widget/CalendarView.java
index f8c76f2..1b713c3 100644
--- a/core/java/android/widget/CalendarView.java
+++ b/core/java/android/widget/CalendarView.java
@@ -21,6 +21,7 @@ import com.android.internal.R;
import android.annotation.Widget;
import android.app.Service;
import android.content.Context;
+import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.database.DataSetObserver;
import android.graphics.Canvas;
@@ -239,21 +240,11 @@ public class CalendarView extends FrameLayout {
private String[] mDayLabels;
/**
- * Temporary instance to avoid multiple instantiations.
- */
- private Calendar mTempDate = Calendar.getInstance();
-
- /**
* The first day of the week.
*/
private int mFirstDayOfWeek;
/**
- * The first day of the focused month.
- */
- private Calendar mFirstDayOfMonth = Calendar.getInstance();
-
- /**
* Which month should be displayed/highlighted [0-11].
*/
private int mCurrentMonthDisplayed;
@@ -289,14 +280,24 @@ public class CalendarView extends FrameLayout {
private ScrollStateRunnable mScrollStateChangedRunnable = new ScrollStateRunnable();
/**
+ * Temporary instance to avoid multiple instantiations.
+ */
+ private Calendar mTempDate;
+
+ /**
+ * The first day of the focused month.
+ */
+ private Calendar mFirstDayOfMonth;
+
+ /**
* The start date of the range supported by this picker.
*/
- private Calendar mMinDate = Calendar.getInstance();
+ private Calendar mMinDate;
/**
* The end date of the range supported by this picker.
*/
- private Calendar mMaxDate = Calendar.getInstance();
+ private Calendar mMaxDate;
/**
* Date format for parsing dates.
@@ -304,6 +305,11 @@ public class CalendarView extends FrameLayout {
private final java.text.DateFormat mDateFormat = new SimpleDateFormat(DATE_FORMAT);
/**
+ * The current locale.
+ */
+ private Locale mCurrentLocale;
+
+ /**
* The callback used to indicate the user changes the date.
*/
public interface OnDateChangeListener {
@@ -330,6 +336,9 @@ public class CalendarView extends FrameLayout {
public CalendarView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, 0);
+ // initialization based on locale
+ setCurrentLocale(Locale.getDefault());
+
TypedValue calendarViewStyle = new TypedValue();
context.getTheme().resolveAttribute(R.attr.calendarViewStyle, calendarViewStyle, true);
TypedArray attributesArray = context.obtainStyledAttributes(calendarViewStyle.resourceId,
@@ -366,6 +375,7 @@ public class CalendarView extends FrameLayout {
com.android.internal.R.styleable.TextAppearance);
mDateTextSize = dateTextAppearance.getDimensionPixelSize(
R.styleable.TextAppearance_textSize, DEFAULT_DATE_TEXT_SIZE);
+ dateTextAppearance.recycle();
int weekDayTextAppearanceResId = attributesArray.getResourceId(
R.styleable.CalendarView_weekDayTextAppearance,
@@ -413,6 +423,12 @@ public class CalendarView extends FrameLayout {
return mListView.isEnabled();
}
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ setCurrentLocale(newConfig.locale);
+ }
+
/**
* Gets the minimal date supported by this {@link CalendarView} in milliseconds
* since January 1, 1970 00:00:00 in {@link TimeZone#getDefault()} time
@@ -624,6 +640,41 @@ public class CalendarView extends FrameLayout {
}
/**
+ * Sets the current locale.
+ *
+ * @param locale The current locale.
+ */
+ private void setCurrentLocale(Locale locale) {
+ if (locale.equals(mCurrentLocale)) {
+ return;
+ }
+
+ mCurrentLocale = locale;
+
+ mTempDate = getCalendarForLocale(mTempDate, locale);
+ mFirstDayOfMonth = getCalendarForLocale(mFirstDayOfMonth, locale);
+ mMinDate = getCalendarForLocale(mMinDate, locale);
+ mMaxDate = getCalendarForLocale(mMaxDate, locale);
+ }
+
+ /**
+ * Gets a calendar for locale bootstrapped with the value of a given calendar.
+ *
+ * @param oldCalendar The old calendar.
+ * @param locale The locale.
+ */
+ private Calendar getCalendarForLocale(Calendar oldCalendar, Locale locale) {
+ if (oldCalendar == null) {
+ return Calendar.getInstance(locale);
+ } else {
+ final long currentTimeMillis = oldCalendar.getTimeInMillis();
+ Calendar newCalendar = Calendar.getInstance(locale);
+ newCalendar.setTimeInMillis(currentTimeMillis);
+ return newCalendar;
+ }
+ }
+
+ /**
* @return True if the <code>firstDate</code> is the same as the <code>
* secondDate</code>.
*/
diff --git a/core/java/android/widget/DatePicker.java b/core/java/android/widget/DatePicker.java
index 30fb927..4812283 100644
--- a/core/java/android/widget/DatePicker.java
+++ b/core/java/android/widget/DatePicker.java
@@ -16,10 +16,9 @@
package android.widget;
-import com.android.internal.R;
-
import android.annotation.Widget;
import android.content.Context;
+import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.os.Parcel;
import android.os.Parcelable;
@@ -33,6 +32,8 @@ import android.view.LayoutInflater;
import android.view.accessibility.AccessibilityEvent;
import android.widget.NumberPicker.OnValueChangeListener;
+import com.android.internal.R;
+
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
@@ -89,23 +90,23 @@ public class DatePicker extends FrameLayout {
private final CalendarView mCalendarView;
- private OnDateChangedListener mOnDateChangedListener;
+ private Locale mCurrentLocale;
- private Locale mMonthLocale;
+ private OnDateChangedListener mOnDateChangedListener;
- private final Calendar mTempDate = Calendar.getInstance();
+ private String[] mShortMonths;
- private final int mNumberOfMonths = mTempDate.getActualMaximum(Calendar.MONTH) + 1;
+ private final java.text.DateFormat mDateFormat = new SimpleDateFormat(DATE_FORMAT);
- private final String[] mShortMonths = new String[mNumberOfMonths];
+ private int mNumberOfMonths;
- private final java.text.DateFormat mDateFormat = new SimpleDateFormat(DATE_FORMAT);
+ private Calendar mTempDate;
- private final Calendar mMinDate = Calendar.getInstance();
+ private Calendar mMinDate;
- private final Calendar mMaxDate = Calendar.getInstance();
+ private Calendar mMaxDate;
- private final Calendar mCurrentDate = Calendar.getInstance();
+ private Calendar mCurrentDate;
private boolean mIsEnabled = DEFAULT_ENABLED_STATE;
@@ -137,6 +138,9 @@ public class DatePicker extends FrameLayout {
public DatePicker(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
+ // initialization based on locale
+ setCurrentLocale(Locale.getDefault());
+
TypedArray attributesArray = context.obtainStyledAttributes(attrs, R.styleable.DatePicker,
defStyle, 0);
boolean spinnersShown = attributesArray.getBoolean(R.styleable.DatePicker_spinnersShown,
@@ -213,7 +217,7 @@ public class DatePicker extends FrameLayout {
mMonthSpinner = (NumberPicker) findViewById(R.id.month);
mMonthSpinner.setMinValue(0);
mMonthSpinner.setMaxValue(mNumberOfMonths - 1);
- mMonthSpinner.setDisplayedValues(getShortMonths());
+ mMonthSpinner.setDisplayedValues(mShortMonths);
mMonthSpinner.setOnLongPressUpdateInterval(200);
mMonthSpinner.setOnValueChangedListener(onChangeListener);
@@ -363,6 +367,12 @@ public class DatePicker extends FrameLayout {
event.getText().add(selectedDateUtterance);
}
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ setCurrentLocale(newConfig.locale);
+ }
+
/**
* Gets whether the {@link CalendarView} is shown.
*
@@ -411,6 +421,48 @@ public class DatePicker extends FrameLayout {
}
/**
+ * Sets the current locale.
+ *
+ * @param locale The current locale.
+ */
+ private void setCurrentLocale(Locale locale) {
+ if (locale.equals(mCurrentLocale)) {
+ return;
+ }
+
+ mCurrentLocale = locale;
+
+ mTempDate = getCalendarForLocale(mTempDate, locale);
+ mMinDate = getCalendarForLocale(mMinDate, locale);
+ mMaxDate = getCalendarForLocale(mMaxDate, locale);
+ mCurrentDate = getCalendarForLocale(mCurrentDate, locale);
+
+ mNumberOfMonths = mTempDate.getActualMaximum(Calendar.MONTH) + 1;
+ mShortMonths = new String[mNumberOfMonths];
+ for (int i = 0; i < mNumberOfMonths; i++) {
+ mShortMonths[i] = DateUtils.getMonthString(Calendar.JANUARY + i,
+ DateUtils.LENGTH_MEDIUM);
+ }
+ }
+
+ /**
+ * Gets a calendar for locale bootstrapped with the value of a given calendar.
+ *
+ * @param oldCalendar The old calendar.
+ * @param locale The locale.
+ */
+ private Calendar getCalendarForLocale(Calendar oldCalendar, Locale locale) {
+ if (oldCalendar == null) {
+ return Calendar.getInstance(locale);
+ } else {
+ final long currentTimeMillis = oldCalendar.getTimeInMillis();
+ Calendar newCalendar = Calendar.getInstance(locale);
+ newCalendar.setTimeInMillis(currentTimeMillis);
+ return newCalendar;
+ }
+ }
+
+ /**
* Reorders the spinners according to the date format that is
* explicitly set by the user and if no such is set fall back
* to the current locale's default format.
@@ -507,23 +559,6 @@ public class DatePicker extends FrameLayout {
}
}
- /**
- * @return The short month abbreviations.
- */
- private String[] getShortMonths() {
- final Locale currentLocale = Locale.getDefault();
- if (currentLocale.equals(mMonthLocale)) {
- return mShortMonths;
- } else {
- for (int i = 0; i < mNumberOfMonths; i++) {
- mShortMonths[i] = DateUtils.getMonthString(Calendar.JANUARY + i,
- DateUtils.LENGTH_MEDIUM);
- }
- mMonthLocale = currentLocale;
- return mShortMonths;
- }
- }
-
private boolean isNewDate(int year, int month, int dayOfMonth) {
return (mCurrentDate.get(Calendar.YEAR) != year
|| mCurrentDate.get(Calendar.MONTH) != dayOfMonth
@@ -569,7 +604,7 @@ public class DatePicker extends FrameLayout {
// make sure the month names are a zero based array
// with the months in the month spinner
- String[] displayedValues = Arrays.copyOfRange(getShortMonths(),
+ String[] displayedValues = Arrays.copyOfRange(mShortMonths,
mMonthSpinner.getMinValue(), mMonthSpinner.getMaxValue() + 1);
mMonthSpinner.setDisplayedValues(displayedValues);
diff --git a/core/java/android/widget/ShareActionProvider.java b/core/java/android/widget/ShareActionProvider.java
index d6e426f..2e0cc62 100644
--- a/core/java/android/widget/ShareActionProvider.java
+++ b/core/java/android/widget/ShareActionProvider.java
@@ -18,18 +18,23 @@ package android.widget;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
import android.graphics.drawable.Drawable;
import android.util.TypedValue;
import android.view.ActionProvider;
+import android.view.Menu;
import android.view.MenuItem;
+import android.view.MenuItem.OnMenuItemClickListener;
+import android.view.SubMenu;
import android.view.View;
import com.android.internal.R;
/**
* This is a provider for a share action. It is responsible for creating views
- * that enable data sharing and also to perform a default action for showing
- * a share dialog.
+ * that enable data sharing and also to show a sub menu with sharing activities
+ * if the hosting item is placed on the overflow menu.
* <p>
* Here is how to use the action provider with custom backing file in a {@link MenuItem}:
* </p>
@@ -47,15 +52,13 @@ import com.android.internal.R;
* // {@link ActionProvider#onCreateActionView()} which uses the backing file name. Omit this
* // line if using the default share history file is desired.
* mShareActionProvider.setShareHistoryFileName("custom_share_history.xml");
- * // Get the action view and hold onto it to set the share intent.
- * mActionView = menuItem.getActionView();
* . . .
* }
*
* // Somewhere in the application.
* public void doShare(Intent shareIntent) {
* // When you want to share set the share intent.
- * mShareActionProvider.setShareIntent(mActionView, shareIntent);
+ * mShareActionProvider.setShareIntent(shareIntent);
* }
* </pre>
* </code>
@@ -70,11 +73,34 @@ import com.android.internal.R;
public class ShareActionProvider extends ActionProvider {
/**
+ * The default for the maximal number of activities shown in the sub-menu.
+ */
+ private static final int DEFAULT_INITIAL_ACTIVITY_COUNT = 4;
+
+ /**
+ * The the maximum number activities shown in the sub-menu.
+ */
+ private int mMaxShownActivityCount = DEFAULT_INITIAL_ACTIVITY_COUNT;
+
+ /**
+ * Listener for handling menu item clicks.
+ */
+ private final ShareMenuItemOnMenuItemClickListener mOnMenuItemClickListener =
+ new ShareMenuItemOnMenuItemClickListener();
+
+ /**
* The default name for storing share history.
*/
public static final String DEFAULT_SHARE_HISTORY_FILE_NAME = "share_history.xml";
+ /**
+ * Context for accessing resources.
+ */
private final Context mContext;
+
+ /**
+ * The name of the file with share history data.
+ */
private String mShareHistoryFileName = DEFAULT_SHARE_HISTORY_FILE_NAME;
/**
@@ -92,13 +118,17 @@ public class ShareActionProvider extends ActionProvider {
*/
@Override
public View onCreateActionView() {
+ // Create the view and set its data model.
ActivityChooserModel dataModel = ActivityChooserModel.get(mContext, mShareHistoryFileName);
ActivityChooserView activityChooserView = new ActivityChooserView(mContext);
activityChooserView.setActivityChooserModel(dataModel);
+
+ // Lookup and set the expand action icon.
TypedValue outTypedValue = new TypedValue();
mContext.getTheme().resolveAttribute(R.attr.actionModeShareDrawable, outTypedValue, true);
Drawable drawable = mContext.getResources().getDrawable(outTypedValue.resourceId);
activityChooserView.setExpandActivityOverflowButtonDrawable(drawable);
+
return activityChooserView;
}
@@ -106,12 +136,40 @@ public class ShareActionProvider extends ActionProvider {
* {@inheritDoc}
*/
@Override
- public void onPerformDefaultAction(View actionView) {
- if (actionView instanceof ActivityChooserView) {
- ActivityChooserView activityChooserView = (ActivityChooserView) actionView;
- activityChooserView.showPopup();
- } else {
- throw new IllegalArgumentException("actionView not instance of ActivityChooserView");
+ public boolean hasSubMenu() {
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onPrepareSubMenu(SubMenu subMenu) {
+ // Clear since the order of items may change.
+ subMenu.clear();
+
+ ActivityChooserModel dataModel = ActivityChooserModel.get(mContext, mShareHistoryFileName);
+ PackageManager packageManager = mContext.getPackageManager();
+
+ final int expandedActivityCount = dataModel.getActivityCount();
+ final int collapsedActivityCount = Math.min(expandedActivityCount, mMaxShownActivityCount);
+
+ // Populate the sub-menu with a sub set of the activities.
+ for (int i = 0; i < collapsedActivityCount; i++) {
+ ResolveInfo activity = dataModel.getActivity(i);
+ subMenu.add(0, i, i, activity.loadLabel(packageManager))
+ .setIcon(activity.loadIcon(packageManager))
+ .setOnMenuItemClickListener(mOnMenuItemClickListener);
+ }
+
+ // Add a sub-menu for showing all activities as a list item.
+ SubMenu expandedSubMenu = subMenu.addSubMenu(Menu.NONE, collapsedActivityCount,
+ collapsedActivityCount, mContext.getString(R.string.activity_chooser_view_see_all));
+ for (int i = 0; i < expandedActivityCount; i++) {
+ ResolveInfo activity = dataModel.getActivity(i);
+ expandedSubMenu.add(0, i, i, activity.loadLabel(packageManager))
+ .setIcon(activity.loadIcon(packageManager))
+ .setOnMenuItemClickListener(mOnMenuItemClickListener);
}
}
@@ -147,18 +205,29 @@ public class ShareActionProvider extends ActionProvider {
* </code>
* </p>
*
- * @param actionView An action view created by {@link #onCreateActionView()}.
* @param shareIntent The share intent.
*
* @see Intent#ACTION_SEND
* @see Intent#ACTION_SEND_MULTIPLE
*/
- public void setShareIntent(View actionView, Intent shareIntent) {
- if (actionView instanceof ActivityChooserView) {
- ActivityChooserView activityChooserView = (ActivityChooserView) actionView;
- activityChooserView.getDataModel().setIntent(shareIntent);
- } else {
- throw new IllegalArgumentException("actionView not instance of ActivityChooserView");
+ public void setShareIntent(Intent shareIntent) {
+ ActivityChooserModel dataModel = ActivityChooserModel.get(mContext,
+ mShareHistoryFileName);
+ dataModel.setIntent(shareIntent);
+ }
+
+ /**
+ * Reusable listener for handling share item clicks.
+ */
+ private class ShareMenuItemOnMenuItemClickListener implements OnMenuItemClickListener {
+ @Override
+ public boolean onMenuItemClick(MenuItem item) {
+ ActivityChooserModel dataModel = ActivityChooserModel.get(mContext,
+ mShareHistoryFileName);
+ final int itemId = item.getItemId();
+ Intent launchIntent = dataModel.chooseActivity(itemId);
+ mContext.startActivity(launchIntent);
+ return true;
}
}
}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 766b520..6b4e454 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -16,11 +16,6 @@
package android.widget;
-import com.android.internal.util.FastMath;
-import com.android.internal.widget.EditableInputConnection;
-
-import org.xmlpull.v1.XmlPullParserException;
-
import android.R;
import android.content.ClipData;
import android.content.ClipData.Item;
@@ -132,6 +127,11 @@ import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
import android.widget.RemoteViews.RemoteView;
+import com.android.internal.util.FastMath;
+import com.android.internal.widget.EditableInputConnection;
+
+import org.xmlpull.v1.XmlPullParserException;
+
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.text.BreakIterator;
@@ -260,9 +260,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
class Drawables {
final Rect mCompoundRect = new Rect();
- Drawable mDrawableTop, mDrawableBottom, mDrawableLeft, mDrawableRight;
- int mDrawableSizeTop, mDrawableSizeBottom, mDrawableSizeLeft, mDrawableSizeRight;
- int mDrawableWidthTop, mDrawableWidthBottom, mDrawableHeightLeft, mDrawableHeightRight;
+ Drawable mDrawableTop, mDrawableBottom, mDrawableLeft, mDrawableRight,
+ mDrawableStart, mDrawableEnd;
+ int mDrawableSizeTop, mDrawableSizeBottom, mDrawableSizeLeft, mDrawableSizeRight,
+ mDrawableSizeStart, mDrawableSizeEnd;
+ int mDrawableWidthTop, mDrawableWidthBottom, mDrawableHeightLeft, mDrawableHeightRight,
+ mDrawableHeightStart, mDrawableHeightEnd;
int mDrawablePadding;
}
private Drawables mDrawables;
@@ -352,6 +355,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
INHERIT, GRAVITY, TEXT_START, TEXT_END, CENTER, VIEW_START, VIEW_END;
}
+ private boolean bResolvedDrawables = false;
+
/*
* Kick-start the font cache for the zygote process (to pay the cost of
* initializing freetype for our default font only once).
@@ -494,7 +499,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
int buffertype = 0;
boolean selectallonfocus = false;
Drawable drawableLeft = null, drawableTop = null, drawableRight = null,
- drawableBottom = null;
+ drawableBottom = null, drawableStart = null, drawableEnd = null;
int drawablePadding = 0;
int ellipsize = -1;
boolean singleLine = false;
@@ -571,6 +576,14 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
drawableBottom = a.getDrawable(attr);
break;
+ case com.android.internal.R.styleable.TextView_drawableStart:
+ drawableStart = a.getDrawable(attr);
+ break;
+
+ case com.android.internal.R.styleable.TextView_drawableEnd:
+ drawableEnd = a.getDrawable(attr);
+ break;
+
case com.android.internal.R.styleable.TextView_drawablePadding:
drawablePadding = a.getDimensionPixelSize(attr, drawablePadding);
break;
@@ -980,6 +993,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
setCompoundDrawablesWithIntrinsicBounds(
drawableLeft, drawableTop, drawableRight, drawableBottom);
+ setRelativeDrawablesIfNeeded(drawableStart, drawableEnd);
setCompoundDrawablePadding(drawablePadding);
// Same as setSingleLine(), but make sure the transformation method and the maximum number
@@ -1105,6 +1119,42 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
setTypeface(tf, styleIndex);
}
+ private void setRelativeDrawablesIfNeeded(Drawable start, Drawable end) {
+ boolean hasRelativeDrawables = (start != null) || (end != null);
+ if (hasRelativeDrawables) {
+ Drawables dr = mDrawables;
+ if (dr == null) {
+ mDrawables = dr = new Drawables();
+ }
+ final Rect compoundRect = dr.mCompoundRect;
+ int[] state = getDrawableState();
+ if (start != null) {
+ start.setBounds(0, 0, start.getIntrinsicWidth(), start.getIntrinsicHeight());
+ start.setState(state);
+ start.copyBounds(compoundRect);
+ start.setCallback(this);
+
+ dr.mDrawableStart = start;
+ dr.mDrawableSizeStart = compoundRect.width();
+ dr.mDrawableHeightStart = compoundRect.height();
+ } else {
+ dr.mDrawableSizeStart = dr.mDrawableHeightStart = 0;
+ }
+ if (end != null) {
+ end.setBounds(0, 0, end.getIntrinsicWidth(), end.getIntrinsicHeight());
+ end.setState(state);
+ end.copyBounds(compoundRect);
+ end.setCallback(this);
+
+ dr.mDrawableEnd = end;
+ dr.mDrawableSizeEnd = compoundRect.width();
+ dr.mDrawableHeightEnd = compoundRect.height();
+ } else {
+ dr.mDrawableSizeEnd = dr.mDrawableHeightEnd = 0;
+ }
+ }
+ }
+
@Override
public void setEnabled(boolean enabled) {
if (enabled == isEnabled()) {
@@ -1119,6 +1169,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
}
super.setEnabled(enabled);
+ prepareCursorControllers();
}
/**
@@ -1410,6 +1461,40 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
/**
+ * Returns the start padding of the view, plus space for the start
+ * Drawable if any.
+ *
+ * @hide
+ */
+ public int getCompoundPaddingStart() {
+ resolveDrawables();
+ switch(getResolvedLayoutDirection()) {
+ default:
+ case LAYOUT_DIRECTION_LTR:
+ return getCompoundPaddingLeft();
+ case LAYOUT_DIRECTION_RTL:
+ return getCompoundPaddingRight();
+ }
+ }
+
+ /**
+ * Returns the end padding of the view, plus space for the end
+ * Drawable if any.
+ *
+ * @hide
+ */
+ public int getCompoundPaddingEnd() {
+ resolveDrawables();
+ switch(getResolvedLayoutDirection()) {
+ default:
+ case LAYOUT_DIRECTION_LTR:
+ return getCompoundPaddingRight();
+ case LAYOUT_DIRECTION_RTL:
+ return getCompoundPaddingLeft();
+ }
+ }
+
+ /**
* Returns the extended top padding of the view, including both the
* top Drawable if any and any extra space to keep more than maxLines
* of text from showing. It is only valid to call this after measuring.
@@ -1492,6 +1577,26 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
/**
+ * Returns the total start padding of the view, including the start
+ * Drawable if any.
+ *
+ * @hide
+ */
+ public int getTotalPaddingStart() {
+ return getCompoundPaddingStart();
+ }
+
+ /**
+ * Returns the total end padding of the view, including the end
+ * Drawable if any.
+ *
+ * @hide
+ */
+ public int getTotalPaddingEnd() {
+ return getCompoundPaddingEnd();
+ }
+
+ /**
* Returns the total top padding of the view, including the top
* Drawable if any, the extra space to keep more than maxLines
* from showing, and the vertical offset for gravity, if any.
@@ -1678,6 +1783,185 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
/**
+ * Sets the Drawables (if any) to appear to the start of, above,
+ * to the end of, and below the text. Use null if you do not
+ * want a Drawable there. The Drawables must already have had
+ * {@link Drawable#setBounds} called.
+ *
+ * @attr ref android.R.styleable#TextView_drawableStart
+ * @attr ref android.R.styleable#TextView_drawableTop
+ * @attr ref android.R.styleable#TextView_drawableEnd
+ * @attr ref android.R.styleable#TextView_drawableBottom
+ *
+ * @hide
+ */
+ public void setCompoundDrawablesRelative(Drawable start, Drawable top,
+ Drawable end, Drawable bottom) {
+ Drawables dr = mDrawables;
+
+ final boolean drawables = start != null || top != null
+ || end != null || bottom != null;
+
+ if (!drawables) {
+ // Clearing drawables... can we free the data structure?
+ if (dr != null) {
+ if (dr.mDrawablePadding == 0) {
+ mDrawables = null;
+ } else {
+ // We need to retain the last set padding, so just clear
+ // out all of the fields in the existing structure.
+ if (dr.mDrawableStart != null) dr.mDrawableStart.setCallback(null);
+ dr.mDrawableStart = null;
+ if (dr.mDrawableTop != null) dr.mDrawableTop.setCallback(null);
+ dr.mDrawableTop = null;
+ if (dr.mDrawableEnd != null) dr.mDrawableEnd.setCallback(null);
+ dr.mDrawableEnd = null;
+ if (dr.mDrawableBottom != null) dr.mDrawableBottom.setCallback(null);
+ dr.mDrawableBottom = null;
+ dr.mDrawableSizeStart = dr.mDrawableHeightStart = 0;
+ dr.mDrawableSizeEnd = dr.mDrawableHeightEnd = 0;
+ dr.mDrawableSizeTop = dr.mDrawableWidthTop = 0;
+ dr.mDrawableSizeBottom = dr.mDrawableWidthBottom = 0;
+ }
+ }
+ } else {
+ if (dr == null) {
+ mDrawables = dr = new Drawables();
+ }
+
+ if (dr.mDrawableStart != start && dr.mDrawableStart != null) {
+ dr.mDrawableStart.setCallback(null);
+ }
+ dr.mDrawableStart = start;
+
+ if (dr.mDrawableTop != top && dr.mDrawableTop != null) {
+ dr.mDrawableTop.setCallback(null);
+ }
+ dr.mDrawableTop = top;
+
+ if (dr.mDrawableEnd != end && dr.mDrawableEnd != null) {
+ dr.mDrawableEnd.setCallback(null);
+ }
+ dr.mDrawableEnd = end;
+
+ if (dr.mDrawableBottom != bottom && dr.mDrawableBottom != null) {
+ dr.mDrawableBottom.setCallback(null);
+ }
+ dr.mDrawableBottom = bottom;
+
+ final Rect compoundRect = dr.mCompoundRect;
+ int[] state;
+
+ state = getDrawableState();
+
+ if (start != null) {
+ start.setState(state);
+ start.copyBounds(compoundRect);
+ start.setCallback(this);
+ dr.mDrawableSizeStart = compoundRect.width();
+ dr.mDrawableHeightStart = compoundRect.height();
+ } else {
+ dr.mDrawableSizeStart = dr.mDrawableHeightStart = 0;
+ }
+
+ if (end != null) {
+ end.setState(state);
+ end.copyBounds(compoundRect);
+ end.setCallback(this);
+ dr.mDrawableSizeEnd = compoundRect.width();
+ dr.mDrawableHeightEnd = compoundRect.height();
+ } else {
+ dr.mDrawableSizeEnd = dr.mDrawableHeightEnd = 0;
+ }
+
+ if (top != null) {
+ top.setState(state);
+ top.copyBounds(compoundRect);
+ top.setCallback(this);
+ dr.mDrawableSizeTop = compoundRect.height();
+ dr.mDrawableWidthTop = compoundRect.width();
+ } else {
+ dr.mDrawableSizeTop = dr.mDrawableWidthTop = 0;
+ }
+
+ if (bottom != null) {
+ bottom.setState(state);
+ bottom.copyBounds(compoundRect);
+ bottom.setCallback(this);
+ dr.mDrawableSizeBottom = compoundRect.height();
+ dr.mDrawableWidthBottom = compoundRect.width();
+ } else {
+ dr.mDrawableSizeBottom = dr.mDrawableWidthBottom = 0;
+ }
+ }
+
+ resolveDrawables();
+ invalidate();
+ requestLayout();
+ }
+
+ /**
+ * Sets the Drawables (if any) to appear to the start of, above,
+ * to the end of, and below the text. Use 0 if you do not
+ * want a Drawable there. The Drawables' bounds will be set to
+ * their intrinsic bounds.
+ *
+ * @param start Resource identifier of the start Drawable.
+ * @param top Resource identifier of the top Drawable.
+ * @param end Resource identifier of the end Drawable.
+ * @param bottom Resource identifier of the bottom Drawable.
+ *
+ * @attr ref android.R.styleable#TextView_drawableStart
+ * @attr ref android.R.styleable#TextView_drawableTop
+ * @attr ref android.R.styleable#TextView_drawableEnd
+ * @attr ref android.R.styleable#TextView_drawableBottom
+ *
+ * @hide
+ */
+ public void setCompoundDrawablesRelativeWithIntrinsicBounds(int start, int top, int end,
+ int bottom) {
+ resetResolvedDrawables();
+ final Resources resources = getContext().getResources();
+ setCompoundDrawablesRelativeWithIntrinsicBounds(
+ start != 0 ? resources.getDrawable(start) : null,
+ top != 0 ? resources.getDrawable(top) : null,
+ end != 0 ? resources.getDrawable(end) : null,
+ bottom != 0 ? resources.getDrawable(bottom) : null);
+ }
+
+ /**
+ * Sets the Drawables (if any) to appear to the start of, above,
+ * to the end of, and below the text. Use null if you do not
+ * want a Drawable there. The Drawables' bounds will be set to
+ * their intrinsic bounds.
+ *
+ * @attr ref android.R.styleable#TextView_drawableStart
+ * @attr ref android.R.styleable#TextView_drawableTop
+ * @attr ref android.R.styleable#TextView_drawableEnd
+ * @attr ref android.R.styleable#TextView_drawableBottom
+ *
+ * @hide
+ */
+ public void setCompoundDrawablesRelativeWithIntrinsicBounds(Drawable start, Drawable top,
+ Drawable end, Drawable bottom) {
+
+ resetResolvedDrawables();
+ if (start != null) {
+ start.setBounds(0, 0, start.getIntrinsicWidth(), start.getIntrinsicHeight());
+ }
+ if (end != null) {
+ end.setBounds(0, 0, end.getIntrinsicWidth(), end.getIntrinsicHeight());
+ }
+ if (top != null) {
+ top.setBounds(0, 0, top.getIntrinsicWidth(), top.getIntrinsicHeight());
+ }
+ if (bottom != null) {
+ bottom.setBounds(0, 0, bottom.getIntrinsicWidth(), bottom.getIntrinsicHeight());
+ }
+ setCompoundDrawablesRelative(start, top, end, bottom);
+ }
+
+ /**
* Returns drawables for the left, top, right, and bottom borders.
*/
public Drawable[] getCompoundDrawables() {
@@ -1692,6 +1976,22 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
/**
+ * Returns drawables for the start, top, end, and bottom borders.
+ *
+ * @hide
+ */
+ public Drawable[] getCompoundDrawablesRelative() {
+ final Drawables dr = mDrawables;
+ if (dr != null) {
+ return new Drawable[] {
+ dr.mDrawableStart, dr.mDrawableTop, dr.mDrawableEnd, dr.mDrawableBottom
+ };
+ } else {
+ return new Drawable[] { null, null, null, null };
+ }
+ }
+
+ /**
* Sets the size of the padding between the compound drawables and
* the text.
*
@@ -2494,6 +2794,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
if (dr.mDrawableRight != null && dr.mDrawableRight.isStateful()) {
dr.mDrawableRight.setState(state);
}
+ if (dr.mDrawableStart != null && dr.mDrawableStart.isStateful()) {
+ dr.mDrawableStart.setState(state);
+ }
+ if (dr.mDrawableEnd != null && dr.mDrawableEnd.isStateful()) {
+ dr.mDrawableEnd.setState(state);
+ }
}
}
@@ -2841,8 +3147,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
// Do not change the movement method for text that support text selection as it
// would prevent an arbitrary cursor displacement.
- final boolean hasTextSelection = this instanceof EditText || mTextIsSelectable;
- if (mLinksClickable && !hasTextSelection) {
+ if (mLinksClickable && !textCanBeSelected()) {
setMovementMethod(LinkMovementMethod.getInstance());
}
}
@@ -3546,7 +3851,17 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
mErrorWasChanged = true;
final Drawables dr = mDrawables;
if (dr != null) {
- setCompoundDrawables(dr.mDrawableLeft, dr.mDrawableTop, icon, dr.mDrawableBottom);
+ switch (getResolvedLayoutDirection()) {
+ default:
+ case LAYOUT_DIRECTION_LTR:
+ setCompoundDrawables(dr.mDrawableLeft, dr.mDrawableTop, icon,
+ dr.mDrawableBottom);
+ break;
+ case LAYOUT_DIRECTION_RTL:
+ setCompoundDrawables(icon, dr.mDrawableTop, dr.mDrawableRight,
+ dr.mDrawableBottom);
+ break;
+ }
} else {
setCompoundDrawables(null, null, icon, null);
}
@@ -4048,6 +4363,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
if (mSelectionModifierCursorController != null) {
observer.addOnTouchModeChangeListener(mSelectionModifierCursorController);
}
+
+ // Resolve drawables as the layout direction has been resolved
+ resolveDrawables();
}
@Override
@@ -4077,6 +4395,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
hideControllers();
+
+ resetResolvedDrawables();
}
@Override
@@ -4111,7 +4431,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
final boolean verified = super.verifyDrawable(who);
if (!verified && mDrawables != null) {
return who == mDrawables.mDrawableLeft || who == mDrawables.mDrawableTop ||
- who == mDrawables.mDrawableRight || who == mDrawables.mDrawableBottom;
+ who == mDrawables.mDrawableRight || who == mDrawables.mDrawableBottom ||
+ who == mDrawables.mDrawableStart || who == mDrawables.mDrawableEnd;
}
return verified;
}
@@ -4132,6 +4453,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
if (mDrawables.mDrawableBottom != null) {
mDrawables.mDrawableBottom.jumpToCurrentState();
}
+ if (mDrawables.mDrawableStart != null) {
+ mDrawables.mDrawableStart.jumpToCurrentState();
+ }
+ if (mDrawables.mDrawableEnd != null) {
+ mDrawables.mDrawableEnd.jumpToCurrentState();
+ }
}
}
@@ -4192,7 +4519,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
if (mDrawables != null) {
final Drawables drawables = mDrawables;
if (who == drawables.mDrawableLeft || who == drawables.mDrawableRight ||
- who == drawables.mDrawableTop || who == drawables.mDrawableBottom) {
+ who == drawables.mDrawableTop || who == drawables.mDrawableBottom ||
+ who == drawables.mDrawableStart || who == drawables.mDrawableEnd) {
return getResolvedLayoutDirection();
}
}
@@ -4211,6 +4539,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
if (dr.mDrawableTop != null) dr.mDrawableTop.mutate().setAlpha(alpha);
if (dr.mDrawableRight != null) dr.mDrawableRight.mutate().setAlpha(alpha);
if (dr.mDrawableBottom != null) dr.mDrawableBottom.mutate().setAlpha(alpha);
+ if (dr.mDrawableStart != null) dr.mDrawableStart.mutate().setAlpha(alpha);
+ if (dr.mDrawableEnd != null) dr.mDrawableEnd.mutate().setAlpha(alpha);
}
return true;
}
@@ -4228,7 +4558,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
* {@link android.R.styleable#TextView_textIsSelectable} XML attribute to make this TextView
* selectable (text is not selectable by default).
*
- * Note that the content of an EditText is always selectable.
+ * Note that this method simply returns the state of this flag. Although this flag has to be set
+ * in order to select text in non-editable TextView, the content of an {@link EditText} can
+ * always be selected, independently of the value of this flag.
*
* @return True if the text displayed in this TextView can be selected by the user.
*
@@ -4465,12 +4797,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
selStart = getSelectionStart();
selEnd = getSelectionEnd();
- if ((isCursorVisible() || mTextIsSelectable) && selStart >= 0 && isEnabled()) {
+ if (selStart >= 0) {
if (mHighlightPath == null)
mHighlightPath = new Path();
if (selStart == selEnd) {
- if (!mTextIsSelectable &&
+ if (isCursorVisible() &&
(SystemClock.uptimeMillis() - mShowCursor) % (2 * BLINK) < BLINK) {
if (mHighlightPathBogus) {
mHighlightPath.reset();
@@ -4489,7 +4821,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
highlight = mHighlightPath;
drawCursor = mCursorCount > 0;
}
- } else {
+ } else if (textCanBeSelected()) {
if (mHighlightPathBogus) {
mHighlightPath.reset();
mLayout.getSelectionPath(selStart, selEnd, mHighlightPath);
@@ -7450,9 +7782,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
public boolean onTouchEvent(MotionEvent event) {
final int action = event.getActionMasked();
- if (hasInsertionController()) {
- getInsertionController().onTouchEvent(event);
- }
if (hasSelectionController()) {
getSelectionController().onTouchEvent(event);
}
@@ -7880,7 +8209,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
// prepareCursorController() relies on this method.
// If you change this condition, make sure prepareCursorController is called anywhere
// the value of this condition might be changed.
- return mText instanceof Spannable && mMovement != null && mMovement.canSelectArbitrarily();
+ if (mMovement == null || !mMovement.canSelectArbitrarily()) return false;
+ return isTextEditable() || (mTextIsSelectable && mText instanceof Spannable && isEnabled());
}
private boolean canCut() {
@@ -7968,6 +8298,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
final int minOffset = extractRangeStartFromLong(lastTouchOffsets);
final int maxOffset = extractRangeEndFromLong(lastTouchOffsets);
+ // Safety check in case standard touch event handling has been bypassed
+ if (minOffset < 0 || minOffset >= mText.length()) return false;
+ if (maxOffset < 0 || maxOffset >= mText.length()) return false;
+
int selectionStart, selectionEnd;
// If a URLSpan (web address, email, phone...) is found at that position, select it.
@@ -9723,13 +10057,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
public void hide();
/**
- * This method is called by {@link #onTouchEvent(MotionEvent)} and gives the controller
- * a chance to become active and/or visible.
- * @param event The touch event
- */
- public boolean onTouchEvent(MotionEvent event);
-
- /**
* Called when the view is detached from window. Perform house keeping task, such as
* stopping Runnable thread that would otherwise keep a reference on the context, thus
* preventing the activity from being recycled.
@@ -9756,10 +10083,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
}
- public boolean onTouchEvent(MotionEvent ev) {
- return false;
- }
-
public void onTouchModeChanged(boolean isInTouchMode) {
if (!isInTouchMode) {
hide();
@@ -9818,52 +10141,49 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
if (mEndHandle != null) mEndHandle.hide();
}
- public boolean onTouchEvent(MotionEvent event) {
+ public void onTouchEvent(MotionEvent event) {
// This is done even when the View does not have focus, so that long presses can start
// selection and tap can move cursor from this tap position.
- if (isTextEditable() || mTextIsSelectable) {
- switch (event.getActionMasked()) {
- case MotionEvent.ACTION_DOWN:
- final float x = event.getX();
- final float y = event.getY();
-
- // Remember finger down position, to be able to start selection from there
- mMinTouchOffset = mMaxTouchOffset = getOffsetForPosition(x, y);
-
- // Double tap detection
- long duration = SystemClock.uptimeMillis() - mPreviousTapUpTime;
- if (duration <= ViewConfiguration.getDoubleTapTimeout() &&
- isPositionOnText(x, y)) {
- final float deltaX = x - mPreviousTapPositionX;
- final float deltaY = y - mPreviousTapPositionY;
- final float distanceSquared = deltaX * deltaX + deltaY * deltaY;
- if (distanceSquared < mSquaredTouchSlopDistance) {
- showSuggestions();
- mDiscardNextActionUp = true;
- }
+ switch (event.getActionMasked()) {
+ case MotionEvent.ACTION_DOWN:
+ final float x = event.getX();
+ final float y = event.getY();
+
+ // Remember finger down position, to be able to start selection from there
+ mMinTouchOffset = mMaxTouchOffset = getOffsetForPosition(x, y);
+
+ // Double tap detection
+ long duration = SystemClock.uptimeMillis() - mPreviousTapUpTime;
+ if (duration <= ViewConfiguration.getDoubleTapTimeout() &&
+ isPositionOnText(x, y)) {
+ final float deltaX = x - mPreviousTapPositionX;
+ final float deltaY = y - mPreviousTapPositionY;
+ final float distanceSquared = deltaX * deltaX + deltaY * deltaY;
+ if (distanceSquared < mSquaredTouchSlopDistance) {
+ showSuggestions();
+ mDiscardNextActionUp = true;
}
+ }
- mPreviousTapPositionX = x;
- mPreviousTapPositionY = y;
+ mPreviousTapPositionX = x;
+ mPreviousTapPositionY = y;
- break;
+ break;
- case MotionEvent.ACTION_POINTER_DOWN:
- case MotionEvent.ACTION_POINTER_UP:
- // Handle multi-point gestures. Keep min and max offset positions.
- // Only activated for devices that correctly handle multi-touch.
- if (mContext.getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT)) {
- updateMinAndMaxOffsets(event);
- }
- break;
+ case MotionEvent.ACTION_POINTER_DOWN:
+ case MotionEvent.ACTION_POINTER_UP:
+ // Handle multi-point gestures. Keep min and max offset positions.
+ // Only activated for devices that correctly handle multi-touch.
+ if (mContext.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT)) {
+ updateMinAndMaxOffsets(event);
+ }
+ break;
- case MotionEvent.ACTION_UP:
- mPreviousTapUpTime = SystemClock.uptimeMillis();
- break;
- }
+ case MotionEvent.ACTION_UP:
+ mPreviousTapUpTime = SystemClock.uptimeMillis();
+ break;
}
- return false;
}
/**
@@ -10285,6 +10605,68 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
return (dir == Character.DIRECTIONALITY_LEFT_TO_RIGHT);
}
+ /**
+ * Subclasses will need to override this method to implement their own way of resolving
+ * drawables depending on the layout direction.
+ *
+ * A call to the super method will be required from the subclasses implementation.
+ *
+ */
+ protected void resolveDrawables() {
+ // No need to resolve twice
+ if (bResolvedDrawables) {
+ return;
+ }
+ // No drawable to resolve
+ if (mDrawables == null) {
+ return;
+ }
+ // No relative drawable to resolve
+ if (mDrawables.mDrawableStart == null && mDrawables.mDrawableEnd == null) {
+ bResolvedDrawables = true;
+ return;
+ }
+
+ Drawables dr = mDrawables;
+ switch(getResolvedLayoutDirection()) {
+ case LAYOUT_DIRECTION_RTL:
+ if (dr.mDrawableStart != null) {
+ dr.mDrawableRight = dr.mDrawableStart;
+
+ dr.mDrawableSizeRight = dr.mDrawableSizeStart;
+ dr.mDrawableHeightRight = dr.mDrawableHeightStart;
+ }
+ if (dr.mDrawableEnd != null) {
+ dr.mDrawableLeft = dr.mDrawableEnd;
+
+ dr.mDrawableSizeLeft = dr.mDrawableSizeEnd;
+ dr.mDrawableHeightLeft = dr.mDrawableHeightEnd;
+ }
+ break;
+
+ case LAYOUT_DIRECTION_LTR:
+ default:
+ if (dr.mDrawableStart != null) {
+ dr.mDrawableLeft = dr.mDrawableStart;
+
+ dr.mDrawableSizeLeft = dr.mDrawableSizeStart;
+ dr.mDrawableHeightLeft = dr.mDrawableHeightStart;
+ }
+ if (dr.mDrawableEnd != null) {
+ dr.mDrawableRight = dr.mDrawableEnd;
+
+ dr.mDrawableSizeRight = dr.mDrawableSizeEnd;
+ dr.mDrawableHeightRight = dr.mDrawableHeightEnd;
+ }
+ break;
+ }
+ bResolvedDrawables = true;
+ }
+
+ protected void resetResolvedDrawables() {
+ bResolvedDrawables = false;
+ }
+
@ViewDebug.ExportedProperty(category = "text")
private CharSequence mText;
private CharSequence mTransformed;
diff --git a/core/java/android/widget/TimePicker.java b/core/java/android/widget/TimePicker.java
index 423e735..0547438 100644
--- a/core/java/android/widget/TimePicker.java
+++ b/core/java/android/widget/TimePicker.java
@@ -20,6 +20,7 @@ import com.android.internal.R;
import android.annotation.Widget;
import android.content.Context;
+import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.os.Parcel;
import android.os.Parcelable;
@@ -32,6 +33,7 @@ import android.widget.NumberPicker.OnValueChangeListener;
import java.text.DateFormatSymbols;
import java.util.Calendar;
+import java.util.Locale;
/**
* A view for selecting the time of day, in either 24 hour or AM/PM mode. The
@@ -92,6 +94,8 @@ public class TimePicker extends FrameLayout {
private Calendar mTempCalendar;
+ private Locale mCurrentLocale;
+
/**
* The callback interface used to indicate the time has been adjusted.
*/
@@ -116,6 +120,9 @@ public class TimePicker extends FrameLayout {
public TimePicker(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
+ // initialization based on locale
+ setCurrentLocale(Locale.getDefault());
+
// process style attributes
TypedArray attributesArray = context.obtainStyledAttributes(
attrs, R.styleable.TimePicker, defStyle, 0);
@@ -211,8 +218,6 @@ public class TimePicker extends FrameLayout {
updateHourControl();
updateAmPmControl();
- // initialize to current time
- mTempCalendar = Calendar.getInstance();
setOnTimeChangedListener(NO_OP_CHANGE_LISTENER);
// set to current time
@@ -248,6 +253,25 @@ public class TimePicker extends FrameLayout {
return mIsEnabled;
}
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ setCurrentLocale(newConfig.locale);
+ }
+
+ /**
+ * Sets the current locale.
+ *
+ * @param locale The current locale.
+ */
+ private void setCurrentLocale(Locale locale) {
+ if (locale.equals(mCurrentLocale)) {
+ return;
+ }
+ mCurrentLocale = locale;
+ mTempCalendar = Calendar.getInstance(locale);
+ }
+
/**
* Used to save / restore state of time picker
*/
diff --git a/core/java/com/android/internal/view/menu/MenuBuilder.java b/core/java/com/android/internal/view/menu/MenuBuilder.java
index a4edbc5..164d581 100644
--- a/core/java/com/android/internal/view/menu/MenuBuilder.java
+++ b/core/java/com/android/internal/view/menu/MenuBuilder.java
@@ -28,6 +28,7 @@ import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Parcelable;
import android.util.SparseArray;
+import android.view.ActionProvider;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
@@ -798,7 +799,7 @@ public class MenuBuilder implements Menu {
if (itemImpl == null || !itemImpl.isEnabled()) {
return false;
}
-
+
boolean invoked = itemImpl.invoke();
if (itemImpl.hasCollapsibleActionView()) {
@@ -807,7 +808,12 @@ public class MenuBuilder implements Menu {
} else if (item.hasSubMenu()) {
close(false);
- invoked |= dispatchSubMenuSelected((SubMenuBuilder) item.getSubMenu());
+ final SubMenuBuilder subMenu = (SubMenuBuilder) item.getSubMenu();
+ final ActionProvider provider = item.getActionProvider();
+ if (provider != null && provider.hasSubMenu()) {
+ provider.onPrepareSubMenu(subMenu);
+ }
+ invoked |= dispatchSubMenuSelected(subMenu);
if (!invoked) close(true);
} else {
if ((flags & FLAG_PERFORM_NO_CLOSE) == 0) {
diff --git a/core/java/com/android/internal/view/menu/MenuItemImpl.java b/core/java/com/android/internal/view/menu/MenuItemImpl.java
index 20b7d80..541d101 100644
--- a/core/java/com/android/internal/view/menu/MenuItemImpl.java
+++ b/core/java/com/android/internal/view/menu/MenuItemImpl.java
@@ -164,10 +164,7 @@ public final class MenuItemImpl implements MenuItem {
}
}
- if (mActionProvider != null) {
- // The action view is created by the provider in this case.
- View actionView = getActionView();
- mActionProvider.onPerformDefaultAction(actionView);
+ if (mActionProvider != null && mActionProvider.onPerformDefaultAction()) {
return true;
}
@@ -323,11 +320,6 @@ public final class MenuItemImpl implements MenuItem {
}
void setSubMenu(SubMenuBuilder subMenu) {
- if ((mMenu != null) && (mMenu instanceof SubMenu)) {
- throw new UnsupportedOperationException(
- "Attempt to add a sub-menu to a sub-menu.");
- }
-
mSubMenu = subMenu;
subMenu.setHeaderTitle(getTitle());
diff --git a/core/java/com/android/server/NetworkManagementSocketTagger.java b/core/java/com/android/server/NetworkManagementSocketTagger.java
index 2871073..c446cfb 100644
--- a/core/java/com/android/server/NetworkManagementSocketTagger.java
+++ b/core/java/com/android/server/NetworkManagementSocketTagger.java
@@ -16,24 +16,37 @@
package com.android.server;
+import android.os.SystemProperties;
+import android.util.Log;
+
import dalvik.system.SocketTagger;
+
import java.io.FileDescriptor;
-import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.math.BigInteger;
import java.net.SocketException;
import java.nio.charset.Charsets;
+import libcore.io.IoUtils;
+
/**
* Assigns tags to sockets for traffic stats.
*/
public final class NetworkManagementSocketTagger extends SocketTagger {
+ private static final String TAG = "NetworkManagementSocketTagger";
+ private static final boolean LOGD = false;
- private static final boolean LOGI = false;
- private static final boolean ENABLE_TAGGING = false;
+ /**
+ * {@link SystemProperties} key that indicates if {@code qtaguid} bandwidth
+ * controls have been enabled.
+ */
+ // TODO: remove when always enabled, or once socket tagging silently fails.
+ public static final String PROP_QTAGUID_ENABLED = "net.qtaguid_enabled";
private static ThreadLocal<SocketTags> threadSocketTags = new ThreadLocal<SocketTags>() {
- @Override protected SocketTags initialValue() {
+ @Override
+ protected SocketTags initialValue() {
return new SocketTags();
}
};
@@ -50,11 +63,12 @@ public final class NetworkManagementSocketTagger extends SocketTagger {
threadSocketTags.get().statsUid = uid;
}
- @Override public void tag(FileDescriptor fd) throws SocketException {
+ @Override
+ public void tag(FileDescriptor fd) throws SocketException {
final SocketTags options = threadSocketTags.get();
- if (LOGI) {
- System.logI("tagSocket(" + fd.getInt$() + ") with statsTag="
- + options.statsTag + ", statsUid=" + options.statsUid);
+ if (LOGD) {
+ Log.d(TAG, "tagSocket(" + fd.getInt$() + ") with statsTag=" + options.statsTag
+ + ", statsUid=" + options.statsUid);
}
try {
// TODO: skip tagging when options would be no-op
@@ -82,9 +96,10 @@ public final class NetworkManagementSocketTagger extends SocketTagger {
internalModuleCtrl(cmd);
}
- @Override public void untag(FileDescriptor fd) throws SocketException {
- if (LOGI) {
- System.logI("untagSocket(" + fd.getInt$() + ")");
+ @Override
+ public void untag(FileDescriptor fd) throws SocketException {
+ if (LOGD) {
+ Log.i(TAG, "untagSocket(" + fd.getInt$() + ")");
}
try {
unTagSocketFd(fd);
@@ -125,31 +140,22 @@ public final class NetworkManagementSocketTagger extends SocketTagger {
*
*/
private void internalModuleCtrl(String cmd) throws IOException {
- if (!ENABLE_TAGGING) return;
+ if (!SystemProperties.getBoolean(PROP_QTAGUID_ENABLED, false)) return;
- final FileOutputStream procOut;
- // TODO: Use something like
- // android.os.SystemProperties.getInt("persist.bandwidth.enable", 0)
- // to see if tagging should happen or not.
+ // TODO: migrate to native library for tagging commands
+ FileOutputStream procOut = null;
try {
procOut = new FileOutputStream("/proc/net/xt_qtaguid/ctrl");
- } catch (FileNotFoundException e) {
- if (LOGI) {
- System.logI("Can't talk to kernel module:" + e);
- }
- return;
- }
- try {
procOut.write(cmd.getBytes(Charsets.US_ASCII));
} finally {
- procOut.close();
+ IoUtils.closeQuietly(procOut);
}
}
/**
* Convert {@link Integer} tag to {@code /proc/} format. Assumes unsigned
* base-10 format like {@code 2147483647}. Currently strips signed bit to
- * avoid using {@link java.math.BigInteger}.
+ * avoid using {@link BigInteger}.
*/
public static String tagToKernel(int tag) {
// TODO: eventually write in hex, since that's what proc exports