diff options
Diffstat (limited to 'core/java/android')
26 files changed, 613 insertions, 192 deletions
diff --git a/core/java/android/net/http/HttpResponseCache.java b/core/java/android/net/http/HttpResponseCache.java new file mode 100644 index 0000000..b5d64e4 --- /dev/null +++ b/core/java/android/net/http/HttpResponseCache.java @@ -0,0 +1,268 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.http; + +import android.content.Context; +import java.io.Closeable; +import java.io.File; +import java.io.IOException; +import java.net.CacheRequest; +import java.net.CacheResponse; +import java.net.HttpURLConnection; +import java.net.ResponseCache; +import java.net.URI; +import java.net.URLConnection; +import java.util.List; +import java.util.Map; +import javax.net.ssl.HttpsURLConnection; +import libcore.io.DiskLruCache; +import libcore.io.IoUtils; +import org.apache.http.impl.client.DefaultHttpClient; + +/** + * Caches HTTP and HTTPS responses to the filesystem so they may be reused, + * saving time and bandwidth. This class supports {@link HttpURLConnection} and + * {@link HttpsURLConnection}; there is no platform-provided cache for {@link + * DefaultHttpClient} or {@link AndroidHttpClient}. + * + * <h3>Installing an HTTP response cache</h3> + * Enable caching of all of your application's HTTP requests by installing the + * cache at application startup. For example, this code installs a 10 MiB cache + * in the {@link Context#getCacheDir() application-specific cache directory} of + * the filesystem}: <pre> {@code + * protected void onCreate(Bundle savedInstanceState) { + * ... + * + * try { + * File httpCacheDir = new File(context.getCacheDir(), "http"); + * long httpCacheSize = 10 * 1024 * 1024; // 10 MiB + * HttpResponseCache.install(httpCacheDir, httpCacheSize); + * } catch (IOException e) { + * Log.i(TAG, "HTTP response cache installation failed:" + e); + * } + * } + * + * protected void onStop() { + * ... + * + * HttpResponseCache cache = HttpResponseCache.getInstalled(); + * if (cache != null) { + * cache.flush(); + * } + * }}</pre> + * This cache will evict entries as necessary to keep its size from exceeding + * 10 MiB. The best cache size is application specific and depends on the size + * and frequency of the files being downloaded. Increasing the limit may improve + * the hit rate, but it may also just waste filesystem space! + * + * <p>For some applications it may be preferable to create the cache in the + * external storage directory. Although it often has more free space, external + * storage is optional and—even if available—can disappear during + * use. Retrieve the external cache directory using {@link Context#getExternalCacheDir()}. If this method + * returns null, your application should fall back to either not caching or + * caching on non-external storage. If the external storage is removed during + * use, the cache hit rate will drop to zero and ongoing cache reads will fail. + * + * <p>Flushing the cache forces its data to the filesystem. This ensures that + * all responses written to the cache will be readable the next time the + * activity starts. + * + * <h3>Cache Optimization</h3> + * To measure cache effectiveness, this class tracks three statistics: + * <ul> + * <li><strong>{@link #getRequestCount() Request Count:}</strong> the number + * of HTTP requests issued since this cache was created. + * <li><strong>{@link #getNetworkCount() Network Count:}</strong> the + * number of those requests that required network use. + * <li><strong>{@link #getHitCount() Hit Count:}</strong> the number of + * those requests whose responses were served by the cache. + * </ul> + * Sometimes a request will result in a conditional cache hit. If the cache + * contains a stale copy of the response, the client will issue a conditional + * {@code GET}. The server will then send either the updated response if it has + * changed, or a short 'not modified' response if the client's copy is still + * valid. Such responses increment both the network count and hit count. + * + * <p>The best way to improve the cache hit rate is by configuring the web + * server to return cacheable responses. Although this client honors all <a + * href="http://www.ietf.org/rfc/rfc2616.txt">HTTP/1.1 (RFC 2068)</a> cache + * headers, it doesn't cache partial responses. + * + * <h3>Force a Network Response</h3> + * In some situations, such as after a user clicks a 'refresh' button, it may be + * necessary to skip the cache, and fetch data directly from the server. To force + * a full refresh, add the {@code no-cache} directive: <pre> {@code + * connection.addRequestProperty("Cache-Control", "no-cache"); + * }</pre> + * If it is only necessary to force a cached response to be validated by the + * server, use the more efficient {@code max-age=0} instead: <pre> {@code + * connection.addRequestProperty("Cache-Control", "max-age=0"); + * }</pre> + * + * <h3>Force a Cache Response</h3> + * Sometimes you'll want to show resources if they are available immediately, + * but not otherwise. This can be used so your application can show + * <i>something</i> while waiting for the latest data to be downloaded. To + * restrict a request to locally-cached resources, add the {@code + * only-if-cached} directive: <pre> {@code + * try { + * connection.addRequestProperty("Cache-Control", "only-if-cached"); + * InputStream cached = connection.getInputStream(); + * // the resource was cached! show it + * } catch (FileNotFoundException e) { + * // the resource was not cached + * } + * }</pre> + * This technique works even better in situations where a stale response is + * better than no response. To permit stale cached responses, use the {@code + * max-stale} directive with the maximum staleness in seconds: <pre> {@code + * int maxStale = 60 * 60 * 24 * 28; // tolerate 4-weeks stale + * connection.addRequestProperty("Cache-Control", "max-stale=" + maxStale); + * }</pre> + */ +public final class HttpResponseCache extends ResponseCache implements Closeable { + + private final libcore.net.http.HttpResponseCache delegate; + + private HttpResponseCache(File directory, long maxSize) throws IOException { + this.delegate = new libcore.net.http.HttpResponseCache(directory, maxSize); + } + + /** + * Returns the currently-installed {@code HttpResponseCache}, or null if + * there is no cache installed or it is not a {@code HttpResponseCache}. + */ + public static HttpResponseCache getInstalled() { + ResponseCache installed = ResponseCache.getDefault(); + return installed instanceof HttpResponseCache ? (HttpResponseCache) installed : null; + } + + /** + * Creates a new HTTP response cache and {@link ResponseCache#setDefault + * sets it} as the system default cache. + * + * @param directory the directory to hold cache data. + * @param maxSize the maximum size of the cache in bytes. + * @return the newly-installed cache + * @throws IOException if {@code directory} cannot be used for this cache. + * Most applications should respond to this exception by logging a + * warning. + */ + public static HttpResponseCache install(File directory, long maxSize) throws IOException { + HttpResponseCache installed = getInstalled(); + if (installed != null) { + // don't close and reopen if an equivalent cache is already installed + DiskLruCache installedCache = installed.delegate.getCache(); + if (installedCache.getDirectory().equals(directory) + && installedCache.maxSize() == maxSize + && !installedCache.isClosed()) { + return installed; + } else { + IoUtils.closeQuietly(installed); + } + } + + HttpResponseCache result = new HttpResponseCache(directory, maxSize); + ResponseCache.setDefault(result); + return result; + } + + @Override public CacheResponse get(URI uri, String requestMethod, + Map<String, List<String>> requestHeaders) throws IOException { + return delegate.get(uri, requestMethod, requestHeaders); + } + + @Override public CacheRequest put(URI uri, URLConnection urlConnection) throws IOException { + return delegate.put(uri, urlConnection); + } + + /** + * Returns the number of bytes currently being used to store the values in + * this cache. This may be greater than the {@link #maxSize} if a background + * deletion is pending. + */ + public long size() { + return delegate.getCache().size(); + } + + /** + * Returns the maximum number of bytes that this cache should use to store + * its data. + */ + public long maxSize() { + return delegate.getCache().maxSize(); + } + + /** + * Force buffered operations to the filesystem. This ensures that responses + * written to the cache will be available the next time the cache is opened, + * even if this process is killed. + */ + public void flush() { + try { + delegate.getCache().flush(); // TODO: fix flush() to not throw? + } catch (IOException ignored) { + } + } + + /** + * Returns the number of HTTP requests that required the network to either + * supply a response or validate a locally cached response. + */ + public int getNetworkCount() { + return delegate.getNetworkCount(); + } + + /** + * Returns the number of HTTP requests whose response was provided by the + * cache. This may include conditional {@code GET} requests that were + * validated over the network. + */ + public int getHitCount() { + return delegate.getHitCount(); + } + + /** + * Returns the total number of HTTP requests that were made. This includes + * both client requests and requests that were made on the client's behalf + * to handle a redirects and retries. + */ + public int getRequestCount() { + return delegate.getRequestCount(); + } + + /** + * Uninstalls the cache and releases any active resources. Stored contents + * will remain on the filesystem. + */ + @Override public void close() throws IOException { + if (ResponseCache.getDefault() == this) { + ResponseCache.setDefault(null); + } + delegate.getCache().close(); + } + + /** + * Uninstalls the cache and deletes all of its stored contents. + */ + public void delete() throws IOException { + if (ResponseCache.getDefault() == this) { + ResponseCache.setDefault(null); + } + delegate.getCache().delete(); + } +} diff --git a/core/java/android/nfc/NdefRecord.java b/core/java/android/nfc/NdefRecord.java index 746d3df..fe215fd 100644 --- a/core/java/android/nfc/NdefRecord.java +++ b/core/java/android/nfc/NdefRecord.java @@ -163,6 +163,18 @@ public final class NdefRecord implements Parcelable { * must not be null */ public NdefRecord(short tnf, byte[] type, byte[] id, byte[] payload) { + /* New NDEF records created by applications will have FLAG_MB|FLAG_ME + * set by default; when multiple records are stored in a + * {@link NdefMessage}, these flags will be corrected when the {@link NdefMessage} + * is serialized to bytes. + */ + this(tnf, type, id, payload, (byte)(FLAG_MB|FLAG_ME)); + } + + /** + * @hide + */ + /*package*/ NdefRecord(short tnf, byte[] type, byte[] id, byte[] payload, byte flags) { /* check arguments */ if ((type == null) || (id == null) || (payload == null)) { throw new IllegalArgumentException("Illegal null argument"); @@ -172,9 +184,6 @@ public final class NdefRecord implements Parcelable { throw new IllegalArgumentException("TNF out of range " + tnf); } - /* generate flag */ - byte flags = FLAG_MB | FLAG_ME; - /* Determine if it is a short record */ if(payload.length < 0xFF) { flags |= FLAG_SR; @@ -258,6 +267,7 @@ public final class NdefRecord implements Parcelable { } public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mFlags); dest.writeInt(mTnf); dest.writeInt(mType.length); dest.writeByteArray(mType); @@ -270,6 +280,7 @@ public final class NdefRecord implements Parcelable { public static final Parcelable.Creator<NdefRecord> CREATOR = new Parcelable.Creator<NdefRecord>() { public NdefRecord createFromParcel(Parcel in) { + byte flags = (byte)in.readInt(); short tnf = (short)in.readInt(); int typeLength = in.readInt(); byte[] type = new byte[typeLength]; @@ -281,7 +292,7 @@ public final class NdefRecord implements Parcelable { byte[] payload = new byte[payloadLength]; in.readByteArray(payload); - return new NdefRecord(tnf, type, id, payload); + return new NdefRecord(tnf, type, id, payload, flags); } public NdefRecord[] newArray(int size) { return new NdefRecord[size]; @@ -290,4 +301,4 @@ public final class NdefRecord implements Parcelable { private native int parseNdefRecord(byte[] data); private native byte[] generate(short flags, short tnf, byte[] type, byte[] id, byte[] data); -}
\ No newline at end of file +} diff --git a/core/java/android/nfc/tech/MifareUltralight.java b/core/java/android/nfc/tech/MifareUltralight.java index 87c8d99..42c2e3e 100644 --- a/core/java/android/nfc/tech/MifareUltralight.java +++ b/core/java/android/nfc/tech/MifareUltralight.java @@ -18,6 +18,7 @@ package android.nfc.tech; import android.nfc.Tag; import android.nfc.TagLostException; +import android.os.Bundle; import android.os.RemoteException; import java.io.IOException; @@ -69,6 +70,9 @@ public final class MifareUltralight extends BasicTagTechnology { private static final int NXP_MANUFACTURER_ID = 0x04; private static final int MAX_PAGE_COUNT = 256; + /** @hide */ + public static final String EXTRA_IS_UL_C = "isulc"; + private int mType; /** @@ -101,10 +105,12 @@ public final class MifareUltralight extends BasicTagTechnology { mType = TYPE_UNKNOWN; if (a.getSak() == 0x00 && tag.getId()[0] == NXP_MANUFACTURER_ID) { - // could be UL or UL-C - //TODO: stack should use NXP AN1303 procedure to make a best guess - // attempt at classifying Ultralight vs Ultralight C. - mType = TYPE_ULTRALIGHT; + Bundle extras = tag.getTechExtras(TagTechnology.MIFARE_ULTRALIGHT); + if (extras.getBoolean(EXTRA_IS_UL_C)) { + mType = TYPE_ULTRALIGHT_C; + } else { + mType = TYPE_ULTRALIGHT; + } } } diff --git a/core/java/android/preference/Preference.java b/core/java/android/preference/Preference.java index 5e1be21..b6d1594 100644 --- a/core/java/android/preference/Preference.java +++ b/core/java/android/preference/Preference.java @@ -472,11 +472,15 @@ public class Preference implements Comparable<Preference>, OnDependencyChangeLis final View layout = layoutInflater.inflate(mLayoutResId, parent, false); - if (mWidgetLayoutResId != 0) { - final ViewGroup widgetFrame = (ViewGroup)layout.findViewById(com.android.internal.R.id.widget_frame); - layoutInflater.inflate(mWidgetLayoutResId, widgetFrame); + final ViewGroup widgetFrame = (ViewGroup) layout + .findViewById(com.android.internal.R.id.widget_frame); + if (widgetFrame != null) { + if (mWidgetLayoutResId != 0) { + layoutInflater.inflate(mWidgetLayoutResId, widgetFrame); + } else { + widgetFrame.setVisibility(View.GONE); + } } - return layout; } @@ -514,14 +518,18 @@ public class Preference implements Comparable<Preference>, OnDependencyChangeLis } ImageView imageView = (ImageView) view.findViewById(com.android.internal.R.id.icon); - if (imageView != null && (mIconResId != 0 || mIcon != null)) { - if (mIcon == null) { - mIcon = getContext().getResources().getDrawable(mIconResId); - } - if (mIcon != null) { - imageView.setImageDrawable(mIcon); + if (imageView != null) { + if (mIconResId != 0 || mIcon != null) { + if (mIcon == null) { + mIcon = getContext().getResources().getDrawable(mIconResId); + } + if (mIcon != null) { + imageView.setImageDrawable(mIcon); + } } + imageView.setVisibility(mIcon != null ? View.VISIBLE : View.GONE); } + if (mShouldDisableView) { setEnabledStateOnViews(view, isEnabled()); } @@ -633,6 +641,7 @@ public class Preference implements Comparable<Preference>, OnDependencyChangeLis public void setIcon(Drawable icon) { if ((icon == null && mIcon != null) || (icon != null && mIcon != icon)) { mIcon = icon; + notifyChanged(); } } diff --git a/core/java/android/preference/PreferenceActivity.java b/core/java/android/preference/PreferenceActivity.java index 15d5898..14e7bed 100644 --- a/core/java/android/preference/PreferenceActivity.java +++ b/core/java/android/preference/PreferenceActivity.java @@ -18,9 +18,6 @@ package android.preference; import com.android.internal.util.XmlUtils; -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; - import android.app.ActionBar; import android.app.Fragment; import android.app.FragmentBreadCrumbs; @@ -44,8 +41,8 @@ import android.util.TypedValue; import android.util.Xml; import android.view.LayoutInflater; import android.view.View; -import android.view.View.OnClickListener; import android.view.ViewGroup; +import android.view.View.OnClickListener; import android.widget.AbsListView; import android.widget.ArrayAdapter; import android.widget.Button; @@ -58,6 +55,9 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + /** * This is the base class for an activity to show a hierarchy of preferences * to the user. Prior to {@link android.os.Build.VERSION_CODES#HONEYCOMB} @@ -503,12 +503,7 @@ public abstract class PreferenceActivity extends ListActivity implements protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - if (getResources().getConfiguration().isLayoutSizeAtLeast( - Configuration.SCREENLAYOUT_SIZE_LARGE)) { - setContentView(com.android.internal.R.layout.preference_list_content_large); - } else { - setContentView(com.android.internal.R.layout.preference_list_content); - } + setContentView(com.android.internal.R.layout.preference_list_content); mListFooter = (FrameLayout)findViewById(com.android.internal.R.id.list_footer); mPrefsContainer = (ViewGroup) findViewById(com.android.internal.R.id.prefs_frame); @@ -585,12 +580,7 @@ public abstract class PreferenceActivity extends ListActivity implements } else { // If there are no headers, we are in the old "just show a screen // of preferences" mode. - if (getResources().getConfiguration().isLayoutSizeAtLeast( - Configuration.SCREENLAYOUT_SIZE_LARGE)) { - setContentView(com.android.internal.R.layout.preference_list_content_single_large); - } else { - setContentView(com.android.internal.R.layout.preference_list_content_single); - } + setContentView(com.android.internal.R.layout.preference_list_content_single); mListFooter = (FrameLayout) findViewById(com.android.internal.R.id.list_footer); mPrefsContainer = (ViewGroup) findViewById(com.android.internal.R.id.prefs); mPreferenceManager = new PreferenceManager(this, FIRST_REQUEST_CODE); @@ -674,17 +664,9 @@ public abstract class PreferenceActivity extends ListActivity implements * enough. */ public boolean onIsMultiPane() { - Configuration config = getResources().getConfiguration(); - if ((config.screenLayout&Configuration.SCREENLAYOUT_SIZE_MASK) - == Configuration.SCREENLAYOUT_SIZE_XLARGE) { - return true; - } - if ((config.screenLayout&Configuration.SCREENLAYOUT_SIZE_MASK) - == Configuration.SCREENLAYOUT_SIZE_LARGE - && config.orientation == Configuration.ORIENTATION_LANDSCAPE) { - return true; - } - return false; + boolean preferMultiPane = getResources().getBoolean( + com.android.internal.R.bool.preferences_prefer_dual_pane); + return preferMultiPane; } /** diff --git a/core/java/android/preference/PreferenceFragment.java b/core/java/android/preference/PreferenceFragment.java index 7511e14..9d46b7a 100644 --- a/core/java/android/preference/PreferenceFragment.java +++ b/core/java/android/preference/PreferenceFragment.java @@ -152,14 +152,8 @@ public abstract class PreferenceFragment extends Fragment implements @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - if (getResources().getConfiguration().isLayoutSizeAtLeast( - Configuration.SCREENLAYOUT_SIZE_LARGE)) { - return inflater.inflate(com.android.internal.R.layout.preference_list_fragment_large, - container, false); - } else { - return inflater.inflate(com.android.internal.R.layout.preference_list_fragment, - container, false); - } + return inflater.inflate(com.android.internal.R.layout.preference_list_fragment, container, + false); } @Override diff --git a/core/java/android/text/MeasuredText.java b/core/java/android/text/MeasuredText.java index 761c271..a81be09 100644 --- a/core/java/android/text/MeasuredText.java +++ b/core/java/android/text/MeasuredText.java @@ -16,14 +16,14 @@ package android.text; -import com.android.internal.util.ArrayUtils; - import android.graphics.Canvas; import android.graphics.Paint; import android.text.style.MetricAffectingSpan; import android.text.style.ReplacementSpan; import android.util.Log; +import com.android.internal.util.ArrayUtils; + /** * @hide */ @@ -187,6 +187,7 @@ class MeasuredText { w[mPos] = wid; for (int i = mPos + 1, e = mPos + len; i < e; i++) w[i] = 0; + mPos += len; } if (fm != null) { diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java index 0f8097a..f5249611 100644 --- a/core/java/android/text/TextLine.java +++ b/core/java/android/text/TextLine.java @@ -43,6 +43,8 @@ import android.util.Log; * @hide */ class TextLine { + private static final boolean DEBUG = false; + private TextPaint mPaint; private CharSequence mText; private int mStart; @@ -56,7 +58,7 @@ class TextLine { private Spanned mSpanned; private final TextPaint mWorkPaint = new TextPaint(); - private static TextLine[] cached = new TextLine[3]; + private static final TextLine[] sCached = new TextLine[3]; /** * Returns a new TextLine from the shared pool. @@ -65,17 +67,19 @@ class TextLine { */ static TextLine obtain() { TextLine tl; - synchronized (cached) { - for (int i = cached.length; --i >= 0;) { - if (cached[i] != null) { - tl = cached[i]; - cached[i] = null; + synchronized (sCached) { + for (int i = sCached.length; --i >= 0;) { + if (sCached[i] != null) { + tl = sCached[i]; + sCached[i] = null; return tl; } } } tl = new TextLine(); - Log.v("TLINE", "new: " + tl); + if (DEBUG) { + Log.v("TLINE", "new: " + tl); + } return tl; } @@ -90,10 +94,10 @@ class TextLine { tl.mText = null; tl.mPaint = null; tl.mDirections = null; - synchronized(cached) { - for (int i = 0; i < cached.length; ++i) { - if (cached[i] == null) { - cached[i] = tl; + synchronized(sCached) { + for (int i = 0; i < sCached.length; ++i) { + if (sCached[i] == null) { + sCached[i] = tl; break; } } @@ -175,11 +179,11 @@ class TextLine { void draw(Canvas c, float x, int top, int y, int bottom) { if (!mHasTabs) { if (mDirections == Layout.DIRS_ALL_LEFT_TO_RIGHT) { - drawRun(c, 0, 0, mLen, false, x, top, y, bottom, false); + drawRun(c, 0, mLen, false, x, top, y, bottom, false); return; } if (mDirections == Layout.DIRS_ALL_RIGHT_TO_LEFT) { - drawRun(c, 0, 0, mLen, true, x, top, y, bottom, false); + drawRun(c, 0, mLen, true, x, top, y, bottom, false); return; } } @@ -216,7 +220,7 @@ class TextLine { } if (j == runLimit || codept == '\t' || bm != null) { - h += drawRun(c, i, segstart, j, runIsRtl, x+h, top, y, bottom, + h += drawRun(c, segstart, j, runIsRtl, x+h, top, y, bottom, i != lastRunIndex || j != mLen); if (codept == '\t') { @@ -275,10 +279,10 @@ class TextLine { if (!mHasTabs) { if (mDirections == Layout.DIRS_ALL_LEFT_TO_RIGHT) { - return measureRun(0, 0, offset, mLen, false, fmi); + return measureRun(0, offset, mLen, false, fmi); } if (mDirections == Layout.DIRS_ALL_RIGHT_TO_LEFT) { - return measureRun(0, 0, offset, mLen, true, fmi); + return measureRun(0, offset, mLen, true, fmi); } } @@ -315,14 +319,14 @@ class TextLine { boolean advance = (mDir == Layout.DIR_RIGHT_TO_LEFT) == runIsRtl; if (inSegment && advance) { - return h += measureRun(i, segstart, offset, j, runIsRtl, fmi); + return h += measureRun(segstart, offset, j, runIsRtl, fmi); } - float w = measureRun(i, segstart, j, j, runIsRtl, fmi); + float w = measureRun(segstart, j, j, runIsRtl, fmi); h += advance ? w : -w; if (inSegment) { - return h += measureRun(i, segstart, offset, j, runIsRtl, null); + return h += measureRun(segstart, offset, j, runIsRtl, null); } if (codept == '\t') { @@ -353,8 +357,8 @@ class TextLine { /** * Draws a unidirectional (but possibly multi-styled) run of text. * + * * @param c the canvas to draw on - * @param runIndex the index of this directional run * @param start the line-relative start * @param limit the line-relative limit * @param runIsRtl true if the run is right-to-left @@ -366,25 +370,25 @@ class TextLine { * @return the signed width of the run, based on the paragraph direction. * Only valid if needWidth is true. */ - private float drawRun(Canvas c, int runIndex, int start, + private float drawRun(Canvas c, int start, int limit, boolean runIsRtl, float x, int top, int y, int bottom, boolean needWidth) { if ((mDir == Layout.DIR_LEFT_TO_RIGHT) == runIsRtl) { - float w = -measureRun(runIndex, start, limit, limit, runIsRtl, null); - handleRun(runIndex, start, limit, limit, runIsRtl, c, x + w, top, + float w = -measureRun(start, limit, limit, runIsRtl, null); + handleRun(start, limit, limit, runIsRtl, c, x + w, top, y, bottom, null, false); return w; } - return handleRun(runIndex, start, limit, limit, runIsRtl, c, x, top, + return handleRun(start, limit, limit, runIsRtl, c, x, top, y, bottom, null, needWidth); } /** * Measures a unidirectional (but possibly multi-styled) run of text. * - * @param runIndex the run index + * * @param start the line-relative start of the run * @param offset the offset to measure to, between start and limit inclusive * @param limit the line-relative limit of the run @@ -394,10 +398,9 @@ class TextLine { * @return the signed width from the start of the run to the leading edge * of the character at offset, based on the run (not paragraph) direction */ - private float measureRun(int runIndex, int start, - int offset, int limit, boolean runIsRtl, FontMetricsInt fmi) { - return handleRun(runIndex, start, offset, limit, runIsRtl, null, - 0, 0, 0, 0, fmi, true); + private float measureRun(int start, int offset, int limit, boolean runIsRtl, + FontMetricsInt fmi) { + return handleRun(start, offset, limit, runIsRtl, null, 0, 0, 0, 0, fmi, true); } /** @@ -751,9 +754,9 @@ class TextLine { /** * Utility function for measuring and rendering a replacement. * + * * @param replacement the replacement * @param wp the work paint - * @param runIndex the run index * @param start the start of the run * @param limit the limit of the run * @param runIsRtl true if the run is right-to-left @@ -768,7 +771,7 @@ class TextLine { * valid if needWidth is true */ private float handleReplacement(ReplacementSpan replacement, TextPaint wp, - int runIndex, int start, int limit, boolean runIsRtl, Canvas c, + int start, int limit, boolean runIsRtl, Canvas c, float x, int top, int y, int bottom, FontMetricsInt fmi, boolean needWidth) { @@ -796,7 +799,7 @@ class TextLine { * Utility function for handling a unidirectional run. The run must not * contain tabs or emoji but can contain styles. * - * @param runIndex the run index + * * @param start the line-relative start of the run * @param measureLimit the offset to measure to, between start and limit inclusive * @param limit the limit of the run @@ -811,7 +814,7 @@ class TextLine { * @return the signed width of the run based on the run direction; only * valid if needWidth is true */ - private float handleRun(int runIndex, int start, int measureLimit, + private float handleRun(int start, int measureLimit, int limit, boolean runIsRtl, Canvas c, float x, int top, int y, int bottom, FontMetricsInt fmi, boolean needWidth) { @@ -859,7 +862,7 @@ class TextLine { } if (replacement != null) { - x += handleReplacement(replacement, wp, runIndex, i, + x += handleReplacement(replacement, wp, i, mlimit, runIsRtl, c, x, top, y, bottom, fmi, needWidth || mlimit < measureLimit); continue; diff --git a/core/java/android/view/Gravity.java b/core/java/android/view/Gravity.java index cf79638..176c487 100644 --- a/core/java/android/view/Gravity.java +++ b/core/java/android/view/Gravity.java @@ -80,9 +80,12 @@ public class Gravity /** Flag to clip the edges of the object to its container along the * horizontal axis. */ public static final int CLIP_HORIZONTAL = AXIS_CLIP<<AXIS_X_SHIFT; - + + /** Raw bit controlling whether the horizontal direction is relative (before/after) or not. */ + public static final int RELATIVE_HORIZONTAL_DIRECTION = 0x00800000; + /** - * Binary mask to get the horizontal gravity of a gravity. + * Binary mask to get the absolute horizontal gravity of a gravity. */ public static final int HORIZONTAL_GRAVITY_MASK = (AXIS_SPECIFIED | AXIS_PULL_BEFORE | AXIS_PULL_AFTER) << AXIS_X_SHIFT; @@ -106,8 +109,19 @@ public class Gravity */ public static final int DISPLAY_CLIP_HORIZONTAL = 0x01000000; + /** Push object to x-axis position before its container, not changing its size. */ + public static final int BEFORE = RELATIVE_HORIZONTAL_DIRECTION | LEFT; + + /** Push object to x-axis position after its container, not changing its size. */ + public static final int AFTER = RELATIVE_HORIZONTAL_DIRECTION | RIGHT; + /** - * Apply a gravity constant to an object. + * Binary mask for the horizontal gravity and script specific direction bit. + */ + public static final int RELATIVE_HORIZONTAL_GRAVITY_MASK = BEFORE | AFTER; + + /** + * Apply a gravity constant to an object. This suppose that the layout direction is LTR. * * @param gravity The desired placement of the object, as defined by the * constants in this class. @@ -119,12 +133,33 @@ public class Gravity * @param outRect Receives the computed frame of the object in its * container. */ - public static void apply(int gravity, int w, int h, Rect container, - Rect outRect) { + public static void apply(int gravity, int w, int h, Rect container, Rect outRect) { apply(gravity, w, h, container, 0, 0, outRect); } /** + * Apply a gravity constant to an object and take care if layout direction is RTL or not. + * + * @param gravity The desired placement of the object, as defined by the + * constants in this class. + * @param w The horizontal size of the object. + * @param h The vertical size of the object. + * @param container The frame of the containing space, in which the object + * will be placed. Should be large enough to contain the + * width and height of the object. + * @param outRect Receives the computed frame of the object in its + * container. + * @param isRtl Whether the layout is right-to-left. + * + * @hide + */ + public static void apply(int gravity, int w, int h, Rect container, + Rect outRect, boolean isRtl) { + int absGravity = getAbsoluteGravity(gravity, isRtl); + apply(absGravity, w, h, container, 0, 0, outRect); + } + + /** * Apply a gravity constant to an object. * * @param gravity The desired placement of the object, as defined by the @@ -146,7 +181,7 @@ public class Gravity * container. */ public static void apply(int gravity, int w, int h, Rect container, - int xAdj, int yAdj, Rect outRect) { + int xAdj, int yAdj, Rect outRect) { switch (gravity&((AXIS_PULL_BEFORE|AXIS_PULL_AFTER)<<AXIS_X_SHIFT)) { case 0: outRect.left = container.left @@ -301,6 +336,54 @@ public class Gravity * @return true if the supplied gravity has an horizontal pull */ public static boolean isHorizontal(int gravity) { - return gravity > 0 && (gravity & HORIZONTAL_GRAVITY_MASK) != 0; + return gravity > 0 && (gravity & RELATIVE_HORIZONTAL_GRAVITY_MASK) != 0; + } + + /** + * <p>Convert script specific gravity to absolute horizontal value.</p> + * + * if horizontal direction is LTR, then BEFORE will set LEFT and AFTER will set RIGHT. + * if horizontal direction is RTL, then BEFORE will set RIGHT and AFTER will set LEFT. + * + * If no horizontal direction is found, then just add LEFT to the existing gravity + * + * @param gravity The gravity to convert to absolute (horizontal) values. + * @param isRtl Whether the layout is right-to-left. + * @return gravity converted to absolute (horizontal) values. + */ + public static int getAbsoluteGravity(int gravity, boolean isRtl) { + int result = gravity; + // Set default gravity, if no horizontal gravity is specified + if ((result & HORIZONTAL_GRAVITY_MASK) == 0) { + result |= Gravity.LEFT; + } + // If layout is script specific and gravity is horizontal relative (BEFORE or AFTER) + if ((result & RELATIVE_HORIZONTAL_DIRECTION) > 0) { + if ((result & Gravity.BEFORE) == Gravity.BEFORE) { + // Remove the BEFORE bit + result &= ~BEFORE; + if (isRtl) { + // Set the RIGHT bit + result |= RIGHT; + } else { + // Set the LEFT bit + result |= LEFT; + } + } else if ((result & Gravity.AFTER) == Gravity.AFTER) { + // Remove the AFTER bit + result &= ~AFTER; + if (isRtl) { + // Set the LEFT bit + result |= LEFT; + } else { + // Set the RIGHT bit + result |= RIGHT; + } + } + // Don't need the script specific bit any more, so remove it as we are converting to + // absolute values (LEFT or RIGHT) + result &= ~RELATIVE_HORIZONTAL_DIRECTION; + } + return result; } } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index e54046d..017e5e3 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -631,7 +631,7 @@ import java.util.concurrent.CopyOnWriteArrayList; * * @see android.view.ViewGroup */ -public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource { +public class View implements Drawable.Callback2, KeyEvent.Callback, AccessibilityEventSource { private static final boolean DBG = false; /** @@ -10238,6 +10238,15 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } } + /** + * Check if a given Drawable is in RTL layout direction. + * + * @param who the recipient of the action + */ + public boolean isLayoutRtl(Drawable who) { + return (who == mBGDrawable) && isLayoutRtl(); + } + /** * If your view subclass is displaying its own Drawable objects, it should * override this function and return true for any Drawable it is diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 9395d5c..a1ddd08 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -77,8 +77,8 @@ public interface WindowManager extends ViewManager { implements Parcelable { /** * X position for this window. With the default gravity it is ignored. - * When using {@link Gravity#LEFT} or {@link Gravity#RIGHT} it provides - * an offset from the given edge. + * When using {@link Gravity#LEFT} or {@link Gravity#BEFORE} or {@link Gravity#RIGHT} or + * {@link Gravity#AFTER} it provides an offset from the given edge. */ @ViewDebug.ExportedProperty public int x; diff --git a/core/java/android/webkit/DataLoader.java b/core/java/android/webkit/DataLoader.java index 235dc5be..e8d9069 100644 --- a/core/java/android/webkit/DataLoader.java +++ b/core/java/android/webkit/DataLoader.java @@ -22,7 +22,7 @@ import com.android.internal.R; import java.io.ByteArrayInputStream; -import org.apache.harmony.luni.util.Base64; +import libcore.io.Base64; /** * This class is a concrete implementation of StreamLoader that uses the diff --git a/core/java/android/webkit/JWebCoreJavaBridge.java b/core/java/android/webkit/JWebCoreJavaBridge.java index 12391df..5b78586 100644 --- a/core/java/android/webkit/JWebCoreJavaBridge.java +++ b/core/java/android/webkit/JWebCoreJavaBridge.java @@ -16,6 +16,7 @@ package android.webkit; +import android.net.ProxyProperties; import android.net.Uri; import android.os.Handler; import android.os.Message; @@ -294,6 +295,20 @@ final class JWebCoreJavaBridge extends Handler { mContentUriToFilePathMap.put(contentUri, path); } + public void updateProxy(ProxyProperties proxyProperties) { + if (proxyProperties == null) { + nativeUpdateProxy("", ""); + return; + } + + String host = proxyProperties.getHost(); + int port = proxyProperties.getPort(); + if (port != 0) + host += ":" + port; + + nativeUpdateProxy(host, proxyProperties.getExclusionList()); + } + private native void nativeConstructor(); private native void nativeFinalize(); private native void sharedTimerFired(); @@ -304,5 +319,5 @@ final class JWebCoreJavaBridge extends Handler { public native void addPackageNames(Set<String> packageNames); public native void addPackageName(String packageName); public native void removePackageName(String packageName); - public native void updateProxy(String newProxy); + public native void nativeUpdateProxy(String newProxy, String exclusionList); } diff --git a/core/java/android/webkit/WebTextView.java b/core/java/android/webkit/WebTextView.java index 7f4f103..d54230c 100644 --- a/core/java/android/webkit/WebTextView.java +++ b/core/java/android/webkit/WebTextView.java @@ -34,6 +34,7 @@ import android.text.BoringLayout.Metrics; import android.text.DynamicLayout; import android.text.Editable; import android.text.InputFilter; +import android.text.InputType; import android.text.Layout; import android.text.Selection; import android.text.Spannable; @@ -853,7 +854,7 @@ import junit.framework.Assert; public void setAdapterCustom(AutoCompleteAdapter adapter) { if (adapter != null) { setInputType(getInputType() - | EditorInfo.TYPE_TEXT_FLAG_AUTO_COMPLETE); + | InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE); adapter.setTextView(this); if (mAutoFillable) { setOnItemClickListener(this); @@ -934,7 +935,7 @@ import junit.framework.Assert; */ /* package */ void setInPassword(boolean inPassword) { if (inPassword) { - setInputType(EditorInfo.TYPE_CLASS_TEXT | EditorInfo. + setInputType(InputType.TYPE_CLASS_TEXT | EditorInfo. TYPE_TEXT_VARIATION_WEB_PASSWORD); createBackground(); } @@ -1146,8 +1147,8 @@ import junit.framework.Assert; boolean single = true; boolean inPassword = false; int maxLength = -1; - int inputType = EditorInfo.TYPE_CLASS_TEXT - | EditorInfo.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT; + int inputType = InputType.TYPE_CLASS_TEXT + | InputType.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT; int imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI | EditorInfo.IME_FLAG_NO_FULLSCREEN; if (TEXT_AREA != type @@ -1160,9 +1161,9 @@ import junit.framework.Assert; break; case TEXT_AREA: single = false; - inputType |= EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE - | EditorInfo.TYPE_TEXT_FLAG_CAP_SENTENCES - | EditorInfo.TYPE_TEXT_FLAG_AUTO_CORRECT; + inputType |= InputType.TYPE_TEXT_FLAG_MULTI_LINE + | InputType.TYPE_TEXT_FLAG_CAP_SENTENCES + | InputType.TYPE_TEXT_FLAG_AUTO_CORRECT; imeOptions |= EditorInfo.IME_ACTION_NONE; break; case PASSWORD: @@ -1173,17 +1174,21 @@ import junit.framework.Assert; imeOptions |= EditorInfo.IME_ACTION_SEARCH; break; case EMAIL: - inputType = EditorInfo.TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS; + // inputType needs to be overwritten because of the different text variation. + inputType = InputType.TYPE_CLASS_TEXT + | InputType.TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS; imeOptions |= EditorInfo.IME_ACTION_GO; break; case NUMBER: - inputType |= EditorInfo.TYPE_CLASS_NUMBER; + // inputType needs to be overwritten because of the different class. + inputType = InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_NORMAL; // Number and telephone do not have both a Tab key and an // action, so set the action to NEXT imeOptions |= EditorInfo.IME_ACTION_NEXT; break; case TELEPHONE: - inputType |= EditorInfo.TYPE_CLASS_PHONE; + // inputType needs to be overwritten because of the different class. + inputType = InputType.TYPE_CLASS_PHONE; imeOptions |= EditorInfo.IME_ACTION_NEXT; break; case URL: diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 61a69ca..3c2c8f6 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -1067,20 +1067,10 @@ public class WebView extends AbsoluteLayout private static void handleProxyBroadcast(Intent intent) { ProxyProperties proxyProperties = (ProxyProperties)intent.getExtra(Proxy.EXTRA_PROXY_INFO); if (proxyProperties == null || proxyProperties.getHost() == null) { - WebViewCore.sendStaticMessage(EventHub.PROXY_CHANGED, ""); + WebViewCore.sendStaticMessage(EventHub.PROXY_CHANGED, null); return; } - - String host = proxyProperties.getHost(); - int port = proxyProperties.getPort(); - if (port != 0) - host += ":" + port; - - // TODO: Handle exclusion list - // The plan is to make an AndroidProxyResolver, and handle the blacklist - // there - String exclusionList = proxyProperties.getExclusionList(); - WebViewCore.sendStaticMessage(EventHub.PROXY_CHANGED, host); + WebViewCore.sendStaticMessage(EventHub.PROXY_CHANGED, proxyProperties); } /* diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java index db5ff54..13a9793 100644 --- a/core/java/android/webkit/WebViewCore.java +++ b/core/java/android/webkit/WebViewCore.java @@ -24,6 +24,7 @@ import android.graphics.Point; import android.graphics.Rect; import android.graphics.Region; import android.media.MediaFile; +import android.net.ProxyProperties; import android.net.Uri; import android.os.Handler; import android.os.Looper; @@ -708,7 +709,7 @@ public final class WebViewCore { throw new IllegalStateException( "No WebView has been created in this process!"); } - BrowserFrame.sJavaBridge.updateProxy((String) msg.obj); + BrowserFrame.sJavaBridge.updateProxy((ProxyProperties)msg.obj); break; } } diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index f4300a5..680bb7d 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -2572,17 +2572,6 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } @Override - public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) { - // Add a record for ourselves as well. - AccessibilityEvent record = AccessibilityEvent.obtain(); - // Set the class since it is not populated in #dispatchPopulateAccessibilityEvent - record.setClassName(getClass().getName()); - child.dispatchPopulateAccessibilityEvent(record); - event.appendRecord(record); - return true; - } - - @Override public boolean onKeyDown(int keyCode, KeyEvent event) { return false; } diff --git a/core/java/android/widget/AdapterView.java b/core/java/android/widget/AdapterView.java index 14ea853..c4d05e9 100644 --- a/core/java/android/widget/AdapterView.java +++ b/core/java/android/widget/AdapterView.java @@ -887,14 +887,28 @@ public abstract class AdapterView<T extends Adapter> extends ViewGroup { // We first get a chance to populate the event. onPopulateAccessibilityEvent(event); + return false; + } + + @Override + public void onPopulateAccessibilityEvent(AccessibilityEvent event) { // We send selection events only from AdapterView to avoid // generation of such event for each child. View selectedView = getSelectedView(); if (selectedView != null) { - return selectedView.dispatchPopulateAccessibilityEvent(event); + selectedView.dispatchPopulateAccessibilityEvent(event); } + } - return false; + @Override + public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) { + // Add a record for ourselves as well. + AccessibilityEvent record = AccessibilityEvent.obtain(); + // Set the class since it is not populated in #dispatchPopulateAccessibilityEvent + record.setClassName(getClass().getName()); + child.dispatchPopulateAccessibilityEvent(record); + event.appendRecord(record); + return true; } @Override diff --git a/core/java/android/widget/FrameLayout.java b/core/java/android/widget/FrameLayout.java index 0659063..2a1398d 100644 --- a/core/java/android/widget/FrameLayout.java +++ b/core/java/android/widget/FrameLayout.java @@ -115,7 +115,7 @@ public class FrameLayout extends ViewGroup { } /** - * Describes how the foreground is positioned. Defaults to FILL. + * Describes how the foreground is positioned. Defaults to BEFORE and TOP. * * @param foregroundGravity See {@link android.view.Gravity} * @@ -124,8 +124,8 @@ public class FrameLayout extends ViewGroup { @android.view.RemotableViewMethod public void setForegroundGravity(int foregroundGravity) { if (mForegroundGravity != foregroundGravity) { - if ((foregroundGravity & Gravity.HORIZONTAL_GRAVITY_MASK) == 0) { - foregroundGravity |= Gravity.LEFT; + if ((foregroundGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) { + foregroundGravity |= Gravity.BEFORE; } if ((foregroundGravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) { @@ -364,7 +364,7 @@ public class FrameLayout extends ViewGroup { gravity = DEFAULT_CHILD_GRAVITY; } - final int horizontalGravity = gravity & Gravity.HORIZONTAL_GRAVITY_MASK; + final int horizontalGravity = Gravity.getAbsoluteGravity(gravity, isLayoutRtl()); final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK; switch (horizontalGravity) { @@ -436,7 +436,7 @@ public class FrameLayout extends ViewGroup { } Gravity.apply(mForegroundGravity, foreground.getIntrinsicWidth(), - foreground.getIntrinsicHeight(), selfBounds, overlayBounds); + foreground.getIntrinsicHeight(), selfBounds, overlayBounds, isLayoutRtl()); foreground.setBounds(overlayBounds); } diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java index 0383b5c..732cedc 100644 --- a/core/java/android/widget/GridView.java +++ b/core/java/android/widget/GridView.java @@ -1408,19 +1408,20 @@ public class GridView extends AbsListView { int childLeft; final int childTop = flow ? y : y - h; - switch (mGravity & Gravity.HORIZONTAL_GRAVITY_MASK) { - case Gravity.LEFT: - childLeft = childrenLeft; - break; - case Gravity.CENTER_HORIZONTAL: - childLeft = childrenLeft + ((mColumnWidth - w) / 2); - break; - case Gravity.RIGHT: - childLeft = childrenLeft + mColumnWidth - w; - break; - default: - childLeft = childrenLeft; - break; + final int absoluteGravity = Gravity.getAbsoluteGravity(mGravity,isLayoutRtl()); + switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) { + case Gravity.LEFT: + childLeft = childrenLeft; + break; + case Gravity.CENTER_HORIZONTAL: + childLeft = childrenLeft + ((mColumnWidth - w) / 2); + break; + case Gravity.RIGHT: + childLeft = childrenLeft + mColumnWidth - w; + break; + default: + childLeft = childrenLeft; + break; } if (needToMeasure) { diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java index d8068f9..4b870ec 100644 --- a/core/java/android/widget/ImageView.java +++ b/core/java/android/widget/ImageView.java @@ -187,6 +187,11 @@ public class ImageView extends View { } @Override + public boolean isLayoutRtl(Drawable dr) { + return (dr == mDrawable) ? isLayoutRtl() : super.isLayoutRtl(dr); + } + + @Override protected boolean onSetAlpha(int alpha) { if (getBackground() == null) { int scale = alpha + (alpha >> 7); diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java index 6f30452..8d449e0 100644 --- a/core/java/android/widget/LinearLayout.java +++ b/core/java/android/widget/LinearLayout.java @@ -110,6 +110,8 @@ public class LinearLayout extends ViewGroup { @ViewDebug.IntToString(from = Gravity.BOTTOM, to = "BOTTOM"), @ViewDebug.IntToString(from = Gravity.LEFT, to = "LEFT"), @ViewDebug.IntToString(from = Gravity.RIGHT, to = "RIGHT"), + @ViewDebug.IntToString(from = Gravity.BEFORE, to = "BEFORE"), + @ViewDebug.IntToString(from = Gravity.AFTER, to = "AFTER"), @ViewDebug.IntToString(from = Gravity.CENTER_VERTICAL, to = "CENTER_VERTICAL"), @ViewDebug.IntToString(from = Gravity.FILL_VERTICAL, to = "FILL_VERTICAL"), @ViewDebug.IntToString(from = Gravity.CENTER_HORIZONTAL, to = "CENTER_HORIZONTAL"), @@ -117,7 +119,7 @@ public class LinearLayout extends ViewGroup { @ViewDebug.IntToString(from = Gravity.CENTER, to = "CENTER"), @ViewDebug.IntToString(from = Gravity.FILL, to = "FILL") }) - private int mGravity = Gravity.LEFT | Gravity.TOP; + private int mGravity = Gravity.BEFORE | Gravity.TOP; @ViewDebug.ExportedProperty(category = "measurement") private int mTotalLength; @@ -1394,7 +1396,7 @@ public class LinearLayout extends ViewGroup { final int count = getVirtualChildCount(); final int majorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK; - final int minorGravity = mGravity & Gravity.HORIZONTAL_GRAVITY_MASK; + final int minorGravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK; switch (majorGravity) { case Gravity.BOTTOM: @@ -1428,7 +1430,7 @@ public class LinearLayout extends ViewGroup { if (gravity < 0) { gravity = minorGravity; } - + gravity = Gravity.getAbsoluteGravity(gravity, isLayoutRtl()); switch (gravity & Gravity.HORIZONTAL_GRAVITY_MASK) { case Gravity.CENTER_HORIZONTAL: childLeft = paddingLeft + ((childSpace - childWidth) / 2) @@ -1483,7 +1485,7 @@ public class LinearLayout extends ViewGroup { final int count = getVirtualChildCount(); - final int majorGravity = mGravity & Gravity.HORIZONTAL_GRAVITY_MASK; + final int majorGravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK; final int minorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK; final boolean baselineAligned = mBaselineAligned; @@ -1491,7 +1493,7 @@ public class LinearLayout extends ViewGroup { final int[] maxAscent = mMaxAscent; final int[] maxDescent = mMaxDescent; - switch (majorGravity) { + switch (Gravity.getAbsoluteGravity(majorGravity, isLayoutRtl())) { case Gravity.RIGHT: // mTotalLength contains the padding already childLeft = mPaddingLeft + mRight - mLeft - mTotalLength; @@ -1630,8 +1632,8 @@ public class LinearLayout extends ViewGroup { @android.view.RemotableViewMethod public void setGravity(int gravity) { if (mGravity != gravity) { - if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == 0) { - gravity |= Gravity.LEFT; + if ((gravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) { + gravity |= Gravity.BEFORE; } if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) { @@ -1645,9 +1647,9 @@ public class LinearLayout extends ViewGroup { @android.view.RemotableViewMethod public void setHorizontalGravity(int horizontalGravity) { - final int gravity = horizontalGravity & Gravity.HORIZONTAL_GRAVITY_MASK; - if ((mGravity & Gravity.HORIZONTAL_GRAVITY_MASK) != gravity) { - mGravity = (mGravity & ~Gravity.HORIZONTAL_GRAVITY_MASK) | gravity; + final int gravity = horizontalGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK; + if ((mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) != gravity) { + mGravity = (mGravity & ~Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) | gravity; requestLayout(); } } @@ -1724,6 +1726,8 @@ public class LinearLayout extends ViewGroup { @ViewDebug.IntToString(from = Gravity.BOTTOM, to = "BOTTOM"), @ViewDebug.IntToString(from = Gravity.LEFT, to = "LEFT"), @ViewDebug.IntToString(from = Gravity.RIGHT, to = "RIGHT"), + @ViewDebug.IntToString(from = Gravity.BEFORE, to = "BEFORE"), + @ViewDebug.IntToString(from = Gravity.AFTER, to = "AFTER"), @ViewDebug.IntToString(from = Gravity.CENTER_VERTICAL, to = "CENTER_VERTICAL"), @ViewDebug.IntToString(from = Gravity.FILL_VERTICAL, to = "FILL_VERTICAL"), @ViewDebug.IntToString(from = Gravity.CENTER_HORIZONTAL, to = "CENTER_HORIZONTAL"), diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java index 30374af..ed9114a 100644 --- a/core/java/android/widget/ProgressBar.java +++ b/core/java/android/widget/ProgressBar.java @@ -916,6 +916,12 @@ public class ProgressBar extends View { } @Override + public boolean isLayoutRtl(Drawable who) { + return (who == mProgressDrawable || who == mIndeterminateDrawable) ? + isLayoutRtl() : super.isLayoutRtl(who); + } + + @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { updateDrawableBounds(w, h); } diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java index 9069283..acd8539 100644 --- a/core/java/android/widget/RelativeLayout.java +++ b/core/java/android/widget/RelativeLayout.java @@ -16,32 +16,32 @@ package android.widget; -import com.android.internal.R; - import android.content.Context; -import android.content.res.TypedArray; import android.content.res.Resources; +import android.content.res.TypedArray; import android.graphics.Rect; import android.util.AttributeSet; -import android.util.SparseArray; -import android.util.Poolable; import android.util.Pool; -import android.util.Pools; +import android.util.Poolable; import android.util.PoolableManager; -import static android.util.Log.d; +import android.util.Pools; +import android.util.SparseArray; import android.view.Gravity; import android.view.View; import android.view.ViewDebug; import android.view.ViewGroup; import android.view.accessibility.AccessibilityEvent; import android.widget.RemoteViews.RemoteView; +import com.android.internal.R; +import java.util.ArrayList; import java.util.Comparator; +import java.util.HashSet; +import java.util.LinkedList; import java.util.SortedSet; import java.util.TreeSet; -import java.util.LinkedList; -import java.util.HashSet; -import java.util.ArrayList; + +import static android.util.Log.d; /** * A Layout where the positions of the children can be described in relation to each other or to the @@ -221,8 +221,8 @@ public class RelativeLayout extends ViewGroup { @android.view.RemotableViewMethod public void setGravity(int gravity) { if (mGravity != gravity) { - if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == 0) { - gravity |= Gravity.LEFT; + if ((gravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) { + gravity |= Gravity.BEFORE; } if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) { @@ -236,9 +236,9 @@ public class RelativeLayout extends ViewGroup { @android.view.RemotableViewMethod public void setHorizontalGravity(int horizontalGravity) { - final int gravity = horizontalGravity & Gravity.HORIZONTAL_GRAVITY_MASK; - if ((mGravity & Gravity.HORIZONTAL_GRAVITY_MASK) != gravity) { - mGravity = (mGravity & ~Gravity.HORIZONTAL_GRAVITY_MASK) | gravity; + final int gravity = horizontalGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK; + if ((mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) != gravity) { + mGravity = (mGravity & ~Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) | gravity; requestLayout(); } } @@ -339,7 +339,7 @@ public class RelativeLayout extends ViewGroup { mHasBaselineAlignedChild = false; View ignore = null; - int gravity = mGravity & Gravity.HORIZONTAL_GRAVITY_MASK; + int gravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK; final boolean horizontalGravity = gravity != Gravity.LEFT && gravity != 0; gravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK; final boolean verticalGravity = gravity != Gravity.TOP && gravity != 0; @@ -494,7 +494,8 @@ public class RelativeLayout extends ViewGroup { height - mPaddingBottom); final Rect contentBounds = mContentBounds; - Gravity.apply(mGravity, right - left, bottom - top, selfBounds, contentBounds); + Gravity.apply(mGravity, right - left, bottom - top, selfBounds, contentBounds, + isLayoutRtl()); final int horizontalOffset = contentBounds.left - left; final int verticalOffset = contentBounds.top - top; diff --git a/core/java/android/widget/TableRow.java b/core/java/android/widget/TableRow.java index b612004..5f20c85 100644 --- a/core/java/android/widget/TableRow.java +++ b/core/java/android/widget/TableRow.java @@ -224,7 +224,8 @@ public class TableRow extends LinearLayout { final int childWidth = child.getMeasuredWidth(); lp.mOffset[LayoutParams.LOCATION_NEXT] = columnWidth - childWidth; - switch (gravity & Gravity.HORIZONTAL_GRAVITY_MASK) { + final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, isLayoutRtl()); + switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) { case Gravity.LEFT: // don't offset on X axis break; diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 8835a04..9ec3a26 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -2093,8 +2093,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * @attr ref android.R.styleable#TextView_gravity */ public void setGravity(int gravity) { - if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == 0) { - gravity |= Gravity.LEFT; + if ((gravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) { + gravity |= Gravity.BEFORE; } if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) { gravity |= Gravity.TOP; @@ -2102,8 +2102,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener boolean newLayout = false; - if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) != - (mGravity & Gravity.HORIZONTAL_GRAVITY_MASK)) { + if ((gravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) != + (mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK)) { newLayout = true; } @@ -4142,6 +4142,17 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } @Override + public boolean isLayoutRtl(Drawable who) { + if (who == null) return false; + final TextView.Drawables drawables = mDrawables; + if (who == drawables.mDrawableLeft || who == drawables.mDrawableRight || + who == drawables.mDrawableTop || who == drawables.mDrawableBottom) { + return isLayoutRtl(); + } + return super.isLayoutRtl(who); + } + + @Override protected boolean onSetAlpha(int alpha) { // Alpha is supported if and only if the drawing can be done in one pass. // TODO text with spans with a background color currently do not respect this alpha. @@ -4380,9 +4391,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener canvas.translate(compoundPaddingLeft, extendedPaddingTop + voffsetText); } + final int absoluteGravity = Gravity.getAbsoluteGravity(mGravity, isLayoutRtl()); if (mEllipsize == TextUtils.TruncateAt.MARQUEE) { if (!mSingleLine && getLineCount() == 1 && canMarquee() && - (mGravity & Gravity.HORIZONTAL_GRAVITY_MASK) != Gravity.LEFT) { + (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) != Gravity.LEFT) { canvas.translate(mLayout.getLineRight(0) - (mRight - mLeft - getCompoundPaddingLeft() - getCompoundPaddingRight()), 0.0f); } @@ -5528,7 +5540,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } Layout.Alignment alignment; - switch (mGravity & Gravity.HORIZONTAL_GRAVITY_MASK) { + final int absoluteGravity = Gravity.getAbsoluteGravity(mGravity, isLayoutRtl()); + switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) { case Gravity.CENTER_HORIZONTAL: alignment = Layout.Alignment.ALIGN_CENTER; break; @@ -7563,7 +7576,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return 0.0f; } } else if (getLineCount() == 1) { - switch (mGravity & Gravity.HORIZONTAL_GRAVITY_MASK) { + final int absoluteGravity = Gravity.getAbsoluteGravity(mGravity, isLayoutRtl()); + switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) { case Gravity.LEFT: return 0.0f; case Gravity.RIGHT: @@ -7586,7 +7600,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener final Marquee marquee = mMarquee; return (marquee.mMaxFadeScroll - marquee.mScroll) / getHorizontalFadingEdgeLength(); } else if (getLineCount() == 1) { - switch (mGravity & Gravity.HORIZONTAL_GRAVITY_MASK) { + final int absoluteGravity = Gravity.getAbsoluteGravity(mGravity, isLayoutRtl()); + switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) { case Gravity.LEFT: final int textWidth = (mRight - mLeft) - getCompoundPaddingLeft() - getCompoundPaddingRight(); @@ -8527,6 +8542,14 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener String[] suggestions = suggestionInfo.suggestionSpan.getSuggestions(); suggestions[suggestionInfo.suggestionIndex] = originalText; + // Notify source IME of the suggestion pick + if (!TextUtils.isEmpty( + suggestionInfo.suggestionSpan.getNotificationTargetClassName())) { + InputMethodManager imm = InputMethodManager.peekInstance(); + imm.notifySuggestionPicked(suggestionInfo.suggestionSpan, originalText, + suggestionInfo.suggestionIndex); + } + // Restore previous SuggestionSpans final int lengthDifference = suggestion.length() - (spanEnd - spanStart); for (int i = 0; i < length; i++) { |
