diff options
Diffstat (limited to 'core/java/android')
112 files changed, 4259 insertions, 1609 deletions
diff --git a/core/java/android/alsa/AlsaCardsParser.java b/core/java/android/alsa/AlsaCardsParser.java new file mode 100644 index 0000000..f9af979 --- /dev/null +++ b/core/java/android/alsa/AlsaCardsParser.java @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2014 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 com.android.alsascan; + +import android.util.Slog; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.util.Vector; + +/** + * @hide Retrieves information from an ALSA "cards" file. + */ +public class AlsaCardsParser { + private static final String TAG = "AlsaCardsParser"; + + private static LineTokenizer tokenizer_ = new LineTokenizer(" :[]"); + + public class AlsaCardRecord { + public int mCardNum = -1; + public String mField1 = ""; + public String mCardName = ""; + public String mCardDescription = ""; + + public AlsaCardRecord() {} + + public boolean parse(String line, int lineIndex) { + int tokenIndex = 0; + int delimIndex = 0; + if (lineIndex == 0) { + // line # (skip) + tokenIndex = tokenizer_.nextToken(line, tokenIndex); + delimIndex = tokenizer_.nextDelimiter(line, tokenIndex); + + // mField1 + tokenIndex = tokenizer_.nextToken(line, delimIndex); + delimIndex = tokenizer_.nextDelimiter(line, tokenIndex); + mField1 = line.substring(tokenIndex, delimIndex); + + // mCardName + tokenIndex = tokenizer_.nextToken(line, delimIndex); + // delimIndex = tokenizer_.nextDelimiter(line, tokenIndex); + mCardName = line.substring(tokenIndex); + // done + } else if (lineIndex == 1) { + tokenIndex = tokenizer_.nextToken(line, 0); + if (tokenIndex != -1) { + mCardDescription = line.substring(tokenIndex); + } + } + + return true; + } + + public String textFormat() { + return mCardName + " : " + mCardDescription; + } + } + + private Vector<AlsaCardRecord> cardRecords_ = new Vector<AlsaCardRecord>(); + + public void scan() { + cardRecords_.clear(); + final String cardsFilePath = "/proc/asound/cards"; + File cardsFile = new File(cardsFilePath); + try { + FileReader reader = new FileReader(cardsFile); + BufferedReader bufferedReader = new BufferedReader(reader); + String line = ""; + while ((line = bufferedReader.readLine()) != null) { + AlsaCardRecord cardRecord = new AlsaCardRecord(); + cardRecord.parse(line, 0); + cardRecord.parse(line = bufferedReader.readLine(), 1); + cardRecords_.add(cardRecord); + } + reader.close(); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public AlsaCardRecord getCardRecordAt(int index) { + return cardRecords_.get(index); + } + + public int getNumCardRecords() { + return cardRecords_.size(); + } + + public void Log() { + int numCardRecs = getNumCardRecords(); + for (int index = 0; index < numCardRecs; ++index) { + Slog.w(TAG, "usb:" + getCardRecordAt(index).textFormat()); + } + } + + public AlsaCardsParser() {} +} diff --git a/core/java/android/alsa/AlsaDevicesParser.java b/core/java/android/alsa/AlsaDevicesParser.java new file mode 100644 index 0000000..3835942 --- /dev/null +++ b/core/java/android/alsa/AlsaDevicesParser.java @@ -0,0 +1,240 @@ +/* + * Copyright (C) 2014 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 com.android.alsascan; + +import android.util.Slog; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.util.Vector; + +/** + * @hide + * Retrieves information from an ALSA "devices" file. + */ +public class AlsaDevicesParser { + private static final String TAG = "AlsaDevicesParser"; + + private static final int kIndex_CardDeviceField = 5; + private static final int kStartIndex_CardNum = 6; + private static final int kEndIndex_CardNum = 8; // one past + private static final int kStartIndex_DeviceNum = 9; + private static final int kEndIndex_DeviceNum = 11; // one past + private static final int kStartIndex_Type = 14; + + private static LineTokenizer mTokenizer = new LineTokenizer(" :[]-"); + + private boolean mHasCaptureDevices = false; + private boolean mHasPlaybackDevices = false; + private boolean mHasMIDIDevices = false; + + public class AlsaDeviceRecord { + public static final int kDeviceType_Unknown = -1; + public static final int kDeviceType_Audio = 0; + public static final int kDeviceType_Control = 1; + public static final int kDeviceType_MIDI = 2; + + public static final int kDeviceDir_Unknown = -1; + public static final int kDeviceDir_Capture = 0; + public static final int kDeviceDir_Playback = 1; + + int mCardNum = -1; + int mDeviceNum = -1; + int mDeviceType = kDeviceType_Unknown; + int mDeviceDir = kDeviceDir_Unknown; + + public AlsaDeviceRecord() { + } + + public boolean parse(String line) { + // "0123456789012345678901234567890" + // " 2: [ 0-31]: digital audio playback" + // " 3: [ 0-30]: digital audio capture" + // " 35: [ 1] : control" + // " 36: [ 2- 0]: raw midi" + + final int kToken_LineNum = 0; + final int kToken_CardNum = 1; + final int kToken_DeviceNum = 2; + final int kToken_Type0 = 3; // "digital", "control", "raw" + final int kToken_Type1 = 4; // "audio", "midi" + final int kToken_Type2 = 5; // "capture", "playback" + + int tokenOffset = 0; + int delimOffset = 0; + int tokenIndex = kToken_LineNum; + while (true) { + tokenOffset = mTokenizer.nextToken(line, delimOffset); + if (tokenOffset == LineTokenizer.kTokenNotFound) { + break; // bail + } + delimOffset = mTokenizer.nextDelimiter(line, tokenOffset); + if (delimOffset == LineTokenizer.kTokenNotFound) { + delimOffset = line.length(); + } + String token = line.substring(tokenOffset, delimOffset); + + switch (tokenIndex) { + case kToken_LineNum: + // ignore + break; + + case kToken_CardNum: + mCardNum = Integer.parseInt(token); + if (line.charAt(delimOffset) != '-') { + tokenIndex++; // no device # in the token stream + } + break; + + case kToken_DeviceNum: + mDeviceNum = Integer.parseInt(token); + break; + + case kToken_Type0: + if (token.equals("digital")) { + // NOP + } else if (token.equals("control")) { + mDeviceType = kDeviceType_Control; + } else if (token.equals("raw")) { + // NOP + } + break; + + case kToken_Type1: + if (token.equals("audio")) { + mDeviceType = kDeviceType_Audio; + } else if (token.equals("midi")) { + mDeviceType = kDeviceType_MIDI; + mHasMIDIDevices = true; + } + break; + + case kToken_Type2: + if (token.equals("capture")) { + mDeviceDir = kDeviceDir_Capture; + mHasCaptureDevices = true; + } else if (token.equals("playback")) { + mDeviceDir = kDeviceDir_Playback; + mHasPlaybackDevices = true; + } + break; + } // switch (tokenIndex) + + tokenIndex++; + } // while (true) + + return true; + } // parse() + + public String textFormat() { + StringBuilder sb = new StringBuilder(); + sb.append("[" + mCardNum + ":" + mDeviceNum + "]"); + + switch (mDeviceType) { + case kDeviceType_Unknown: + sb.append(" N/A"); + break; + case kDeviceType_Audio: + sb.append(" Audio"); + break; + case kDeviceType_Control: + sb.append(" Control"); + break; + case kDeviceType_MIDI: + sb.append(" MIDI"); + break; + } + + switch (mDeviceDir) { + case kDeviceDir_Unknown: + sb.append(" N/A"); + break; + case kDeviceDir_Capture: + sb.append(" Capture"); + break; + case kDeviceDir_Playback: + sb.append(" Playback"); + break; + } + + return sb.toString(); + } + } + + private Vector<AlsaDeviceRecord> + deviceRecords_ = new Vector<AlsaDeviceRecord>(); + + private boolean isLineDeviceRecord(String line) { + return line.charAt(kIndex_CardDeviceField) == '['; + } + + public AlsaDevicesParser() { + } + + public int getNumDeviceRecords() { + return deviceRecords_.size(); + } + + public AlsaDeviceRecord getDeviceRecordAt(int index) { + return deviceRecords_.get(index); + } + + public void Log() { + int numDevRecs = getNumDeviceRecords(); + for (int index = 0; index < numDevRecs; ++index) { + Slog.w(TAG, "usb:" + getDeviceRecordAt(index).textFormat()); + } + } + + public boolean hasPlaybackDevices() { + return mHasPlaybackDevices; + } + + public boolean hasCaptureDevices() { + return mHasCaptureDevices; + } + + public boolean hasMIDIDevices() { + return mHasMIDIDevices; + } + + public void scan() { + deviceRecords_.clear(); + + final String devicesFilePath = "/proc/asound/devices"; + File devicesFile = new File(devicesFilePath); + try { + FileReader reader = new FileReader(devicesFile); + BufferedReader bufferedReader = new BufferedReader(reader); + String line = ""; + while ((line = bufferedReader.readLine()) != null) { + if (isLineDeviceRecord(line)) { + AlsaDeviceRecord deviceRecord = new AlsaDeviceRecord(); + deviceRecord.parse(line); + deviceRecords_.add(deviceRecord); + } + } + reader.close(); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } +} // class AlsaDevicesParser + diff --git a/core/java/android/alsa/LineTokenizer.java b/core/java/android/alsa/LineTokenizer.java new file mode 100644 index 0000000..c138fc5 --- /dev/null +++ b/core/java/android/alsa/LineTokenizer.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2014 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 com.android.alsascan; + +/** + * @hide + * Breaks lines in an ALSA "cards" or "devices" file into tokens. + * TODO(pmclean) Look into replacing this with String.split(). + */ +public class LineTokenizer { + public static final int kTokenNotFound = -1; + + private String mDelimiters = ""; + + public LineTokenizer(String delimiters) { + mDelimiters = delimiters; + } + + int nextToken(String line, int startIndex) { + int len = line.length(); + int offset = startIndex; + for (; offset < len; offset++) { + if (mDelimiters.indexOf(line.charAt(offset)) == -1) { + // past a delimiter + break; + } + } + + return offset < len ? offset : kTokenNotFound; + } + + int nextDelimiter(String line, int startIndex) { + int len = line.length(); + int offset = startIndex; + for (; offset < len; offset++) { + if (mDelimiters.indexOf(line.charAt(offset)) != -1) { + // past a delimiter + break; + } + } + + return offset < len ? offset : kTokenNotFound; + } +} diff --git a/core/java/android/animation/RevealAnimator.java b/core/java/android/animation/RevealAnimator.java new file mode 100644 index 0000000..77a536a --- /dev/null +++ b/core/java/android/animation/RevealAnimator.java @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2014 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.animation; + +import android.view.View; + +import java.util.ArrayList; + +/** + * Reveals a View with an animated clipping circle. + * The clipping is implemented efficiently by talking to a private reveal API on View. + * This hidden class currently only accessed by the {@link android.view.View}. + * + * @hide + */ +public class RevealAnimator extends ValueAnimator { + private final static String LOGTAG = "RevealAnimator"; + private ValueAnimator.AnimatorListener mListener; + private ValueAnimator.AnimatorUpdateListener mUpdateListener; + private RevealCircle mReuseRevealCircle = new RevealCircle(0); + private RevealAnimator(final View clipView, final int x, final int y, + float startRadius, float endRadius, final boolean inverseClip) { + + setObjectValues(new RevealCircle(startRadius), new RevealCircle(endRadius)); + setEvaluator(new RevealCircleEvaluator(mReuseRevealCircle)); + + mUpdateListener = new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + RevealCircle circle = (RevealCircle) animation.getAnimatedValue(); + float radius = circle.getRadius(); + clipView.setRevealClip(true, inverseClip, x, y, radius); + } + }; + mListener = new AnimatorListenerAdapter() { + @Override + public void onAnimationCancel(Animator animation) { + clipView.setRevealClip(false, false, 0, 0, 0); + } + + @Override + public void onAnimationEnd(Animator animation) { + clipView.setRevealClip(false, false, 0, 0, 0); + } + }; + addUpdateListener(mUpdateListener); + addListener(mListener); + } + + public static RevealAnimator ofRevealCircle(View clipView, int x, int y, + float startRadius, float endRadius, boolean inverseClip) { + RevealAnimator anim = new RevealAnimator(clipView, x, y, + startRadius, endRadius, inverseClip); + return anim; + } + + + /** + * {@inheritDoc} + */ + @Override + public void removeAllUpdateListeners() { + super.removeAllUpdateListeners(); + addUpdateListener(mUpdateListener); + } + + /** + * {@inheritDoc} + */ + @Override + public void removeAllListeners() { + super.removeAllListeners(); + addListener(mListener); + } + + /** + * {@inheritDoc} + */ + @Override + public ArrayList<AnimatorListener> getListeners() { + ArrayList<AnimatorListener> allListeners = + (ArrayList<AnimatorListener>) super.getListeners().clone(); + allListeners.remove(mListener); + return allListeners; + } + + private class RevealCircle { + float mRadius; + + public RevealCircle(float radius) { + mRadius = radius; + } + + public void setRadius(float radius) { + mRadius = radius; + } + + public float getRadius() { + return mRadius; + } + } + + private class RevealCircleEvaluator implements TypeEvaluator<RevealCircle> { + + private RevealCircle mRevealCircle; + + public RevealCircleEvaluator() { + } + + public RevealCircleEvaluator(RevealCircle reuseCircle) { + mRevealCircle = reuseCircle; + } + + @Override + public RevealCircle evaluate(float fraction, RevealCircle startValue, + RevealCircle endValue) { + float currentRadius = startValue.mRadius + + ((endValue.mRadius - startValue.mRadius) * fraction); + if (mRevealCircle == null) { + return new RevealCircle(currentRadius); + } else { + mRevealCircle.setRadius(currentRadius); + return mRevealCircle; + } + } + } +} diff --git a/core/java/android/app/ActionBar.java b/core/java/android/app/ActionBar.java index fbe8987..34b0f3a 100644 --- a/core/java/android/app/ActionBar.java +++ b/core/java/android/app/ActionBar.java @@ -20,9 +20,11 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; +import android.content.res.Configuration; import android.content.res.TypedArray; import android.graphics.drawable.Drawable; import android.util.AttributeSet; +import android.view.ActionMode; import android.view.Gravity; import android.view.View; import android.view.ViewDebug; @@ -30,31 +32,57 @@ import android.view.ViewGroup; import android.view.ViewGroup.MarginLayoutParams; import android.view.Window; import android.widget.SpinnerAdapter; +import android.widget.Toolbar; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Map; /** - * A window feature at the top of the activity that may display the activity title, navigation - * modes, and other interactive items. + * A primary toolbar within the activity that may display the activity title, application-level + * navigation affordances, and other interactive items. + * * <p>Beginning with Android 3.0 (API level 11), the action bar appears at the top of an * activity's window when the activity uses the system's {@link * android.R.style#Theme_Holo Holo} theme (or one of its descendant themes), which is the default. * You may otherwise add the action bar by calling {@link * android.view.Window#requestFeature requestFeature(FEATURE_ACTION_BAR)} or by declaring it in a * custom theme with the {@link android.R.styleable#Theme_windowActionBar windowActionBar} property. - * <p>By default, the action bar shows the application icon on + * </p> + * + * <p>Beginning with Android L (API level 21), the action bar may be represented by any + * Toolbar widget within the application layout. The application may signal to the Activity + * which Toolbar should be treated as the Activity's action bar. Activities that use this + * feature should use one of the supplied <code>.NoActionBar</code> themes, set the + * {@link android.R.styleable#Theme_windowActionBar windowActionBar} attribute to <code>false</code> + * or otherwise not request the window feature.</p> + * + * <p>By adjusting the window features requested by the theme and the layouts used for + * an Activity's content view, an app can use the standard system action bar on older platform + * releases and the newer inline toolbars on newer platform releases. The <code>ActionBar</code> + * object obtained from the Activity can be used to control either configuration transparently.</p> + * + * <p>When using the Holo themes the action bar shows the application icon on * the left, followed by the activity title. If your activity has an options menu, you can make * select items accessible directly from the action bar as "action items". You can also * modify various characteristics of the action bar or remove it completely.</p> + * + * <p>When using the Quantum themes (default in API 21 or newer) the navigation button + * (formerly "Home") takes over the space previously occupied by the application icon. + * Apps wishing to express a stronger branding should use their brand colors heavily + * in the action bar and other application chrome or use a {@link #setLogo(int) logo} + * in place of their standard title text.</p> + * * <p>From your activity, you can retrieve an instance of {@link ActionBar} by calling {@link * android.app.Activity#getActionBar getActionBar()}.</p> + * * <p>In some cases, the action bar may be overlayed by another bar that enables contextual actions, * using an {@link android.view.ActionMode}. For example, when the user selects one or more items in * your activity, you can enable an action mode that offers actions specific to the selected * items, with a UI that temporarily replaces the action bar. Although the UI may occupy the * same space, the {@link android.view.ActionMode} APIs are distinct and independent from those for - * {@link ActionBar}. + * {@link ActionBar}.</p> + * * <div class="special reference"> * <h3>Developer Guides</h3> * <p>For information about how to use the action bar, including how to add action items, navigation @@ -73,6 +101,11 @@ public abstract class ActionBar { * and title text with an optional subtitle. Clicking any of these elements * will dispatch onOptionsItemSelected to the host Activity with * a MenuItem with item ID android.R.id.home. + * + * @deprecated Action bar navigation modes are deprecated and not supported by inline + * toolbar action bars. Consider using other + * <a href="http://developer.android.com/design/patterns/navigation.html">common + * navigation patterns</a> instead. */ public static final int NAVIGATION_MODE_STANDARD = 0; @@ -80,12 +113,22 @@ public abstract class ActionBar { * List navigation mode. Instead of static title text this mode * presents a list menu for navigation within the activity. * e.g. this might be presented to the user as a dropdown list. + * + * @deprecated Action bar navigation modes are deprecated and not supported by inline + * toolbar action bars. Consider using other + * <a href="http://developer.android.com/design/patterns/navigation.html">common + * navigation patterns</a> instead. */ public static final int NAVIGATION_MODE_LIST = 1; /** * Tab navigation mode. Instead of static title text this mode * presents a series of tabs for navigation within the activity. + * + * @deprecated Action bar navigation modes are deprecated and not supported by inline + * toolbar action bars. Consider using other + * <a href="http://developer.android.com/design/patterns/navigation.html">common + * navigation patterns</a> instead. */ public static final int NAVIGATION_MODE_TABS = 2; @@ -288,6 +331,11 @@ public abstract class ActionBar { * within the dropdown navigation menu. * @param callback An OnNavigationListener that will receive events when the user * selects a navigation item. + * + * @deprecated Action bar navigation modes are deprecated and not supported by inline + * toolbar action bars. Consider using other + * <a href="http://developer.android.com/design/patterns/navigation.html">common + * navigation patterns</a> instead. */ public abstract void setListNavigationCallbacks(SpinnerAdapter adapter, OnNavigationListener callback); @@ -296,6 +344,11 @@ public abstract class ActionBar { * Set the selected navigation item in list or tabbed navigation modes. * * @param position Position of the item to select. + * + * @deprecated Action bar navigation modes are deprecated and not supported by inline + * toolbar action bars. Consider using other + * <a href="http://developer.android.com/design/patterns/navigation.html">common + * navigation patterns</a> instead. */ public abstract void setSelectedNavigationItem(int position); @@ -303,6 +356,11 @@ public abstract class ActionBar { * Get the position of the selected navigation item in list or tabbed navigation modes. * * @return Position of the selected item. + * + * @deprecated Action bar navigation modes are deprecated and not supported by inline + * toolbar action bars. Consider using other + * <a href="http://developer.android.com/design/patterns/navigation.html">common + * navigation patterns</a> instead. */ public abstract int getSelectedNavigationIndex(); @@ -310,6 +368,11 @@ public abstract class ActionBar { * Get the number of navigation items present in the current navigation mode. * * @return Number of navigation items. + * + * @deprecated Action bar navigation modes are deprecated and not supported by inline + * toolbar action bars. Consider using other + * <a href="http://developer.android.com/design/patterns/navigation.html">common + * navigation patterns</a> instead. */ public abstract int getNavigationItemCount(); @@ -507,6 +570,11 @@ public abstract class ActionBar { * </ul> * * @return The current navigation mode. + * + * @deprecated Action bar navigation modes are deprecated and not supported by inline + * toolbar action bars. Consider using other + * <a href="http://developer.android.com/design/patterns/navigation.html">common + * navigation patterns</a> instead. */ @NavigationMode public abstract int getNavigationMode(); @@ -518,6 +586,11 @@ public abstract class ActionBar { * @see #NAVIGATION_MODE_STANDARD * @see #NAVIGATION_MODE_LIST * @see #NAVIGATION_MODE_TABS + * + * @deprecated Action bar navigation modes are deprecated and not supported by inline + * toolbar action bars. Consider using other + * <a href="http://developer.android.com/design/patterns/navigation.html">common + * navigation patterns</a> instead. */ public abstract void setNavigationMode(@NavigationMode int mode); @@ -539,6 +612,11 @@ public abstract class ActionBar { * @return A new Tab * * @see #addTab(Tab) + * + * @deprecated Action bar navigation modes are deprecated and not supported by inline + * toolbar action bars. Consider using other + * <a href="http://developer.android.com/design/patterns/navigation.html">common + * navigation patterns</a> instead. */ public abstract Tab newTab(); @@ -547,6 +625,11 @@ public abstract class ActionBar { * If this is the first tab to be added it will become the selected tab. * * @param tab Tab to add + * + * @deprecated Action bar navigation modes are deprecated and not supported by inline + * toolbar action bars. Consider using other + * <a href="http://developer.android.com/design/patterns/navigation.html">common + * navigation patterns</a> instead. */ public abstract void addTab(Tab tab); @@ -555,6 +638,11 @@ public abstract class ActionBar { * * @param tab Tab to add * @param setSelected True if the added tab should become the selected tab. + * + * @deprecated Action bar navigation modes are deprecated and not supported by inline + * toolbar action bars. Consider using other + * <a href="http://developer.android.com/design/patterns/navigation.html">common + * navigation patterns</a> instead. */ public abstract void addTab(Tab tab, boolean setSelected); @@ -565,6 +653,11 @@ public abstract class ActionBar { * * @param tab The tab to add * @param position The new position of the tab + * + * @deprecated Action bar navigation modes are deprecated and not supported by inline + * toolbar action bars. Consider using other + * <a href="http://developer.android.com/design/patterns/navigation.html">common + * navigation patterns</a> instead. */ public abstract void addTab(Tab tab, int position); @@ -575,6 +668,11 @@ public abstract class ActionBar { * @param tab The tab to add * @param position The new position of the tab * @param setSelected True if the added tab should become the selected tab. + * + * @deprecated Action bar navigation modes are deprecated and not supported by inline + * toolbar action bars. Consider using other + * <a href="http://developer.android.com/design/patterns/navigation.html">common + * navigation patterns</a> instead. */ public abstract void addTab(Tab tab, int position, boolean setSelected); @@ -583,6 +681,11 @@ public abstract class ActionBar { * and another tab will be selected if present. * * @param tab The tab to remove + * + * @deprecated Action bar navigation modes are deprecated and not supported by inline + * toolbar action bars. Consider using other + * <a href="http://developer.android.com/design/patterns/navigation.html">common + * navigation patterns</a> instead. */ public abstract void removeTab(Tab tab); @@ -591,11 +694,21 @@ public abstract class ActionBar { * and another tab will be selected if present. * * @param position Position of the tab to remove + * + * @deprecated Action bar navigation modes are deprecated and not supported by inline + * toolbar action bars. Consider using other + * <a href="http://developer.android.com/design/patterns/navigation.html">common + * navigation patterns</a> instead. */ public abstract void removeTabAt(int position); /** * Remove all tabs from the action bar and deselect the current tab. + * + * @deprecated Action bar navigation modes are deprecated and not supported by inline + * toolbar action bars. Consider using other + * <a href="http://developer.android.com/design/patterns/navigation.html">common + * navigation patterns</a> instead. */ public abstract void removeAllTabs(); @@ -605,6 +718,11 @@ public abstract class ActionBar { * <p>Note: If you want to select by index, use {@link #setSelectedNavigationItem(int)}.</p> * * @param tab Tab to select + * + * @deprecated Action bar navigation modes are deprecated and not supported by inline + * toolbar action bars. Consider using other + * <a href="http://developer.android.com/design/patterns/navigation.html">common + * navigation patterns</a> instead. */ public abstract void selectTab(Tab tab); @@ -613,6 +731,11 @@ public abstract class ActionBar { * one tab present. * * @return The currently selected tab or null + * + * @deprecated Action bar navigation modes are deprecated and not supported by inline + * toolbar action bars. Consider using other + * <a href="http://developer.android.com/design/patterns/navigation.html">common + * navigation patterns</a> instead. */ public abstract Tab getSelectedTab(); @@ -621,12 +744,22 @@ public abstract class ActionBar { * * @param index Index value in the range 0-get * @return + * + * @deprecated Action bar navigation modes are deprecated and not supported by inline + * toolbar action bars. Consider using other + * <a href="http://developer.android.com/design/patterns/navigation.html">common + * navigation patterns</a> instead. */ public abstract Tab getTabAt(int index); /** * Returns the number of tabs currently registered with the action bar. * @return Tab count + * + * @deprecated Action bar navigation modes are deprecated and not supported by inline + * toolbar action bars. Consider using other + * <a href="http://developer.android.com/design/patterns/navigation.html">common + * navigation patterns</a> instead. */ public abstract int getTabCount(); @@ -799,8 +932,38 @@ public abstract class ActionBar { */ public void setHomeActionContentDescription(int resId) { } + /** @hide */ + public void setDefaultDisplayHomeAsUpEnabled(boolean enabled) { + } + + /** @hide */ + public void setShowHideAnimationEnabled(boolean enabled) { + } + + /** @hide */ + public void onConfigurationChanged(Configuration config) { + } + + /** @hide */ + public void dispatchMenuVisibilityChanged(boolean visible) { + } + + /** @hide */ + public void captureSharedElements(Map<String, View> sharedElements) { + } + + /** @hide */ + public ActionMode startActionMode(ActionMode.Callback callback) { + return null; + } + /** * Listener interface for ActionBar navigation events. + * + * @deprecated Action bar navigation modes are deprecated and not supported by inline + * toolbar action bars. Consider using other + * <a href="http://developer.android.com/design/patterns/navigation.html">common + * navigation patterns</a> instead. */ public interface OnNavigationListener { /** @@ -833,6 +996,11 @@ public abstract class ActionBar { * A tab in the action bar. * * <p>Tabs manage the hiding and showing of {@link Fragment}s. + * + * @deprecated Action bar navigation modes are deprecated and not supported by inline + * toolbar action bars. Consider using other + * <a href="http://developer.android.com/design/patterns/navigation.html">common + * navigation patterns</a> instead. */ public static abstract class Tab { /** @@ -984,6 +1152,11 @@ public abstract class ActionBar { /** * Callback interface invoked when a tab is focused, unfocused, added, or removed. + * + * @deprecated Action bar navigation modes are deprecated and not supported by inline + * toolbar action bars. Consider using other + * <a href="http://developer.android.com/design/patterns/navigation.html">common + * navigation patterns</a> instead. */ public interface TabListener { /** @@ -1025,27 +1198,27 @@ public abstract class ActionBar { * * @attr ref android.R.styleable#ActionBar_LayoutParams_layout_gravity */ - public static class LayoutParams extends MarginLayoutParams { + public static class LayoutParams extends ViewGroup.MarginLayoutParams { /** * Gravity for the view associated with these LayoutParams. * * @see android.view.Gravity */ @ViewDebug.ExportedProperty(category = "layout", mapping = { - @ViewDebug.IntToString(from = -1, to = "NONE"), - @ViewDebug.IntToString(from = Gravity.NO_GRAVITY, to = "NONE"), - @ViewDebug.IntToString(from = Gravity.TOP, to = "TOP"), - @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.START, to = "START"), - @ViewDebug.IntToString(from = Gravity.END, to = "END"), - @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"), - @ViewDebug.IntToString(from = Gravity.FILL_HORIZONTAL, to = "FILL_HORIZONTAL"), - @ViewDebug.IntToString(from = Gravity.CENTER, to = "CENTER"), - @ViewDebug.IntToString(from = Gravity.FILL, to = "FILL") + @ViewDebug.IntToString(from = -1, to = "NONE"), + @ViewDebug.IntToString(from = Gravity.NO_GRAVITY, to = "NONE"), + @ViewDebug.IntToString(from = Gravity.TOP, to = "TOP"), + @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.START, to = "START"), + @ViewDebug.IntToString(from = Gravity.END, to = "END"), + @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"), + @ViewDebug.IntToString(from = Gravity.FILL_HORIZONTAL, to = "FILL_HORIZONTAL"), + @ViewDebug.IntToString(from = Gravity.CENTER, to = "CENTER"), + @ViewDebug.IntToString(from = Gravity.FILL, to = "FILL") }) public int gravity = Gravity.NO_GRAVITY; @@ -1062,12 +1235,10 @@ public abstract class ActionBar { public LayoutParams(int width, int height) { super(width, height); - this.gravity = Gravity.CENTER_VERTICAL | Gravity.START; } public LayoutParams(int width, int height, int gravity) { super(width, height); - this.gravity = gravity; } public LayoutParams(int gravity) { @@ -1076,12 +1247,14 @@ public abstract class ActionBar { public LayoutParams(LayoutParams source) { super(source); - - this.gravity = source.gravity; } public LayoutParams(ViewGroup.LayoutParams source) { super(source); } + + public LayoutParams(MarginLayoutParams source) { + super(source); + } } } diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index e38bbb3..e8fdcaf 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -23,7 +23,9 @@ import android.transition.TransitionManager; import android.util.ArrayMap; import android.util.Pair; import android.util.SuperNotCalledException; -import com.android.internal.app.ActionBarImpl; +import android.widget.Toolbar; +import com.android.internal.app.WindowDecorActionBar; +import com.android.internal.app.ToolbarActionBar; import com.android.internal.policy.PolicyManager; import android.annotation.IntDef; @@ -723,7 +725,7 @@ public class Activity extends ContextThemeWrapper /*package*/ boolean mWindowAdded = false; /*package*/ boolean mVisibleFromServer = false; /*package*/ boolean mVisibleFromClient = true; - /*package*/ ActionBarImpl mActionBar = null; + /*package*/ ActionBar mActionBar = null; private boolean mEnableDefaultActionBarUp; private CharSequence mTitle; @@ -1906,15 +1908,39 @@ public class Activity extends ContextThemeWrapper */ @Nullable public ActionBar getActionBar() { - initActionBar(); + initWindowDecorActionBar(); return mActionBar; } + + /** + * Set a {@link android.widget.Toolbar Toolbar} to act as the {@link ActionBar} for this + * Activity window. + * + * <p>When set to a non-null value the {@link #getActionBar()} method will return + * an {@link ActionBar} object that can be used to control the given toolbar as if it were + * a traditional window decor action bar. The toolbar's menu will be populated with the + * Activity's options menu and the navigation button will be wired through the standard + * {@link android.R.id#home home} menu select action.</p> + * + * <p>In order to use a Toolbar within the Activity's window content the application + * must not request the window feature {@link Window#FEATURE_ACTION_BAR FEATURE_ACTION_BAR}.</p> + * + * @param actionBar Toolbar to set as the Activity's action bar + */ + public void setActionBar(@Nullable Toolbar actionBar) { + if (getActionBar() instanceof WindowDecorActionBar) { + throw new IllegalStateException("This Activity already has an action bar supplied " + + "by the window decor. Do not request Window.FEATURE_ACTION_BAR and set " + + "android:windowActionBar to false in your theme to use a Toolbar instead."); + } + mActionBar = new ToolbarActionBar(actionBar); + } /** * Creates a new ActionBar, locates the inflated ActionBarView, * initializes the ActionBar with the view, and sets mActionBar. */ - private void initActionBar() { + private void initWindowDecorActionBar() { Window window = getWindow(); // Initializing the window decor can change window feature flags. @@ -1925,7 +1951,7 @@ public class Activity extends ContextThemeWrapper return; } - mActionBar = new ActionBarImpl(this); + mActionBar = new WindowDecorActionBar(this); mActionBar.setDefaultDisplayHomeAsUpEnabled(mEnableDefaultActionBarUp); mWindow.setDefaultIcon(mActivityInfo.getIconResource()); @@ -1943,7 +1969,7 @@ public class Activity extends ContextThemeWrapper */ public void setContentView(int layoutResID) { getWindow().setContentView(layoutResID); - initActionBar(); + initWindowDecorActionBar(); } /** @@ -1963,7 +1989,7 @@ public class Activity extends ContextThemeWrapper */ public void setContentView(View view) { getWindow().setContentView(view); - initActionBar(); + initWindowDecorActionBar(); } /** @@ -1979,7 +2005,7 @@ public class Activity extends ContextThemeWrapper */ public void setContentView(View view, ViewGroup.LayoutParams params) { getWindow().setContentView(view, params); - initActionBar(); + initWindowDecorActionBar(); } /** @@ -1991,7 +2017,7 @@ public class Activity extends ContextThemeWrapper */ public void addContentView(View view, ViewGroup.LayoutParams params) { getWindow().addContentView(view, params); - initActionBar(); + initWindowDecorActionBar(); } /** @@ -2636,7 +2662,7 @@ public class Activity extends ContextThemeWrapper */ public boolean onMenuOpened(int featureId, Menu menu) { if (featureId == Window.FEATURE_ACTION_BAR) { - initActionBar(); + initWindowDecorActionBar(); if (mActionBar != null) { mActionBar.dispatchMenuVisibilityChanged(true); } else { @@ -2717,7 +2743,7 @@ public class Activity extends ContextThemeWrapper break; case Window.FEATURE_ACTION_BAR: - initActionBar(); + initWindowDecorActionBar(); mActionBar.dispatchMenuVisibilityChanged(false); break; } @@ -3417,7 +3443,7 @@ public class Activity extends ContextThemeWrapper public MenuInflater getMenuInflater() { // Make sure that action views can get an appropriate theme. if (mMenuInflater == null) { - initActionBar(); + initWindowDecorActionBar(); if (mActionBar != null) { mMenuInflater = new MenuInflater(mActionBar.getThemedContext(), this); } else { @@ -4701,42 +4727,31 @@ public class Activity extends ContextThemeWrapper } /** - * Set a label to be used in the Recents task display. The activities of a task are traversed - * in order from the topmost activity to the bottommost. As soon as one activity returns a - * non-null Recents label the traversal is ended and that value will be used in - * {@link ActivityManager.RecentTaskInfo#activityLabel} - * - * @see ActivityManager#getRecentTasks - * - * @param recentsLabel The label to use in the RecentTaskInfo. - */ - public void setRecentsLabel(CharSequence recentsLabel) { - try { - ActivityManagerNative.getDefault().setRecentsLabel(mToken, recentsLabel); - } catch (RemoteException e) { - } - } - - /** - * Set an icon to be used in the Recents task display. The activities of a task are traversed - * in order from the topmost activity to the bottommost. As soon as one activity returns a - * non-null Recents icon the traversal is ended and that value will be used in - * {@link ActivityManager.RecentTaskInfo#activityIcon}. + * Set a label and icon to be used in the Recents task display. When {@link + * ActivityManager#getRecentTasks} is called, the activities of each task are + * traversed in order from the topmost activity to the bottommost. As soon as one activity is + * found with either a non-null label or a non-null icon set by this call the traversal is + * ended. For each task those values will be returned in {@link + * ActivityManager.RecentTaskInfo#activityLabel} and {@link + * ActivityManager.RecentTaskInfo#activityIcon}. * * @see ActivityManager#getRecentTasks + * @see ActivityManager.RecentTaskInfo * - * @param recentsIcon The Bitmap to use in the RecentTaskInfo. + * @param activityLabel The label to use in the RecentTaskInfo. + * @param activityIcon The Bitmap to use in the RecentTaskInfo. */ - public void setRecentsIcon(Bitmap recentsIcon) { + public void setActivityLabelAndIcon(CharSequence activityLabel, Bitmap activityIcon) { final Bitmap scaledIcon; - if (recentsIcon != null) { + if (activityIcon != null) { final int size = ActivityManager.getLauncherLargeIconSizeInner(this); - scaledIcon = Bitmap.createScaledBitmap(recentsIcon, size, size, true); + scaledIcon = Bitmap.createScaledBitmap(activityIcon, size, size, true); } else { scaledIcon = null; } try { - ActivityManagerNative.getDefault().setRecentsIcon(mToken, scaledIcon); + ActivityManagerNative.getDefault().setActivityLabelAndIcon(mToken, activityLabel, + scaledIcon); } catch (RemoteException e) { } } @@ -5150,7 +5165,7 @@ public class Activity extends ContextThemeWrapper @Nullable @Override public ActionMode onWindowStartingActionMode(ActionMode.Callback callback) { - initActionBar(); + initWindowDecorActionBar(); if (mActionBar != null) { return mActionBar.startActionMode(callback); } diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index d386eff..c027e99 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -516,14 +516,14 @@ public class ActivityManager { public int userId; /** - * The label of the highest activity in the task stack to have set a label - * {@link Activity#setRecentsLabel}. + * The label of the highest activity in the task stack to have set a label using + * {@link Activity#setActivityLabelAndIcon(CharSequence, android.graphics.Bitmap)}. */ public CharSequence activityLabel; /** * The Bitmap icon of the highest activity in the task stack to set a Bitmap using - * {@link Activity#setRecentsIcon}. + * {@link Activity#setActivityLabelAndIcon(CharSequence, android.graphics.Bitmap)}. */ public Bitmap activityIcon; @@ -563,11 +563,7 @@ public class ActivityManager { public void readFromParcel(Parcel source) { id = source.readInt(); persistentId = source.readInt(); - if (source.readInt() != 0) { - baseIntent = Intent.CREATOR.createFromParcel(source); - } else { - baseIntent = null; - } + baseIntent = source.readInt() > 0 ? Intent.CREATOR.createFromParcel(source) : null; origActivity = ComponentName.readFromParcel(source); description = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source); activityLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source); @@ -605,11 +601,11 @@ public class ActivityManager { public static final int RECENT_IGNORE_UNAVAILABLE = 0x0002; /** - * Provides a list that also contains recent tasks for user - * and related users. + * Provides a list that contains recent tasks for all + * profiles of a user. * @hide */ - public static final int RECENT_INCLUDE_RELATED = 0x0004; + public static final int RECENT_INCLUDE_PROFILES = 0x0004; /** * Return a list of the tasks that the user has recently launched, with diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index 707a038..a37a35a 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -2129,21 +2129,13 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM return true; } - case SET_RECENTS_LABEL_TRANSACTION: { + case SET_ACTIVITY_LABEL_ICON_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); IBinder token = data.readStrongBinder(); - CharSequence recentsLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(data); - setRecentsLabel(token, recentsLabel); - reply.writeNoException(); - return true; - } - - case SET_RECENTS_ICON_TRANSACTION: { - data.enforceInterface(IActivityManager.descriptor); - IBinder token = data.readStrongBinder(); - Bitmap recentsIcon = data.readInt() != 0 + CharSequence activityLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(data); + Bitmap activityIcon = data.readInt() > 0 ? Bitmap.CREATOR.createFromParcel(data) : null; - setRecentsIcon(token, recentsIcon); + setActivityLabelAndIcon(token, activityLabel, activityIcon); reply.writeNoException(); return true; } @@ -4918,32 +4910,22 @@ class ActivityManagerProxy implements IActivityManager return isInLockTaskMode; } - public void setRecentsLabel(IBinder token, CharSequence recentsLabel) throws RemoteException - { - Parcel data = Parcel.obtain(); - Parcel reply = Parcel.obtain(); - data.writeInterfaceToken(IActivityManager.descriptor); - data.writeStrongBinder(token); - TextUtils.writeToParcel(recentsLabel, data, 0); - mRemote.transact(SET_RECENTS_LABEL_TRANSACTION, data, reply, IBinder.FLAG_ONEWAY); - reply.readException(); - data.recycle(); - reply.recycle(); - } - - public void setRecentsIcon(IBinder token, Bitmap recentsBitmap) throws RemoteException + @Override + public void setActivityLabelAndIcon(IBinder token, CharSequence activityLabel, + Bitmap activityIcon) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); data.writeStrongBinder(token); - if (recentsBitmap != null) { + TextUtils.writeToParcel(activityLabel, data, 0); + if (activityIcon != null) { data.writeInt(1); - recentsBitmap.writeToParcel(data, 0); + activityIcon.writeToParcel(data, 0); } else { data.writeInt(0); } - mRemote.transact(SET_RECENTS_ICON_TRANSACTION, data, reply, IBinder.FLAG_ONEWAY); + mRemote.transact(SET_ACTIVITY_LABEL_ICON_TRANSACTION, data, reply, IBinder.FLAG_ONEWAY); reply.readException(); data.recycle(); reply.recycle(); diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 0615bd9..6ca5244 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -29,6 +29,7 @@ import android.content.pm.FeatureInfo; import android.content.pm.IPackageDataObserver; import android.content.pm.IPackageDeleteObserver; import android.content.pm.IPackageInstallObserver; +import android.content.pm.IPackageInstallObserver2; import android.content.pm.IPackageManager; import android.content.pm.IPackageMoveObserver; import android.content.pm.IPackageStatsObserver; @@ -1073,7 +1074,7 @@ final class ApplicationPackageManager extends PackageManager { public void installPackage(Uri packageURI, IPackageInstallObserver observer, int flags, String installerPackageName) { try { - mPM.installPackage(packageURI, observer, flags, installerPackageName); + mPM.installPackageEtc(packageURI, observer, null, flags, installerPackageName); } catch (RemoteException e) { // Should never happen! } @@ -1084,8 +1085,8 @@ final class ApplicationPackageManager extends PackageManager { int flags, String installerPackageName, Uri verificationURI, ManifestDigest manifestDigest, ContainerEncryptionParams encryptionParams) { try { - mPM.installPackageWithVerification(packageURI, observer, flags, installerPackageName, - verificationURI, manifestDigest, encryptionParams); + mPM.installPackageWithVerificationEtc(packageURI, observer, null, flags, + installerPackageName, verificationURI, manifestDigest, encryptionParams); } catch (RemoteException e) { // Should never happen! } @@ -1096,8 +1097,46 @@ final class ApplicationPackageManager extends PackageManager { IPackageInstallObserver observer, int flags, String installerPackageName, VerificationParams verificationParams, ContainerEncryptionParams encryptionParams) { try { - mPM.installPackageWithVerificationAndEncryption(packageURI, observer, flags, - installerPackageName, verificationParams, encryptionParams); + mPM.installPackageWithVerificationAndEncryptionEtc(packageURI, observer, null, + flags, installerPackageName, verificationParams, encryptionParams); + } catch (RemoteException e) { + // Should never happen! + } + } + + // Expanded observer-API versions + @Override + public void installPackage(Uri packageURI, PackageInstallObserver observer, + int flags, String installerPackageName) { + try { + mPM.installPackageEtc(packageURI, null, observer.mObserver, + flags, installerPackageName); + } catch (RemoteException e) { + // Should never happen! + } + } + + @Override + public void installPackageWithVerification(Uri packageURI, + PackageInstallObserver observer, int flags, String installerPackageName, + Uri verificationURI, ManifestDigest manifestDigest, + ContainerEncryptionParams encryptionParams) { + try { + mPM.installPackageWithVerificationEtc(packageURI, null, observer.mObserver, flags, + installerPackageName, verificationURI, manifestDigest, encryptionParams); + } catch (RemoteException e) { + // Should never happen! + } + } + + @Override + public void installPackageWithVerificationAndEncryption(Uri packageURI, + PackageInstallObserver observer, int flags, String installerPackageName, + VerificationParams verificationParams, ContainerEncryptionParams encryptionParams) { + try { + mPM.installPackageWithVerificationAndEncryptionEtc(packageURI, null, + observer.mObserver, flags, installerPackageName, verificationParams, + encryptionParams); } catch (RemoteException e) { // Should never happen! } diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 589c82f..7149ab9 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -75,6 +75,8 @@ import android.net.nsd.INsdManager; import android.net.nsd.NsdManager; import android.net.wifi.IWifiManager; import android.net.wifi.WifiManager; +import android.net.wifi.hotspot.IWifiHotspotManager; +import android.net.wifi.hotspot.WifiHotspotManager; import android.net.wifi.p2p.IWifiP2pManager; import android.net.wifi.p2p.WifiP2pManager; import android.nfc.NfcManager; @@ -117,6 +119,7 @@ import android.view.textservice.TextServicesManager; import android.accounts.AccountManager; import android.accounts.IAccountManager; import android.app.admin.DevicePolicyManager; +import android.app.trust.TrustManager; import com.android.internal.annotations.GuardedBy; import com.android.internal.app.IAppOpsService; @@ -465,7 +468,8 @@ class ContextImpl extends Context { outerContext.getApplicationInfo().targetSdkVersion, com.android.internal.R.style.Theme_Dialog, com.android.internal.R.style.Theme_Holo_Dialog, - com.android.internal.R.style.Theme_DeviceDefault_Dialog)), + com.android.internal.R.style.Theme_DeviceDefault_Dialog, + com.android.internal.R.style.Theme_DeviceDefault_Light_Dialog)), ctx.mMainThread.getHandler()); }}); @@ -552,6 +556,13 @@ class ContextImpl extends Context { return new WifiManager(ctx.getOuterContext(), service); }}); + registerService(WIFI_HOTSPOT_SERVICE, new ServiceFetcher() { + public Object createService(ContextImpl ctx) { + IBinder b = ServiceManager.getService(WIFI_HOTSPOT_SERVICE); + IWifiHotspotManager service = IWifiHotspotManager.Stub.asInterface(b); + return new WifiHotspotManager(ctx.getOuterContext(), service); + }}); + registerService(WIFI_P2P_SERVICE, new ServiceFetcher() { public Object createService(ContextImpl ctx) { IBinder b = ServiceManager.getService(WIFI_P2P_SERVICE); @@ -612,6 +623,12 @@ class ContextImpl extends Context { return new MediaSessionManager(ctx); } }); + registerService(TRUST_SERVICE, new ServiceFetcher() { + public Object createService(ContextImpl ctx) { + IBinder b = ServiceManager.getService(TRUST_SERVICE); + return new TrustManager(b); + } + }); } static ContextImpl getImpl(Context context) { diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java index fb96d8d..07583fd 100644 --- a/core/java/android/app/Dialog.java +++ b/core/java/android/app/Dialog.java @@ -17,7 +17,7 @@ package android.app; import android.content.pm.ApplicationInfo; -import com.android.internal.app.ActionBarImpl; +import com.android.internal.app.WindowDecorActionBar; import com.android.internal.policy.PolicyManager; import android.content.ComponentName; @@ -87,7 +87,7 @@ public class Dialog implements DialogInterface, Window.Callback, final WindowManager mWindowManager; Window mWindow; View mDecor; - private ActionBarImpl mActionBar; + private ActionBar mActionBar; /** * This field should be made private, so it is hidden from the SDK. * {@hide} @@ -280,7 +280,7 @@ public class Dialog implements DialogInterface, Window.Callback, final ApplicationInfo info = mContext.getApplicationInfo(); mWindow.setDefaultIcon(info.icon); mWindow.setDefaultLogo(info.logo); - mActionBar = new ActionBarImpl(this); + mActionBar = new WindowDecorActionBar(this); } WindowManager.LayoutParams l = mWindow.getAttributes(); diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index 3b56839..f7416d6 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -437,10 +437,8 @@ public interface IActivityManager extends IInterface { public boolean isInLockTaskMode() throws RemoteException; /** @hide */ - public void setRecentsLabel(IBinder token, CharSequence recentsLabel) throws RemoteException; - - /** @hide */ - public void setRecentsIcon(IBinder token, Bitmap recentsBitmap) throws RemoteException; + public void setActivityLabelAndIcon(IBinder token, CharSequence activityLabel, + Bitmap activityBitmap) throws RemoteException; /* * Private non-Binder interfaces @@ -741,6 +739,5 @@ public interface IActivityManager extends IInterface { int START_LOCK_TASK_BY_TOKEN_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+214; int STOP_LOCK_TASK_MODE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+215; int IS_IN_LOCK_TASK_MODE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+216; - int SET_RECENTS_LABEL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+217; - int SET_RECENTS_ICON_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+218; + int SET_ACTIVITY_LABEL_ICON_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+217; } diff --git a/core/java/android/app/IBackupAgent.aidl b/core/java/android/app/IBackupAgent.aidl index 4ca06ed..7036aea 100644 --- a/core/java/android/app/IBackupAgent.aidl +++ b/core/java/android/app/IBackupAgent.aidl @@ -124,4 +124,12 @@ oneway interface IBackupAgent { int type, String domain, String path, long mode, long mtime, int token, IBackupManager callbackBinder); + /** + * Out of band: instruct the agent to crash within the client process. This is used + * when the backup infrastructure detects a semantic error post-hoc and needs to + * pass the problem back to the app. + * + * @param message The message to be passed to the agent's application in an exception. + */ + void fail(String message); } diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index 9f933ca..9911467 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -45,7 +45,8 @@ interface INotificationManager void unregisterListener(in INotificationListener listener, int userid); void cancelNotificationFromListener(in INotificationListener token, String pkg, String tag, int id); - void cancelAllNotificationsFromListener(in INotificationListener token); + void cancelNotificationsFromListener(in INotificationListener token, in String[] keys); - StatusBarNotification[] getActiveNotificationsFromListener(in INotificationListener token); + StatusBarNotification[] getActiveNotificationsFromListener(in INotificationListener token, in String[] keys); + String[] getActiveNotificationKeysFromListener(in INotificationListener token); }
\ No newline at end of file diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 13e74da..36d2635 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -17,12 +17,14 @@ package android.app; import com.android.internal.R; +import com.android.internal.util.LegacyNotificationUtil; import android.annotation.IntDef; import android.content.Context; import android.content.Intent; import android.content.res.Resources; import android.graphics.Bitmap; +import android.graphics.PorterDuff; import android.media.AudioManager; import android.net.Uri; import android.os.BadParcelableException; @@ -653,13 +655,6 @@ public class Notification implements Parcelable public static final String EXTRA_AS_HEADS_UP = "headsup"; /** - * Extra added from {@link Notification.Builder} to indicate that the remote views were inflated - * from the builder, as opposed to being created directly from the application. - * @hide - */ - public static final String EXTRA_BUILDER_REMOTE_VIEWS = "android.builderRemoteViews"; - - /** * Allow certain system-generated notifications to appear before the device is provisioned. * Only available to notifications coming from the android package. * @hide @@ -1315,6 +1310,7 @@ public class Notification implements Parcelable private int mVisibility = VISIBILITY_PRIVATE; private Notification mPublicVersion = null; private boolean mQuantumTheme; + private final LegacyNotificationUtil mLegacyNotificationUtil; /** * Constructs a new Builder with the defaults: @@ -1345,6 +1341,10 @@ public class Notification implements Parcelable // TODO: Decide on targetSdk from calling app whether to use quantum theme. mQuantumTheme = true; + + // TODO: Decide on targetSdk from calling app whether to instantiate the processor at + // all. + mLegacyNotificationUtil = LegacyNotificationUtil.getInstance(); } /** @@ -1846,42 +1846,50 @@ public class Notification implements Parcelable boolean showLine3 = false; boolean showLine2 = false; int smallIconImageViewId = R.id.icon; - if (mLargeIcon != null) { - contentView.setImageViewBitmap(R.id.icon, mLargeIcon); - smallIconImageViewId = R.id.right_icon; - } if (!mQuantumTheme && mPriority < PRIORITY_LOW) { contentView.setInt(R.id.icon, "setBackgroundResource", R.drawable.notification_template_icon_low_bg); contentView.setInt(R.id.status_bar_latest_event_content, "setBackgroundResource", R.drawable.notification_bg_low); } + if (mLargeIcon != null) { + contentView.setImageViewBitmap(R.id.icon, mLargeIcon); + processLegacyLargeIcon(mLargeIcon, contentView); + smallIconImageViewId = R.id.right_icon; + } if (mSmallIcon != 0) { contentView.setImageViewResource(smallIconImageViewId, mSmallIcon); contentView.setViewVisibility(smallIconImageViewId, View.VISIBLE); + if (mLargeIcon != null) { + processLegacySmallIcon(mSmallIcon, smallIconImageViewId, contentView); + } else { + processLegacyLargeIcon(mSmallIcon, contentView); + } + } else { contentView.setViewVisibility(smallIconImageViewId, View.GONE); } if (mContentTitle != null) { - contentView.setTextViewText(R.id.title, mContentTitle); + contentView.setTextViewText(R.id.title, processLegacyText(mContentTitle)); } if (mContentText != null) { - contentView.setTextViewText(R.id.text, mContentText); + contentView.setTextViewText(R.id.text, processLegacyText(mContentText)); showLine3 = true; } if (mContentInfo != null) { - contentView.setTextViewText(R.id.info, mContentInfo); + contentView.setTextViewText(R.id.info, processLegacyText(mContentInfo)); contentView.setViewVisibility(R.id.info, View.VISIBLE); showLine3 = true; } else if (mNumber > 0) { final int tooBig = mContext.getResources().getInteger( R.integer.status_bar_notification_info_maxnum); if (mNumber > tooBig) { - contentView.setTextViewText(R.id.info, mContext.getResources().getString( - R.string.status_bar_notification_info_overflow)); + contentView.setTextViewText(R.id.info, processLegacyText( + mContext.getResources().getString( + R.string.status_bar_notification_info_overflow))); } else { NumberFormat f = NumberFormat.getIntegerInstance(); - contentView.setTextViewText(R.id.info, f.format(mNumber)); + contentView.setTextViewText(R.id.info, processLegacyText(f.format(mNumber))); } contentView.setViewVisibility(R.id.info, View.VISIBLE); showLine3 = true; @@ -1891,9 +1899,9 @@ public class Notification implements Parcelable // Need to show three lines? if (mSubText != null) { - contentView.setTextViewText(R.id.text, mSubText); + contentView.setTextViewText(R.id.text, processLegacyText(mSubText)); if (mContentText != null) { - contentView.setTextViewText(R.id.text2, mContentText); + contentView.setTextViewText(R.id.text2, processLegacyText(mContentText)); contentView.setViewVisibility(R.id.text2, View.VISIBLE); showLine2 = true; } else { @@ -2001,15 +2009,78 @@ public class Notification implements Parcelable tombstone ? getActionTombstoneLayoutResource() : getActionLayoutResource()); button.setTextViewCompoundDrawablesRelative(R.id.action0, action.icon, 0, 0, 0); - button.setTextViewText(R.id.action0, action.title); + button.setTextViewText(R.id.action0, processLegacyText(action.title)); if (!tombstone) { button.setOnClickPendingIntent(R.id.action0, action.actionIntent); } button.setContentDescription(R.id.action0, action.title); + processLegacyAction(action, button); return button; } /** + * @return Whether we are currently building a notification from a legacy (an app that + * doesn't create quantum notifications by itself) app. + */ + private boolean isLegacy() { + return mLegacyNotificationUtil != null; + } + + private void processLegacyAction(Action action, RemoteViews button) { + if (isLegacy()) { + if (mLegacyNotificationUtil.isGrayscale(mContext, action.icon)) { + button.setTextViewCompoundDrawablesRelativeColorFilter(R.id.action0, 0, + mContext.getResources().getColor( + R.color.notification_action_legacy_color_filter), + PorterDuff.Mode.MULTIPLY); + } + } + } + + private CharSequence processLegacyText(CharSequence charSequence) { + if (isLegacy()) { + return mLegacyNotificationUtil.invertCharSequenceColors(charSequence); + } else { + return charSequence; + } + } + + private void processLegacyLargeIcon(int largeIconId, RemoteViews contentView) { + if (isLegacy()) { + processLegacyLargeIcon( + mLegacyNotificationUtil.isGrayscale(mContext, largeIconId), + contentView); + } + } + + private void processLegacyLargeIcon(Bitmap largeIcon, RemoteViews contentView) { + if (isLegacy()) { + processLegacyLargeIcon( + mLegacyNotificationUtil.isGrayscale(largeIcon), + contentView); + } + } + + private void processLegacyLargeIcon(boolean isGrayscale, RemoteViews contentView) { + if (isLegacy() && isGrayscale) { + contentView.setInt(R.id.icon, "setBackgroundResource", + R.drawable.notification_icon_legacy_bg_inset); + } + } + + private void processLegacySmallIcon(int smallIconDrawableId, int smallIconImageViewId, + RemoteViews contentView) { + if (isLegacy()) { + if (mLegacyNotificationUtil.isGrayscale(mContext, smallIconDrawableId)) { + contentView.setDrawableParameters(smallIconImageViewId, false, -1, + mContext.getResources().getColor( + R.color.notification_action_legacy_color_filter), + PorterDuff.Mode.MULTIPLY, -1); + } + } + } + + /** * Apply the unstyled operations and return a new {@link Notification} object. * @hide */ @@ -2075,7 +2146,6 @@ public class Notification implements Parcelable extras.putBoolean(EXTRA_PROGRESS_INDETERMINATE, mProgressIndeterminate); extras.putBoolean(EXTRA_SHOW_CHRONOMETER, mUseChronometer); extras.putBoolean(EXTRA_SHOW_WHEN, mShowWhen); - extras.putBoolean(EXTRA_BUILDER_REMOTE_VIEWS, mContentView == null); if (mLargeIcon != null) { extras.putParcelable(EXTRA_LARGE_ICON, mLargeIcon); } @@ -2226,7 +2296,7 @@ public class Notification implements Parcelable mSummaryTextSet ? mSummaryText : mBuilder.mSubText; if (overflowText != null) { - contentView.setTextViewText(R.id.text, overflowText); + contentView.setTextViewText(R.id.text, mBuilder.processLegacyText(overflowText)); contentView.setViewVisibility(R.id.overflow_divider, View.VISIBLE); contentView.setViewVisibility(R.id.line3, View.VISIBLE); } else { @@ -2437,7 +2507,7 @@ public class Notification implements Parcelable contentView.setViewPadding(R.id.line1, 0, 0, 0, 0); } - contentView.setTextViewText(R.id.big_text, mBigText); + contentView.setTextViewText(R.id.big_text, mBuilder.processLegacyText(mBigText)); contentView.setViewVisibility(R.id.big_text, View.VISIBLE); contentView.setViewVisibility(R.id.text2, View.GONE); @@ -2542,7 +2612,7 @@ public class Notification implements Parcelable CharSequence str = mTexts.get(i); if (str != null && !str.equals("")) { contentView.setViewVisibility(rowIds[i], View.VISIBLE); - contentView.setTextViewText(rowIds[i], str); + contentView.setTextViewText(rowIds[i], mBuilder.processLegacyText(str)); } i++; } diff --git a/core/java/android/app/PackageInstallObserver.java b/core/java/android/app/PackageInstallObserver.java new file mode 100644 index 0000000..dacffb4 --- /dev/null +++ b/core/java/android/app/PackageInstallObserver.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2014 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.app; + +import android.content.pm.IPackageInstallObserver2; +import android.os.Bundle; +import android.os.RemoteException; + +/** + * @hide + * + * New-style observer for package installers to use. + */ +public class PackageInstallObserver { + IPackageInstallObserver2.Stub mObserver = new IPackageInstallObserver2.Stub() { + @Override + public void packageInstalled(String pkgName, Bundle extras, int result) + throws RemoteException { + PackageInstallObserver.this.packageInstalled(pkgName, extras, result); + } + }; + + /** + * This method will be called to report the result of the package installation attempt. + * + * @param pkgName Name of the package whose installation was attempted + * @param extras If non-null, this Bundle contains extras providing additional information + * about an install failure. See {@link android.content.pm.PackageManager} for + * documentation about which extras apply to various failures; in particular the + * strings named EXTRA_FAILURE_*. + * @param result The numeric success or failure code indicating the basic outcome + */ + public void packageInstalled(String pkgName, Bundle extras, int result) { + } +} diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java index 8efc14f..cf14202 100644 --- a/core/java/android/app/PendingIntent.java +++ b/core/java/android/app/PendingIntent.java @@ -121,8 +121,8 @@ public final class PendingIntent implements Parcelable { */ public static final int FLAG_ONE_SHOT = 1<<30; /** - * Flag indicating that if the described PendingIntent already - * exists, then simply return null instead of creating it. + * Flag indicating that if the described PendingIntent does not + * already exist, then simply return null instead of creating it. * For use with {@link #getActivity}, {@link #getBroadcast}, and * {@link #getService}. */ diff --git a/core/java/android/app/UiModeManager.java b/core/java/android/app/UiModeManager.java index 0c22740..c6731c9 100644 --- a/core/java/android/app/UiModeManager.java +++ b/core/java/android/app/UiModeManager.java @@ -166,9 +166,11 @@ public class UiModeManager { /** * Return the current running mode type. May be one of * {@link Configuration#UI_MODE_TYPE_NORMAL Configuration.UI_MODE_TYPE_NORMAL}, - * {@link Configuration#UI_MODE_TYPE_DESK Configuration.UI_MODE_TYPE_DESK}, or - * {@link Configuration#UI_MODE_TYPE_CAR Configuration.UI_MODE_TYPE_CAR}, or - * {@link Configuration#UI_MODE_TYPE_TELEVISION Configuration.UI_MODE_TYPE_APPLIANCE}. + * {@link Configuration#UI_MODE_TYPE_DESK Configuration.UI_MODE_TYPE_DESK}, + * {@link Configuration#UI_MODE_TYPE_CAR Configuration.UI_MODE_TYPE_CAR}, + * {@link Configuration#UI_MODE_TYPE_TELEVISION Configuration.UI_MODE_TYPE_TELEVISION}, + * {@link Configuration#UI_MODE_TYPE_APPLIANCE Configuration.UI_MODE_TYPE_APPLIANCE}, or + * {@link Configuration#UI_MODE_TYPE_WATCH Configuration.UI_MODE_TYPE_WATCH}. */ public int getCurrentModeType() { if (mService != null) { diff --git a/core/java/android/app/admin/DeviceAdminInfo.java b/core/java/android/app/admin/DeviceAdminInfo.java index 66fc816..3074b49 100644 --- a/core/java/android/app/admin/DeviceAdminInfo.java +++ b/core/java/android/app/admin/DeviceAdminInfo.java @@ -52,6 +52,22 @@ public final class DeviceAdminInfo implements Parcelable { static final String TAG = "DeviceAdminInfo"; /** + * A type of policy that this device admin can use: device owner meta-policy + * for an admin that is designated as owner of the device. + * + * @hide + */ + public static final int USES_POLICY_DEVICE_OWNER = -2; + + /** + * A type of policy that this device admin can use: profile owner meta-policy + * for admins that have been installed as owner of some user profile. + * + * @hide + */ + public static final int USES_POLICY_PROFILE_OWNER = -1; + + /** * A type of policy that this device admin can use: limit the passwords * that the user can select, via {@link DevicePolicyManager#setPasswordQuality} * and {@link DevicePolicyManager#setPasswordMinimumLength}. diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java index 67c772b..3c31f8d 100644 --- a/core/java/android/app/backup/BackupAgent.java +++ b/core/java/android/app/backup/BackupAgent.java @@ -128,6 +128,13 @@ public abstract class BackupAgent extends ContextWrapper { Handler mHandler = null; + Handler getHandler() { + if (mHandler == null) { + mHandler = new Handler(Looper.getMainLooper()); + } + return mHandler; + } + class SharedPrefsSynchronizer implements Runnable { public final CountDownLatch mLatch = new CountDownLatch(1); @@ -140,12 +147,9 @@ public abstract class BackupAgent extends ContextWrapper { // Syncing shared preferences deferred writes needs to happen on the main looper thread private void waitForSharedPrefs() { - if (mHandler == null) { - mHandler = new Handler(Looper.getMainLooper()); - } - + Handler h = getHandler(); final SharedPrefsSynchronizer s = new SharedPrefsSynchronizer(); - mHandler.postAtFrontOfQueue(s); + h.postAtFrontOfQueue(s); try { s.mLatch.await(); } catch (InterruptedException e) { /* ignored */ } @@ -680,5 +684,23 @@ public abstract class BackupAgent extends ContextWrapper { } } } + + @Override + public void fail(String message) { + getHandler().post(new FailRunnable(message)); + } + } + + static class FailRunnable implements Runnable { + private String mMessage; + + FailRunnable(String message) { + mMessage = message; + } + + @Override + public void run() { + throw new IllegalStateException(mMessage); + } } } diff --git a/core/java/android/app/backup/BackupDataOutput.java b/core/java/android/app/backup/BackupDataOutput.java index 845784f..fc5fb3d 100644 --- a/core/java/android/app/backup/BackupDataOutput.java +++ b/core/java/android/app/backup/BackupDataOutput.java @@ -85,11 +85,6 @@ public class BackupDataOutput { * @throws IOException if the write failed */ public int writeEntityHeader(String key, int dataSize) throws IOException { - if (key != null && key.charAt(0) >= 0xff00) { - if (Process.myUid() != Process.SYSTEM_UID) { - throw new IllegalArgumentException("Invalid key " + key); - } - } int result = writeEntityHeader_native(mBackupWriter, key, dataSize); if (result >= 0) { return result; diff --git a/core/java/android/app/trust/ITrustListener.aidl b/core/java/android/app/trust/ITrustListener.aidl new file mode 100644 index 0000000..4680043 --- /dev/null +++ b/core/java/android/app/trust/ITrustListener.aidl @@ -0,0 +1,26 @@ +/* +** +** Copyright 2014, 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.app.trust; + +/** + * Private API to be notified about trust changes. + * + * {@hide} + */ +oneway interface ITrustListener { + void onTrustChanged(boolean enabled, int userId); +}
\ No newline at end of file diff --git a/core/java/android/app/trust/ITrustManager.aidl b/core/java/android/app/trust/ITrustManager.aidl new file mode 100644 index 0000000..ad4ccbb --- /dev/null +++ b/core/java/android/app/trust/ITrustManager.aidl @@ -0,0 +1,31 @@ +/* +** +** Copyright 2014, 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.app.trust; + +import android.app.trust.ITrustListener; + +/** + * System private API to comunicate with trust service. + * + * {@hide} + */ +interface ITrustManager { + void reportUnlockAttempt(boolean successful, int userId); + void reportEnabledTrustAgentsChanged(int userId); + void registerTrustListener(in ITrustListener trustListener); + void unregisterTrustListener(in ITrustListener trustListener); +} diff --git a/core/java/android/app/trust/TrustManager.java b/core/java/android/app/trust/TrustManager.java new file mode 100644 index 0000000..e31c624 --- /dev/null +++ b/core/java/android/app/trust/TrustManager.java @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2014 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.app.trust; + +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.RemoteException; +import android.util.ArrayMap; +import android.util.Log; + +/** + * See {@link com.android.server.trust.TrustManagerService} + * @hide + */ +public class TrustManager { + + private static final int MSG_TRUST_CHANGED = 1; + + private static final String TAG = "TrustManager"; + + private final ITrustManager mService; + private final ArrayMap<TrustListener, ITrustListener> mTrustListeners; + + public TrustManager(IBinder b) { + mService = ITrustManager.Stub.asInterface(b); + mTrustListeners = new ArrayMap<TrustListener, ITrustListener>(); + } + + /** + * Reports that user {@param userId} has tried to unlock the device. + * + * @param successful if true, the unlock attempt was successful. + * + * Requires the {@link android.Manifest.permission#ACCESS_KEYGUARD_SECURE_STORAGE} permission. + */ + public void reportUnlockAttempt(boolean successful, int userId) { + try { + mService.reportUnlockAttempt(successful, userId); + } catch (RemoteException e) { + onError(e); + } + } + + /** + * Reports that the list of enabled trust agents changed for user {@param userId}. + * + * Requires the {@link android.Manifest.permission#ACCESS_KEYGUARD_SECURE_STORAGE} permission. + */ + public void reportEnabledTrustAgentsChanged(int userId) { + try { + mService.reportEnabledTrustAgentsChanged(userId); + } catch (RemoteException e) { + onError(e); + } + } + + /** + * Registers a listener for trust events. + * + * Requires the {@link android.Manifest.permission#TRUST_LISTENER} permission. + */ + public void registerTrustListener(final TrustListener trustListener) { + try { + ITrustListener.Stub iTrustListener = new ITrustListener.Stub() { + @Override + public void onTrustChanged(boolean enabled, int userId) throws RemoteException { + mHandler.obtainMessage(MSG_TRUST_CHANGED, (enabled ? 1 : 0), userId, + trustListener).sendToTarget(); + } + }; + mService.registerTrustListener(iTrustListener); + mTrustListeners.put(trustListener, iTrustListener); + } catch (RemoteException e) { + onError(e); + } + } + + /** + * Unregisters a listener for trust events. + * + * Requires the {@link android.Manifest.permission#TRUST_LISTENER} permission. + */ + public void unregisterTrustListener(final TrustListener trustListener) { + ITrustListener iTrustListener = mTrustListeners.remove(trustListener); + if (iTrustListener != null) { + try { + mService.unregisterTrustListener(iTrustListener); + } catch (RemoteException e) { + onError(e); + } + } + } + + private void onError(Exception e) { + Log.e(TAG, "Error while calling TrustManagerService", e); + } + + private final Handler mHandler = new Handler(Looper.getMainLooper()) { + @Override + public void handleMessage(Message msg) { + switch(msg.what) { + case MSG_TRUST_CHANGED: + ((TrustListener)msg.obj).onTrustChanged(msg.arg1 != 0, msg.arg2); + break; + } + } + }; + + public interface TrustListener { + + /** + * Reports that the trust state has changed. + * @param enabled if true, the system believes the environment to be trusted. + * @param userId the user, for which the trust changed. + */ + void onTrustChanged(boolean enabled, int userId); + } +} diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index a4374b8..e79deec 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -567,7 +567,7 @@ public final class BluetoothAdapter { } /** - * Stop BLE advertising. + * Stop BLE advertising. The callback has to be the same one used for start advertising. * * @param callback - {@link AdvertiseCallback} * @return true if BLE advertising stops, false otherwise. @@ -1986,7 +1986,13 @@ public final class BluetoothAdapter { public void onAdvertiseStateChange(int advertiseState, int status) { Log.d(TAG, "on advertise call back, state: " + advertiseState + " status: " + status); if (advertiseState == STATE_ADVERTISE_STARTED) { - mAdvertiseCallback.onAdvertiseStart(status); + if (status == ADVERTISE_CALLBACK_SUCCESS) { + mAdvertiseCallback.onAdvertiseStart(status); + } else { + // If status is unsuccessful and advertise state is started, it means stop + // advertising fails. + mAdvertiseCallback.onAdvertiseStop(status); + } } else { synchronized (this) { if (status == ADVERTISE_CALLBACK_SUCCESS) { @@ -2008,8 +2014,22 @@ public final class BluetoothAdapter { } } } - mAdvertiseCallback.onAdvertiseStop(status); + if (status == ADVERTISE_CALLBACK_SUCCESS) { + mAdvertiseCallback.onAdvertiseStop(status); + } else{ + // If status is unsuccesful and advertise state is stopped, it means start + // advertising fails. + mAdvertiseCallback.onAdvertiseStart(status); + } } } + + /** + * Callback reporting LE ATT MTU. + * @hide + */ + public void onConfigureMTU(String address, int mtu, int status) { + // no op + } } } diff --git a/core/java/android/bluetooth/BluetoothGatt.java b/core/java/android/bluetooth/BluetoothGatt.java index 39305b0..101b721 100644 --- a/core/java/android/bluetooth/BluetoothGatt.java +++ b/core/java/android/bluetooth/BluetoothGatt.java @@ -550,6 +550,23 @@ public final class BluetoothGatt implements BluetoothProfile { public void onAdvertiseStateChange(int state, int status) { if (DBG) Log.d(TAG, "onAdvertiseStateChange() - state = " + state + " status=" + status); + } + + /** + * Callback invoked when the MTU for a given connection changes + * @hide + */ + public void onConfigureMTU(String address, int mtu, int status) { + if (DBG) Log.d(TAG, "onConfigureMTU() - Device=" + address + + " mtu=" + mtu + " status=" + status); + if (!address.equals(mDevice.getAddress())) { + return; + } + try { + mCallback.onConfigureMTU(BluetoothGatt.this, mtu, status); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception in callback", ex); + } } }; @@ -1137,6 +1154,36 @@ public final class BluetoothGatt implements BluetoothProfile { } /** + * Configure the MTU used for a given connection. + * + * <p>When performing a write request operation (write without response), + * the data sent is truncated to the MTU size. This function may be used + * to request a larget MTU size to be able to send more data at once. + * + * <p>A {@link BluetoothGattCallback#onConfigureMTU} callback will indicate + * whether this operation was successful. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @return true, if the new MTU value has been requested successfully + * @hide + */ + public boolean configureMTU(int mtu) { + if (DBG) Log.d(TAG, "configureMTU() - device: " + mDevice.getAddress() + + " mtu: " + mtu); + if (mService == null || mClientIf == 0) return false; + + try { + mService.configureMTU(mClientIf, mDevice.getAddress(), mtu); + } catch (RemoteException e) { + Log.e(TAG,"",e); + return false; + } + + return true; + } + + /** * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)} * with {@link BluetoothProfile#GATT} as argument * diff --git a/core/java/android/bluetooth/BluetoothGattCallback.java b/core/java/android/bluetooth/BluetoothGattCallback.java index 80ea4a6..5180259 100644 --- a/core/java/android/bluetooth/BluetoothGattCallback.java +++ b/core/java/android/bluetooth/BluetoothGattCallback.java @@ -138,4 +138,19 @@ public abstract class BluetoothGattCallback { */ public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) { } + + /** + * Callback indicating the MTU for a given device connection has changed. + * + * This callback is triggered in response to the + * {@link BluetoothGatt#configureMTU} function, or in response to a connection + * event. + * + * @param gatt GATT client invoked {@link BluetoothGatt#configureMTU} + * @param mtu The new MTU size + * @param status {@link BluetoothGatt#GATT_SUCCESS} if the MTU has been changed successfully + * @hide + */ + public void onConfigureMTU(BluetoothGatt gatt, int mtu, int status) { + } } diff --git a/core/java/android/bluetooth/IBluetoothGatt.aidl b/core/java/android/bluetooth/IBluetoothGatt.aidl index 784cdcc..c6b5c3d 100644 --- a/core/java/android/bluetooth/IBluetoothGatt.aidl +++ b/core/java/android/bluetooth/IBluetoothGatt.aidl @@ -73,6 +73,7 @@ interface IBluetoothGatt { void beginReliableWrite(in int clientIf, in String address); void endReliableWrite(in int clientIf, in String address, in boolean execute); void readRemoteRssi(in int clientIf, in String address); + void configureMTU(in int clientIf, in String address, in int mtu); void registerServer(in ParcelUuid appId, in IBluetoothGattServerCallback callback); void unregisterServer(in int serverIf); diff --git a/core/java/android/bluetooth/IBluetoothGattCallback.aidl b/core/java/android/bluetooth/IBluetoothGattCallback.aidl index 7c69a06..a78c29b 100644 --- a/core/java/android/bluetooth/IBluetoothGattCallback.aidl +++ b/core/java/android/bluetooth/IBluetoothGattCallback.aidl @@ -64,4 +64,5 @@ interface IBluetoothGattCallback { in byte[] value); void onReadRemoteRssi(in String address, in int rssi, in int status); oneway void onAdvertiseStateChange(in int advertiseState, in int status); + void onConfigureMTU(in String address, in int mtu, in int status); } diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 9f0c384..27e526b 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -573,6 +573,9 @@ public abstract class Context { * Open a private file associated with this Context's application package * for writing. Creates the file if it doesn't already exist. * + * <p>No permissions are required to invoke this method, since it uses internal + * storage. + * * @param name The name of the file to open; can not contain path * separators. * @param mode Operating mode. Use 0 or {@link #MODE_PRIVATE} for the @@ -630,6 +633,9 @@ public abstract class Context { * Returns the absolute path to the directory on the filesystem where * files created with {@link #openFileOutput} are stored. * + * <p>No permissions are required to read or write to the returned path, since this + * path is internal storage. + * * @return The path of the directory holding application files. * * @see #openFileOutput @@ -1974,6 +1980,7 @@ public abstract class Context { //@hide: NETWORK_STATS_SERVICE, //@hide: NETWORK_POLICY_SERVICE, WIFI_SERVICE, + WIFI_HOTSPOT_SERVICE, WIFI_P2P_SERVICE, NSD_SERVICE, AUDIO_SERVICE, @@ -2324,6 +2331,16 @@ public abstract class Context { /** * Use with {@link #getSystemService} to retrieve a {@link + * android.net.wifi.hotspot.WifiHotspotManager} for handling management of + * Wi-Fi hotspot access. + * + * @see #getSystemService + * @see android.net.wifi.hotspot.WifiHotspotManager + */ + public static final String WIFI_HOTSPOT_SERVICE = "wifihotspot"; + + /** + * Use with {@link #getSystemService} to retrieve a {@link * android.net.wifi.p2p.WifiP2pManager} for handling management of * Wi-Fi peer-to-peer connections. * @@ -2584,6 +2601,14 @@ public abstract class Context { public static final String CONSUMER_IR_SERVICE = "consumer_ir"; /** + * {@link android.app.trust.TrustManager} for managing trust agents. + * @see #getSystemService + * @see android.app.trust.TrustManager + * @hide + */ + public static final String TRUST_SERVICE = "trust"; + + /** * Determine whether the given permission is allowed for a particular * process and user ID running in the system. * diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 0175d62..d189a34 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -2173,6 +2173,11 @@ public class Intent implements Parcelable, Cloneable { /** * Broadcast Action: Wired Headset plugged in or unplugged. * + * You <em>cannot</em> receive this through components declared + * in manifests, only by explicitly registering for it with + * {@link Context#registerReceiver(BroadcastReceiver, IntentFilter) + * Context.registerReceiver()}. + * * <p>The intent will have the following extra values: * <ul> * <li><em>state</em> - 0 for unplugged, 1 for plugged. </li> @@ -2879,6 +2884,14 @@ public class Intent implements Parcelable, Cloneable { @SdkConstant(SdkConstantType.INTENT_CATEGORY) public static final String CATEGORY_CAR_MODE = "android.intent.category.CAR_MODE"; + /** + * An activity that provides a user interface for adjusting notification preferences for its + * containing application. Optional but recommended for apps that post + * {@link android.app.Notification Notifications}. + */ + @SdkConstant(SdkConstantType.INTENT_CATEGORY) + public static final String CATEGORY_NOTIFICATION_PREFERENCES = "android.intent.category.NOTIFICATION_PREFERENCES"; + // --------------------------------------------------------------------- // --------------------------------------------------------------------- // Application launch intent categories (see addCategory()). diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java index 40275d8..9916476 100644 --- a/core/java/android/content/pm/ActivityInfo.java +++ b/core/java/android/content/pm/ActivityInfo.java @@ -189,6 +189,13 @@ public class ActivityInfo extends ComponentInfo */ public static final int FLAG_IMMERSIVE = 0x0800; /** + * Bit in {@link #flags} indicating that this activity is to be persisted across + * reboots for display in the Recents list. + * {@link android.R.attr#persistable} + * @hide + */ + public static final int FLAG_PERSISTABLE = 0x1000; + /** * @hide Bit in {@link #flags}: If set, this component will only be seen * by the primary user. Only works with broadcast receivers. Set from the * {@link android.R.attr#primaryUserOnly} attribute. diff --git a/core/java/android/content/pm/IPackageInstallObserver2.aidl b/core/java/android/content/pm/IPackageInstallObserver2.aidl new file mode 100644 index 0000000..2602ab5 --- /dev/null +++ b/core/java/android/content/pm/IPackageInstallObserver2.aidl @@ -0,0 +1,45 @@ +/* +** +** Copyright 2014, 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.content.pm; + +import android.os.Bundle; + +/** + * API for installation callbacks from the Package Manager. In certain result cases + * additional information will be provided. + * @hide + */ +oneway interface IPackageInstallObserver2 { + /** + * The install operation has completed. {@code returnCode} holds a numeric code + * indicating success or failure. In certain cases the {@code extras} Bundle will + * contain additional details: + * + * <p><table> + * <tr> + * <td>INSTALL_FAILED_DUPLICATE_PERMISSION</td> + * <td>Two strings are provided in the extras bundle: EXTRA_EXISTING_PERMISSION + * is the name of the permission that the app is attempting to define, and + * EXTRA_EXISTING_PACKAGE is the package name of the app which has already + * defined the permission.</td> + * </tr> + * </table> + */ + void packageInstalled(in String packageName, in Bundle extras, int returnCode); +} + diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index c9fb530..ae0899f 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -25,6 +25,7 @@ import android.content.pm.ApplicationInfo; import android.content.pm.ContainerEncryptionParams; import android.content.pm.FeatureInfo; import android.content.pm.IPackageInstallObserver; +import android.content.pm.IPackageInstallObserver2; import android.content.pm.IPackageDeleteObserver; import android.content.pm.IPackageDataObserver; import android.content.pm.IPackageMoveObserver; @@ -406,6 +407,21 @@ interface IPackageManager { in VerificationParams verificationParams, in ContainerEncryptionParams encryptionParams); + /** Expanded observer versions */ + void installPackageEtc(in Uri packageURI, IPackageInstallObserver observer, + IPackageInstallObserver2 observer2, int flags, in String installerPackageName); + + void installPackageWithVerificationEtc(in Uri packageURI, + in IPackageInstallObserver observer, IPackageInstallObserver2 observer2, + int flags, in String installerPackageName, in Uri verificationURI, + in ManifestDigest manifestDigest, in ContainerEncryptionParams encryptionParams); + + void installPackageWithVerificationAndEncryptionEtc(in Uri packageURI, + in IPackageInstallObserver observer, in IPackageInstallObserver2 observer2, + int flags, in String installerPackageName, + in VerificationParams verificationParams, + in ContainerEncryptionParams encryptionParams); + int installExistingPackageAsUser(String packageName, int userId); void verifyPendingInstall(int id, int verificationCode); diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index e86833b..3300e9d 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -19,6 +19,7 @@ package android.content.pm; import android.annotation.IntDef; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.app.PackageInstallObserver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -683,6 +684,20 @@ public abstract class PackageManager { public static final int INSTALL_FAILED_USER_RESTRICTED = -111; /** + * Installation failed return code: this is passed to the {@link IPackageInstallObserver} by + * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} + * if the system failed to install the package because it is attempting to define a + * permission that is already defined by some existing package. + * + * <p>The package name of the app which has already defined the permission is passed to + * a {@link IPackageInstallObserver2}, if any, as the {@link #EXTRA_EXISTING_PACKAGE} + * string extra; and the name of the permission being redefined is passed in the + * {@link #EXTRA_EXISTING_PERMISSION} string extra. + * @hide + */ + public static final int INSTALL_FAILED_DUPLICATE_PERMISSION = -112; + + /** * Flag parameter for {@link #deletePackage} to indicate that you don't want to delete the * package's data directory. * @@ -1301,6 +1316,13 @@ public abstract class PackageManager { public static final String FEATURE_BACKUP = "android.software.backup"; /** + * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: + * The device supports managed profiles for enterprise users. + */ + @SdkConstant(SdkConstantType.FEATURE) + public static final String FEATURE_MANAGEDPROFILES = "android.software.managedprofiles"; + + /** * Action to external storage service to clean out removed apps. * @hide */ @@ -1390,6 +1412,24 @@ public abstract class PackageManager { = "android.content.pm.extra.PERMISSION_LIST"; /** + * String extra for {@link IPackageInstallObserver2} in the 'extras' Bundle in case of + * {@link #INSTALL_FAILED_DUPLICATE_PERMISSION}. This extra names the package which provides + * the existing definition for the permission. + * @hide + */ + public static final String EXTRA_FAILURE_EXISTING_PACKAGE + = "android.content.pm.extra.FAILURE_EXISTING_PACKAGE"; + + /** + * String extra for {@link IPackageInstallObserver2} in the 'extras' Bundle in case of + * {@link #INSTALL_FAILED_DUPLICATE_PERMISSION}. This extra names the permission that is + * being redundantly defined by the package being installed. + * @hide + */ + public static final String EXTRA_FAILURE_EXISTING_PERMISSION + = "android.content.pm.extra.FAILURE_EXISTING_PERMISSION"; + + /** * Retrieve overall information about an application package that is * installed on the system. * <p> @@ -2457,7 +2497,7 @@ public abstract class PackageManager { /** * Return the generic icon for an activity that is used when no specific * icon is defined. - * + * * @return Drawable Image of the icon. */ public abstract Drawable getDefaultActivityIcon(); @@ -2752,11 +2792,14 @@ public abstract class PackageManager { * 'content:' URI. * @param observer An observer callback to get notified when the package installation is * complete. {@link IPackageInstallObserver#packageInstalled(String, int)} will be - * called when that happens. observer may be null to indicate that no callback is desired. + * called when that happens. This parameter must not be null. * @param flags - possible values: {@link #INSTALL_FORWARD_LOCK}, * {@link #INSTALL_REPLACE_EXISTING}, {@link #INSTALL_ALLOW_TEST}. * @param installerPackageName Optional package name of the application that is performing the * installation. This identifies which market the package came from. + * @deprecated Use {@link #installPackage(Uri, IPackageInstallObserver2, int, String)} + * instead. This method will continue to be supported but the older observer interface + * will not get additional failure details. */ public abstract void installPackage( Uri packageURI, IPackageInstallObserver observer, int flags, @@ -2772,11 +2815,9 @@ public abstract class PackageManager { * @param observer An observer callback to get notified when the package * installation is complete. * {@link IPackageInstallObserver#packageInstalled(String, int)} - * will be called when that happens. observer may be null to - * indicate that no callback is desired. + * will be called when that happens. This parameter must not be null. * @param flags - possible values: {@link #INSTALL_FORWARD_LOCK}, - * {@link #INSTALL_REPLACE_EXISTING}, {@link #INSTALL_ALLOW_TEST} - * . + * {@link #INSTALL_REPLACE_EXISTING}, {@link #INSTALL_ALLOW_TEST}. * @param installerPackageName Optional package name of the application that * is performing the installation. This identifies which market * the package came from. @@ -2789,6 +2830,10 @@ public abstract class PackageManager { * these parameters describing the encryption and authentication * used. May be {@code null}. * @hide + * @deprecated Use {@link #installPackageWithVerification(Uri, IPackageInstallObserver2, + * int, String, Uri, ManifestDigest, ContainerEncryptionParams)} instead. This method will + * continue to be supported but the older observer interface will not get additional failure + * details. */ public abstract void installPackageWithVerification(Uri packageURI, IPackageInstallObserver observer, int flags, String installerPackageName, @@ -2805,11 +2850,9 @@ public abstract class PackageManager { * @param observer An observer callback to get notified when the package * installation is complete. * {@link IPackageInstallObserver#packageInstalled(String, int)} - * will be called when that happens. observer may be null to - * indicate that no callback is desired. + * will be called when that happens. This parameter must not be null. * @param flags - possible values: {@link #INSTALL_FORWARD_LOCK}, - * {@link #INSTALL_REPLACE_EXISTING}, {@link #INSTALL_ALLOW_TEST} - * . + * {@link #INSTALL_REPLACE_EXISTING}, {@link #INSTALL_ALLOW_TEST}. * @param installerPackageName Optional package name of the application that * is performing the installation. This identifies which market * the package came from. @@ -2820,12 +2863,101 @@ public abstract class PackageManager { * used. May be {@code null}. * * @hide + * @deprecated Use {@link #installPackageWithVerificationAndEncryption(Uri, + * IPackageInstallObserver2, int, String, VerificationParams, + * ContainerEncryptionParams)} instead. This method will continue to be + * supported but the older observer interface will not get additional failure details. */ + @Deprecated public abstract void installPackageWithVerificationAndEncryption(Uri packageURI, IPackageInstallObserver observer, int flags, String installerPackageName, VerificationParams verificationParams, ContainerEncryptionParams encryptionParams); + // Package-install variants that take the new, expanded form of observer interface. + // Note that these *also* take the original observer type and will redundantly + // report the same information to that observer if supplied; but it is not required. + + /** + * @hide + * + * Install a package. Since this may take a little while, the result will + * be posted back to the given observer. An installation will fail if the calling context + * lacks the {@link android.Manifest.permission#INSTALL_PACKAGES} permission, if the + * package named in the package file's manifest is already installed, or if there's no space + * available on the device. + * + * @param packageURI The location of the package file to install. This can be a 'file:' or a + * 'content:' URI. + * @param observer An observer callback to get notified when the package installation is + * complete. {@link PackageInstallObserver#packageInstalled(String, Bundle, int)} will be + * called when that happens. This parameter must not be null. + * @param flags - possible values: {@link #INSTALL_FORWARD_LOCK}, + * {@link #INSTALL_REPLACE_EXISTING}, {@link #INSTALL_ALLOW_TEST}. + * @param installerPackageName Optional package name of the application that is performing the + * installation. This identifies which market the package came from. + */ + public abstract void installPackage( + Uri packageURI, PackageInstallObserver observer, + int flags, String installerPackageName); + + /** + * Similar to + * {@link #installPackage(Uri, IPackageInstallObserver, int, String)} but + * with an extra verification file provided. + * + * @param packageURI The location of the package file to install. This can + * be a 'file:' or a 'content:' URI. + * @param observer An observer callback to get notified when the package installation is + * complete. {@link PackageInstallObserver#packageInstalled(String, Bundle, int)} will be + * called when that happens. This parameter must not be null. + * @param flags - possible values: {@link #INSTALL_FORWARD_LOCK}, + * {@link #INSTALL_REPLACE_EXISTING}, {@link #INSTALL_ALLOW_TEST}. + * @param installerPackageName Optional package name of the application that + * is performing the installation. This identifies which market + * the package came from. + * @param verificationURI The location of the supplementary verification + * file. This can be a 'file:' or a 'content:' URI. May be + * {@code null}. + * @param manifestDigest an object that holds the digest of the package + * which can be used to verify ownership. May be {@code null}. + * @param encryptionParams if the package to be installed is encrypted, + * these parameters describing the encryption and authentication + * used. May be {@code null}. + * @hide + */ + public abstract void installPackageWithVerification(Uri packageURI, + PackageInstallObserver observer, int flags, String installerPackageName, + Uri verificationURI, ManifestDigest manifestDigest, + ContainerEncryptionParams encryptionParams); + + /** + * Similar to + * {@link #installPackage(Uri, IPackageInstallObserver, int, String)} but + * with an extra verification information provided. + * + * @param packageURI The location of the package file to install. This can + * be a 'file:' or a 'content:' URI. + * @param observer An observer callback to get notified when the package installation is + * complete. {@link PackageInstallObserver#packageInstalled(String, Bundle, int)} will be + * called when that happens. This parameter must not be null. + * @param flags - possible values: {@link #INSTALL_FORWARD_LOCK}, + * {@link #INSTALL_REPLACE_EXISTING}, {@link #INSTALL_ALLOW_TEST}. + * @param installerPackageName Optional package name of the application that + * is performing the installation. This identifies which market + * the package came from. + * @param verificationParams an object that holds signal information to + * assist verification. May be {@code null}. + * @param encryptionParams if the package to be installed is encrypted, + * these parameters describing the encryption and authentication + * used. May be {@code null}. + * + * @hide + */ + public abstract void installPackageWithVerificationAndEncryption(Uri packageURI, + PackageInstallObserver observer, int flags, String installerPackageName, + VerificationParams verificationParams, ContainerEncryptionParams encryptionParams); + /** * If there is already an application with the given package name installed * on the system for other users, also install it for the calling user. diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index cf44ad8..4b5616f 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -57,7 +57,6 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.jar.JarEntry; import java.util.jar.StrictJarFile; import java.util.zip.ZipEntry; @@ -1010,13 +1009,14 @@ public class PackageParser { pkg.mSharedUserLabel = sa.getResourceId( com.android.internal.R.styleable.AndroidManifest_sharedUserLabel, 0); } - sa.recycle(); pkg.installLocation = sa.getInteger( com.android.internal.R.styleable.AndroidManifest_installLocation, PARSE_DEFAULT_INSTALL_LOCATION); pkg.applicationInfo.installLocation = pkg.installLocation; + sa.recycle(); + /* Set the global "forward lock" flag */ if ((flags & PARSE_FORWARD_LOCK) != 0) { pkg.applicationInfo.flags |= ApplicationInfo.FLAG_FORWARD_LOCK; @@ -1105,7 +1105,6 @@ public class PackageParser { if (!parseUsesPermission(pkg, res, parser, attrs, outError)) { return null; } - } else if (tagName.equals("uses-configuration")) { ConfigurationInfo cPref = new ConfigurationInfo(); sa = res.obtainAttributes(attrs, @@ -2448,6 +2447,11 @@ public class PackageParser { a.info.flags |= ActivityInfo.FLAG_IMMERSIVE; } + if (sa.getBoolean( + com.android.internal.R.styleable.AndroidManifestActivity_persistable, false)) { + a.info.flags |= ActivityInfo.FLAG_PERSISTABLE; + } + if (!receiver) { if (sa.getBoolean( com.android.internal.R.styleable.AndroidManifestActivity_hardwareAccelerated, diff --git a/core/java/android/content/pm/UserInfo.java b/core/java/android/content/pm/UserInfo.java index 6f1d4f8..f53aa4c 100644 --- a/core/java/android/content/pm/UserInfo.java +++ b/core/java/android/content/pm/UserInfo.java @@ -71,7 +71,7 @@ public class UserInfo implements Parcelable { public static final int FLAG_MANAGED_PROFILE = 0x00000020; - public static final int NO_RELATED_GROUP_ID = -1; + public static final int NO_PROFILE_GROUP_ID = -1; public int id; public int serialNumber; @@ -80,7 +80,7 @@ public class UserInfo implements Parcelable { public int flags; public long creationTime; public long lastLoggedInTime; - public int relatedGroupId; + public int profileGroupId; /** User is only partially created. */ public boolean partial; @@ -94,7 +94,7 @@ public class UserInfo implements Parcelable { this.name = name; this.flags = flags; this.iconPath = iconPath; - this.relatedGroupId = NO_RELATED_GROUP_ID; + this.profileGroupId = NO_PROFILE_GROUP_ID; } public boolean isPrimary() { @@ -137,7 +137,7 @@ public class UserInfo implements Parcelable { creationTime = orig.creationTime; lastLoggedInTime = orig.lastLoggedInTime; partial = orig.partial; - relatedGroupId = orig.relatedGroupId; + profileGroupId = orig.profileGroupId; } public UserHandle getUserHandle() { @@ -162,7 +162,7 @@ public class UserInfo implements Parcelable { dest.writeLong(creationTime); dest.writeLong(lastLoggedInTime); dest.writeInt(partial ? 1 : 0); - dest.writeInt(relatedGroupId); + dest.writeInt(profileGroupId); } public static final Parcelable.Creator<UserInfo> CREATOR @@ -184,6 +184,6 @@ public class UserInfo implements Parcelable { creationTime = source.readLong(); lastLoggedInTime = source.readLong(); partial = source.readInt() != 0; - relatedGroupId = source.readInt(); + profileGroupId = source.readInt(); } } diff --git a/core/java/android/content/res/ColorStateList.java b/core/java/android/content/res/ColorStateList.java index 419abf2..5674154 100644 --- a/core/java/android/content/res/ColorStateList.java +++ b/core/java/android/content/res/ColorStateList.java @@ -19,6 +19,7 @@ package android.content.res; import android.graphics.Color; import com.android.internal.util.ArrayUtils; +import com.android.internal.util.GrowingArrayUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -181,10 +182,9 @@ public class ColorStateList implements Parcelable { final int innerDepth = parser.getDepth()+1; int depth; - int listAllocated = 20; + int[][] stateSpecList = ArrayUtils.newUnpaddedArray(int[].class, 20); + int[] colorList = new int[stateSpecList.length]; int listSize = 0; - int[] colorList = new int[listAllocated]; - int[][] stateSpecList = new int[listAllocated][]; while ((type=parser.next()) != XmlPullParser.END_DOCUMENT && ((depth=parser.getDepth()) >= innerDepth @@ -248,21 +248,8 @@ public class ColorStateList implements Parcelable { mDefaultColor = color; } - if (listSize + 1 >= listAllocated) { - listAllocated = ArrayUtils.idealIntArraySize(listSize + 1); - - int[] ncolor = new int[listAllocated]; - System.arraycopy(colorList, 0, ncolor, 0, listSize); - - int[][] nstate = new int[listAllocated][]; - System.arraycopy(stateSpecList, 0, nstate, 0, listSize); - - colorList = ncolor; - stateSpecList = nstate; - } - - colorList[listSize] = color; - stateSpecList[listSize] = stateSpec; + colorList = GrowingArrayUtils.append(colorList, listSize, color); + stateSpecList = GrowingArrayUtils.append(stateSpecList, listSize, stateSpec); listSize++; } diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java index 48b6fca..a07fc97 100644 --- a/core/java/android/content/res/Configuration.java +++ b/core/java/android/content/res/Configuration.java @@ -440,6 +440,11 @@ public final class Configuration implements Parcelable, Comparable<Configuration * <a href="{@docRoot}guide/topics/resources/providing-resources.html#UiModeQualifier">appliance</a> * resource qualifier. */ public static final int UI_MODE_TYPE_APPLIANCE = 0x05; + /** Constant for {@link #uiMode}: a {@link #UI_MODE_TYPE_MASK} + * value that corresponds to the + * <a href="{@docRoot}guide/topics/resources/providing-resources.html#UiModeQualifier">watch</a> + * resource qualifier. */ + public static final int UI_MODE_TYPE_WATCH = 0x06; /** Constant for {@link #uiMode}: bits that encode the night mode. */ public static final int UI_MODE_NIGHT_MASK = 0x30; @@ -462,8 +467,8 @@ public final class Configuration implements Parcelable, Comparable<Configuration * <p>The {@link #UI_MODE_TYPE_MASK} bits define the overall ui mode of the * device. They may be one of {@link #UI_MODE_TYPE_UNDEFINED}, * {@link #UI_MODE_TYPE_NORMAL}, {@link #UI_MODE_TYPE_DESK}, - * {@link #UI_MODE_TYPE_CAR}, {@link #UI_MODE_TYPE_TELEVISION}, or - * {@link #UI_MODE_TYPE_APPLIANCE}. + * {@link #UI_MODE_TYPE_CAR}, {@link #UI_MODE_TYPE_TELEVISION}, + * {@link #UI_MODE_TYPE_APPLIANCE}, or {@link #UI_MODE_TYPE_WATCH}. * * <p>The {@link #UI_MODE_NIGHT_MASK} defines whether the screen * is in a special mode. They may be one of {@link #UI_MODE_NIGHT_UNDEFINED}, @@ -700,6 +705,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration case UI_MODE_TYPE_CAR: sb.append(" car"); break; case UI_MODE_TYPE_TELEVISION: sb.append(" television"); break; case UI_MODE_TYPE_APPLIANCE: sb.append(" appliance"); break; + case UI_MODE_TYPE_WATCH: sb.append(" watch"); break; default: sb.append(" uimode="); sb.append(uiMode&UI_MODE_TYPE_MASK); break; } switch ((uiMode&UI_MODE_NIGHT_MASK)) { diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index affc784..d6eafc6 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -114,7 +114,6 @@ public class Resources { private boolean mPreloading; private TypedArray mCachedStyledAttributes = null; - private RuntimeException mLastRetrievedAttrs = null; private int mLastCachedXmlBlockIndex = -1; private final int[] mCachedXmlBlockIds = { 0, 0, 0, 0 }; @@ -136,17 +135,31 @@ public class Resources { sPreloadedDrawables[1] = new LongSparseArray<ConstantState>(); } - /** @hide */ + /** + * Returns the most appropriate default theme for the specified target SDK version. + * <ul> + * <li>Below API 11: Gingerbread + * <li>APIs 11 thru 14: Holo + * <li>APIs 14 thru XX: Device default dark + * <li>API XX and above: Device default light with dark action bar + * </ul> + * + * @param curTheme The current theme, or 0 if not specified. + * @param targetSdkVersion The target SDK version. + * @return A theme resource identifier + * @hide + */ public static int selectDefaultTheme(int curTheme, int targetSdkVersion) { return selectSystemTheme(curTheme, targetSdkVersion, com.android.internal.R.style.Theme, com.android.internal.R.style.Theme_Holo, - com.android.internal.R.style.Theme_DeviceDefault); + com.android.internal.R.style.Theme_DeviceDefault, + com.android.internal.R.style.Theme_DeviceDefault_Light_DarkActionBar); } - + /** @hide */ - public static int selectSystemTheme(int curTheme, int targetSdkVersion, - int orig, int holo, int deviceDefault) { + public static int selectSystemTheme(int curTheme, int targetSdkVersion, int orig, int holo, + int dark, int deviceDefault) { if (curTheme != 0) { return curTheme; } @@ -156,9 +169,12 @@ public class Resources { if (targetSdkVersion < Build.VERSION_CODES.ICE_CREAM_SANDWICH) { return holo; } + if (targetSdkVersion < Build.VERSION_CODES.CUR_DEVELOPMENT) { + return dark; + } return deviceDefault; } - + /** * This exception is thrown by the resource APIs when a requested resource * can not be found. @@ -1253,12 +1269,9 @@ public class Resources { public void applyStyle(int resid, boolean force) { AssetManager.applyThemeStyle(mTheme, resid, force); - if (!mHasStyle) { - mHasStyle = true; - mThemeResId = resid; - } else if (resid != mThemeResId) { - mThemeResId = 0; - } + // TODO: In very rare cases, we may end up with a hybrid theme + // that can't map to a single theme ID. + mThemeResId = resid; } /** @@ -1273,7 +1286,6 @@ public class Resources { public void setTo(Theme other) { AssetManager.copyTheme(mTheme, other.mTheme); - mHasStyle = other.mHasStyle; mThemeResId = other.mThemeResId; } @@ -1562,10 +1574,6 @@ public class Resources { mAssets.releaseTheme(mTheme); } - /*package*/ boolean canCacheDrawables() { - return mHasStyle && mThemeResId != 0; - } - /*package*/ Theme() { mAssets = Resources.this.mAssets; mTheme = mAssets.createTheme(); @@ -1575,12 +1583,8 @@ public class Resources { private final AssetManager mAssets; private final long mTheme; - /** - * Resource identifier for the theme. If multiple styles have been - * applied to this theme, this value will be 0 (invalid). - */ + /** Resource identifier for the theme. */ private int mThemeResId = 0; - private boolean mHasStyle = false; } /** @@ -2260,11 +2264,6 @@ public class Resources { return; } - // Abort if the drawable is themed, but the theme cannot be cached. - if (dr.canApplyTheme() && theme != null && !theme.canCacheDrawables()) { - return; - } - if (mPreloading) { // Preloaded drawables never have a theme, but may be themeable. final int changingConfigs = cs.getChangingConfigurations(); @@ -2289,11 +2288,7 @@ public class Resources { } else { synchronized (mAccessLock) { final LongSparseArray<WeakReference<ConstantState>> themedCache; - if (!dr.canApplyTheme()) { - themedCache = caches.getUnthemed(true); - } else { - themedCache = caches.getOrCreate(theme == null ? 0 : theme.mThemeResId); - } + themedCache = caches.getOrCreate(theme == null ? 0 : theme.mThemeResId); themedCache.put(key, new WeakReference<ConstantState>(cs)); } } @@ -2354,21 +2349,6 @@ public class Resources { private Drawable getCachedDrawable(ThemedCaches<ConstantState> caches, long key, Theme theme) { synchronized (mAccessLock) { - // First, check for a matching unthemed drawable. - final LongSparseArray<WeakReference<ConstantState>> unthemed = caches.getUnthemed(false); - if (unthemed != null) { - final Drawable unthemedDrawable = getCachedDrawableLocked(unthemed, key); - if (unthemedDrawable != null) { - return unthemedDrawable; - } - } - - final boolean themeCannotCache = theme != null && !theme.canCacheDrawables(); - if (themeCannotCache) { - return null; - } - - // Next, check for a matching themed drawable. final int themeKey = theme != null ? theme.mThemeResId : 0; final LongSparseArray<WeakReference<ConstantState>> themedCache = caches.get(themeKey); if (themedCache != null) { @@ -2606,18 +2586,6 @@ public class Resources { } static class ThemedCaches<T> extends SparseArray<LongSparseArray<WeakReference<T>>> { - private LongSparseArray<WeakReference<T>> mUnthemed = null; - - /** - * Returns the cache of drawables with no themeable attributes. - */ - public LongSparseArray<WeakReference<T>> getUnthemed(boolean autoCreate) { - if (mUnthemed == null && autoCreate) { - mUnthemed = new LongSparseArray<WeakReference<T>>(1); - } - return mUnthemed; - } - /** * Returns the cache of drawables styled for the specified theme. * <p> diff --git a/core/java/android/content/res/TypedArray.java b/core/java/android/content/res/TypedArray.java index d7199ff..15337ce 100644 --- a/core/java/android/content/res/TypedArray.java +++ b/core/java/android/content/res/TypedArray.java @@ -32,7 +32,7 @@ import java.util.Arrays; * {@link Resources.Theme#obtainStyledAttributes(AttributeSet, int[], int, int)} * or {@link Resources#obtainAttributes}. Be * sure to call {@link #recycle} when done with them. - * + * * The indices used to retrieve values from this structure correspond to * the positions of the attributes given to obtainStyledAttributes. */ @@ -46,6 +46,7 @@ public class TypedArray { attrs.mResources = res; attrs.mMetrics = res.getDisplayMetrics(); attrs.mAssets = res.getAssets(); + attrs.mRecycled = false; final int fullLen = len * AssetManager.STYLE_NUM_ENTRIES; if (attrs.mData.length >= fullLen) { @@ -65,6 +66,8 @@ public class TypedArray { private Resources mResources; private DisplayMetrics mMetrics; private AssetManager mAssets; + private boolean mRecycled; + /*package*/ XmlBlock.Parser mXml; /*package*/ Resources.Theme mTheme; /*package*/ int[] mData; @@ -76,45 +79,65 @@ public class TypedArray { * Return the number of values in this array. */ public int length() { + if (mRecycled) { + throw new RuntimeException("Cannot make calls to a recycled instance!"); + } + return mLength; } - + /** * Return the number of indices in the array that actually have data. */ public int getIndexCount() { + if (mRecycled) { + throw new RuntimeException("Cannot make calls to a recycled instance!"); + } + return mIndices[0]; } - + /** * Return an index in the array that has data. - * + * * @param at The index you would like to returned, ranging from 0 to * {@link #getIndexCount()}. - * + * * @return The index at the given offset, which can be used with * {@link #getValue} and related APIs. */ public int getIndex(int at) { + if (mRecycled) { + throw new RuntimeException("Cannot make calls to a recycled instance!"); + } + return mIndices[1+at]; } - + /** * Return the Resources object this array was loaded from. */ public Resources getResources() { + if (mRecycled) { + throw new RuntimeException("Cannot make calls to a recycled instance!"); + } + return mResources; } - + /** * Retrieve the styled string value for the attribute at <var>index</var>. - * + * * @param index Index of attribute to retrieve. - * - * @return CharSequence holding string data. May be styled. Returns + * + * @return CharSequence holding string data. May be styled. Returns * null if the attribute is not defined. */ public CharSequence getText(int index) { + if (mRecycled) { + throw new RuntimeException("Cannot make calls to a recycled instance!"); + } + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; final int type = data[index+AssetManager.STYLE_TYPE]; @@ -136,13 +159,17 @@ public class TypedArray { /** * Retrieve the string value for the attribute at <var>index</var>. - * + * * @param index Index of attribute to retrieve. - * + * * @return String holding string data. Any styling information is * removed. Returns null if the attribute is not defined. */ public String getString(int index) { + if (mRecycled) { + throw new RuntimeException("Cannot make calls to a recycled instance!"); + } + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; final int type = data[index+AssetManager.STYLE_TYPE]; @@ -170,14 +197,18 @@ public class TypedArray { * attributes, or conversions from other types. As such, this method * will only return strings for TypedArray objects that come from * attributes in an XML file. - * + * * @param index Index of attribute to retrieve. - * + * * @return String holding string data. Any styling information is * removed. Returns null if the attribute is not defined or is not * an immediate string value. */ public String getNonResourceString(int index) { + if (mRecycled) { + throw new RuntimeException("Cannot make calls to a recycled instance!"); + } + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; final int type = data[index+AssetManager.STYLE_TYPE]; @@ -190,12 +221,12 @@ public class TypedArray { } return null; } - + /** * @hide * Retrieve the string value for the attribute at <var>index</var> that is * not allowed to change with the given configurations. - * + * * @param index Index of attribute to retrieve. * @param allowedChangingConfigs Bit mask of configurations from * {@link Configuration}.NATIVE_CONFIG_* that are allowed to change. @@ -204,6 +235,10 @@ public class TypedArray { * removed. Returns null if the attribute is not defined. */ public String getNonConfigurationString(int index, int allowedChangingConfigs) { + if (mRecycled) { + throw new RuntimeException("Cannot make calls to a recycled instance!"); + } + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; final int type = data[index+AssetManager.STYLE_TYPE]; @@ -229,13 +264,17 @@ public class TypedArray { /** * Retrieve the boolean value for the attribute at <var>index</var>. - * + * * @param index Index of attribute to retrieve. * @param defValue Value to return if the attribute is not defined. - * + * * @return Attribute boolean value, or defValue if not defined. */ public boolean getBoolean(int index, boolean defValue) { + if (mRecycled) { + throw new RuntimeException("Cannot make calls to a recycled instance!"); + } + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; final int type = data[index+AssetManager.STYLE_TYPE]; @@ -259,13 +298,17 @@ public class TypedArray { /** * Retrieve the integer value for the attribute at <var>index</var>. - * + * * @param index Index of attribute to retrieve. * @param defValue Value to return if the attribute is not defined. - * + * * @return Attribute int value, or defValue if not defined. */ public int getInt(int index, int defValue) { + if (mRecycled) { + throw new RuntimeException("Cannot make calls to a recycled instance!"); + } + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; final int type = data[index+AssetManager.STYLE_TYPE]; @@ -289,12 +332,16 @@ public class TypedArray { /** * Retrieve the float value for the attribute at <var>index</var>. - * + * * @param index Index of attribute to retrieve. - * + * * @return Attribute float value, or defValue if not defined.. */ public float getFloat(int index, float defValue) { + if (mRecycled) { + throw new RuntimeException("Cannot make calls to a recycled instance!"); + } + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; final int type = data[index+AssetManager.STYLE_TYPE]; @@ -319,20 +366,24 @@ public class TypedArray { + Integer.toHexString(type)); return defValue; } - + /** * Retrieve the color value for the attribute at <var>index</var>. If * the attribute references a color resource holding a complex * {@link android.content.res.ColorStateList}, then the default color from * the set is returned. - * + * * @param index Index of attribute to retrieve. * @param defValue Value to return if the attribute is not defined or * not a resource. - * + * * @return Attribute color value, or defValue if not defined. */ public int getColor(int index, int defValue) { + if (mRecycled) { + throw new RuntimeException("Cannot make calls to a recycled instance!"); + } + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; final int type = data[index+AssetManager.STYLE_TYPE]; @@ -359,12 +410,16 @@ public class TypedArray { * Retrieve the ColorStateList for the attribute at <var>index</var>. * The value may be either a single solid color or a reference to * a color or complex {@link android.content.res.ColorStateList} description. - * + * * @param index Index of attribute to retrieve. - * + * * @return ColorStateList for the attribute, or null if not defined. */ public ColorStateList getColorStateList(int index) { + if (mRecycled) { + throw new RuntimeException("Cannot make calls to a recycled instance!"); + } + final TypedValue value = mValue; if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { return mResources.loadColorStateList(value, value.resourceId); @@ -374,14 +429,18 @@ public class TypedArray { /** * Retrieve the integer value for the attribute at <var>index</var>. - * + * * @param index Index of attribute to retrieve. * @param defValue Value to return if the attribute is not defined or * not a resource. - * + * * @return Attribute integer value, or defValue if not defined. */ public int getInteger(int index, int defValue) { + if (mRecycled) { + throw new RuntimeException("Cannot make calls to a recycled instance!"); + } + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; final int type = data[index+AssetManager.STYLE_TYPE]; @@ -397,22 +456,26 @@ public class TypedArray { } /** - * Retrieve a dimensional unit attribute at <var>index</var>. Unit - * conversions are based on the current {@link DisplayMetrics} - * associated with the resources this {@link TypedArray} object - * came from. - * + * Retrieve a dimensional unit attribute at <var>index</var>. Unit + * conversions are based on the current {@link DisplayMetrics} + * associated with the resources this {@link TypedArray} object + * came from. + * * @param index Index of attribute to retrieve. * @param defValue Value to return if the attribute is not defined or * not a resource. - * - * @return Attribute dimension value multiplied by the appropriate + * + * @return Attribute dimension value multiplied by the appropriate * metric, or defValue if not defined. - * + * * @see #getDimensionPixelOffset * @see #getDimensionPixelSize */ public float getDimension(int index, float defValue) { + if (mRecycled) { + throw new RuntimeException("Cannot make calls to a recycled instance!"); + } + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; final int type = data[index+AssetManager.STYLE_TYPE]; @@ -433,18 +496,22 @@ public class TypedArray { * {@link #getDimension}, except the returned value is converted to * integer pixels for you. An offset conversion involves simply * truncating the base value to an integer. - * + * * @param index Index of attribute to retrieve. * @param defValue Value to return if the attribute is not defined or * not a resource. - * - * @return Attribute dimension value multiplied by the appropriate + * + * @return Attribute dimension value multiplied by the appropriate * metric and truncated to integer pixels, or defValue if not defined. - * + * * @see #getDimension * @see #getDimensionPixelSize */ public int getDimensionPixelOffset(int index, int defValue) { + if (mRecycled) { + throw new RuntimeException("Cannot make calls to a recycled instance!"); + } + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; final int type = data[index+AssetManager.STYLE_TYPE]; @@ -466,18 +533,22 @@ public class TypedArray { * integer pixels for use as a size. A size conversion involves * rounding the base value, and ensuring that a non-zero base value * is at least one pixel in size. - * + * * @param index Index of attribute to retrieve. * @param defValue Value to return if the attribute is not defined or * not a resource. - * - * @return Attribute dimension value multiplied by the appropriate + * + * @return Attribute dimension value multiplied by the appropriate * metric and truncated to integer pixels, or defValue if not defined. - * + * * @see #getDimension * @see #getDimensionPixelOffset */ public int getDimensionPixelSize(int index, int defValue) { + if (mRecycled) { + throw new RuntimeException("Cannot make calls to a recycled instance!"); + } + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; final int type = data[index+AssetManager.STYLE_TYPE]; @@ -497,14 +568,18 @@ public class TypedArray { * {@link android.view.ViewGroup}'s layout_width and layout_height * attributes. This is only here for performance reasons; applications * should use {@link #getDimensionPixelSize}. - * + * * @param index Index of the attribute to retrieve. * @param name Textual name of attribute for error reporting. - * - * @return Attribute dimension value multiplied by the appropriate + * + * @return Attribute dimension value multiplied by the appropriate * metric and truncated to integer pixels. */ public int getLayoutDimension(int index, String name) { + if (mRecycled) { + throw new RuntimeException("Cannot make calls to a recycled instance!"); + } + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; final int type = data[index+AssetManager.STYLE_TYPE]; @@ -519,21 +594,25 @@ public class TypedArray { throw new RuntimeException(getPositionDescription() + ": You must supply a " + name + " attribute."); } - + /** * Special version of {@link #getDimensionPixelSize} for retrieving * {@link android.view.ViewGroup}'s layout_width and layout_height * attributes. This is only here for performance reasons; applications * should use {@link #getDimensionPixelSize}. - * + * * @param index Index of the attribute to retrieve. * @param defValue The default value to return if this attribute is not * default or contains the wrong type of data. - * - * @return Attribute dimension value multiplied by the appropriate + * + * @return Attribute dimension value multiplied by the appropriate * metric and truncated to integer pixels. */ public int getLayoutDimension(int index, int defValue) { + if (mRecycled) { + throw new RuntimeException("Cannot make calls to a recycled instance!"); + } + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; final int type = data[index+AssetManager.STYLE_TYPE]; @@ -550,20 +629,24 @@ public class TypedArray { /** * Retrieve a fractional unit attribute at <var>index</var>. - * - * @param index Index of attribute to retrieve. - * @param base The base value of this fraction. In other words, a + * + * @param index Index of attribute to retrieve. + * @param base The base value of this fraction. In other words, a * standard fraction is multiplied by this value. - * @param pbase The parent base value of this fraction. In other + * @param pbase The parent base value of this fraction. In other * words, a parent fraction (nn%p) is multiplied by this * value. * @param defValue Value to return if the attribute is not defined or * not a resource. - * - * @return Attribute fractional value multiplied by the appropriate - * base value, or defValue if not defined. + * + * @return Attribute fractional value multiplied by the appropriate + * base value, or defValue if not defined. */ public float getFraction(int index, int base, int pbase, float defValue) { + if (mRecycled) { + throw new RuntimeException("Cannot make calls to a recycled instance!"); + } + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; final int type = data[index+AssetManager.STYLE_TYPE]; @@ -580,19 +663,23 @@ public class TypedArray { /** * Retrieve the resource identifier for the attribute at - * <var>index</var>. Note that attribute resource as resolved when - * the overall {@link TypedArray} object is retrieved. As a - * result, this function will return the resource identifier of the - * final resource value that was found, <em>not</em> necessarily the - * original resource that was specified by the attribute. - * + * <var>index</var>. Note that attribute resource as resolved when + * the overall {@link TypedArray} object is retrieved. As a + * result, this function will return the resource identifier of the + * final resource value that was found, <em>not</em> necessarily the + * original resource that was specified by the attribute. + * * @param index Index of attribute to retrieve. * @param defValue Value to return if the attribute is not defined or * not a resource. - * + * * @return Attribute resource identifier, or defValue if not defined. */ public int getResourceId(int index, int defValue) { + if (mRecycled) { + throw new RuntimeException("Cannot make calls to a recycled instance!"); + } + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; if (data[index+AssetManager.STYLE_TYPE] != TypedValue.TYPE_NULL) { @@ -615,6 +702,10 @@ public class TypedArray { * @hide */ public int getThemeAttributeId(int index, int defValue) { + if (mRecycled) { + throw new RuntimeException("Cannot make calls to a recycled instance!"); + } + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; if (data[index + AssetManager.STYLE_TYPE] == TypedValue.TYPE_ATTRIBUTE) { @@ -628,12 +719,16 @@ public class TypedArray { * gets the resource ID of the selected attribute, and uses * {@link Resources#getDrawable Resources.getDrawable} of the owning * Resources object to retrieve its Drawable. - * + * * @param index Index of attribute to retrieve. - * + * * @return Drawable for the attribute, or null if not defined. */ public Drawable getDrawable(int index) { + if (mRecycled) { + throw new RuntimeException("Cannot make calls to a recycled instance!"); + } + final TypedValue value = mValue; if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { if (false) { @@ -655,12 +750,16 @@ public class TypedArray { * This gets the resource ID of the selected attribute, and uses * {@link Resources#getTextArray Resources.getTextArray} of the owning * Resources object to retrieve its String[]. - * + * * @param index Index of attribute to retrieve. - * + * * @return CharSequence[] for the attribute, or null if not defined. */ public CharSequence[] getTextArray(int index) { + if (mRecycled) { + throw new RuntimeException("Cannot make calls to a recycled instance!"); + } + final TypedValue value = mValue; if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { if (false) { @@ -679,36 +778,19 @@ public class TypedArray { /** * Retrieve the raw TypedValue for the attribute at <var>index</var>. - * + * * @param index Index of attribute to retrieve. * @param outValue TypedValue object in which to place the attribute's * data. - * - * @return Returns true if the value was retrieved, else false. - */ - public boolean getValue(int index, TypedValue outValue) { - return getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, outValue); - } - - /** - * Determines whether this TypedArray contains an attribute of the specified - * type. * - * @param type Type of data, e.g. {@link TypedValue#TYPE_ATTRIBUTE} - * @return True if the TypedArray contains an attribute of the specified - * type. - * @hide + * @return Returns true if the value was retrieved, else false. */ - public boolean hasType(int type) { - final int[] data = mData; - final int N = getIndexCount(); - for (int i = 0; i < N; i++) { - final int index = getIndex(i) * AssetManager.STYLE_NUM_ENTRIES; - if (data[index + AssetManager.STYLE_TYPE] == type) { - return true; - } + public boolean getValue(int index, TypedValue outValue) { + if (mRecycled) { + throw new RuntimeException("Cannot make calls to a recycled instance!"); } - return false; + + return getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, outValue); } /** @@ -718,36 +800,48 @@ public class TypedArray { * @return Attribute type. */ public int getType(int index) { + if (mRecycled) { + throw new RuntimeException("Cannot make calls to a recycled instance!"); + } + index *= AssetManager.STYLE_NUM_ENTRIES; return mData[index + AssetManager.STYLE_TYPE]; } /** * Determines whether there is an attribute at <var>index</var>. - * + * * @param index Index of attribute to retrieve. - * + * * @return True if the attribute has a value, false otherwise. */ public boolean hasValue(int index) { + if (mRecycled) { + throw new RuntimeException("Cannot make calls to a recycled instance!"); + } + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; final int type = data[index+AssetManager.STYLE_TYPE]; return type != TypedValue.TYPE_NULL; } - + /** - * Retrieve the raw TypedValue for the attribute at <var>index</var> - * and return a temporary object holding its data. This object is only - * valid until the next call on to {@link TypedArray}. - * + * Retrieve the raw TypedValue for the attribute at <var>index</var> + * and return a temporary object holding its data. This object is only + * valid until the next call on to {@link TypedArray}. + * * @param index Index of attribute to retrieve. - * - * @return Returns a TypedValue object if the attribute is defined, + * + * @return Returns a TypedValue object if the attribute is defined, * containing its data; otherwise returns null. (You will not * receive a TypedValue whose type is TYPE_NULL.) */ public TypedValue peekValue(int index) { + if (mRecycled) { + throw new RuntimeException("Cannot make calls to a recycled instance!"); + } + final TypedValue value = mValue; if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { return value; @@ -759,13 +853,23 @@ public class TypedArray { * Returns a message about the parser state suitable for printing error messages. */ public String getPositionDescription() { + if (mRecycled) { + throw new RuntimeException("Cannot make calls to a recycled instance!"); + } + return mXml != null ? mXml.getPositionDescription() : "<internal>"; } /** - * Give back a previously retrieved array, for later re-use. + * Recycle the TypedArray, to be re-used by a later caller. After calling + * this function you must not ever touch the typed array again. */ public void recycle() { + if (mRecycled) { + throw new RuntimeException(toString() + " recycled twice!"); + } + + mRecycled = true; mResources = null; mMetrics = null; mAssets = null; @@ -791,12 +895,15 @@ public class TypedArray { * @hide */ public int[] extractThemeAttrs() { + if (mRecycled) { + throw new RuntimeException("Cannot make calls to a recycled instance!"); + } + int[] attrs = null; - final int N = getIndexCount(); + final int N = length(); for (int i = 0; i < N; i++) { - final int index = getIndex(i); - final int attrId = getThemeAttributeId(index, 0); + final int attrId = getThemeAttributeId(i, 0); if (attrId != 0) { if (attrs == null) { attrs = new int[N]; diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java index 37bead8..ff77580 100644 --- a/core/java/android/hardware/camera2/CameraCharacteristics.java +++ b/core/java/android/hardware/camera2/CameraCharacteristics.java @@ -1093,8 +1093,8 @@ public final class CameraCharacteristics extends CameraMetadata { * * @see CaptureRequest#SENSOR_TEST_PATTERN_MODE */ - public static final Key<Byte> SENSOR_AVAILABLE_TEST_PATTERN_MODES = - new Key<Byte>("android.sensor.availableTestPatternModes", byte.class); + public static final Key<int[]> SENSOR_AVAILABLE_TEST_PATTERN_MODES = + new Key<int[]>("android.sensor.availableTestPatternModes", int[].class); /** * <p>Which face detection modes are available, diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java index 42c8e3d..679310a 100644 --- a/core/java/android/hardware/camera2/CameraMetadata.java +++ b/core/java/android/hardware/camera2/CameraMetadata.java @@ -1002,8 +1002,11 @@ public abstract class CameraMetadata { * controls; the camera device will ignore those settings while * USE_SCENE_MODE is active (except for FACE_PRIORITY * scene mode). Other control entries are still active. - * This setting can only be used if availableSceneModes != - * UNSUPPORTED</p> + * This setting can only be used if scene mode is supported + * (i.e. {@link CameraCharacteristics#CONTROL_AVAILABLE_SCENE_MODES android.control.availableSceneModes} contain some modes + * other than DISABLED).</p> + * + * @see CameraCharacteristics#CONTROL_AVAILABLE_SCENE_MODES * @see CaptureRequest#CONTROL_MODE */ public static final int CONTROL_MODE_USE_SCENE_MODE = 2; diff --git a/core/java/android/hardware/camera2/impl/CameraDevice.java b/core/java/android/hardware/camera2/impl/CameraDevice.java index 2c8a5c2..ecc461e 100644 --- a/core/java/android/hardware/camera2/impl/CameraDevice.java +++ b/core/java/android/hardware/camera2/impl/CameraDevice.java @@ -335,7 +335,9 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice { mRepeatingRequestId = REQUEST_ID_NONE; // Queue for deletion after in-flight requests finish - mRepeatingRequestIdDeletedList.add(requestId); + if (mCaptureListenerMap.get(requestId) != null) { + mRepeatingRequestIdDeletedList.add(requestId); + } try { mRemoteDevice.cancelRequest(requestId); @@ -367,8 +369,6 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice { } mRepeatingRequestId = REQUEST_ID_NONE; - mRepeatingRequestIdDeletedList.clear(); - mCaptureListenerMap.clear(); } } diff --git a/core/java/android/hardware/hdmi/HdmiCec.java b/core/java/android/hardware/hdmi/HdmiCec.java index 38d9de4..8578a32 100644 --- a/core/java/android/hardware/hdmi/HdmiCec.java +++ b/core/java/android/hardware/hdmi/HdmiCec.java @@ -160,6 +160,12 @@ public final class HdmiCec { public static final int MESSAGE_SET_EXTERNAL_TIMER = 0xA2; public static final int MESSAGE_ABORT = 0xFF; + public static final int POWER_STATUS_UNKNOWN = -1; + public static final int POWER_STATUS_ON = 0; + public static final int POWER_STATUS_STANDBY = 1; + public static final int POWER_TRANSIENT_TO_ON = 2; + public static final int POWER_TRANSIENT_TO_STANDBY = 3; + private static final int[] ADDRESS_TO_TYPE = { DEVICE_TV, // ADDR_TV DEVICE_RECORDER, // ADDR_RECORDER_1 diff --git a/core/java/android/hardware/hdmi/HdmiCecClient.java b/core/java/android/hardware/hdmi/HdmiCecClient.java index d7f4a72..1f382e6 100644 --- a/core/java/android/hardware/hdmi/HdmiCecClient.java +++ b/core/java/android/hardware/hdmi/HdmiCecClient.java @@ -110,16 +110,20 @@ public final class HdmiCecClient { } /** - * Send <GiveDevicePowerStatus> message. + * Returns true if the TV or attached display is powered on. + * <p> + * The result of this method is only meaningful on playback devices (where the device + * type is {@link HdmiCec#DEVICE_PLAYBACK}). + * </p> * - * @param address logical address of the device to send the message to, such as - * {@link HdmiCec#ADDR_TV}. + * @return true if TV is on; otherwise false. */ - public void sendGiveDevicePowerStatus(int address) { + public boolean isTvOn() { try { - mService.sendGiveDevicePowerStatus(mBinder, address); + return mService.isTvOn(mBinder); } catch (RemoteException e) { - Log.e(TAG, "sendGiveDevicePowerStatus threw exception ", e); + Log.e(TAG, "isTvOn threw exception ", e); } + return false; } } diff --git a/core/java/android/hardware/hdmi/HdmiCecManager.java b/core/java/android/hardware/hdmi/HdmiCecManager.java index 575785d..10b058c 100644 --- a/core/java/android/hardware/hdmi/HdmiCecManager.java +++ b/core/java/android/hardware/hdmi/HdmiCecManager.java @@ -45,6 +45,9 @@ public final class HdmiCecManager { * @return {@link HdmiCecClient} instance. {@code null} on failure. */ public HdmiCecClient getClient(int type, HdmiCecClient.Listener listener) { + if (mService == null) { + return null; + } try { IBinder b = mService.allocateLogicalDevice(type, getListenerWrapper(listener)); return HdmiCecClient.create(mService, b); diff --git a/core/java/android/hardware/hdmi/IHdmiCecService.aidl b/core/java/android/hardware/hdmi/IHdmiCecService.aidl index 6fefcf8..b5df131 100644 --- a/core/java/android/hardware/hdmi/IHdmiCecService.aidl +++ b/core/java/android/hardware/hdmi/IHdmiCecService.aidl @@ -29,12 +29,11 @@ import android.os.IBinder; interface IHdmiCecService { IBinder allocateLogicalDevice(int type, IHdmiCecListener listener); void removeServiceListener(IBinder b, IHdmiCecListener listener); - void setOsdName(IBinder b, String name); void sendActiveSource(IBinder b); void sendInactiveSource(IBinder b); void sendImageViewOn(IBinder b); void sendTextViewOn(IBinder b); - void sendGiveDevicePowerStatus(IBinder b, int address); + boolean isTvOn(IBinder b); void sendMessage(IBinder b, in HdmiCecMessage message); } diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index 81ad28b..a355d1e 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -647,6 +647,7 @@ public class InputMethodService extends AbstractInputMethodService { getApplicationInfo().targetSdkVersion, android.R.style.Theme_InputMethod, android.R.style.Theme_Holo_InputMethod, + android.R.style.Theme_DeviceDefault_InputMethod, android.R.style.Theme_DeviceDefault_InputMethod); super.setTheme(mTheme); super.onCreate(); diff --git a/core/java/android/net/http/CertificateChainValidator.java b/core/java/android/net/http/CertificateChainValidator.java index a28b5a7..d06355d 100644 --- a/core/java/android/net/http/CertificateChainValidator.java +++ b/core/java/android/net/http/CertificateChainValidator.java @@ -22,6 +22,8 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.lang.reflect.Method; import java.security.GeneralSecurityException; +import java.security.KeyStore; +import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.cert.Certificate; import java.security.cert.CertificateException; @@ -74,13 +76,16 @@ public class CertificateChainValidator { private CertificateChainValidator() { try { TrustManagerFactory tmf = TrustManagerFactory.getInstance("X.509"); + tmf.init((KeyStore) null); for (TrustManager tm : tmf.getTrustManagers()) { if (tm instanceof X509ExtendedTrustManager) { mTrustManager = (X509ExtendedTrustManager) tm; } } } catch (NoSuchAlgorithmException e) { - throw new RuntimeException("X.509 TrustManager factory must be available", e); + throw new RuntimeException("X.509 TrustManagerFactory must be available", e); + } catch (KeyStoreException e) { + throw new RuntimeException("X.509 TrustManagerFactory cannot be initialized", e); } if (mTrustManager == null) { @@ -166,9 +171,13 @@ public class CertificateChainValidator { TrustManagerFactory tmf; try { tmf = TrustManagerFactory.getInstance("X.509"); + tmf.init((KeyStore) null); } catch (NoSuchAlgorithmException e) { Slog.w(TAG, "Couldn't find default X.509 TrustManagerFactory"); return; + } catch (KeyStoreException e) { + Slog.w(TAG, "Couldn't initialize default X.509 TrustManagerFactory", e); + return; } TrustManager[] tms = tmf.getTrustManagers(); diff --git a/core/java/android/net/http/X509TrustManagerExtensions.java b/core/java/android/net/http/X509TrustManagerExtensions.java index a500bcf..830ddce 100644 --- a/core/java/android/net/http/X509TrustManagerExtensions.java +++ b/core/java/android/net/http/X509TrustManagerExtensions.java @@ -56,7 +56,8 @@ public class X509TrustManagerExtensions { if (tm instanceof TrustManagerImpl) { mDelegate = (TrustManagerImpl) tm; } else { - throw new IllegalArgumentException("tm is not a supported type of X509TrustManager"); + throw new IllegalArgumentException("tm is an instance of " + tm.getClass().getName() + + " which is not a supported type of X509TrustManager"); } } diff --git a/core/java/android/net/nsd/NsdManager.java b/core/java/android/net/nsd/NsdManager.java index d8e8e2c..377ed88 100644 --- a/core/java/android/net/nsd/NsdManager.java +++ b/core/java/android/net/nsd/NsdManager.java @@ -211,6 +211,7 @@ public final class NsdManager { private Context mContext; private static final int INVALID_LISTENER_KEY = 0; + private static final int BUSY_LISTENER_KEY = -1; private int mListenerKey = 1; private final SparseArray mListenerMap = new SparseArray(); private final SparseArray<NsdServiceInfo> mServiceMap = new SparseArray<NsdServiceInfo>(); @@ -317,71 +318,74 @@ public final class NsdManager { Log.d(TAG, "Stale key " + message.arg2); return; } - boolean listenerRemove = true; NsdServiceInfo ns = getNsdService(message.arg2); switch (message.what) { case DISCOVER_SERVICES_STARTED: String s = getNsdServiceInfoType((NsdServiceInfo) message.obj); ((DiscoveryListener) listener).onDiscoveryStarted(s); - // Keep listener until stop discovery - listenerRemove = false; break; case DISCOVER_SERVICES_FAILED: + removeListener(message.arg2); ((DiscoveryListener) listener).onStartDiscoveryFailed(getNsdServiceInfoType(ns), message.arg1); break; case SERVICE_FOUND: ((DiscoveryListener) listener).onServiceFound((NsdServiceInfo) message.obj); - // Keep listener until stop discovery - listenerRemove = false; break; case SERVICE_LOST: ((DiscoveryListener) listener).onServiceLost((NsdServiceInfo) message.obj); - // Keep listener until stop discovery - listenerRemove = false; break; case STOP_DISCOVERY_FAILED: + removeListener(message.arg2); ((DiscoveryListener) listener).onStopDiscoveryFailed(getNsdServiceInfoType(ns), message.arg1); break; case STOP_DISCOVERY_SUCCEEDED: + removeListener(message.arg2); ((DiscoveryListener) listener).onDiscoveryStopped(getNsdServiceInfoType(ns)); break; case REGISTER_SERVICE_FAILED: + removeListener(message.arg2); ((RegistrationListener) listener).onRegistrationFailed(ns, message.arg1); break; case REGISTER_SERVICE_SUCCEEDED: ((RegistrationListener) listener).onServiceRegistered( (NsdServiceInfo) message.obj); - // Keep listener until unregister - listenerRemove = false; break; case UNREGISTER_SERVICE_FAILED: + removeListener(message.arg2); ((RegistrationListener) listener).onUnregistrationFailed(ns, message.arg1); break; case UNREGISTER_SERVICE_SUCCEEDED: + removeListener(message.arg2); ((RegistrationListener) listener).onServiceUnregistered(ns); break; case RESOLVE_SERVICE_FAILED: + removeListener(message.arg2); ((ResolveListener) listener).onResolveFailed(ns, message.arg1); break; case RESOLVE_SERVICE_SUCCEEDED: + removeListener(message.arg2); ((ResolveListener) listener).onServiceResolved((NsdServiceInfo) message.obj); break; default: Log.d(TAG, "Ignored " + message); break; } - if (listenerRemove) { - removeListener(message.arg2); - } } } + // if the listener is already in the map, reject it. Otherwise, add it and + // return its key. + private int putListener(Object listener, NsdServiceInfo s) { if (listener == null) return INVALID_LISTENER_KEY; int key; synchronized (mMapLock) { + int valueIndex = mListenerMap.indexOfValue(listener); + if (valueIndex != -1) { + return BUSY_LISTENER_KEY; + } do { key = mListenerKey++; } while (key == INVALID_LISTENER_KEY); @@ -422,7 +426,6 @@ public final class NsdManager { return INVALID_LISTENER_KEY; } - private String getNsdServiceInfoType(NsdServiceInfo s) { if (s == null) return "?"; return s.getServiceType(); @@ -449,14 +452,18 @@ public final class NsdManager { * Register a service to be discovered by other services. * * <p> The function call immediately returns after sending a request to register service - * to the framework. The application is notified of a success to initiate - * discovery through the callback {@link RegistrationListener#onServiceRegistered} or a failure + * to the framework. The application is notified of a successful registration + * through the callback {@link RegistrationListener#onServiceRegistered} or a failure * through {@link RegistrationListener#onRegistrationFailed}. * + * <p> The application should call {@link #unregisterService} when the service + * registration is no longer required, and/or whenever the application is stopped. + * * @param serviceInfo The service being registered * @param protocolType The service discovery protocol * @param listener The listener notifies of a successful registration and is used to * unregister this service through a call on {@link #unregisterService}. Cannot be null. + * Cannot be in use for an active service registration. */ public void registerService(NsdServiceInfo serviceInfo, int protocolType, RegistrationListener listener) { @@ -473,8 +480,11 @@ public final class NsdManager { if (protocolType != PROTOCOL_DNS_SD) { throw new IllegalArgumentException("Unsupported protocol"); } - mAsyncChannel.sendMessage(REGISTER_SERVICE, 0, putListener(listener, serviceInfo), - serviceInfo); + int key = putListener(listener, serviceInfo); + if (key == BUSY_LISTENER_KEY) { + throw new IllegalArgumentException("listener already in use"); + } + mAsyncChannel.sendMessage(REGISTER_SERVICE, 0, key, serviceInfo); } /** @@ -484,7 +494,11 @@ public final class NsdManager { * * @param listener This should be the listener object that was passed to * {@link #registerService}. It identifies the service that should be unregistered - * and notifies of a successful unregistration. + * and notifies of a successful or unsuccessful unregistration via the listener + * callbacks. In API versions 20 and above, the listener object may be used for + * another service registration once the callback has been called. In API versions <= 19, + * there is no entirely reliable way to know when a listener may be re-used, and a new + * listener should be created for each service registration request. */ public void unregisterService(RegistrationListener listener) { int id = getListenerKey(listener); @@ -514,12 +528,16 @@ public final class NsdManager { * <p> Upon failure to start, service discovery is not active and application does * not need to invoke {@link #stopServiceDiscovery} * + * <p> The application should call {@link #stopServiceDiscovery} when discovery of this + * service type is no longer required, and/or whenever the application is paused or + * stopped. + * * @param serviceType The service type being discovered. Examples include "_http._tcp" for * http services or "_ipp._tcp" for printers * @param protocolType The service discovery protocol * @param listener The listener notifies of a successful discovery and is used * to stop discovery on this serviceType through a call on {@link #stopServiceDiscovery}. - * Cannot be null. + * Cannot be null. Cannot be in use for an active service discovery. */ public void discoverServices(String serviceType, int protocolType, DiscoveryListener listener) { if (listener == null) { @@ -535,11 +553,17 @@ public final class NsdManager { NsdServiceInfo s = new NsdServiceInfo(); s.setServiceType(serviceType); - mAsyncChannel.sendMessage(DISCOVER_SERVICES, 0, putListener(listener, s), s); + + int key = putListener(listener, s); + if (key == BUSY_LISTENER_KEY) { + throw new IllegalArgumentException("listener already in use"); + } + + mAsyncChannel.sendMessage(DISCOVER_SERVICES, 0, key, s); } /** - * Stop service discovery initiated with {@link #discoverServices}. An active service + * Stop service discovery initiated with {@link #discoverServices}. An active service * discovery is notified to the application with {@link DiscoveryListener#onDiscoveryStarted} * and it stays active until the application invokes a stop service discovery. A successful * stop is notified to with a call to {@link DiscoveryListener#onDiscoveryStopped}. @@ -548,7 +572,11 @@ public final class NsdManager { * {@link DiscoveryListener#onStopDiscoveryFailed}. * * @param listener This should be the listener object that was passed to {@link #discoverServices}. - * It identifies the discovery that should be stopped and notifies of a successful stop. + * It identifies the discovery that should be stopped and notifies of a successful or + * unsuccessful stop. In API versions 20 and above, the listener object may be used for + * another service discovery once the callback has been called. In API versions <= 19, + * there is no entirely reliable way to know when a listener may be re-used, and a new + * listener should be created for each service discovery request. */ public void stopServiceDiscovery(DiscoveryListener listener) { int id = getListenerKey(listener); @@ -568,6 +596,7 @@ public final class NsdManager { * * @param serviceInfo service to be resolved * @param listener to receive callback upon success or failure. Cannot be null. + * Cannot be in use for an active service resolution. */ public void resolveService(NsdServiceInfo serviceInfo, ResolveListener listener) { if (TextUtils.isEmpty(serviceInfo.getServiceName()) || @@ -577,8 +606,13 @@ public final class NsdManager { if (listener == null) { throw new IllegalArgumentException("listener cannot be null"); } - mAsyncChannel.sendMessage(RESOLVE_SERVICE, 0, putListener(listener, serviceInfo), - serviceInfo); + + int key = putListener(listener, serviceInfo); + + if (key == BUSY_LISTENER_KEY) { + throw new IllegalArgumentException("listener already in use"); + } + mAsyncChannel.sendMessage(RESOLVE_SERVICE, 0, key, serviceInfo); } /** Internal use only @hide */ diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl index d03b0c5..be3c0cc 100644 --- a/core/java/android/os/IPowerManager.aidl +++ b/core/java/android/os/IPowerManager.aidl @@ -23,7 +23,7 @@ import android.os.WorkSource; interface IPowerManager { - // WARNING: The first four methods must remain the first three methods because their + // WARNING: The first five methods must remain the first five methods because their // transaction numbers must not change unless IPowerManager.cpp is also updated. void acquireWakeLock(IBinder lock, int flags, String tag, String packageName, in WorkSource ws, String historyTag); @@ -31,6 +31,7 @@ interface IPowerManager int uidtoblame); void releaseWakeLock(IBinder lock, int flags); void updateWakeLockUids(IBinder lock, in int[] uids); + oneway void powerHint(int hintId, int data); void updateWakeLockWorkSource(IBinder lock, in WorkSource ws, String historyTag); boolean isWakeLockLevelSupported(int level); diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl index 6e6c06d..1192a45 100644 --- a/core/java/android/os/IUserManager.aidl +++ b/core/java/android/os/IUserManager.aidl @@ -28,13 +28,13 @@ import android.graphics.Bitmap; */ interface IUserManager { UserInfo createUser(in String name, int flags); - UserInfo createRelatedUser(in String name, int flags, int relatedUserId); + UserInfo createProfileForUser(in String name, int flags, int userHandle); boolean removeUser(int userHandle); void setUserName(int userHandle, String name); void setUserIcon(int userHandle, in Bitmap icon); Bitmap getUserIcon(int userHandle); List<UserInfo> getUsers(boolean excludeDying); - List<UserInfo> getRelatedUsers(int userHandle); + List<UserInfo> getProfiles(int userHandle); UserInfo getUserInfo(int userHandle); boolean isRestricted(); void setGuestEnabled(boolean enable); diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java index 057f516..3241693 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -16,10 +16,11 @@ package android.os; -import android.net.LocalSocketAddress; import android.net.LocalSocket; +import android.net.LocalSocketAddress; import android.util.Log; -import dalvik.system.Zygote; + +import com.android.internal.os.Zygote; import java.io.BufferedWriter; import java.io.DataInputStream; diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 1ec5cd5..520a08c 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -410,20 +410,29 @@ public class UserManager { } /** - * Creates a user with the specified name and options. + * Renamed, just present to avoid multi project commit. + * TODO delete. + * @hide + */ + public UserInfo createRelatedUser(String name, int flags, int relatedUserId) { + return createProfileForUser(name, flags, relatedUserId); + } + + /** + * Creates a user with the specified name and options as a profile of another user. * Requires {@link android.Manifest.permission#MANAGE_USERS} permission. * * @param name the user's name * @param flags flags that identify the type of user and other properties. * @see UserInfo - * @param relatedUserId new user will be related to this user id. + * @param userHandle new user will be a profile of this use. * * @return the UserInfo object for the created user, or null if the user could not be created. * @hide */ - public UserInfo createRelatedUser(String name, int flags, int relatedUserId) { + public UserInfo createProfileForUser(String name, int flags, int userHandle) { try { - return mService.createRelatedUser(name, flags, relatedUserId); + return mService.createProfileForUser(name, flags, userHandle); } catch (RemoteException re) { Log.w(TAG, "Could not create a user", re); return null; @@ -454,15 +463,26 @@ public class UserManager { } /** - * Returns information for all users related to userId - * Requires {@link android.Manifest.permission#MANAGE_USERS} permission. - * @param userHandle users related to this user id will be returned. - * @return the list of related users. + * Renaming, left to avoid multi project commit. + * TODO Delete. * @hide */ public List<UserInfo> getRelatedUsers(int userHandle) { + return getProfiles(userHandle); + } + + /** + * Returns list of the profiles of userHandle including + * userHandle itself. + * + * Requires {@link android.Manifest.permission#MANAGE_USERS} permission. + * @param userHandle profiles of this user will be returned. + * @return the list of profiles. + * @hide + */ + public List<UserInfo> getProfiles(int userHandle) { try { - return mService.getRelatedUsers(userHandle); + return mService.getProfiles(userHandle); } catch (RemoteException re) { Log.w(TAG, "Could not get user list", re); return null; diff --git a/core/java/android/os/storage/IMountService.java b/core/java/android/os/storage/IMountService.java index 4180860..2ef5b66 100644 --- a/core/java/android/os/storage/IMountService.java +++ b/core/java/android/os/storage/IMountService.java @@ -713,15 +713,46 @@ public interface IMountService extends IInterface { public void clearPassword() throws RemoteException { Parcel _data = Parcel.obtain(); Parcel _reply = Parcel.obtain(); + try { + _data.writeInterfaceToken(DESCRIPTOR); + mRemote.transact(Stub.TRANSACTION_clearPassword, _data, _reply, IBinder.FLAG_ONEWAY); + _reply.readException(); + } finally { + _reply.recycle(); + _data.recycle(); + } + } + + public void setField(String field, String data) throws RemoteException { + Parcel _data = Parcel.obtain(); + Parcel _reply = Parcel.obtain(); + try { + _data.writeInterfaceToken(DESCRIPTOR); + _data.writeString(field); + _data.writeString(data); + mRemote.transact(Stub.TRANSACTION_setField, _data, _reply, IBinder.FLAG_ONEWAY); + _reply.readException(); + } finally { + _reply.recycle(); + _data.recycle(); + } + } + + public String getField(String field) throws RemoteException { + Parcel _data = Parcel.obtain(); + Parcel _reply = Parcel.obtain(); String _result; try { _data.writeInterfaceToken(DESCRIPTOR); - mRemote.transact(Stub.TRANSACTION_clearPassword, _data, _reply, 0); + _data.writeString(field); + mRemote.transact(Stub.TRANSACTION_getField, _data, _reply, 0); _reply.readException(); + _result = _reply.readString(); } finally { _reply.recycle(); _data.recycle(); } + return _result; } public StorageVolume[] getVolumeList() throws RemoteException { @@ -882,6 +913,10 @@ public interface IMountService extends IInterface { static final int TRANSACTION_clearPassword = IBinder.FIRST_CALL_TRANSACTION + 37; + static final int TRANSACTION_setField = IBinder.FIRST_CALL_TRANSACTION + 38; + + static final int TRANSACTION_getField = IBinder.FIRST_CALL_TRANSACTION + 39; + /** * Cast an IBinder object into an IMountService interface, generating a * proxy if needed. @@ -1255,6 +1290,22 @@ public interface IMountService extends IInterface { reply.writeNoException(); return true; } + case TRANSACTION_setField: { + data.enforceInterface(DESCRIPTOR); + String field = data.readString(); + String contents = data.readString(); + setField(field, contents); + reply.writeNoException(); + return true; + } + case TRANSACTION_getField: { + data.enforceInterface(DESCRIPTOR); + String field = data.readString(); + String contents = getField(field); + reply.writeNoException(); + reply.writeString(contents); + return true; + } } return super.onTransact(code, data, reply, flags); } @@ -1504,4 +1555,18 @@ public interface IMountService extends IInterface { * Securely clear password from vold */ public void clearPassword() throws RemoteException; + + /** + * Set a field in the crypto header. + * @param field field to set + * @param contents contents to set in field + */ + public void setField(String field, String contents) throws RemoteException; + + /** + * Gets a field from the crypto header. + * @param field field to get + * @return contents of field + */ + public String getField(String field) throws RemoteException; } diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java index bd576af..ae24968 100644 --- a/core/java/android/provider/MediaStore.java +++ b/core/java/android/provider/MediaStore.java @@ -173,6 +173,10 @@ public final class MediaStore { */ public static final String EXTRA_MEDIA_GENRE = "android.intent.extra.genre"; /** + * The name of the Intent-extra used to define the playlist. + */ + public static final String EXTRA_MEDIA_PLAYLIST = "android.intent.extra.playlist"; + /** * The name of the Intent-extra used to define the radio channel. */ public static final String EXTRA_MEDIA_RADIO_CHANNEL = "android.intent.extra.radio_channel"; diff --git a/core/java/android/provider/SearchIndexablesContract.java b/core/java/android/provider/SearchIndexablesContract.java index b8635b8..05f3a1c 100644 --- a/core/java/android/provider/SearchIndexablesContract.java +++ b/core/java/android/provider/SearchIndexablesContract.java @@ -73,7 +73,8 @@ public class SearchIndexablesContract { public static final String[] INDEXABLES_RAW_COLUMNS = new String[] { RawData.COLUMN_RANK, RawData.COLUMN_TITLE, - RawData.COLUMN_SUMMARY, + RawData.COLUMN_SUMMARY_ON, + RawData.COLUMN_SUMMARY_OFF, RawData.COLUMN_KEYWORDS, RawData.COLUMN_SCREEN_TITLE, RawData.COLUMN_CLASS_NAME, @@ -123,9 +124,14 @@ public class SearchIndexablesContract { public static final String COLUMN_TITLE = "title"; /** - * Summary's raw data. + * Summary's raw data when the data is "ON". */ - public static final String COLUMN_SUMMARY = "summary"; + public static final String COLUMN_SUMMARY_ON = "summaryOn"; + + /** + * Summary's raw data when the data is "OFF". + */ + public static final String COLUMN_SUMMARY_OFF = "summaryOff"; /** * Keywords' raw data. diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 7777334..7062933 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -6098,21 +6098,18 @@ public final class Settings { "lock_screen_show_notifications"; /** - * Defines global zen mode. One of ZEN_MODE_OFF, ZEN_MODE_LIMITED, ZEN_MODE_FULL. + * Defines global zen mode. ZEN_MODE_OFF or ZEN_MODE_ON. * * @hide */ public static final String ZEN_MODE = "zen_mode"; /** @hide */ public static final int ZEN_MODE_OFF = 0; - /** @hide */ public static final int ZEN_MODE_LIMITED = 1; - /** @hide */ public static final int ZEN_MODE_FULL = 2; + /** @hide */ public static final int ZEN_MODE_ON = 1; /** @hide */ public static String zenModeToString(int mode) { if (mode == ZEN_MODE_OFF) return "ZEN_MODE_OFF"; - if (mode == ZEN_MODE_LIMITED) return "ZEN_MODE_LIMITED"; - if (mode == ZEN_MODE_FULL) return "ZEN_MODE_FULL"; - throw new IllegalArgumentException("Invalid zen mode: " + mode); + return "ZEN_MODE_ON"; } /** diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java index 7647c22..de9eeff 100644 --- a/core/java/android/service/dreams/DreamService.java +++ b/core/java/android/service/dreams/DreamService.java @@ -153,11 +153,11 @@ public class DreamService extends Service implements Window.Callback { private final Handler mHandler = new Handler(); private IBinder mWindowToken; private Window mWindow; - private WindowManager mWindowManager; - private boolean mInteractive = false; + private boolean mInteractive; private boolean mLowProfile = true; - private boolean mFullscreen = false; + private boolean mFullscreen; private boolean mScreenBright = true; + private boolean mStarted; private boolean mFinished; private boolean mCanDoze; private boolean mDozing; @@ -340,7 +340,7 @@ public class DreamService extends Service implements Window.Callback { * @return The current window manager, or null if the dream is not started. */ public WindowManager getWindowManager() { - return mWindowManager; + return mWindow != null ? mWindow.getWindowManager() : null; } /** @@ -623,7 +623,7 @@ public class DreamService extends Service implements Window.Callback { * @hide experimental */ public DozeHardware getDozeHardware() { - if (mCanDoze && mDozeHardware == null) { + if (mCanDoze && mDozeHardware == null && mWindowToken != null) { try { IDozeHardware hardware = mSandman.getDozeHardware(mWindowToken); if (hardware != null) { @@ -701,24 +701,25 @@ public class DreamService extends Service implements Window.Callback { * Must run on mHandler. */ private final void detach() { - if (mWindow == null) { - // already detached! - return; + if (mStarted) { + if (mDebug) Slog.v(TAG, "detach(): Calling onDreamingStopped()"); + mStarted = false; + onDreamingStopped(); } - if (mDebug) Slog.v(TAG, "detach(): Calling onDreamingStopped()"); - onDreamingStopped(); - - if (mDebug) Slog.v(TAG, "detach(): Removing window from window manager"); - - // force our window to be removed synchronously - mWindowManager.removeViewImmediate(mWindow.getDecorView()); - // the following will print a log message if it finds any other leaked windows - WindowManagerGlobal.getInstance().closeAll(mWindowToken, - this.getClass().getName(), "Dream"); + if (mWindow != null) { + // force our window to be removed synchronously + if (mDebug) Slog.v(TAG, "detach(): Removing window from window manager"); + mWindow.getWindowManager().removeViewImmediate(mWindow.getDecorView()); + mWindow = null; + } - mWindow = null; - mWindowToken = null; + if (mWindowToken != null) { + // the following will print a log message if it finds any other leaked windows + WindowManagerGlobal.getInstance().closeAll(mWindowToken, + this.getClass().getName(), "Dream"); + mWindowToken = null; + } } /** @@ -746,12 +747,13 @@ public class DreamService extends Service implements Window.Callback { if (mDebug) Slog.v(TAG, "Attached on thread " + Thread.currentThread().getId()); mWindowToken = windowToken; + mCanDoze = canDoze; + mWindow = PolicyManager.makeNewWindow(this); mWindow.setCallback(this); mWindow.requestFeature(Window.FEATURE_NO_TITLE); mWindow.setBackgroundDrawable(new ColorDrawable(0xFF000000)); mWindow.setFormat(PixelFormat.OPAQUE); - mCanDoze = canDoze; if (mDebug) Slog.v(TAG, String.format("Attaching window token: %s to window of type %s", windowToken, WindowManager.LayoutParams.TYPE_DREAM)); @@ -769,26 +771,28 @@ public class DreamService extends Service implements Window.Callback { | (mScreenBright ? WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON : 0) ); mWindow.setAttributes(lp); - - if (mDebug) Slog.v(TAG, "Created and attached window: " + mWindow); - mWindow.setWindowManager(null, windowToken, "dream", true); - mWindowManager = mWindow.getWindowManager(); - if (mDebug) Slog.v(TAG, "Window added on thread " + Thread.currentThread().getId()); applySystemUiVisibilityFlags( (mLowProfile ? View.SYSTEM_UI_FLAG_LOW_PROFILE : 0), View.SYSTEM_UI_FLAG_LOW_PROFILE); - getWindowManager().addView(mWindow.getDecorView(), mWindow.getAttributes()); + + try { + getWindowManager().addView(mWindow.getDecorView(), mWindow.getAttributes()); + } catch (WindowManager.BadTokenException ex) { + // This can happen because the dream manager service will remove the token + // immediately without necessarily waiting for the dream to start. + // We should receive a finish message soon. + Slog.i(TAG, "attach() called after window token already removed, dream will " + + "finish soon"); + mWindow = null; + return; + } // start it up - mHandler.post(new Runnable() { - @Override - public void run() { - if (mDebug) Slog.v(TAG, "Calling onDreamingStarted()"); - onDreamingStarted(); - } - }); + if (mDebug) Slog.v(TAG, "Calling onDreamingStarted()"); + mStarted = true; + onDreamingStarted(); } private void safelyFinish() { @@ -831,7 +835,7 @@ public class DreamService extends Service implements Window.Callback { WindowManager.LayoutParams lp = mWindow.getAttributes(); lp.flags = applyFlags(lp.flags, flags, mask); mWindow.setAttributes(lp); - mWindowManager.updateViewLayout(mWindow.getDecorView(), lp); + mWindow.getWindowManager().updateViewLayout(mWindow.getDecorView(), lp); } } diff --git a/core/java/android/service/notification/INotificationListener.aidl b/core/java/android/service/notification/INotificationListener.aidl index 425fdc1..d4b29d8 100644 --- a/core/java/android/service/notification/INotificationListener.aidl +++ b/core/java/android/service/notification/INotificationListener.aidl @@ -21,6 +21,7 @@ import android.service.notification.StatusBarNotification; /** @hide */ oneway interface INotificationListener { + void onListenerConnected(in String[] notificationKeys); void onNotificationPosted(in StatusBarNotification notification); void onNotificationRemoved(in StatusBarNotification notification); }
\ No newline at end of file diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java index cf862b8..050e1a0 100644 --- a/core/java/android/service/notification/NotificationListenerService.java +++ b/core/java/android/service/notification/NotificationListenerService.java @@ -83,6 +83,17 @@ public abstract class NotificationListenerService extends Service { */ public abstract void onNotificationRemoved(StatusBarNotification sbn); + /** + * Implement this method to learn about when the listener is enabled and connected to + * the notification manager. You are safe to call {@link #getActiveNotifications(String[]) + * at this time. + * + * @param notificationKeys The notification keys for all currently posted notifications. + */ + public void onListenerConnected(String[] notificationKeys) { + // optional + } + private final INotificationManager getNotificationInterface() { if (mNoMan == null) { mNoMan = INotificationManager.Stub.asInterface( @@ -132,9 +143,23 @@ public abstract class NotificationListenerService extends Service { * {@see #cancelNotification(String, String, int)} */ public final void cancelAllNotifications() { + cancelNotifications(null /*all*/); + } + + /** + * Inform the notification manager about dismissal of specific notifications. + * <p> + * Use this if your listener has a user interface that allows the user to dismiss + * multiple notifications at once. + * + * @param keys Notifications to dismiss, or {@code null} to dismiss all. + * + * {@see #cancelNotification(String, String, int)} + */ + public final void cancelNotifications(String[] keys) { if (!isBound()) return; try { - getNotificationInterface().cancelAllNotificationsFromListener(mWrapper); + getNotificationInterface().cancelNotificationsFromListener(mWrapper, keys); } catch (android.os.RemoteException ex) { Log.v(TAG, "Unable to contact notification manager", ex); } @@ -142,14 +167,43 @@ public abstract class NotificationListenerService extends Service { /** * Request the list of outstanding notifications (that is, those that are visible to the - * current user). Useful when starting up and you don't know what's already been posted. + * current user). Useful when you don't know what's already been posted. * * @return An array of active notifications. */ public StatusBarNotification[] getActiveNotifications() { + return getActiveNotifications(null /*all*/); + } + + /** + * Request the list of outstanding notifications (that is, those that are visible to the + * current user). Useful when you don't know what's already been posted. + * + * @param keys A specific list of notification keys, or {@code null} for all. + * @return An array of active notifications. + */ + public StatusBarNotification[] getActiveNotifications(String[] keys) { + if (!isBound()) return null; + try { + return getNotificationInterface().getActiveNotificationsFromListener(mWrapper, keys); + } catch (android.os.RemoteException ex) { + Log.v(TAG, "Unable to contact notification manager", ex); + } + return null; + } + + /** + * Request the list of outstanding notification keys(that is, those that are visible to the + * current user). You can use the notification keys for subsequent retrieval via + * {@link #getActiveNotifications(String[]) or dismissal via + * {@link #cancelNotifications(String[]). + * + * @return An array of active notification keys. + */ + public String[] getActiveNotificationKeys() { if (!isBound()) return null; try { - return getNotificationInterface().getActiveNotificationsFromListener(mWrapper); + return getNotificationInterface().getActiveNotificationKeysFromListener(mWrapper); } catch (android.os.RemoteException ex) { Log.v(TAG, "Unable to contact notification manager", ex); } @@ -189,5 +243,13 @@ public abstract class NotificationListenerService extends Service { Log.w(TAG, "Error running onNotificationRemoved", t); } } + @Override + public void onListenerConnected(String[] notificationKeys) { + try { + NotificationListenerService.this.onListenerConnected(notificationKeys); + } catch (Throwable t) { + Log.w(TAG, "Error running onListenerConnected", t); + } + } } } diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java index b5b9e14..7f15ab8 100644 --- a/core/java/android/service/notification/StatusBarNotification.java +++ b/core/java/android/service/notification/StatusBarNotification.java @@ -29,6 +29,7 @@ public class StatusBarNotification implements Parcelable { private final String pkg; private final int id; private final String tag; + private final String key; private final int uid; private final String basePkg; @@ -68,8 +69,8 @@ public class StatusBarNotification implements Parcelable { this.notification = notification; this.user = user; this.notification.setUser(user); - this.postTime = postTime; + this.key = key(); } public StatusBarNotification(Parcel in) { @@ -88,6 +89,11 @@ public class StatusBarNotification implements Parcelable { this.user = UserHandle.readFromParcel(in); this.notification.setUser(this.user); this.postTime = in.readLong(); + this.key = key(); + } + + private String key() { + return pkg + '|' + basePkg + '|' + id + '|' + tag + '|' + uid; } public void writeToParcel(Parcel out, int flags) { @@ -148,9 +154,9 @@ public class StatusBarNotification implements Parcelable { @Override public String toString() { return String.format( - "StatusBarNotification(pkg=%s user=%s id=%d tag=%s score=%d: %s)", + "StatusBarNotification(pkg=%s user=%s id=%d tag=%s score=%d key=%s: %s)", this.pkg, this.user, this.id, this.tag, - this.score, this.notification); + this.score, this.key, this.notification); } /** Convenience method to check the notification's flags for @@ -230,4 +236,11 @@ public class StatusBarNotification implements Parcelable { public int getScore() { return score; } + + /** + * A unique instance key for this notification record. + */ + public String getKey() { + return key; + } } diff --git a/core/java/android/service/trust/ITrustAgentService.aidl b/core/java/android/service/trust/ITrustAgentService.aidl new file mode 100644 index 0000000..863a249 --- /dev/null +++ b/core/java/android/service/trust/ITrustAgentService.aidl @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.service.trust; + +import android.os.Bundle; +import android.service.trust.ITrustAgentServiceCallback; + +/** + * Communication channel from TrustManagerService to the TrustAgent. + * @hide + */ +oneway interface ITrustAgentService { + void onUnlockAttempt(boolean successful); + void setCallback(ITrustAgentServiceCallback callback); +} diff --git a/core/java/android/view/DisplayList.java b/core/java/android/service/trust/ITrustAgentServiceCallback.aidl index ed52803..c346771 100644 --- a/core/java/android/view/DisplayList.java +++ b/core/java/android/service/trust/ITrustAgentServiceCallback.aidl @@ -13,13 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package android.service.trust; -package android.view; +import android.os.Bundle; +import android.os.UserHandle; -/** TODO: Remove once frameworks/webview is updated - * @hide +/** + * Communication channel from the TrustAgentService back to TrustManagerService. + * @hide */ -public class DisplayList { - /** @hide */ - public static final int STATUS_DONE = 0x0; +oneway interface ITrustAgentServiceCallback { + void enableTrust(String message, long durationMs, boolean initiatedByUser); + void revokeTrust(); } diff --git a/core/java/android/service/trust/TrustAgentService.java b/core/java/android/service/trust/TrustAgentService.java new file mode 100644 index 0000000..d5ce429 --- /dev/null +++ b/core/java/android/service/trust/TrustAgentService.java @@ -0,0 +1,148 @@ +/** + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.trust; + +import android.annotation.SdkConstant; +import android.app.Service; +import android.content.Intent; +import android.os.Handler; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Slog; + +/** + * A service that notifies the system about whether it believes the environment of the device + * to be trusted. + * + * <p>To extend this class, you must declare the service in your manifest file with + * the {@link android.Manifest.permission#BIND_TRUST_AGENT_SERVICE} permission + * and include an intent filter with the {@link #SERVICE_INTERFACE} action. For example:</p> + * <pre> + * <service android:name=".TrustAgent" + * android:label="@string/service_name" + * android:permission="android.permission.BIND_TRUST_AGENT_SERVICE"> + * <intent-filter> + * <action android:name="android.service.trust.TrustAgentService" /> + * </intent-filter> + * <meta-data android:name="android.service.trust.trustagent" + * android:value="@xml/trust_agent" /> + * </service></pre> + * + * <p>The associated meta-data file can specify an activity that is accessible through Settings + * and should allow configuring the trust agent, as defined in + * {@link android.R.styleable#TrustAgent}. For example:</p> + * + * <pre> + * <trust_agent xmlns:android="http://schemas.android.com/apk/res/android" + * android:settingsActivity=".TrustAgentSettings" /></pre> + */ +public class TrustAgentService extends Service { + private final String TAG = TrustAgentService.class.getSimpleName() + + "[" + getClass().getSimpleName() + "]"; + + /** + * The {@link Intent} that must be declared as handled by the service. + */ + @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION) + public static final String SERVICE_INTERFACE + = "android.service.trust.TrustAgentService"; + + /** + * The name of the {@code meta-data} tag pointing to additional configuration of the trust + * agent. + */ + public static final String TRUST_AGENT_META_DATA = "android.service.trust.trustagent"; + + private static final int MSG_UNLOCK_ATTEMPT = 1; + + private static final boolean DEBUG = false; + + private ITrustAgentServiceCallback mCallback; + + private Handler mHandler = new Handler() { + public void handleMessage(android.os.Message msg) { + switch (msg.what) { + case MSG_UNLOCK_ATTEMPT: + onUnlockAttempt(msg.arg1 != 0); + break; + } + }; + }; + + /** + * Called when the user attempted to authenticate on the device. + * + * @param successful true if the attempt succeeded + */ + protected void onUnlockAttempt(boolean successful) { + } + + private void onError(String msg) { + Slog.v(TAG, "Remote exception while " + msg); + } + + /** + * Call to enable trust on the device. + * + * @param message describes why the device is trusted, e.g. "Trusted by location". + * @param durationMs amount of time in milliseconds to keep the device in a trusted state. Trust + * for this agent will automatically be revoked when the timeout expires. + * @param initiatedByUser indicates that the user has explicitly initiated an action that proves + * the user is about to use the device. + */ + protected final void enableTrust(String message, long durationMs, boolean initiatedByUser) { + if (mCallback != null) { + try { + mCallback.enableTrust(message, durationMs, initiatedByUser); + } catch (RemoteException e) { + onError("calling enableTrust()"); + } + } + } + + /** + * Call to revoke trust on the device. + */ + protected final void revokeTrust() { + if (mCallback != null) { + try { + mCallback.revokeTrust(); + } catch (RemoteException e) { + onError("calling revokeTrust()"); + } + } + } + + @Override + public final IBinder onBind(Intent intent) { + if (DEBUG) Slog.v(TAG, "onBind() intent = " + intent); + return new TrustAgentServiceWrapper(); + } + + private final class TrustAgentServiceWrapper extends ITrustAgentService.Stub { + @Override + public void onUnlockAttempt(boolean successful) { + mHandler.obtainMessage(MSG_UNLOCK_ATTEMPT, successful ? 1 : 0, 0) + .sendToTarget(); + } + + public void setCallback(ITrustAgentServiceCallback callback) { + mCallback = callback; + } + } + +} diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java index 06935ae..77ef1da 100644 --- a/core/java/android/text/DynamicLayout.java +++ b/core/java/android/text/DynamicLayout.java @@ -21,6 +21,7 @@ import android.text.style.UpdateLayout; import android.text.style.WrapTogetherSpan; import com.android.internal.util.ArrayUtils; +import com.android.internal.util.GrowingArrayUtils; import java.lang.ref.WeakReference; @@ -401,7 +402,7 @@ public class DynamicLayout extends Layout if (mBlockEndLines == null) { // Initial creation of the array, no test on previous block ending line - mBlockEndLines = new int[ArrayUtils.idealIntArraySize(1)]; + mBlockEndLines = ArrayUtils.newUnpaddedIntArray(1); mBlockEndLines[mNumberOfBlocks] = line; mNumberOfBlocks++; return; @@ -409,13 +410,7 @@ public class DynamicLayout extends Layout final int previousBlockEndLine = mBlockEndLines[mNumberOfBlocks - 1]; if (line > previousBlockEndLine) { - if (mNumberOfBlocks == mBlockEndLines.length) { - // Grow the array if needed - int[] blockEndLines = new int[ArrayUtils.idealIntArraySize(mNumberOfBlocks + 1)]; - System.arraycopy(mBlockEndLines, 0, blockEndLines, 0, mNumberOfBlocks); - mBlockEndLines = blockEndLines; - } - mBlockEndLines[mNumberOfBlocks] = line; + mBlockEndLines = GrowingArrayUtils.append(mBlockEndLines, mNumberOfBlocks, line); mNumberOfBlocks++; } } @@ -483,9 +478,9 @@ public class DynamicLayout extends Layout } if (newNumberOfBlocks > mBlockEndLines.length) { - final int newSize = ArrayUtils.idealIntArraySize(newNumberOfBlocks); - int[] blockEndLines = new int[newSize]; - int[] blockIndices = new int[newSize]; + int[] blockEndLines = ArrayUtils.newUnpaddedIntArray( + Math.max(mBlockEndLines.length * 2, newNumberOfBlocks)); + int[] blockIndices = new int[blockEndLines.length]; System.arraycopy(mBlockEndLines, 0, blockEndLines, 0, firstBlock); System.arraycopy(mBlockIndices, 0, blockIndices, 0, firstBlock); System.arraycopy(mBlockEndLines, lastBlock + 1, diff --git a/core/java/android/text/Html.java b/core/java/android/text/Html.java index c80321c..2fcc597 100644 --- a/core/java/android/text/Html.java +++ b/core/java/android/text/Html.java @@ -211,7 +211,7 @@ public class Html { private static String getOpenParaTagWithDirection(Spanned text, int start, int end) { final int len = end - start; - final byte[] levels = new byte[ArrayUtils.idealByteArraySize(len)]; + final byte[] levels = ArrayUtils.newUnpaddedByteArray(len); final char[] buffer = TextUtils.obtain(len); TextUtils.getChars(text, start, end, buffer, 0); diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java index 9dfd383..4bfcaff 100644 --- a/core/java/android/text/Layout.java +++ b/core/java/android/text/Layout.java @@ -31,6 +31,7 @@ import android.text.style.ReplacementSpan; import android.text.style.TabStopSpan; import com.android.internal.util.ArrayUtils; +import com.android.internal.util.GrowingArrayUtils; import java.util.Arrays; @@ -403,14 +404,9 @@ public abstract class Layout { // construction if (mLineBackgroundSpans.spanStarts[j] >= end || mLineBackgroundSpans.spanEnds[j] <= start) continue; - if (spansLength == spans.length) { - // The spans array needs to be expanded - int newSize = ArrayUtils.idealObjectArraySize(2 * spansLength); - ParagraphStyle[] newSpans = new ParagraphStyle[newSize]; - System.arraycopy(spans, 0, newSpans, 0, spansLength); - spans = newSpans; - } - spans[spansLength++] = mLineBackgroundSpans.spans[j]; + spans = GrowingArrayUtils.append( + spans, spansLength, mLineBackgroundSpans.spans[j]); + spansLength++; } } } diff --git a/core/java/android/text/MeasuredText.java b/core/java/android/text/MeasuredText.java index 101d6a2..f8e3c83 100644 --- a/core/java/android/text/MeasuredText.java +++ b/core/java/android/text/MeasuredText.java @@ -98,10 +98,10 @@ class MeasuredText { mPos = 0; if (mWidths == null || mWidths.length < len) { - mWidths = new float[ArrayUtils.idealFloatArraySize(len)]; + mWidths = ArrayUtils.newUnpaddedFloatArray(len); } if (mChars == null || mChars.length < len) { - mChars = new char[ArrayUtils.idealCharArraySize(len)]; + mChars = ArrayUtils.newUnpaddedCharArray(len); } TextUtils.getChars(text, start, end, mChars, 0); @@ -130,7 +130,7 @@ class MeasuredText { mEasy = true; } else { if (mLevels == null || mLevels.length < len) { - mLevels = new byte[ArrayUtils.idealByteArraySize(len)]; + mLevels = ArrayUtils.newUnpaddedByteArray(len); } int bidiRequest; if (textDir == TextDirectionHeuristics.LTR) { diff --git a/core/java/android/text/PackedIntVector.java b/core/java/android/text/PackedIntVector.java index d87f600..546ab44 100644 --- a/core/java/android/text/PackedIntVector.java +++ b/core/java/android/text/PackedIntVector.java @@ -17,6 +17,7 @@ package android.text; import com.android.internal.util.ArrayUtils; +import com.android.internal.util.GrowingArrayUtils; /** @@ -252,9 +253,9 @@ class PackedIntVector { */ private final void growBuffer() { final int columns = mColumns; - int newsize = size() + 1; - newsize = ArrayUtils.idealIntArraySize(newsize * columns) / columns; - int[] newvalues = new int[newsize * columns]; + int[] newvalues = ArrayUtils.newUnpaddedIntArray( + GrowingArrayUtils.growSize(size()) * columns); + int newsize = newvalues.length / columns; final int[] valuegap = mValueGap; final int rowgapstart = mRowGapStart; diff --git a/core/java/android/text/PackedObjectVector.java b/core/java/android/text/PackedObjectVector.java index a29df09..b777e16 100644 --- a/core/java/android/text/PackedObjectVector.java +++ b/core/java/android/text/PackedObjectVector.java @@ -17,6 +17,9 @@ package android.text; import com.android.internal.util.ArrayUtils; +import com.android.internal.util.GrowingArrayUtils; + +import libcore.util.EmptyArray; class PackedObjectVector<E> { @@ -32,12 +35,11 @@ class PackedObjectVector<E> PackedObjectVector(int columns) { mColumns = columns; - mRows = ArrayUtils.idealIntArraySize(0) / mColumns; + mValues = EmptyArray.OBJECT; + mRows = 0; mRowGapStart = 0; mRowGapLength = mRows; - - mValues = new Object[mRows * mColumns]; } public E @@ -109,10 +111,9 @@ class PackedObjectVector<E> private void growBuffer() { - int newsize = size() + 1; - newsize = ArrayUtils.idealIntArraySize(newsize * mColumns) / mColumns; - Object[] newvalues = new Object[newsize * mColumns]; - + Object[] newvalues = ArrayUtils.newUnpaddedObjectArray( + GrowingArrayUtils.growSize(size()) * mColumns); + int newsize = newvalues.length / mColumns; int after = mRows - (mRowGapStart + mRowGapLength); System.arraycopy(mValues, 0, newvalues, 0, mColumns * mRowGapStart); diff --git a/core/java/android/text/SpannableStringBuilder.java b/core/java/android/text/SpannableStringBuilder.java index b55cd6a..f440853 100644 --- a/core/java/android/text/SpannableStringBuilder.java +++ b/core/java/android/text/SpannableStringBuilder.java @@ -21,6 +21,9 @@ import android.graphics.Paint; import android.util.Log; import com.android.internal.util.ArrayUtils; +import com.android.internal.util.GrowingArrayUtils; + +import libcore.util.EmptyArray; import java.lang.reflect.Array; @@ -54,19 +57,17 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable if (srclen < 0) throw new StringIndexOutOfBoundsException(); - int len = ArrayUtils.idealCharArraySize(srclen + 1); - mText = new char[len]; + mText = ArrayUtils.newUnpaddedCharArray(GrowingArrayUtils.growSize(srclen)); mGapStart = srclen; - mGapLength = len - srclen; + mGapLength = mText.length - srclen; TextUtils.getChars(text, start, end, mText, 0); mSpanCount = 0; - int alloc = ArrayUtils.idealIntArraySize(0); - mSpans = new Object[alloc]; - mSpanStarts = new int[alloc]; - mSpanEnds = new int[alloc]; - mSpanFlags = new int[alloc]; + mSpans = EmptyArray.OBJECT; + mSpanStarts = EmptyArray.INT; + mSpanEnds = EmptyArray.INT; + mSpanFlags = EmptyArray.INT; if (text instanceof Spanned) { Spanned sp = (Spanned) text; @@ -130,12 +131,14 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable private void resizeFor(int size) { final int oldLength = mText.length; - final int newLength = ArrayUtils.idealCharArraySize(size + 1); - final int delta = newLength - oldLength; - if (delta == 0) return; + if (size + 1 <= oldLength) { + return; + } - char[] newText = new char[newLength]; + char[] newText = ArrayUtils.newUnpaddedCharArray(GrowingArrayUtils.growSize(size)); System.arraycopy(mText, 0, newText, 0, mGapStart); + final int newLength = newText.length; + final int delta = newLength - oldLength; final int after = oldLength - (mGapStart + mGapLength); System.arraycopy(mText, oldLength - after, newText, newLength - after, after); mText = newText; @@ -679,28 +682,10 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable } } - if (mSpanCount + 1 >= mSpans.length) { - int newsize = ArrayUtils.idealIntArraySize(mSpanCount + 1); - Object[] newspans = new Object[newsize]; - int[] newspanstarts = new int[newsize]; - int[] newspanends = new int[newsize]; - int[] newspanflags = new int[newsize]; - - System.arraycopy(mSpans, 0, newspans, 0, mSpanCount); - System.arraycopy(mSpanStarts, 0, newspanstarts, 0, mSpanCount); - System.arraycopy(mSpanEnds, 0, newspanends, 0, mSpanCount); - System.arraycopy(mSpanFlags, 0, newspanflags, 0, mSpanCount); - - mSpans = newspans; - mSpanStarts = newspanstarts; - mSpanEnds = newspanends; - mSpanFlags = newspanflags; - } - - mSpans[mSpanCount] = what; - mSpanStarts[mSpanCount] = start; - mSpanEnds[mSpanCount] = end; - mSpanFlags[mSpanCount] = flags; + mSpans = GrowingArrayUtils.append(mSpans, mSpanCount, what); + mSpanStarts = GrowingArrayUtils.append(mSpanStarts, mSpanCount, start); + mSpanEnds = GrowingArrayUtils.append(mSpanEnds, mSpanCount, end); + mSpanFlags = GrowingArrayUtils.append(mSpanFlags, mSpanCount, flags); mSpanCount++; if (send) sendSpanAdded(what, nstart, nend); diff --git a/core/java/android/text/SpannableStringInternal.java b/core/java/android/text/SpannableStringInternal.java index 456a3e5..d114d32 100644 --- a/core/java/android/text/SpannableStringInternal.java +++ b/core/java/android/text/SpannableStringInternal.java @@ -17,6 +17,9 @@ package android.text; import com.android.internal.util.ArrayUtils; +import com.android.internal.util.GrowingArrayUtils; + +import libcore.util.EmptyArray; import java.lang.reflect.Array; @@ -29,9 +32,8 @@ import java.lang.reflect.Array; else mText = source.toString().substring(start, end); - int initial = ArrayUtils.idealIntArraySize(0); - mSpans = new Object[initial]; - mSpanData = new int[initial * 3]; + mSpans = EmptyArray.OBJECT; + mSpanData = EmptyArray.INT; if (source instanceof Spanned) { Spanned sp = (Spanned) source; @@ -115,9 +117,9 @@ import java.lang.reflect.Array; } if (mSpanCount + 1 >= mSpans.length) { - int newsize = ArrayUtils.idealIntArraySize(mSpanCount + 1); - Object[] newtags = new Object[newsize]; - int[] newdata = new int[newsize * 3]; + Object[] newtags = ArrayUtils.newUnpaddedObjectArray( + GrowingArrayUtils.growSize(mSpanCount)); + int[] newdata = new int[newtags.length * 3]; System.arraycopy(mSpans, 0, newtags, 0, mSpanCount); System.arraycopy(mSpanData, 0, newdata, 0, mSpanCount * 3); diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java index e7d6fda..535eee1 100644 --- a/core/java/android/text/StaticLayout.java +++ b/core/java/android/text/StaticLayout.java @@ -26,6 +26,7 @@ import android.text.style.TabStopSpan; import android.util.Log; import com.android.internal.util.ArrayUtils; +import com.android.internal.util.GrowingArrayUtils; /** * StaticLayout is a Layout for text that will not be edited after it @@ -130,9 +131,8 @@ public class StaticLayout extends Layout { mEllipsizedWidth = outerwidth; } - mLines = new int[ArrayUtils.idealIntArraySize(2 * mColumns)]; - mLineDirections = new Directions[ - ArrayUtils.idealIntArraySize(2 * mColumns)]; + mLineDirections = ArrayUtils.newUnpaddedArray(Directions.class, 2 * mColumns); + mLines = new int[mLineDirections.length]; mMaximumVisibleLineCount = maxLines; mMeasured = MeasuredText.obtain(); @@ -149,8 +149,8 @@ public class StaticLayout extends Layout { super(text, null, 0, null, 0, 0); mColumns = COLUMNS_ELLIPSIZE; - mLines = new int[ArrayUtils.idealIntArraySize(2 * mColumns)]; - mLineDirections = new Directions[ArrayUtils.idealIntArraySize(2 * mColumns)]; + mLineDirections = ArrayUtils.newUnpaddedArray(Directions.class, 2 * mColumns); + mLines = new int[mLineDirections.length]; // FIXME This is never recycled mMeasured = MeasuredText.obtain(); } @@ -215,8 +215,7 @@ public class StaticLayout extends Layout { if (chooseHt.length != 0) { if (chooseHtv == null || chooseHtv.length < chooseHt.length) { - chooseHtv = new int[ArrayUtils.idealIntArraySize( - chooseHt.length)]; + chooseHtv = ArrayUtils.newUnpaddedIntArray(chooseHt.length); } for (int i = 0; i < chooseHt.length; i++) { @@ -599,16 +598,16 @@ public class StaticLayout extends Layout { int[] lines = mLines; if (want >= lines.length) { - int nlen = ArrayUtils.idealIntArraySize(want + 1); - int[] grow = new int[nlen]; - System.arraycopy(lines, 0, grow, 0, lines.length); - mLines = grow; - lines = grow; - - Directions[] grow2 = new Directions[nlen]; + Directions[] grow2 = ArrayUtils.newUnpaddedArray( + Directions.class, GrowingArrayUtils.growSize(want)); System.arraycopy(mLineDirections, 0, grow2, 0, mLineDirections.length); mLineDirections = grow2; + + int[] grow = new int[grow2.length]; + System.arraycopy(lines, 0, grow, 0, lines.length); + mLines = grow; + lines = grow; } if (chooseHt != null) { diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java index 1fecf81..d892f19 100644 --- a/core/java/android/text/TextLine.java +++ b/core/java/android/text/TextLine.java @@ -153,7 +153,7 @@ class TextLine { if (mCharsValid) { if (mChars == null || mChars.length < mLen) { - mChars = new char[ArrayUtils.idealCharArraySize(mLen)]; + mChars = ArrayUtils.newUnpaddedCharArray(mLen); } TextUtils.getChars(text, start, limit, mChars, 0); if (hasReplacement) { diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java index 596ca8c..f06ae71 100644 --- a/core/java/android/text/TextUtils.java +++ b/core/java/android/text/TextUtils.java @@ -1321,7 +1321,7 @@ public class TextUtils { } if (buf == null || buf.length < len) - buf = new char[ArrayUtils.idealCharArraySize(len)]; + buf = ArrayUtils.newUnpaddedCharArray(len); return buf; } diff --git a/core/java/android/util/ArrayMap.java b/core/java/android/util/ArrayMap.java index df1d4cd..9a0b7fc 100644 --- a/core/java/android/util/ArrayMap.java +++ b/core/java/android/util/ArrayMap.java @@ -16,6 +16,8 @@ package android.util; +import libcore.util.EmptyArray; + import java.util.Collection; import java.util.Map; import java.util.Set; @@ -234,8 +236,8 @@ public final class ArrayMap<K, V> implements Map<K, V> { * will grow once items are added to it. */ public ArrayMap() { - mHashes = ContainerHelpers.EMPTY_INTS; - mArray = ContainerHelpers.EMPTY_OBJECTS; + mHashes = EmptyArray.INT; + mArray = EmptyArray.OBJECT; mSize = 0; } @@ -244,8 +246,8 @@ public final class ArrayMap<K, V> implements Map<K, V> { */ public ArrayMap(int capacity) { if (capacity == 0) { - mHashes = ContainerHelpers.EMPTY_INTS; - mArray = ContainerHelpers.EMPTY_OBJECTS; + mHashes = EmptyArray.INT; + mArray = EmptyArray.OBJECT; } else { allocArrays(capacity); } @@ -253,8 +255,8 @@ public final class ArrayMap<K, V> implements Map<K, V> { } private ArrayMap(boolean immutable) { - mHashes = EMPTY_IMMUTABLE_INTS; - mArray = ContainerHelpers.EMPTY_OBJECTS; + mHashes = EmptyArray.INT; + mArray = EmptyArray.OBJECT; mSize = 0; } @@ -275,8 +277,8 @@ public final class ArrayMap<K, V> implements Map<K, V> { public void clear() { if (mSize > 0) { freeArrays(mHashes, mArray, mSize); - mHashes = ContainerHelpers.EMPTY_INTS; - mArray = ContainerHelpers.EMPTY_OBJECTS; + mHashes = EmptyArray.INT; + mArray = EmptyArray.OBJECT; mSize = 0; } } @@ -540,8 +542,8 @@ public final class ArrayMap<K, V> implements Map<K, V> { // Now empty. if (DEBUG) Log.d(TAG, "remove: shrink from " + mHashes.length + " to 0"); freeArrays(mHashes, mArray, mSize); - mHashes = ContainerHelpers.EMPTY_INTS; - mArray = ContainerHelpers.EMPTY_OBJECTS; + mHashes = EmptyArray.INT; + mArray = EmptyArray.OBJECT; mSize = 0; } else { if (mHashes.length > (BASE_SIZE*2) && mSize < mHashes.length/3) { diff --git a/core/java/android/util/ArraySet.java b/core/java/android/util/ArraySet.java index 3c695e9..9d4b720 100644 --- a/core/java/android/util/ArraySet.java +++ b/core/java/android/util/ArraySet.java @@ -16,6 +16,8 @@ package android.util; +import libcore.util.EmptyArray; + import java.lang.reflect.Array; import java.util.Collection; import java.util.Iterator; @@ -222,8 +224,8 @@ public final class ArraySet<E> implements Collection<E>, Set<E> { * will grow once items are added to it. */ public ArraySet() { - mHashes = ContainerHelpers.EMPTY_INTS; - mArray = ContainerHelpers.EMPTY_OBJECTS; + mHashes = EmptyArray.INT; + mArray = EmptyArray.OBJECT; mSize = 0; } @@ -232,8 +234,8 @@ public final class ArraySet<E> implements Collection<E>, Set<E> { */ public ArraySet(int capacity) { if (capacity == 0) { - mHashes = ContainerHelpers.EMPTY_INTS; - mArray = ContainerHelpers.EMPTY_OBJECTS; + mHashes = EmptyArray.INT; + mArray = EmptyArray.OBJECT; } else { allocArrays(capacity); } @@ -258,8 +260,8 @@ public final class ArraySet<E> implements Collection<E>, Set<E> { public void clear() { if (mSize != 0) { freeArrays(mHashes, mArray, mSize); - mHashes = ContainerHelpers.EMPTY_INTS; - mArray = ContainerHelpers.EMPTY_OBJECTS; + mHashes = EmptyArray.INT; + mArray = EmptyArray.OBJECT; mSize = 0; } } @@ -413,8 +415,8 @@ public final class ArraySet<E> implements Collection<E>, Set<E> { // Now empty. if (DEBUG) Log.d(TAG, "remove: shrink from " + mHashes.length + " to 0"); freeArrays(mHashes, mArray, mSize); - mHashes = ContainerHelpers.EMPTY_INTS; - mArray = ContainerHelpers.EMPTY_OBJECTS; + mHashes = EmptyArray.INT; + mArray = EmptyArray.OBJECT; mSize = 0; } else { if (mHashes.length > (BASE_SIZE*2) && mSize < mHashes.length/3) { diff --git a/core/java/android/util/ContainerHelpers.java b/core/java/android/util/ContainerHelpers.java index 624c4bd..4e5fefb 100644 --- a/core/java/android/util/ContainerHelpers.java +++ b/core/java/android/util/ContainerHelpers.java @@ -17,10 +17,6 @@ package android.util; class ContainerHelpers { - static final boolean[] EMPTY_BOOLEANS = new boolean[0]; - static final int[] EMPTY_INTS = new int[0]; - static final long[] EMPTY_LONGS = new long[0]; - static final Object[] EMPTY_OBJECTS = new Object[0]; // This is Arrays.binarySearch(), but doesn't do any argument validation. static int binarySearch(int[] array, int size, int value) { diff --git a/core/java/android/util/LongArray.java b/core/java/android/util/LongArray.java index d5f15f0..54a6882 100644 --- a/core/java/android/util/LongArray.java +++ b/core/java/android/util/LongArray.java @@ -17,6 +17,7 @@ package android.util; import com.android.internal.util.ArrayUtils; +import libcore.util.EmptyArray; /** * Implements a growing array of long primitives. @@ -41,10 +42,9 @@ public class LongArray implements Cloneable { */ public LongArray(int initialCapacity) { if (initialCapacity == 0) { - mValues = ContainerHelpers.EMPTY_LONGS; + mValues = EmptyArray.LONG; } else { - initialCapacity = ArrayUtils.idealLongArraySize(initialCapacity); - mValues = new long[initialCapacity]; + mValues = ArrayUtils.newUnpaddedLongArray(initialCapacity); } mSize = 0; } @@ -97,7 +97,7 @@ public class LongArray implements Cloneable { final int targetCap = currentSize + (currentSize < (MIN_CAPACITY_INCREMENT / 2) ? MIN_CAPACITY_INCREMENT : currentSize >> 1); final int newCapacity = targetCap > minCapacity ? targetCap : minCapacity; - final long[] newValues = new long[ArrayUtils.idealLongArraySize(newCapacity)]; + final long[] newValues = ArrayUtils.newUnpaddedLongArray(newCapacity); System.arraycopy(mValues, 0, newValues, 0, currentSize); mValues = newValues; } diff --git a/core/java/android/util/LongSparseArray.java b/core/java/android/util/LongSparseArray.java index dab853a..6b45ff4 100644 --- a/core/java/android/util/LongSparseArray.java +++ b/core/java/android/util/LongSparseArray.java @@ -17,6 +17,9 @@ package android.util; import com.android.internal.util.ArrayUtils; +import com.android.internal.util.GrowingArrayUtils; + +import libcore.util.EmptyArray; /** * SparseArray mapping longs to Objects. Unlike a normal array of Objects, @@ -70,12 +73,11 @@ public class LongSparseArray<E> implements Cloneable { */ public LongSparseArray(int initialCapacity) { if (initialCapacity == 0) { - mKeys = ContainerHelpers.EMPTY_LONGS; - mValues = ContainerHelpers.EMPTY_OBJECTS; + mKeys = EmptyArray.LONG; + mValues = EmptyArray.OBJECT; } else { - initialCapacity = ArrayUtils.idealLongArraySize(initialCapacity); - mKeys = new long[initialCapacity]; - mValues = new Object[initialCapacity]; + mKeys = ArrayUtils.newUnpaddedLongArray(initialCapacity); + mValues = ArrayUtils.newUnpaddedObjectArray(initialCapacity); } mSize = 0; } @@ -202,28 +204,8 @@ public class LongSparseArray<E> implements Cloneable { i = ~ContainerHelpers.binarySearch(mKeys, mSize, key); } - if (mSize >= mKeys.length) { - int n = ArrayUtils.idealLongArraySize(mSize + 1); - - long[] nkeys = new long[n]; - Object[] nvalues = new Object[n]; - - // Log.e("SparseArray", "grow " + mKeys.length + " to " + n); - System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length); - System.arraycopy(mValues, 0, nvalues, 0, mValues.length); - - mKeys = nkeys; - mValues = nvalues; - } - - if (mSize - i != 0) { - // Log.e("SparseArray", "move " + (mSize - i)); - System.arraycopy(mKeys, i, mKeys, i + 1, mSize - i); - System.arraycopy(mValues, i, mValues, i + 1, mSize - i); - } - - mKeys[i] = key; - mValues[i] = value; + mKeys = GrowingArrayUtils.insert(mKeys, mSize, i, key); + mValues = GrowingArrayUtils.insert(mValues, mSize, i, value); mSize++; } } @@ -353,24 +335,9 @@ public class LongSparseArray<E> implements Cloneable { gc(); } - int pos = mSize; - if (pos >= mKeys.length) { - int n = ArrayUtils.idealLongArraySize(pos + 1); - - long[] nkeys = new long[n]; - Object[] nvalues = new Object[n]; - - // Log.e("SparseArray", "grow " + mKeys.length + " to " + n); - System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length); - System.arraycopy(mValues, 0, nvalues, 0, mValues.length); - - mKeys = nkeys; - mValues = nvalues; - } - - mKeys[pos] = key; - mValues[pos] = value; - mSize = pos + 1; + mKeys = GrowingArrayUtils.append(mKeys, mSize, key); + mValues = GrowingArrayUtils.append(mValues, mSize, value); + mSize++; } /** diff --git a/core/java/android/util/LongSparseLongArray.java b/core/java/android/util/LongSparseLongArray.java index b8073dd..a361457 100644 --- a/core/java/android/util/LongSparseLongArray.java +++ b/core/java/android/util/LongSparseLongArray.java @@ -17,6 +17,9 @@ package android.util; import com.android.internal.util.ArrayUtils; +import com.android.internal.util.GrowingArrayUtils; + +import libcore.util.EmptyArray; /** * Map of {@code long} to {@code long}. Unlike a normal array of longs, there @@ -62,12 +65,11 @@ public class LongSparseLongArray implements Cloneable { */ public LongSparseLongArray(int initialCapacity) { if (initialCapacity == 0) { - mKeys = ContainerHelpers.EMPTY_LONGS; - mValues = ContainerHelpers.EMPTY_LONGS; + mKeys = EmptyArray.LONG; + mValues = EmptyArray.LONG; } else { - initialCapacity = ArrayUtils.idealLongArraySize(initialCapacity); - mKeys = new long[initialCapacity]; - mValues = new long[initialCapacity]; + mKeys = ArrayUtils.newUnpaddedLongArray(initialCapacity); + mValues = new long[mKeys.length]; } mSize = 0; } @@ -140,17 +142,8 @@ public class LongSparseLongArray implements Cloneable { } else { i = ~i; - if (mSize >= mKeys.length) { - growKeyAndValueArrays(mSize + 1); - } - - if (mSize - i != 0) { - System.arraycopy(mKeys, i, mKeys, i + 1, mSize - i); - System.arraycopy(mValues, i, mValues, i + 1, mSize - i); - } - - mKeys[i] = key; - mValues[i] = value; + mKeys = GrowingArrayUtils.insert(mKeys, mSize, i, key); + mValues = GrowingArrayUtils.insert(mValues, mSize, i, value); mSize++; } } @@ -234,27 +227,9 @@ public class LongSparseLongArray implements Cloneable { return; } - int pos = mSize; - if (pos >= mKeys.length) { - growKeyAndValueArrays(pos + 1); - } - - mKeys[pos] = key; - mValues[pos] = value; - mSize = pos + 1; - } - - private void growKeyAndValueArrays(int minNeededSize) { - int n = ArrayUtils.idealLongArraySize(minNeededSize); - - long[] nkeys = new long[n]; - long[] nvalues = new long[n]; - - System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length); - System.arraycopy(mValues, 0, nvalues, 0, mValues.length); - - mKeys = nkeys; - mValues = nvalues; + mKeys = GrowingArrayUtils.append(mKeys, mSize, key); + mValues = GrowingArrayUtils.append(mValues, mSize, value); + mSize++; } /** diff --git a/core/java/android/util/Patterns.java b/core/java/android/util/Patterns.java index 0f8da44..13cc88b 100644 --- a/core/java/android/util/Patterns.java +++ b/core/java/android/util/Patterns.java @@ -28,7 +28,12 @@ public class Patterns { * List accurate as of 2011/07/18. List taken from: * http://data.iana.org/TLD/tlds-alpha-by-domain.txt * This pattern is auto-generated by frameworks/ex/common/tools/make-iana-tld-pattern.py + * + * @deprecated Due to the recent profileration of gTLDs, this API is + * expected to become out-of-date very quickly. Therefore it is now + * deprecated. */ + @Deprecated public static final String TOP_LEVEL_DOMAIN_STR = "((aero|arpa|asia|a[cdefgilmnoqrstuwxz])" + "|(biz|b[abdefghijmnorstvwyz])" @@ -59,7 +64,9 @@ public class Patterns { /** * Regular expression pattern to match all IANA top-level domains. + * @deprecated This API is deprecated. See {@link #TOP_LEVEL_DOMAIN_STR}. */ + @Deprecated public static final Pattern TOP_LEVEL_DOMAIN = Pattern.compile(TOP_LEVEL_DOMAIN_STR); @@ -68,7 +75,10 @@ public class Patterns { * List accurate as of 2011/07/18. List taken from: * http://data.iana.org/TLD/tlds-alpha-by-domain.txt * This pattern is auto-generated by frameworks/ex/common/tools/make-iana-tld-pattern.py + * + * @deprecated This API is deprecated. See {@link #TOP_LEVEL_DOMAIN_STR}. */ + @Deprecated public static final String TOP_LEVEL_DOMAIN_STR_FOR_WEB_URL = "(?:" + "(?:aero|arpa|asia|a[cdefgilmnoqrstuwxz])" @@ -107,6 +117,24 @@ public class Patterns { public static final String GOOD_IRI_CHAR = "a-zA-Z0-9\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF"; + public static final Pattern IP_ADDRESS + = Pattern.compile( + "((25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9])\\.(25[0-5]|2[0-4]" + + "[0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(25[0-5]|2[0-4][0-9]|[0-1]" + + "[0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}" + + "|[1-9][0-9]|[0-9]))"); + + /** + * RFC 1035 Section 2.3.4 limits the labels to a maximum 63 octets. + */ + private static final String IRI + = "[" + GOOD_IRI_CHAR + "]([" + GOOD_IRI_CHAR + "\\-]{0,61}[" + GOOD_IRI_CHAR + "]){0,1}"; + + private static final String HOST_NAME = IRI + "(?:\\." + IRI + ")+"; + + public static final Pattern DOMAIN_NAME + = Pattern.compile("(" + HOST_NAME + "|" + IP_ADDRESS + ")"); + /** * Regular expression pattern to match most part of RFC 3987 * Internationalized URLs, aka IRIs. Commonly used Unicode characters are @@ -116,13 +144,7 @@ public class Patterns { "((?:(http|https|Http|Https|rtsp|Rtsp):\\/\\/(?:(?:[a-zA-Z0-9\\$\\-\\_\\.\\+\\!\\*\\'\\(\\)" + "\\,\\;\\?\\&\\=]|(?:\\%[a-fA-F0-9]{2})){1,64}(?:\\:(?:[a-zA-Z0-9\\$\\-\\_" + "\\.\\+\\!\\*\\'\\(\\)\\,\\;\\?\\&\\=]|(?:\\%[a-fA-F0-9]{2})){1,25})?\\@)?)?" - + "((?:(?:[" + GOOD_IRI_CHAR + "][" + GOOD_IRI_CHAR + "\\-]{0,64}\\.)+" // named host - + TOP_LEVEL_DOMAIN_STR_FOR_WEB_URL - + "|(?:(?:25[0-5]|2[0-4]" // or ip address - + "[0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9])\\.(?:25[0-5]|2[0-4][0-9]" - + "|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(?:25[0-5]|2[0-4][0-9]|[0-1]" - + "[0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}" - + "|[1-9][0-9]|[0-9])))" + + "(?:" + DOMAIN_NAME + ")" + "(?:\\:\\d{1,5})?)" // plus option port number + "(\\/(?:(?:[" + GOOD_IRI_CHAR + "\\;\\/\\?\\:\\@\\&\\=\\#\\~" // plus option query params + "\\-\\.\\+\\!\\*\\'\\(\\)\\,\\_])|(?:\\%[a-fA-F0-9]{2}))*)?" @@ -130,19 +152,6 @@ public class Patterns { // input. This is to stop foo.sure from // matching as foo.su - public static final Pattern IP_ADDRESS - = Pattern.compile( - "((25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9])\\.(25[0-5]|2[0-4]" - + "[0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(25[0-5]|2[0-4][0-9]|[0-1]" - + "[0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}" - + "|[1-9][0-9]|[0-9]))"); - - public static final Pattern DOMAIN_NAME - = Pattern.compile( - "(((([" + GOOD_IRI_CHAR + "][" + GOOD_IRI_CHAR + "\\-]*)*[" + GOOD_IRI_CHAR + "]\\.)+" - + TOP_LEVEL_DOMAIN + ")|" - + IP_ADDRESS + ")"); - public static final Pattern EMAIL_ADDRESS = Pattern.compile( "[a-zA-Z0-9\\+\\.\\_\\%\\-\\+]{1,256}" + @@ -159,7 +168,7 @@ public class Patterns { * might be phone numbers in arbitrary text, not for validating whether * something is in fact a phone number. It will miss many things that * are legitimate phone numbers. - * + * * <p> The pattern matches the following: * <ul> * <li>Optionally, a + sign followed immediately by one or more digits. Spaces, dots, or dashes diff --git a/core/java/android/util/SparseArray.java b/core/java/android/util/SparseArray.java index 46d9d45..92e874f 100644 --- a/core/java/android/util/SparseArray.java +++ b/core/java/android/util/SparseArray.java @@ -17,6 +17,9 @@ package android.util; import com.android.internal.util.ArrayUtils; +import com.android.internal.util.GrowingArrayUtils; + +import libcore.util.EmptyArray; /** * SparseArrays map integers to Objects. Unlike a normal array of Objects, @@ -70,12 +73,11 @@ public class SparseArray<E> implements Cloneable { */ public SparseArray(int initialCapacity) { if (initialCapacity == 0) { - mKeys = ContainerHelpers.EMPTY_INTS; - mValues = ContainerHelpers.EMPTY_OBJECTS; + mKeys = EmptyArray.INT; + mValues = EmptyArray.OBJECT; } else { - initialCapacity = ArrayUtils.idealIntArraySize(initialCapacity); - mKeys = new int[initialCapacity]; - mValues = new Object[initialCapacity]; + mValues = ArrayUtils.newUnpaddedObjectArray(initialCapacity); + mKeys = new int[mValues.length]; } mSize = 0; } @@ -215,28 +217,8 @@ public class SparseArray<E> implements Cloneable { i = ~ContainerHelpers.binarySearch(mKeys, mSize, key); } - if (mSize >= mKeys.length) { - int n = ArrayUtils.idealIntArraySize(mSize + 1); - - int[] nkeys = new int[n]; - Object[] nvalues = new Object[n]; - - // Log.e("SparseArray", "grow " + mKeys.length + " to " + n); - System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length); - System.arraycopy(mValues, 0, nvalues, 0, mValues.length); - - mKeys = nkeys; - mValues = nvalues; - } - - if (mSize - i != 0) { - // Log.e("SparseArray", "move " + (mSize - i)); - System.arraycopy(mKeys, i, mKeys, i + 1, mSize - i); - System.arraycopy(mValues, i, mValues, i + 1, mSize - i); - } - - mKeys[i] = key; - mValues[i] = value; + mKeys = GrowingArrayUtils.insert(mKeys, mSize, i, key); + mValues = GrowingArrayUtils.insert(mValues, mSize, i, value); mSize++; } } @@ -368,24 +350,9 @@ public class SparseArray<E> implements Cloneable { gc(); } - int pos = mSize; - if (pos >= mKeys.length) { - int n = ArrayUtils.idealIntArraySize(pos + 1); - - int[] nkeys = new int[n]; - Object[] nvalues = new Object[n]; - - // Log.e("SparseArray", "grow " + mKeys.length + " to " + n); - System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length); - System.arraycopy(mValues, 0, nvalues, 0, mValues.length); - - mKeys = nkeys; - mValues = nvalues; - } - - mKeys[pos] = key; - mValues[pos] = value; - mSize = pos + 1; + mKeys = GrowingArrayUtils.append(mKeys, mSize, key); + mValues = GrowingArrayUtils.append(mValues, mSize, value); + mSize++; } /** diff --git a/core/java/android/util/SparseBooleanArray.java b/core/java/android/util/SparseBooleanArray.java index f59ef0f6d..e293b1f 100644 --- a/core/java/android/util/SparseBooleanArray.java +++ b/core/java/android/util/SparseBooleanArray.java @@ -17,6 +17,9 @@ package android.util; import com.android.internal.util.ArrayUtils; +import com.android.internal.util.GrowingArrayUtils; + +import libcore.util.EmptyArray; /** * SparseBooleanArrays map integers to booleans. @@ -57,12 +60,11 @@ public class SparseBooleanArray implements Cloneable { */ public SparseBooleanArray(int initialCapacity) { if (initialCapacity == 0) { - mKeys = ContainerHelpers.EMPTY_INTS; - mValues = ContainerHelpers.EMPTY_BOOLEANS; + mKeys = EmptyArray.INT; + mValues = EmptyArray.BOOLEAN; } else { - initialCapacity = ArrayUtils.idealIntArraySize(initialCapacity); - mKeys = new int[initialCapacity]; - mValues = new boolean[initialCapacity]; + mKeys = ArrayUtils.newUnpaddedIntArray(initialCapacity); + mValues = new boolean[mKeys.length]; } mSize = 0; } @@ -135,28 +137,8 @@ public class SparseBooleanArray implements Cloneable { } else { i = ~i; - if (mSize >= mKeys.length) { - int n = ArrayUtils.idealIntArraySize(mSize + 1); - - int[] nkeys = new int[n]; - boolean[] nvalues = new boolean[n]; - - // Log.e("SparseBooleanArray", "grow " + mKeys.length + " to " + n); - System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length); - System.arraycopy(mValues, 0, nvalues, 0, mValues.length); - - mKeys = nkeys; - mValues = nvalues; - } - - if (mSize - i != 0) { - // Log.e("SparseBooleanArray", "move " + (mSize - i)); - System.arraycopy(mKeys, i, mKeys, i + 1, mSize - i); - System.arraycopy(mValues, i, mValues, i + 1, mSize - i); - } - - mKeys[i] = key; - mValues[i] = value; + mKeys = GrowingArrayUtils.insert(mKeys, mSize, i, key); + mValues = GrowingArrayUtils.insert(mValues, mSize, i, value); mSize++; } } @@ -245,24 +227,9 @@ public class SparseBooleanArray implements Cloneable { return; } - int pos = mSize; - if (pos >= mKeys.length) { - int n = ArrayUtils.idealIntArraySize(pos + 1); - - int[] nkeys = new int[n]; - boolean[] nvalues = new boolean[n]; - - // Log.e("SparseBooleanArray", "grow " + mKeys.length + " to " + n); - System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length); - System.arraycopy(mValues, 0, nvalues, 0, mValues.length); - - mKeys = nkeys; - mValues = nvalues; - } - - mKeys[pos] = key; - mValues[pos] = value; - mSize = pos + 1; + mKeys = GrowingArrayUtils.append(mKeys, mSize, key); + mValues = GrowingArrayUtils.append(mValues, mSize, value); + mSize++; } /** diff --git a/core/java/android/util/SparseIntArray.java b/core/java/android/util/SparseIntArray.java index 4f5ca07..2b85a21 100644 --- a/core/java/android/util/SparseIntArray.java +++ b/core/java/android/util/SparseIntArray.java @@ -17,6 +17,9 @@ package android.util; import com.android.internal.util.ArrayUtils; +import com.android.internal.util.GrowingArrayUtils; + +import libcore.util.EmptyArray; /** * SparseIntArrays map integers to integers. Unlike a normal array of integers, @@ -60,12 +63,11 @@ public class SparseIntArray implements Cloneable { */ public SparseIntArray(int initialCapacity) { if (initialCapacity == 0) { - mKeys = ContainerHelpers.EMPTY_INTS; - mValues = ContainerHelpers.EMPTY_INTS; + mKeys = EmptyArray.INT; + mValues = EmptyArray.INT; } else { - initialCapacity = ArrayUtils.idealIntArraySize(initialCapacity); - mKeys = new int[initialCapacity]; - mValues = new int[initialCapacity]; + mKeys = ArrayUtils.newUnpaddedIntArray(initialCapacity); + mValues = new int[mKeys.length]; } mSize = 0; } @@ -138,28 +140,8 @@ public class SparseIntArray implements Cloneable { } else { i = ~i; - if (mSize >= mKeys.length) { - int n = ArrayUtils.idealIntArraySize(mSize + 1); - - int[] nkeys = new int[n]; - int[] nvalues = new int[n]; - - // Log.e("SparseIntArray", "grow " + mKeys.length + " to " + n); - System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length); - System.arraycopy(mValues, 0, nvalues, 0, mValues.length); - - mKeys = nkeys; - mValues = nvalues; - } - - if (mSize - i != 0) { - // Log.e("SparseIntArray", "move " + (mSize - i)); - System.arraycopy(mKeys, i, mKeys, i + 1, mSize - i); - System.arraycopy(mValues, i, mValues, i + 1, mSize - i); - } - - mKeys[i] = key; - mValues[i] = value; + mKeys = GrowingArrayUtils.insert(mKeys, mSize, i, key); + mValues = GrowingArrayUtils.insert(mValues, mSize, i, value); mSize++; } } @@ -243,24 +225,9 @@ public class SparseIntArray implements Cloneable { return; } - int pos = mSize; - if (pos >= mKeys.length) { - int n = ArrayUtils.idealIntArraySize(pos + 1); - - int[] nkeys = new int[n]; - int[] nvalues = new int[n]; - - // Log.e("SparseIntArray", "grow " + mKeys.length + " to " + n); - System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length); - System.arraycopy(mValues, 0, nvalues, 0, mValues.length); - - mKeys = nkeys; - mValues = nvalues; - } - - mKeys[pos] = key; - mValues[pos] = value; - mSize = pos + 1; + mKeys = GrowingArrayUtils.append(mKeys, mSize, key); + mValues = GrowingArrayUtils.append(mValues, mSize, value); + mSize++; } /** diff --git a/core/java/android/util/SparseLongArray.java b/core/java/android/util/SparseLongArray.java index 39fc8a3..0166c4a 100644 --- a/core/java/android/util/SparseLongArray.java +++ b/core/java/android/util/SparseLongArray.java @@ -17,6 +17,9 @@ package android.util; import com.android.internal.util.ArrayUtils; +import com.android.internal.util.GrowingArrayUtils; + +import libcore.util.EmptyArray; /** * SparseLongArrays map integers to longs. Unlike a normal array of longs, @@ -60,12 +63,11 @@ public class SparseLongArray implements Cloneable { */ public SparseLongArray(int initialCapacity) { if (initialCapacity == 0) { - mKeys = ContainerHelpers.EMPTY_INTS; - mValues = ContainerHelpers.EMPTY_LONGS; + mKeys = EmptyArray.INT; + mValues = EmptyArray.LONG; } else { - initialCapacity = ArrayUtils.idealLongArraySize(initialCapacity); - mKeys = new int[initialCapacity]; - mValues = new long[initialCapacity]; + mValues = ArrayUtils.newUnpaddedLongArray(initialCapacity); + mKeys = new int[mValues.length]; } mSize = 0; } @@ -138,17 +140,8 @@ public class SparseLongArray implements Cloneable { } else { i = ~i; - if (mSize >= mKeys.length) { - growKeyAndValueArrays(mSize + 1); - } - - if (mSize - i != 0) { - System.arraycopy(mKeys, i, mKeys, i + 1, mSize - i); - System.arraycopy(mValues, i, mValues, i + 1, mSize - i); - } - - mKeys[i] = key; - mValues[i] = value; + mKeys = GrowingArrayUtils.insert(mKeys, mSize, i, key); + mValues = GrowingArrayUtils.insert(mValues, mSize, i, value); mSize++; } } @@ -232,27 +225,9 @@ public class SparseLongArray implements Cloneable { return; } - int pos = mSize; - if (pos >= mKeys.length) { - growKeyAndValueArrays(pos + 1); - } - - mKeys[pos] = key; - mValues[pos] = value; - mSize = pos + 1; - } - - private void growKeyAndValueArrays(int minNeededSize) { - int n = ArrayUtils.idealLongArraySize(minNeededSize); - - int[] nkeys = new int[n]; - long[] nvalues = new long[n]; - - System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length); - System.arraycopy(mValues, 0, nvalues, 0, mValues.length); - - mKeys = nkeys; - mValues = nvalues; + mKeys = GrowingArrayUtils.append(mKeys, mSize, key); + mValues = GrowingArrayUtils.append(mValues, mSize, value); + mSize++; } /** diff --git a/core/java/android/view/AccessibilityInteractionController.java b/core/java/android/view/AccessibilityInteractionController.java index abae068..477c994 100644 --- a/core/java/android/view/AccessibilityInteractionController.java +++ b/core/java/android/view/AccessibilityInteractionController.java @@ -52,7 +52,7 @@ import java.util.Queue; */ final class AccessibilityInteractionController { - private static final boolean ENFORCE_NODE_TREE_CONSISTENT = Build.IS_DEBUGGABLE; + private static final boolean ENFORCE_NODE_TREE_CONSISTENT = false; private final ArrayList<AccessibilityNodeInfo> mTempAccessibilityNodeInfoList = new ArrayList<AccessibilityNodeInfo>(); diff --git a/core/java/android/view/GLRenderer.java b/core/java/android/view/GLRenderer.java index 90824ab..d6e1781 100644 --- a/core/java/android/view/GLRenderer.java +++ b/core/java/android/view/GLRenderer.java @@ -555,6 +555,32 @@ public class GLRenderer extends HardwareRenderer { } @Override + public void invokeFunctor(long functor, boolean waitForCompletion) { + boolean needsContext = !isEnabled() || checkRenderContext() == SURFACE_STATE_ERROR; + boolean hasContext = !needsContext; + + if (needsContext) { + GLRendererEglContext managedContext = + (GLRendererEglContext) sEglContextStorage.get(); + if (managedContext != null) { + usePbufferSurface(managedContext.getContext()); + hasContext = true; + } + } + + try { + nInvokeFunctor(functor, hasContext); + } finally { + if (needsContext) { + sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE, + EGL_NO_SURFACE, EGL_NO_CONTEXT); + } + } + } + + private static native void nInvokeFunctor(long functor, boolean hasContext); + + @Override void destroyHardwareResources(final View view) { if (view != null) { safelyRun(new Runnable() { @@ -1096,8 +1122,7 @@ public class GLRenderer extends HardwareRenderer { } if (checkRenderContext() != SURFACE_STATE_ERROR) { - int status = mCanvas.invokeFunctors(mRedrawClip); - handleFunctorStatus(attachInfo, status); + mCanvas.invokeFunctors(mRedrawClip); } } } @@ -1203,7 +1228,7 @@ public class GLRenderer extends HardwareRenderer { private RenderNode buildDisplayList(View view, HardwareCanvas canvas) { if (mDrawDelta <= 0) { - return view.mDisplayList; + return view.mRenderNode; } view.mRecreateDisplayList = (view.mPrivateFlags & View.PFLAG_INVALIDATED) @@ -1214,12 +1239,12 @@ public class GLRenderer extends HardwareRenderer { canvas.clearLayerUpdates(); Trace.traceBegin(Trace.TRACE_TAG_VIEW, "getDisplayList"); - RenderNode displayList = view.getDisplayList(); + RenderNode renderNode = view.getDisplayList(); Trace.traceEnd(Trace.TRACE_TAG_VIEW); endBuildDisplayListProfiling(buildDisplayListStartTime); - return displayList; + return renderNode; } private Rect beginFrame(HardwareCanvas canvas, Rect dirty, int surfaceState) { @@ -1301,7 +1326,6 @@ public class GLRenderer extends HardwareRenderer { mProfileData[mProfileCurrentFrame + 1] = total; } - handleFunctorStatus(attachInfo, status); return status; } @@ -1337,26 +1361,6 @@ public class GLRenderer extends HardwareRenderer { } } - private void handleFunctorStatus(View.AttachInfo attachInfo, int status) { - // If the draw flag is set, functors will be invoked while executing - // the tree of display lists - if ((status & RenderNode.STATUS_DRAW) != 0) { - if (mRedrawClip.isEmpty()) { - attachInfo.mViewRootImpl.invalidate(); - } else { - attachInfo.mViewRootImpl.invalidateChildInParent(null, mRedrawClip); - mRedrawClip.setEmpty(); - } - } - - if ((status & RenderNode.STATUS_INVOKE) != 0 || - attachInfo.mHandler.hasCallbacks(mFunctorsRunnable)) { - attachInfo.mHandler.removeCallbacks(mFunctorsRunnable); - mFunctorsRunnable.attachInfo = attachInfo; - attachInfo.mHandler.postDelayed(mFunctorsRunnable, FUNCTOR_PROCESS_DELAY); - } - } - @Override void detachFunctor(long functor) { if (mCanvas != null) { diff --git a/core/java/android/view/HardwareCanvas.java b/core/java/android/view/HardwareCanvas.java index f695b20..233f846 100644 --- a/core/java/android/view/HardwareCanvas.java +++ b/core/java/android/view/HardwareCanvas.java @@ -23,7 +23,7 @@ import android.graphics.Rect; /** * Hardware accelerated canvas. - * + * * @hide */ public abstract class HardwareCanvas extends Canvas { @@ -40,7 +40,7 @@ public abstract class HardwareCanvas extends Canvas { /** * Invoked before any drawing operation is performed in this canvas. - * + * * @param dirty The dirty rectangle to update, can be null. * @return {@link RenderNode#STATUS_DREW} if anything was drawn (such as a call to clear * the canvas). @@ -70,13 +70,11 @@ public abstract class HardwareCanvas extends Canvas { * Draws the specified display list onto this canvas. * * @param displayList The display list to replay. - * @param dirty The dirty region to redraw in the next pass, matters only - * if this method returns {@link RenderNode#STATUS_DRAW}, can be null. + * @param dirty Ignored, can be null. * @param flags Optional flags about drawing, see {@link RenderNode} for * the possible flags. * - * @return One of {@link RenderNode#STATUS_DONE}, {@link RenderNode#STATUS_DRAW}, or - * {@link RenderNode#STATUS_INVOKE}, or'd with {@link RenderNode#STATUS_DREW} + * @return One of {@link RenderNode#STATUS_DONE} or {@link RenderNode#STATUS_DREW} * if anything was drawn. * * @hide @@ -101,9 +99,8 @@ public abstract class HardwareCanvas extends Canvas { * This function may return true if an invalidation is needed after the call. * * @param drawGLFunction A native function pointer - * - * @return One of {@link RenderNode#STATUS_DONE}, {@link RenderNode#STATUS_DRAW} or - * {@link RenderNode#STATUS_INVOKE} + * + * @return {@link RenderNode#STATUS_DONE} * * @hide */ @@ -114,11 +111,10 @@ public abstract class HardwareCanvas extends Canvas { /** * Invoke all the functors who requested to be invoked during the previous frame. - * - * @param dirty The region to redraw when the functors return {@link RenderNode#STATUS_DRAW} - * - * @return One of {@link RenderNode#STATUS_DONE}, {@link RenderNode#STATUS_DRAW} or - * {@link RenderNode#STATUS_INVOKE} + * + * @param dirty Ignored + * + * @return Ignored * * @hide */ @@ -154,7 +150,7 @@ public abstract class HardwareCanvas extends Canvas { /** * Indicates that the specified layer must be updated as soon as possible. - * + * * @param layer The layer to update * * @see #clearLayerUpdates() @@ -187,7 +183,7 @@ public abstract class HardwareCanvas extends Canvas { /** * Removes all enqueued layer updates. - * + * * @see #pushLayerUpdate(HardwareLayer) * * @hide diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java index 34efcf5..4f646e1 100644 --- a/core/java/android/view/HardwareRenderer.java +++ b/core/java/android/view/HardwareRenderer.java @@ -438,6 +438,17 @@ public abstract class HardwareRenderer { abstract void attachFunctor(View.AttachInfo attachInfo, long functor); /** + * Schedules the functor for execution in either kModeProcess or + * kModeProcessNoContext, depending on whether or not there is an EGLContext. + * + * @param functor The native functor to invoke + * @param waitForCompletion If true, this will not return until the functor + * has invoked. If false, the functor may be invoked + * asynchronously. + */ + public abstract void invokeFunctor(long functor, boolean waitForCompletion); + + /** * Initializes the hardware renderer for the specified surface and setup the * renderer for drawing, if needed. This is invoked when the ViewAncestor has * potentially lost the hardware renderer. The hardware renderer should be diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java index 0b12cbe..ae5f37e 100644 --- a/core/java/android/view/InputDevice.java +++ b/core/java/android/view/InputDevice.java @@ -579,6 +579,18 @@ public final class InputDevice implements Parcelable { } /** + * Determines whether the input device supports the given source or sources. + * + * @param source The input source or sources to check against. This can be a generic device + * type such as {@link InputDevice#SOURCE_MOUSE}, a more generic device class, such as + * {@link InputDevice#SOURCE_CLASS_POINTER}, or a combination of sources bitwise ORed together. + * @return Whether the device can produce all of the given sources. + */ + public boolean supportsSource(int source) { + return (mSources & source) == source; + } + + /** * Gets the keyboard type. * @return The keyboard type. */ diff --git a/core/java/android/view/LayoutInflater.java b/core/java/android/view/LayoutInflater.java index c4fac46..e19bda9 100644 --- a/core/java/android/view/LayoutInflater.java +++ b/core/java/android/view/LayoutInflater.java @@ -590,6 +590,7 @@ public abstract class LayoutInflater { Object[] args = mConstructorArgs; args[1] = attrs; + constructor.setAccessible(true); final View view = constructor.newInstance(args); if (view instanceof ViewStub) { // always use ourselves when inflating ViewStub later diff --git a/core/java/android/view/RenderNode.java b/core/java/android/view/RenderNode.java index 60fb7ac..26eaef8 100644 --- a/core/java/android/view/RenderNode.java +++ b/core/java/android/view/RenderNode.java @@ -16,6 +16,7 @@ package android.view; +import android.annotation.NonNull; import android.graphics.Matrix; import android.graphics.Outline; @@ -196,18 +197,20 @@ public class RenderNode { } /** - * Starts recording the display list. All operations performed on the - * returned canvas are recorded and stored in this display list. + * Starts recording a display list for the render node. All + * operations performed on the returned canvas are recorded and + * stored in this display list. * - * Calling this method will mark the display list invalid until - * {@link #end()} is called. Only valid display lists can be replayed. + * Calling this method will mark the render node invalid until + * {@link #end(HardwareRenderer, HardwareCanvas)} is called. + * Only valid render nodes can be replayed. * - * @param width The width of the display list's viewport - * @param height The height of the display list's viewport + * @param width The width of the recording viewport + * @param height The height of the recording viewport * * @return A canvas to record drawing operations. * - * @see #end() + * @see #end(HardwareRenderer, HardwareCanvas) * @see #isValid() */ public HardwareCanvas start(int width, int height) { @@ -269,8 +272,8 @@ public class RenderNode { } /** - * Returns whether the display list is currently usable. If this returns false, - * the display list should be re-recorded prior to replaying it. + * Returns whether the RenderNode's display list content is currently usable. + * If this returns false, the display list should be re-recorded prior to replaying it. * * @return boolean true if the display list is able to be replayed, false otherwise. */ @@ -284,7 +287,23 @@ public class RenderNode { } /////////////////////////////////////////////////////////////////////////// - // DisplayList Property Setters + // Matrix manipulation + /////////////////////////////////////////////////////////////////////////// + + public boolean hasIdentityMatrix() { + return nHasIdentityMatrix(mNativeDisplayList); + } + + public void getMatrix(@NonNull Matrix outMatrix) { + nGetTransformMatrix(mNativeDisplayList, outMatrix.native_instance); + } + + public void getInverseMatrix(@NonNull Matrix outMatrix) { + nGetInverseTransformMatrix(mNativeDisplayList, outMatrix.native_instance); + } + + /////////////////////////////////////////////////////////////////////////// + // RenderProperty Setters /////////////////////////////////////////////////////////////////////////// /** @@ -301,7 +320,7 @@ public class RenderNode { } /** - * Set whether the display list should clip itself to its bounds. This property is controlled by + * Set whether the Render node should clip itself to its bounds. This property is controlled by * the view's parent. * * @param clipToBounds true if the display list should clip to its bounds @@ -312,9 +331,7 @@ public class RenderNode { /** * Sets whether the display list should be drawn immediately after the - * closest ancestor display list where isolateZVolume is true. If the - * display list itself satisfies this constraint, changing this attribute - * has no effect on drawing order. + * closest ancestor display list containing a projection receiver. * * @param shouldProject true if the display list should be projected onto a * containing volume. @@ -361,13 +378,18 @@ public class RenderNode { } /** + * Controls the RenderNode's circular reveal clip. + */ + public void setRevealClip(boolean shouldClip, boolean inverseClip, + float x, float y, float radius) { + nSetRevealClip(mNativeDisplayList, shouldClip, inverseClip, x, y, radius); + } + + /** * Set the static matrix on the display list. The specified matrix is combined with other * transforms (such as {@link #setScaleX(float)}, {@link #setRotation(float)}, etc.) * * @param matrix A transform matrix to apply to this display list - * - * @see #getMatrix(android.graphics.Matrix) - * @see #getMatrix() */ public void setStaticMatrix(Matrix matrix) { nSetStaticMatrix(mNativeDisplayList, matrix.native_instance); @@ -605,28 +627,6 @@ public class RenderNode { } /** - * Sets all of the transform-related values of the display list - * - * @param alpha The alpha value of the display list - * @param translationX The translationX value of the display list - * @param translationY The translationY value of the display list - * @param rotation The rotation value of the display list - * @param rotationX The rotationX value of the display list - * @param rotationY The rotationY value of the display list - * @param scaleX The scaleX value of the display list - * @param scaleY The scaleY value of the display list - * - * @hide - */ - public void setTransformationInfo(float alpha, - float translationX, float translationY, float translationZ, - float rotation, float rotationX, float rotationY, float scaleX, float scaleY) { - nSetTransformationInfo(mNativeDisplayList, alpha, - translationX, translationY, translationZ, - rotation, rotationX, rotationY, scaleX, scaleY); - } - - /** * Sets the pivot value for the display list on the X axis * * @param pivotX The pivot value of the display list on the X axis, in pixels @@ -668,6 +668,10 @@ public class RenderNode { return nGetPivotY(mNativeDisplayList); } + public boolean isPivotExplicitlySet() { + return nIsPivotExplicitlySet(mNativeDisplayList); + } + /** * Sets the camera distance for the display list. Refer to * {@link View#setCameraDistance(float)} for more information on how to @@ -834,6 +838,12 @@ public class RenderNode { private static native void nDestroyDisplayList(long displayList); private static native void nSetDisplayListName(long displayList, String name); + // Matrix + + private static native void nGetTransformMatrix(long displayList, long nativeMatrix); + private static native void nGetInverseTransformMatrix(long displayList, long nativeMatrix); + private static native boolean nHasIdentityMatrix(long displayList); + // Properties private static native void nOffsetTopAndBottom(long displayList, float offset); @@ -856,6 +866,8 @@ public class RenderNode { private static native void nSetOutlineConvexPath(long displayList, long nativePath); private static native void nSetOutlineEmpty(long displayList); private static native void nSetClipToOutline(long displayList, boolean clipToOutline); + private static native void nSetRevealClip(long displayList, + boolean shouldClip, boolean inverseClip, float x, float y, float radius); private static native void nSetAlpha(long displayList, float alpha); private static native void nSetHasOverlappingRendering(long displayList, boolean hasOverlappingRendering); @@ -867,9 +879,6 @@ public class RenderNode { private static native void nSetRotationY(long displayList, float rotationY); private static native void nSetScaleX(long displayList, float scaleX); private static native void nSetScaleY(long displayList, float scaleY); - private static native void nSetTransformationInfo(long displayList, float alpha, - float translationX, float translationY, float translationZ, - float rotation, float rotationX, float rotationY, float scaleX, float scaleY); private static native void nSetStaticMatrix(long displayList, long nativeMatrix); private static native void nSetAnimationMatrix(long displayList, long animationMatrix); @@ -888,6 +897,7 @@ public class RenderNode { private static native float nGetRotation(long displayList); private static native float nGetRotationX(long displayList); private static native float nGetRotationY(long displayList); + private static native boolean nIsPivotExplicitlySet(long displayList); private static native float nGetPivotX(long displayList); private static native float nGetPivotY(long displayList); private static native void nOutput(long displayList); diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java index 2a488a0..7b8a1ff 100644 --- a/core/java/android/view/ThreadedRenderer.java +++ b/core/java/android/view/ThreadedRenderer.java @@ -186,6 +186,11 @@ public class ThreadedRenderer extends HardwareRenderer { } @Override + public void invokeFunctor(long functor, boolean waitForCompletion) { + nInvokeFunctor(mNativeProxy, functor, waitForCompletion); + } + + @Override HardwareLayer createDisplayListLayer(int width, int height) { long layer = nCreateDisplayListLayer(mNativeProxy, width, height); return HardwareLayer.adoptDisplayListLayer(this, layer); @@ -266,6 +271,7 @@ public class ThreadedRenderer extends HardwareRenderer { private static native void nAttachFunctor(long nativeProxy, long functor); private static native void nDetachFunctor(long nativeProxy, long functor); + private static native void nInvokeFunctor(long nativeProxy, long functor, boolean waitForCompletion); private static native long nCreateDisplayListLayer(long nativeProxy, int width, int height); private static native long nCreateTextureLayer(long nativeProxy); diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 6ee99ec..6c414f6 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -16,6 +16,8 @@ package android.view; +import android.animation.RevealAnimator; +import android.animation.ValueAnimator; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -33,7 +35,6 @@ import android.graphics.LinearGradient; import android.graphics.Matrix; import android.graphics.Outline; import android.graphics.Paint; -import android.graphics.Path; import android.graphics.PixelFormat; import android.graphics.Point; import android.graphics.PorterDuff; @@ -1791,12 +1792,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, private static final int PFLAG_HOVERED = 0x10000000; /** - * Indicates that pivotX or pivotY were explicitly set and we should not assume the center - * for transform operations - * - * @hide + * no longer needed, should be reused */ - private static final int PFLAG_PIVOT_EXPLICITLY_SET = 0x20000000; + private static final int PFLAG_DOES_NOTHING_REUSE_PLEASE = 0x20000000; /** {@hide} */ static final int PFLAG_ACTIVATED = 0x40000000; @@ -2929,123 +2927,23 @@ public class View implements Drawable.Callback, KeyEvent.Callback, static class TransformationInfo { /** * The transform matrix for the View. This transform is calculated internally - * based on the rotation, scaleX, and scaleY properties. The identity matrix - * is used by default. Do *not* use this variable directly; instead call - * getMatrix(), which will automatically recalculate the matrix if necessary - * to get the correct matrix based on the latest rotation and scale properties. + * based on the translation, rotation, and scale properties. + * + * Do *not* use this variable directly; instead call getMatrix(), which will + * load the value from the View's RenderNode. */ private final Matrix mMatrix = new Matrix(); /** - * The transform matrix for the View. This transform is calculated internally - * based on the rotation, scaleX, and scaleY properties. The identity matrix - * is used by default. Do *not* use this variable directly; instead call - * getInverseMatrix(), which will automatically recalculate the matrix if necessary - * to get the correct matrix based on the latest rotation and scale properties. + * The inverse transform matrix for the View. This transform is calculated + * internally based on the translation, rotation, and scale properties. + * + * Do *not* use this variable directly; instead call getInverseMatrix(), + * which will load the value from the View's RenderNode. */ private Matrix mInverseMatrix; /** - * An internal variable that tracks whether we need to recalculate the - * transform matrix, based on whether the rotation or scaleX/Y properties - * have changed since the matrix was last calculated. - */ - boolean mMatrixDirty = false; - - /** - * An internal variable that tracks whether we need to recalculate the - * transform matrix, based on whether the rotation or scaleX/Y properties - * have changed since the matrix was last calculated. - */ - private boolean mInverseMatrixDirty = true; - - /** - * A variable that tracks whether we need to recalculate the - * transform matrix, based on whether the rotation or scaleX/Y properties - * have changed since the matrix was last calculated. This variable - * is only valid after a call to updateMatrix() or to a function that - * calls it such as getMatrix(), hasIdentityMatrix() and getInverseMatrix(). - */ - private boolean mMatrixIsIdentity = true; - - /** - * The Camera object is used to compute a 3D matrix when rotationX or rotationY are set. - */ - private Camera mCamera = null; - - /** - * This matrix is used when computing the matrix for 3D rotations. - */ - private Matrix matrix3D = null; - - /** - * These prev values are used to recalculate a centered pivot point when necessary. The - * pivot point is only used in matrix operations (when rotation, scale, or translation are - * set), so thes values are only used then as well. - */ - private int mPrevWidth = -1; - private int mPrevHeight = -1; - - /** - * The degrees rotation around the vertical axis through the pivot point. - */ - @ViewDebug.ExportedProperty - float mRotationY = 0f; - - /** - * The degrees rotation around the horizontal axis through the pivot point. - */ - @ViewDebug.ExportedProperty - float mRotationX = 0f; - - /** - * The degrees rotation around the pivot point. - */ - @ViewDebug.ExportedProperty - float mRotation = 0f; - - /** - * The amount of translation of the object away from its left property (post-layout). - */ - @ViewDebug.ExportedProperty - float mTranslationX = 0f; - - /** - * The amount of translation of the object away from its top property (post-layout). - */ - @ViewDebug.ExportedProperty - float mTranslationY = 0f; - - @ViewDebug.ExportedProperty - float mTranslationZ = 0f; - - /** - * The amount of scale in the x direction around the pivot point. A - * value of 1 means no scaling is applied. - */ - @ViewDebug.ExportedProperty - float mScaleX = 1f; - - /** - * The amount of scale in the y direction around the pivot point. A - * value of 1 means no scaling is applied. - */ - @ViewDebug.ExportedProperty - float mScaleY = 1f; - - /** - * The x location of the point around which the view is rotated and scaled. - */ - @ViewDebug.ExportedProperty - float mPivotX = 0f; - - /** - * The y location of the point around which the view is rotated and scaled. - */ - @ViewDebug.ExportedProperty - float mPivotY = 0f; - - /** * The opacity of the View. This is a value from 0 to 1, where 0 means * completely transparent and 1 means completely opaque. */ @@ -3550,13 +3448,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, private Bitmap mUnscaledDrawingCache; /** - * Display list used for the View content. + * RenderNode holding View properties, potentially holding a DisplayList of View content. * <p> * When non-null and valid, this is expected to contain an up-to-date copy - * of the View content. It is cleared on temporary detach and reset on + * of the View content. Its DisplayList content is cleared on temporary detach and reset on * cleanup. */ - RenderNode mDisplayList; + final RenderNode mRenderNode; /** * Set to true when the view is sending hover accessibility events because it @@ -3607,6 +3505,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, setOverScrollMode(OVER_SCROLL_IF_CONTENT_SCROLLS); mUserPaddingStart = UNDEFINED_PADDING; mUserPaddingEnd = UNDEFINED_PADDING; + mRenderNode = RenderNode.create(getClass().getName()); if (!sCompatibilityDone && context != null) { final int targetSdkVersion = context.getApplicationInfo().targetSdkVersion; @@ -4171,6 +4070,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ View() { mResources = null; + mRenderNode = RenderNode.create(getClass().getName()); } public String toString() { @@ -4846,10 +4746,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, final float x = r.exactCenterX(); final float y = r.exactCenterY(); - mBackground.setHotspot(Drawable.HOTSPOT_FOCUS, x, y); + mBackground.setHotspot(R.attr.state_focused, x, y); if (!focused) { - mBackground.removeHotspot(Drawable.HOTSPOT_FOCUS); + mBackground.removeHotspot(R.attr.state_focused); } } } @@ -9017,10 +8917,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @return True if the event was handled, false otherwise. */ public boolean onTouchEvent(MotionEvent event) { + final float x = event.getX(); + final float y = event.getY(); final int viewFlags = mViewFlags; if ((viewFlags & ENABLED_MASK) == DISABLED) { if (event.getAction() == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) { + clearHotspot(R.attr.state_pressed); setPressed(false); } // A disabled view that is clickable still consumes the touch @@ -9053,6 +8956,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, // showed it as pressed. Make it show the pressed // state now (before scheduling the click) to ensure // the user sees it. + setHotspot(R.attr.state_pressed, x, y); setPressed(true); } @@ -9085,7 +8989,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, // If the post failed, unpress right now mUnsetPressedState.run(); } + removeTapCallback(); + } else { + clearHotspot(R.attr.state_pressed); } break; @@ -9106,23 +9013,26 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (mPendingCheckForTap == null) { mPendingCheckForTap = new CheckForTap(); } + mPendingCheckForTap.x = event.getX(); + mPendingCheckForTap.y = event.getY(); postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout()); } else { // Not inside a scrolling container, so show the feedback right away + setHotspot(R.attr.state_pressed, x, y); setPressed(true); checkForLongClick(0); } break; case MotionEvent.ACTION_CANCEL: + clearHotspot(R.attr.state_pressed); setPressed(false); removeTapCallback(); removeLongPressCallback(); break; case MotionEvent.ACTION_MOVE: - final int x = (int) event.getX(); - final int y = (int) event.getY(); + setHotspot(R.attr.state_pressed, x, y); // Be lenient about moving outside of buttons if (!pointInView(x, y, mTouchSlop)) { @@ -9138,46 +9048,24 @@ public class View implements Drawable.Callback, KeyEvent.Callback, break; } - if (mBackground != null && mBackground.supportsHotspots()) { - manageTouchHotspot(event); - } - return true; } return false; } - private void manageTouchHotspot(MotionEvent event) { - switch (event.getAction()) { - case MotionEvent.ACTION_DOWN: - case MotionEvent.ACTION_POINTER_DOWN: { - final int index = event.getActionIndex(); - setPointerHotspot(event, index); - } break; - case MotionEvent.ACTION_MOVE: { - final int count = event.getPointerCount(); - for (int index = 0; index < count; index++) { - setPointerHotspot(event, index); - } - } break; - case MotionEvent.ACTION_POINTER_UP: { - final int actionIndex = event.getActionIndex(); - final int pointerId = event.getPointerId(actionIndex); - mBackground.removeHotspot(pointerId); - } break; - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_CANCEL: - mBackground.clearHotspots(); - break; + private void setHotspot(int id, float x, float y) { + final Drawable bg = mBackground; + if (bg != null && bg.supportsHotspots()) { + bg.setHotspot(id, x, y); } } - private void setPointerHotspot(MotionEvent event, int index) { - final int id = event.getPointerId(index); - final float x = event.getX(index); - final float y = event.getY(index); - mBackground.setHotspot(id, x, y); + private void clearHotspot(int id) { + final Drawable bg = mBackground; + if (bg != null && bg.supportsHotspots()) { + bg.removeHotspot(id); + } } /** @@ -9217,6 +9105,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ private void removeUnsetPressCallback() { if ((mPrivateFlags & PFLAG_PRESSED) != 0 && mUnsetPressedState != null) { + clearHotspot(R.attr.state_pressed); setPressed(false); removeCallbacks(mUnsetPressedState); } @@ -9695,21 +9584,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @return The current transform matrix for the view */ public Matrix getMatrix() { - if (mTransformationInfo != null) { - updateMatrix(); - return mTransformationInfo.mMatrix; - } - return Matrix.IDENTITY_MATRIX; - } - - /** - * Utility function to determine if the value is far enough away from zero to be - * considered non-zero. - * @param value A floating point value to check for zero-ness - * @return whether the passed-in value is far enough away from zero to be considered non-zero - */ - private static boolean nonzero(float value) { - return (value < -NONZERO_EPSILON || value > NONZERO_EPSILON); + ensureTransformationInfo(); + final Matrix matrix = mTransformationInfo.mMatrix; + mRenderNode.getMatrix(matrix); + return matrix; } /** @@ -9719,11 +9597,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @return True if the transform matrix is the identity matrix, false otherwise. */ final boolean hasIdentityMatrix() { - if (mTransformationInfo != null) { - updateMatrix(); - return mTransformationInfo.mMatrixIsIdentity; - } - return true; + return mRenderNode.hasIdentityMatrix(); } void ensureTransformationInfo() { @@ -9732,53 +9606,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } } - /** - * Recomputes the transform matrix if necessary. - */ - private void updateMatrix() { - final TransformationInfo info = mTransformationInfo; - if (info == null) { - return; - } - if (info.mMatrixDirty) { - // transform-related properties have changed since the last time someone - // asked for the matrix; recalculate it with the current values - - // Figure out if we need to update the pivot point - if ((mPrivateFlags & PFLAG_PIVOT_EXPLICITLY_SET) == 0) { - if ((mRight - mLeft) != info.mPrevWidth || (mBottom - mTop) != info.mPrevHeight) { - info.mPrevWidth = mRight - mLeft; - info.mPrevHeight = mBottom - mTop; - info.mPivotX = info.mPrevWidth / 2f; - info.mPivotY = info.mPrevHeight / 2f; - } - } - info.mMatrix.reset(); - if (!nonzero(info.mRotationX) && !nonzero(info.mRotationY)) { - info.mMatrix.setTranslate(info.mTranslationX, info.mTranslationY); - info.mMatrix.preRotate(info.mRotation, info.mPivotX, info.mPivotY); - info.mMatrix.preScale(info.mScaleX, info.mScaleY, info.mPivotX, info.mPivotY); - } else { - if (info.mCamera == null) { - info.mCamera = new Camera(); - info.matrix3D = new Matrix(); - } - info.mCamera.save(); - info.mMatrix.preScale(info.mScaleX, info.mScaleY, info.mPivotX, info.mPivotY); - info.mCamera.rotate(info.mRotationX, info.mRotationY, -info.mRotation); - info.mCamera.getMatrix(info.matrix3D); - info.matrix3D.preTranslate(-info.mPivotX, -info.mPivotY); - info.matrix3D.postTranslate(info.mPivotX + info.mTranslationX, - info.mPivotY + info.mTranslationY); - info.mMatrix.postConcat(info.matrix3D); - info.mCamera.restore(); - } - info.mMatrixDirty = false; - info.mMatrixIsIdentity = info.mMatrix.isIdentity(); - info.mInverseMatrixDirty = true; - } - } - /** * Utility method to retrieve the inverse of the current mMatrix property. * We cache the matrix to avoid recalculating it when transform properties @@ -9787,19 +9614,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @return The inverse of the current matrix of this view. */ final Matrix getInverseMatrix() { - final TransformationInfo info = mTransformationInfo; - if (info != null) { - updateMatrix(); - if (info.mInverseMatrixDirty) { - if (info.mInverseMatrix == null) { - info.mInverseMatrix = new Matrix(); - } - info.mMatrix.invert(info.mInverseMatrix); - info.mInverseMatrixDirty = false; - } - return info.mInverseMatrix; + ensureTransformationInfo(); + if (mTransformationInfo.mInverseMatrix == null) { + mTransformationInfo.mInverseMatrix = new Matrix(); } - return Matrix.IDENTITY_MATRIX; + final Matrix matrix = mTransformationInfo.mInverseMatrix; + mRenderNode.getInverseMatrix(matrix); + return matrix; } /** @@ -9810,14 +9631,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @return The distance along the Z axis. */ public float getCameraDistance() { - ensureTransformationInfo(); final float dpi = mResources.getDisplayMetrics().densityDpi; - final TransformationInfo info = mTransformationInfo; - if (info.mCamera == null) { - info.mCamera = new Camera(); - info.matrix3D = new Matrix(); - } - return -(info.mCamera.getLocationZ() * dpi); + return -(mRenderNode.getCameraDistance() * dpi); } /** @@ -9860,27 +9675,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @see #setRotationY(float) */ public void setCameraDistance(float distance) { - invalidateViewProperty(true, false); - - ensureTransformationInfo(); final float dpi = mResources.getDisplayMetrics().densityDpi; - final TransformationInfo info = mTransformationInfo; - if (info.mCamera == null) { - info.mCamera = new Camera(); - info.matrix3D = new Matrix(); - } - - info.mCamera.setLocation(0.0f, 0.0f, -Math.abs(distance) / dpi); - info.mMatrixDirty = true; + invalidateViewProperty(true, false); + mRenderNode.setCameraDistance(-Math.abs(distance) / dpi); invalidateViewProperty(false, false); - if (mDisplayList != null) { - mDisplayList.setCameraDistance(-Math.abs(distance) / dpi); - } - if ((mPrivateFlags2 & PFLAG2_VIEW_QUICK_REJECTED) == PFLAG2_VIEW_QUICK_REJECTED) { - // View was rejected last time it was drawn by its parent; this may have changed - invalidateParentIfNeeded(); - } + + invalidateParentIfNeededAndWasQuickRejected(); } /** @@ -9894,7 +9695,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ @ViewDebug.ExportedProperty(category = "drawing") public float getRotation() { - return mTransformationInfo != null ? mTransformationInfo.mRotation : 0; + return mRenderNode.getRotation(); } /** @@ -9912,21 +9713,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @attr ref android.R.styleable#View_rotation */ public void setRotation(float rotation) { - ensureTransformationInfo(); - final TransformationInfo info = mTransformationInfo; - if (info.mRotation != rotation) { + if (rotation != getRotation()) { // Double-invalidation is necessary to capture view's old and new areas invalidateViewProperty(true, false); - info.mRotation = rotation; - info.mMatrixDirty = true; + mRenderNode.setRotation(rotation); invalidateViewProperty(false, true); - if (mDisplayList != null) { - mDisplayList.setRotation(rotation); - } - if ((mPrivateFlags2 & PFLAG2_VIEW_QUICK_REJECTED) == PFLAG2_VIEW_QUICK_REJECTED) { - // View was rejected last time it was drawn by its parent; this may have changed - invalidateParentIfNeeded(); - } + + invalidateParentIfNeededAndWasQuickRejected(); } } @@ -9941,7 +9734,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ @ViewDebug.ExportedProperty(category = "drawing") public float getRotationY() { - return mTransformationInfo != null ? mTransformationInfo.mRotationY : 0; + return mRenderNode.getRotationY(); } /** @@ -9964,20 +9757,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @attr ref android.R.styleable#View_rotationY */ public void setRotationY(float rotationY) { - ensureTransformationInfo(); - final TransformationInfo info = mTransformationInfo; - if (info.mRotationY != rotationY) { + if (rotationY != getRotationY()) { invalidateViewProperty(true, false); - info.mRotationY = rotationY; - info.mMatrixDirty = true; + mRenderNode.setRotationY(rotationY); invalidateViewProperty(false, true); - if (mDisplayList != null) { - mDisplayList.setRotationY(rotationY); - } - if ((mPrivateFlags2 & PFLAG2_VIEW_QUICK_REJECTED) == PFLAG2_VIEW_QUICK_REJECTED) { - // View was rejected last time it was drawn by its parent; this may have changed - invalidateParentIfNeeded(); - } + + invalidateParentIfNeededAndWasQuickRejected(); } } @@ -9992,7 +9777,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ @ViewDebug.ExportedProperty(category = "drawing") public float getRotationX() { - return mTransformationInfo != null ? mTransformationInfo.mRotationX : 0; + return mRenderNode.getRotationX(); } /** @@ -10015,20 +9800,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @attr ref android.R.styleable#View_rotationX */ public void setRotationX(float rotationX) { - ensureTransformationInfo(); - final TransformationInfo info = mTransformationInfo; - if (info.mRotationX != rotationX) { + if (rotationX != getRotationX()) { invalidateViewProperty(true, false); - info.mRotationX = rotationX; - info.mMatrixDirty = true; + mRenderNode.setRotationX(rotationX); invalidateViewProperty(false, true); - if (mDisplayList != null) { - mDisplayList.setRotationX(rotationX); - } - if ((mPrivateFlags2 & PFLAG2_VIEW_QUICK_REJECTED) == PFLAG2_VIEW_QUICK_REJECTED) { - // View was rejected last time it was drawn by its parent; this may have changed - invalidateParentIfNeeded(); - } + + invalidateParentIfNeededAndWasQuickRejected(); } } @@ -10044,7 +9821,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ @ViewDebug.ExportedProperty(category = "drawing") public float getScaleX() { - return mTransformationInfo != null ? mTransformationInfo.mScaleX : 1; + return mRenderNode.getScaleX(); } /** @@ -10058,20 +9835,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @attr ref android.R.styleable#View_scaleX */ public void setScaleX(float scaleX) { - ensureTransformationInfo(); - final TransformationInfo info = mTransformationInfo; - if (info.mScaleX != scaleX) { + if (scaleX != getScaleX()) { invalidateViewProperty(true, false); - info.mScaleX = scaleX; - info.mMatrixDirty = true; + mRenderNode.setScaleX(scaleX); invalidateViewProperty(false, true); - if (mDisplayList != null) { - mDisplayList.setScaleX(scaleX); - } - if ((mPrivateFlags2 & PFLAG2_VIEW_QUICK_REJECTED) == PFLAG2_VIEW_QUICK_REJECTED) { - // View was rejected last time it was drawn by its parent; this may have changed - invalidateParentIfNeeded(); - } + + invalidateParentIfNeededAndWasQuickRejected(); } } @@ -10087,7 +9856,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ @ViewDebug.ExportedProperty(category = "drawing") public float getScaleY() { - return mTransformationInfo != null ? mTransformationInfo.mScaleY : 1; + return mRenderNode.getScaleY(); } /** @@ -10101,20 +9870,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @attr ref android.R.styleable#View_scaleY */ public void setScaleY(float scaleY) { - ensureTransformationInfo(); - final TransformationInfo info = mTransformationInfo; - if (info.mScaleY != scaleY) { + if (scaleY != getScaleY()) { invalidateViewProperty(true, false); - info.mScaleY = scaleY; - info.mMatrixDirty = true; + mRenderNode.setScaleY(scaleY); invalidateViewProperty(false, true); - if (mDisplayList != null) { - mDisplayList.setScaleY(scaleY); - } - if ((mPrivateFlags2 & PFLAG2_VIEW_QUICK_REJECTED) == PFLAG2_VIEW_QUICK_REJECTED) { - // View was rejected last time it was drawn by its parent; this may have changed - invalidateParentIfNeeded(); - } + + invalidateParentIfNeededAndWasQuickRejected(); } } @@ -10132,7 +9893,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ @ViewDebug.ExportedProperty(category = "drawing") public float getPivotX() { - return mTransformationInfo != null ? mTransformationInfo.mPivotX : 0; + return mRenderNode.getPivotX(); } /** @@ -10151,23 +9912,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @attr ref android.R.styleable#View_transformPivotX */ public void setPivotX(float pivotX) { - ensureTransformationInfo(); - final TransformationInfo info = mTransformationInfo; - boolean pivotSet = (mPrivateFlags & PFLAG_PIVOT_EXPLICITLY_SET) == - PFLAG_PIVOT_EXPLICITLY_SET; - if (info.mPivotX != pivotX || !pivotSet) { - mPrivateFlags |= PFLAG_PIVOT_EXPLICITLY_SET; + if (mRenderNode.isPivotExplicitlySet() || pivotX != getPivotX()) { invalidateViewProperty(true, false); - info.mPivotX = pivotX; - info.mMatrixDirty = true; + mRenderNode.setPivotX(pivotX); invalidateViewProperty(false, true); - if (mDisplayList != null) { - mDisplayList.setPivotX(pivotX); - } - if ((mPrivateFlags2 & PFLAG2_VIEW_QUICK_REJECTED) == PFLAG2_VIEW_QUICK_REJECTED) { - // View was rejected last time it was drawn by its parent; this may have changed - invalidateParentIfNeeded(); - } + + invalidateParentIfNeededAndWasQuickRejected(); } } @@ -10185,7 +9935,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ @ViewDebug.ExportedProperty(category = "drawing") public float getPivotY() { - return mTransformationInfo != null ? mTransformationInfo.mPivotY : 0; + return mRenderNode.getPivotY(); } /** @@ -10203,23 +9953,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @attr ref android.R.styleable#View_transformPivotY */ public void setPivotY(float pivotY) { - ensureTransformationInfo(); - final TransformationInfo info = mTransformationInfo; - boolean pivotSet = (mPrivateFlags & PFLAG_PIVOT_EXPLICITLY_SET) == - PFLAG_PIVOT_EXPLICITLY_SET; - if (info.mPivotY != pivotY || !pivotSet) { - mPrivateFlags |= PFLAG_PIVOT_EXPLICITLY_SET; + if (mRenderNode.isPivotExplicitlySet() || pivotY != getPivotY()) { invalidateViewProperty(true, false); - info.mPivotY = pivotY; - info.mMatrixDirty = true; + mRenderNode.setPivotY(pivotY); invalidateViewProperty(false, true); - if (mDisplayList != null) { - mDisplayList.setPivotY(pivotY); - } - if ((mPrivateFlags2 & PFLAG2_VIEW_QUICK_REJECTED) == PFLAG2_VIEW_QUICK_REJECTED) { - // View was rejected last time it was drawn by its parent; this may have changed - invalidateParentIfNeeded(); - } + + invalidateParentIfNeededAndWasQuickRejected(); } } @@ -10297,9 +10036,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } else { mPrivateFlags &= ~PFLAG_ALPHA_SET; invalidateViewProperty(true, false); - if (mDisplayList != null) { - mDisplayList.setAlpha(getFinalAlpha()); - } + mRenderNode.setAlpha(getFinalAlpha()); } } } @@ -10324,9 +10061,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, return true; } else { mPrivateFlags &= ~PFLAG_ALPHA_SET; - if (mDisplayList != null) { - mDisplayList.setAlpha(getFinalAlpha()); - } + mRenderNode.setAlpha(getFinalAlpha()); } } return false; @@ -10347,9 +10082,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mTransformationInfo.mTransitionAlpha = alpha; mPrivateFlags &= ~PFLAG_ALPHA_SET; invalidateViewProperty(true, false); - if (mDisplayList != null) { - mDisplayList.setAlpha(getFinalAlpha()); - } + mRenderNode.setAlpha(getFinalAlpha()); } } @@ -10396,9 +10129,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ public final void setTop(int top) { if (top != mTop) { - updateMatrix(); - final boolean matrixIsIdentity = mTransformationInfo == null - || mTransformationInfo.mMatrixIsIdentity; + final boolean matrixIsIdentity = hasIdentityMatrix(); if (matrixIsIdentity) { if (mAttachInfo != null) { int minTop; @@ -10421,17 +10152,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, int oldHeight = mBottom - mTop; mTop = top; - if (mDisplayList != null) { - mDisplayList.setTop(mTop); - } + mRenderNode.setTop(mTop); sizeChange(width, mBottom - mTop, width, oldHeight); if (!matrixIsIdentity) { - if ((mPrivateFlags & PFLAG_PIVOT_EXPLICITLY_SET) == 0) { - // A change in dimension means an auto-centered pivot point changes, too - mTransformationInfo.mMatrixDirty = true; - } mPrivateFlags |= PFLAG_DRAWN; // force another invalidation with the new orientation invalidate(true); } @@ -10472,9 +10197,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ public final void setBottom(int bottom) { if (bottom != mBottom) { - updateMatrix(); - final boolean matrixIsIdentity = mTransformationInfo == null - || mTransformationInfo.mMatrixIsIdentity; + final boolean matrixIsIdentity = hasIdentityMatrix(); if (matrixIsIdentity) { if (mAttachInfo != null) { int maxBottom; @@ -10494,17 +10217,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, int oldHeight = mBottom - mTop; mBottom = bottom; - if (mDisplayList != null) { - mDisplayList.setBottom(mBottom); - } + mRenderNode.setBottom(mBottom); sizeChange(width, mBottom - mTop, width, oldHeight); if (!matrixIsIdentity) { - if ((mPrivateFlags & PFLAG_PIVOT_EXPLICITLY_SET) == 0) { - // A change in dimension means an auto-centered pivot point changes, too - mTransformationInfo.mMatrixDirty = true; - } mPrivateFlags |= PFLAG_DRAWN; // force another invalidation with the new orientation invalidate(true); } @@ -10536,9 +10253,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ public final void setLeft(int left) { if (left != mLeft) { - updateMatrix(); - final boolean matrixIsIdentity = mTransformationInfo == null - || mTransformationInfo.mMatrixIsIdentity; + final boolean matrixIsIdentity = hasIdentityMatrix(); if (matrixIsIdentity) { if (mAttachInfo != null) { int minLeft; @@ -10561,17 +10276,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, int height = mBottom - mTop; mLeft = left; - if (mDisplayList != null) { - mDisplayList.setLeft(left); - } + mRenderNode.setLeft(left); sizeChange(mRight - mLeft, height, oldWidth, height); if (!matrixIsIdentity) { - if ((mPrivateFlags & PFLAG_PIVOT_EXPLICITLY_SET) == 0) { - // A change in dimension means an auto-centered pivot point changes, too - mTransformationInfo.mMatrixDirty = true; - } mPrivateFlags |= PFLAG_DRAWN; // force another invalidation with the new orientation invalidate(true); } @@ -10603,9 +10312,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ public final void setRight(int right) { if (right != mRight) { - updateMatrix(); - final boolean matrixIsIdentity = mTransformationInfo == null - || mTransformationInfo.mMatrixIsIdentity; + final boolean matrixIsIdentity = hasIdentityMatrix(); if (matrixIsIdentity) { if (mAttachInfo != null) { int maxRight; @@ -10625,17 +10332,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, int height = mBottom - mTop; mRight = right; - if (mDisplayList != null) { - mDisplayList.setRight(mRight); - } + mRenderNode.setRight(mRight); sizeChange(mRight - mLeft, height, oldWidth, height); if (!matrixIsIdentity) { - if ((mPrivateFlags & PFLAG_PIVOT_EXPLICITLY_SET) == 0) { - // A change in dimension means an auto-centered pivot point changes, too - mTransformationInfo.mMatrixDirty = true; - } mPrivateFlags |= PFLAG_DRAWN; // force another invalidation with the new orientation invalidate(true); } @@ -10657,7 +10358,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ @ViewDebug.ExportedProperty(category = "drawing") public float getX() { - return mLeft + (mTransformationInfo != null ? mTransformationInfo.mTranslationX : 0); + return mLeft + getTranslationX(); } /** @@ -10680,7 +10381,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ @ViewDebug.ExportedProperty(category = "drawing") public float getY() { - return mTop + (mTransformationInfo != null ? mTransformationInfo.mTranslationY : 0); + return mTop + getTranslationY(); } /** @@ -10704,7 +10405,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ @ViewDebug.ExportedProperty(category = "drawing") public float getTranslationX() { - return mTransformationInfo != null ? mTransformationInfo.mTranslationX : 0; + return mRenderNode.getTranslationX(); } /** @@ -10718,21 +10419,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @attr ref android.R.styleable#View_translationX */ public void setTranslationX(float translationX) { - ensureTransformationInfo(); - final TransformationInfo info = mTransformationInfo; - if (info.mTranslationX != translationX) { - // Double-invalidation is necessary to capture view's old and new areas + if (translationX != getTranslationX()) { invalidateViewProperty(true, false); - info.mTranslationX = translationX; - info.mMatrixDirty = true; + mRenderNode.setTranslationX(translationX); invalidateViewProperty(false, true); - if (mDisplayList != null) { - mDisplayList.setTranslationX(translationX); - } - if ((mPrivateFlags2 & PFLAG2_VIEW_QUICK_REJECTED) == PFLAG2_VIEW_QUICK_REJECTED) { - // View was rejected last time it was drawn by its parent; this may have changed - invalidateParentIfNeeded(); - } + + invalidateParentIfNeededAndWasQuickRejected(); } } @@ -10746,7 +10438,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ @ViewDebug.ExportedProperty(category = "drawing") public float getTranslationY() { - return mTransformationInfo != null ? mTransformationInfo.mTranslationY : 0; + return mRenderNode.getTranslationY(); } /** @@ -10760,20 +10452,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @attr ref android.R.styleable#View_translationY */ public void setTranslationY(float translationY) { - ensureTransformationInfo(); - final TransformationInfo info = mTransformationInfo; - if (info.mTranslationY != translationY) { + if (translationY != getTranslationY()) { invalidateViewProperty(true, false); - info.mTranslationY = translationY; - info.mMatrixDirty = true; + mRenderNode.setTranslationY(translationY); invalidateViewProperty(false, true); - if (mDisplayList != null) { - mDisplayList.setTranslationY(translationY); - } - if ((mPrivateFlags2 & PFLAG2_VIEW_QUICK_REJECTED) == PFLAG2_VIEW_QUICK_REJECTED) { - // View was rejected last time it was drawn by its parent; this may have changed - invalidateParentIfNeeded(); - } + + invalidateParentIfNeededAndWasQuickRejected(); } } @@ -10784,7 +10468,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ @ViewDebug.ExportedProperty(category = "drawing") public float getTranslationZ() { - return mTransformationInfo != null ? mTransformationInfo.mTranslationZ : 0; + return mRenderNode.getTranslationZ(); } /** @@ -10793,24 +10477,53 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @attr ref android.R.styleable#View_translationZ */ public void setTranslationZ(float translationZ) { - ensureTransformationInfo(); - final TransformationInfo info = mTransformationInfo; - if (info.mTranslationZ != translationZ) { + if (translationZ != getTranslationZ()) { invalidateViewProperty(true, false); - info.mTranslationZ = translationZ; - info.mMatrixDirty = true; + mRenderNode.setTranslationZ(translationZ); invalidateViewProperty(false, true); - if (mDisplayList != null) { - mDisplayList.setTranslationZ(translationZ); - } - if ((mPrivateFlags2 & PFLAG2_VIEW_QUICK_REJECTED) == PFLAG2_VIEW_QUICK_REJECTED) { - // View was rejected last time it was drawn by its parent; this may have changed - invalidateParentIfNeeded(); - } + + invalidateParentIfNeededAndWasQuickRejected(); } } /** + * Returns a ValueAnimator which can animate a clipping circle. + * <p> + * The View will be clipped to the animating circle. + * <p> + * Any shadow cast by the View will respect the circular clip from this animator. + * + * @param centerX The x coordinate of the center of the animating circle. + * @param centerY The y coordinate of the center of the animating circle. + * @param startRadius The starting radius of the animating circle. + * @param endRadius The ending radius of the animating circle. + */ + public final ValueAnimator createRevealAnimator(int centerX, int centerY, + float startRadius, float endRadius) { + return RevealAnimator.ofRevealCircle(this, centerX, centerY, + startRadius, endRadius, false); + } + + /** + * Returns a ValueAnimator which can animate a clearing circle. + * <p> + * The View is prevented from drawing within the circle, so the content + * behind the View shows through. + * + * @param centerX The x coordinate of the center of the animating circle. + * @param centerY The y coordinate of the center of the animating circle. + * @param startRadius The starting radius of the animating circle. + * @param endRadius The ending radius of the animating circle. + * + * @hide + */ + public final ValueAnimator createClearCircleAnimator(int centerX, int centerY, + float startRadius, float endRadius) { + return RevealAnimator.ofRevealCircle(this, centerX, centerY, + startRadius, endRadius, true); + } + + /** * Sets the outline of the view, which defines the shape of the shadow it * casts, and can used for clipping. * <p> @@ -10839,10 +10552,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } mOutline.set(outline); } - - if (mDisplayList != null) { - mDisplayList.setOutline(mOutline); - } + mRenderNode.setOutline(mOutline); } /** @@ -10877,26 +10587,34 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } else { mPrivateFlags3 &= ~PFLAG3_CLIP_TO_OUTLINE; } - if (mDisplayList != null) { - mDisplayList.setClipToOutline(clipToOutline); - } + mRenderNode.setClipToOutline(clipToOutline); } } /** + * Private API to be used for reveal animation + * + * @hide + */ + public void setRevealClip(boolean shouldClip, boolean inverseClip, + float x, float y, float radius) { + mRenderNode.setRevealClip(shouldClip, inverseClip, x, y, radius); + // TODO: Handle this invalidate in a better way, or purely in native. + invalidate(); + } + + /** * Hit rectangle in parent's coordinates * * @param outRect The hit rectangle of the view. */ public void getHitRect(Rect outRect) { - updateMatrix(); - final TransformationInfo info = mTransformationInfo; - if (info == null || info.mMatrixIsIdentity || mAttachInfo == null) { + if (hasIdentityMatrix() || mAttachInfo == null) { outRect.set(mLeft, mTop, mRight, mBottom); } else { final RectF tmpRect = mAttachInfo.mTmpTransformRect; tmpRect.set(0, 0, getWidth(), getHeight()); - info.mMatrix.mapRect(tmpRect); + getMatrix().mapRect(tmpRect); // TODO: mRenderNode.mapRect(tmpRect) outRect.set((int) tmpRect.left + mLeft, (int) tmpRect.top + mTop, (int) tmpRect.right + mLeft, (int) tmpRect.bottom + mTop); } @@ -10985,11 +10703,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ public void offsetTopAndBottom(int offset) { if (offset != 0) { - updateMatrix(); - final boolean matrixIsIdentity = mTransformationInfo == null - || mTransformationInfo.mMatrixIsIdentity; + final boolean matrixIsIdentity = hasIdentityMatrix(); if (matrixIsIdentity) { - if (mDisplayList != null) { + if (isHardwareAccelerated()) { invalidateViewProperty(false, false); } else { final ViewParent p = mParent; @@ -11017,8 +10733,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mTop += offset; mBottom += offset; - if (mDisplayList != null) { - mDisplayList.offsetTopAndBottom(offset); + mRenderNode.offsetTopAndBottom(offset); + if (isHardwareAccelerated()) { invalidateViewProperty(false, false); } else { if (!matrixIsIdentity) { @@ -11036,11 +10752,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ public void offsetLeftAndRight(int offset) { if (offset != 0) { - updateMatrix(); - final boolean matrixIsIdentity = mTransformationInfo == null - || mTransformationInfo.mMatrixIsIdentity; + final boolean matrixIsIdentity = hasIdentityMatrix(); if (matrixIsIdentity) { - if (mDisplayList != null) { + if (isHardwareAccelerated()) { invalidateViewProperty(false, false); } else { final ViewParent p = mParent; @@ -11065,8 +10779,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mLeft += offset; mRight += offset; - if (mDisplayList != null) { - mDisplayList.offsetLeftAndRight(offset); + mRenderNode.offsetLeftAndRight(offset); + if (isHardwareAccelerated()) { invalidateViewProperty(false, false); } else { if (!matrixIsIdentity) { @@ -11446,7 +11160,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } // Damage the entire IsolatedZVolume recieving this view's shadow. - if (getTranslationZ() != 0) { + if (isHardwareAccelerated() && getTranslationZ() != 0) { damageShadowReceiver(); } } @@ -11509,7 +11223,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * list properties are not being used in this view */ void invalidateViewProperty(boolean invalidateParent, boolean forceRedraw) { - if (mDisplayList == null || (mPrivateFlags & PFLAG_DRAW_ANIMATION) == PFLAG_DRAW_ANIMATION) { + if (!isHardwareAccelerated() + || !mRenderNode.isValid() + || (mPrivateFlags & PFLAG_DRAW_ANIMATION) != 0) { if (invalidateParent) { invalidateParentCaches(); } @@ -11520,7 +11236,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } else { damageInParent(); } - if (invalidateParent && getTranslationZ() != 0) { + if (isHardwareAccelerated() && invalidateParent && getTranslationZ() != 0) { damageShadowReceiver(); } } @@ -11537,7 +11253,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, final Rect r = ai.mTmpInvalRect; r.set(0, 0, mRight - mLeft, mBottom - mTop); if (mParent instanceof ViewGroup) { - ((ViewGroup) mParent).invalidateChildFast(this, r); + ((ViewGroup) mParent).damageChild(this, r); } else { mParent.invalidateChild(this, r); } @@ -11590,6 +11306,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** + * @hide + */ + protected void invalidateParentIfNeededAndWasQuickRejected() { + if ((mPrivateFlags2 & PFLAG2_VIEW_QUICK_REJECTED) != 0) { + // View was rejected last time it was drawn by its parent; this may have changed + invalidateParentIfNeeded(); + } + } + + /** * Indicates whether this View is opaque. An opaque View guarantees that it will * draw all the pixels overlapping its bounds using a fully opaque color. * @@ -13676,10 +13402,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mHardwareLayer.setLayerPaint(mLayerPaint); RenderNode displayList = mHardwareLayer.startRecording(); - if (getDisplayList(displayList, true) != displayList) { - throw new IllegalStateException("getDisplayList() didn't return" - + " the input displaylist for a hardware layer!"); - } + updateDisplayListIfDirty(displayList, true); mHardwareLayer.endRecording(mLocalDirtyRect); mLocalDirtyRect.setEmpty(); } @@ -13820,29 +13543,34 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * Otherwise, the same display list will be returned (after having been rendered into * along the way, depending on the invalidation state of the view). * - * @param displayList The previous version of this displayList, could be null. + * @param renderNode The previous version of this displayList, could be null. * @param isLayer Whether the requester of the display list is a layer. If so, * the view will avoid creating a layer inside the resulting display list. * @return A new or reused DisplayList object. */ - private RenderNode getDisplayList(RenderNode displayList, boolean isLayer) { + private void updateDisplayListIfDirty(@NonNull RenderNode renderNode, boolean isLayer) { final HardwareRenderer renderer = getHardwareRenderer(); + if (renderNode == null) { + throw new IllegalArgumentException("RenderNode must not be null"); + } if (renderer == null || !canHaveDisplayList()) { - return null; + // can't populate RenderNode, don't try + return; } - if (((mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == 0 || - displayList == null || !displayList.isValid() || - (!isLayer && mRecreateDisplayList))) { + if ((mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == 0 + || !renderNode.isValid() + || (!isLayer && mRecreateDisplayList)) { // Don't need to recreate the display list, just need to tell our // children to restore/recreate theirs - if (displayList != null && displayList.isValid() && - !isLayer && !mRecreateDisplayList) { + if (renderNode.isValid() + && !isLayer + && !mRecreateDisplayList) { mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID; mPrivateFlags &= ~PFLAG_DIRTY_MASK; dispatchGetDisplayList(); - return displayList; + return; // no work needed } if (!isLayer) { @@ -13850,20 +13578,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, // we copy in child display lists into ours in drawChild() mRecreateDisplayList = true; } - if (displayList == null) { - displayList = RenderNode.create(getClass().getName()); - // If we're creating a new display list, make sure our parent gets invalidated - // since they will need to recreate their display list to account for this - // new child display list. - invalidateParentCaches(); - } boolean caching = false; int width = mRight - mLeft; int height = mBottom - mTop; int layerType = getLayerType(); - final HardwareCanvas canvas = displayList.start(width, height); + final HardwareCanvas canvas = renderNode.start(width, height); try { if (!isLayer && layerType != LAYER_TYPE_NONE) { @@ -13906,12 +13627,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } } } finally { - displayList.end(renderer, canvas); - displayList.setCaching(caching); + renderNode.end(renderer, canvas); + renderNode.setCaching(caching); if (isLayer) { - displayList.setLeftTopRightBottom(0, 0, width, height); + renderNode.setLeftTopRightBottom(0, 0, width, height); } else { - setDisplayListProperties(displayList); + setDisplayListProperties(renderNode); } if (renderer != getHardwareRenderer()) { @@ -13926,26 +13647,24 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID; mPrivateFlags &= ~PFLAG_DIRTY_MASK; } - - return displayList; } /** - * <p>Returns a display list that can be used to draw this view again - * without executing its draw method.</p> + * Returns a RenderNode with View draw content recorded, which can be + * used to draw this view again without executing its draw method. * - * @return A DisplayList ready to replay, or null if caching is not enabled. + * @return A RenderNode ready to replay, or null if caching is not enabled. * * @hide */ public RenderNode getDisplayList() { - mDisplayList = getDisplayList(mDisplayList, false); - return mDisplayList; + updateDisplayListIfDirty(mRenderNode, false); + return mRenderNode; } private void resetDisplayList() { - if (mDisplayList != null && mDisplayList.isValid()) { - mDisplayList.destroyDisplayListData(); + if (mRenderNode.isValid()) { + mRenderNode.destroyDisplayListData(); } if (mBackgroundDisplayList != null && mBackgroundDisplayList.isValid()) { @@ -14539,21 +14258,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** - * This method is called by getDisplayList() when a display list is created or re-rendered. - * It sets or resets the current value of all properties on that display list (resetting is - * necessary when a display list is being re-created, because we need to make sure that - * previously-set transform values + * This method is called by getDisplayList() when a display list is recorded for a View. + * It pushes any properties to the RenderNode that aren't managed by the RenderNode. */ - void setDisplayListProperties(RenderNode displayList) { - if (displayList != null) { - displayList.setLeftTopRightBottom(mLeft, mTop, mRight, mBottom); - displayList.setHasOverlappingRendering(hasOverlappingRendering()); + void setDisplayListProperties(RenderNode renderNode) { + if (renderNode != null) { + renderNode.setHasOverlappingRendering(hasOverlappingRendering()); if (mParent instanceof ViewGroup) { - displayList.setClipToBounds( + renderNode.setClipToBounds( (((ViewGroup) mParent).mGroupFlags & ViewGroup.FLAG_CLIP_CHILDREN) != 0); } - displayList.setOutline(mOutline); - displayList.setClipToOutline(getClipToOutline()); float alpha = 1; if (mParent instanceof ViewGroup && (((ViewGroup) mParent).mGroupFlags & ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) { @@ -14566,7 +14280,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, alpha = t.getAlpha(); } if ((transformType & Transformation.TYPE_MATRIX) != 0) { - displayList.setStaticMatrix(t.getMatrix()); + renderNode.setStaticMatrix(t.getMatrix()); } } } @@ -14579,23 +14293,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, alpha = 1; } } - displayList.setTransformationInfo(alpha, - mTransformationInfo.mTranslationX, mTransformationInfo.mTranslationY, - mTransformationInfo.mTranslationZ, - mTransformationInfo.mRotation, mTransformationInfo.mRotationX, - mTransformationInfo.mRotationY, mTransformationInfo.mScaleX, - mTransformationInfo.mScaleY); - if (mTransformationInfo.mCamera == null) { - mTransformationInfo.mCamera = new Camera(); - mTransformationInfo.matrix3D = new Matrix(); - } - displayList.setCameraDistance(mTransformationInfo.mCamera.getLocationZ()); - if ((mPrivateFlags & PFLAG_PIVOT_EXPLICITLY_SET) == PFLAG_PIVOT_EXPLICITLY_SET) { - displayList.setPivotX(getPivotX()); - displayList.setPivotY(getPivotY()); - } + renderNode.setAlpha(alpha); } else if (alpha < 1) { - displayList.setAlpha(alpha); + renderNode.setAlpha(alpha); } } } @@ -14642,10 +14342,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } transformToApply = parent.getChildTransformation(); } else { - if ((mPrivateFlags3 & PFLAG3_VIEW_IS_ANIMATING_TRANSFORM) == - PFLAG3_VIEW_IS_ANIMATING_TRANSFORM && mDisplayList != null) { + if ((mPrivateFlags3 & PFLAG3_VIEW_IS_ANIMATING_TRANSFORM) != 0) { // No longer animating: clear out old animation matrix - mDisplayList.setAnimationMatrix(null); + mRenderNode.setAnimationMatrix(null); mPrivateFlags3 &= ~PFLAG3_VIEW_IS_ANIMATING_TRANSFORM; } if (!useDisplayListProperties && @@ -15158,9 +14857,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if ((mPrivateFlags3 & PFLAG3_OUTLINE_DEFINED) == 0) { // Outline not currently define, query from background mOutline = background.getOutline(); - if (mDisplayList != null) { - mDisplayList.setOutline(mOutline); - } + mRenderNode.setOutline(mOutline); } } @@ -15495,20 +15192,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mTop = top; mRight = right; mBottom = bottom; - if (mDisplayList != null) { - mDisplayList.setLeftTopRightBottom(mLeft, mTop, mRight, mBottom); - } + mRenderNode.setLeftTopRightBottom(mLeft, mTop, mRight, mBottom); mPrivateFlags |= PFLAG_HAS_BOUNDS; if (sizeChanged) { - if ((mPrivateFlags & PFLAG_PIVOT_EXPLICITLY_SET) == 0) { - // A change in dimension means an auto-centered pivot point changes, too - if (mTransformationInfo != null) { - mTransformationInfo.mMatrixDirty = true; - } - } sizeChange(newWidth, newHeight, oldWidth, oldHeight); } @@ -19082,10 +18771,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } } - class CheckForLongPress implements Runnable { - + private final class CheckForLongPress implements Runnable { private int mOriginalWindowAttachCount; + @Override public void run() { if (isPressed() && (mParent != null) && mOriginalWindowAttachCount == mWindowAttachCount) { @@ -19101,14 +18790,20 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } private final class CheckForTap implements Runnable { + public float x; + public float y; + + @Override public void run() { mPrivateFlags &= ~PFLAG_PREPRESSED; + setHotspot(R.attr.state_pressed, x, y); setPressed(true); checkForLongClick(ViewConfiguration.getTapTimeout()); } } private final class PerformClick implements Runnable { + @Override public void run() { performClick(); } @@ -19387,7 +19082,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } private final class UnsetPressedState implements Runnable { + @Override public void run() { + clearHotspot(R.attr.state_pressed); setPressed(false); } } diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index aadaa7f..d2c6302 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -31,7 +31,6 @@ import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Region; import android.os.Build; -import android.os.Bundle; import android.os.Parcelable; import android.os.SystemClock; import android.util.AttributeSet; @@ -3174,8 +3173,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager setBooleanFlag(FLAG_CLIP_CHILDREN, clipChildren); for (int i = 0; i < mChildrenCount; ++i) { View child = getChildAt(i); - if (child.mDisplayList != null) { - child.mDisplayList.setClipToBounds(clipChildren); + if (child.mRenderNode != null) { + child.mRenderNode.setClipToBounds(clipChildren); } } } @@ -4450,7 +4449,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * * @hide */ - public void invalidateChildFast(View child, final Rect dirty) { + public void damageChild(View child, final Rect dirty) { ViewParent parent = this; final AttachInfo attachInfo = mAttachInfo; @@ -4473,7 +4472,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager parentVG.invalidate(); parent = null; } else { - parent = parentVG.invalidateChildInParentFast(left, top, dirty); + parent = parentVG.damageChildInParent(left, top, dirty); left = parentVG.mLeft; top = parentVG.mTop; } @@ -4495,9 +4494,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * * @hide */ - protected ViewParent invalidateChildInParentFast(int left, int top, final Rect dirty) { - if ((mPrivateFlags & PFLAG_DRAWN) == PFLAG_DRAWN || - (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID) { + protected ViewParent damageChildInParent(int left, int top, final Rect dirty) { + if ((mPrivateFlags & PFLAG_DRAWN) != 0 + || (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) != 0) { dirty.offset(left - mScrollX, top - mScrollY); if ((mGroupFlags & FLAG_CLIP_CHILDREN) == 0) { dirty.union(0, 0, mRight - mLeft, mBottom - mTop); @@ -4610,9 +4609,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager final View v = children[i]; v.mTop += offset; v.mBottom += offset; - if (v.mDisplayList != null) { + if (v.mRenderNode != null) { invalidate = true; - v.mDisplayList.offsetTopAndBottom(offset); + v.mRenderNode.offsetTopAndBottom(offset); } } diff --git a/core/java/android/view/ViewOverlay.java b/core/java/android/view/ViewOverlay.java index 47de780..0cf9ddd 100644 --- a/core/java/android/view/ViewOverlay.java +++ b/core/java/android/view/ViewOverlay.java @@ -290,7 +290,11 @@ public class ViewOverlay { } } - public void invalidateChildFast(View child, final Rect dirty) { + /** + * @hide + */ + @Override + public void damageChild(View child, final Rect dirty) { if (mHostView != null) { // Note: This is not a "fast" invalidation. Would be nice to instead invalidate // using DisplayList properties and a dirty rect instead of causing a real @@ -309,9 +313,9 @@ public class ViewOverlay { * @hide */ @Override - protected ViewParent invalidateChildInParentFast(int left, int top, Rect dirty) { + protected ViewParent damageChildInParent(int left, int top, Rect dirty) { if (mHostView instanceof ViewGroup) { - return ((ViewGroup) mHostView).invalidateChildInParentFast(left, top, dirty); + return ((ViewGroup) mHostView).damageChildInParent(left, top, dirty); } return null; } diff --git a/core/java/android/view/ViewPropertyAnimator.java b/core/java/android/view/ViewPropertyAnimator.java index 563ffb7..6b21451 100644 --- a/core/java/android/view/ViewPropertyAnimator.java +++ b/core/java/android/view/ViewPropertyAnimator.java @@ -925,51 +925,41 @@ public class ViewPropertyAnimator { */ private void setValue(int propertyConstant, float value) { final View.TransformationInfo info = mView.mTransformationInfo; - final RenderNode displayList = mView.mDisplayList; + final RenderNode renderNode = mView.mRenderNode; switch (propertyConstant) { case TRANSLATION_X: - info.mTranslationX = value; - if (displayList != null) displayList.setTranslationX(value); + renderNode.setTranslationX(value); break; case TRANSLATION_Y: - info.mTranslationY = value; - if (displayList != null) displayList.setTranslationY(value); + renderNode.setTranslationY(value); break; case TRANSLATION_Z: - info.mTranslationZ = value; - if (displayList != null) displayList.setTranslationZ(value); + renderNode.setTranslationZ(value); break; case ROTATION: - info.mRotation = value; - if (displayList != null) displayList.setRotation(value); + renderNode.setRotation(value); break; case ROTATION_X: - info.mRotationX = value; - if (displayList != null) displayList.setRotationX(value); + renderNode.setRotationX(value); break; case ROTATION_Y: - info.mRotationY = value; - if (displayList != null) displayList.setRotationY(value); + renderNode.setRotationY(value); break; case SCALE_X: - info.mScaleX = value; - if (displayList != null) displayList.setScaleX(value); + renderNode.setScaleX(value); break; case SCALE_Y: - info.mScaleY = value; - if (displayList != null) displayList.setScaleY(value); + renderNode.setScaleY(value); break; case X: - info.mTranslationX = value - mView.mLeft; - if (displayList != null) displayList.setTranslationX(value - mView.mLeft); + renderNode.setTranslationX(value - mView.mLeft); break; case Y: - info.mTranslationY = value - mView.mTop; - if (displayList != null) displayList.setTranslationY(value - mView.mTop); + renderNode.setTranslationY(value - mView.mTop); break; case ALPHA: info.mAlpha = value; - if (displayList != null) displayList.setAlpha(value); + renderNode.setAlpha(value); break; } } @@ -981,30 +971,30 @@ public class ViewPropertyAnimator { * @return float The value of the named property */ private float getValue(int propertyConstant) { - final View.TransformationInfo info = mView.mTransformationInfo; + final RenderNode node = mView.mRenderNode; switch (propertyConstant) { case TRANSLATION_X: - return info.mTranslationX; + return node.getTranslationX(); case TRANSLATION_Y: - return info.mTranslationY; + return node.getTranslationY(); case TRANSLATION_Z: - return info.mTranslationZ; + return node.getTranslationZ(); case ROTATION: - return info.mRotation; + return node.getRotation(); case ROTATION_X: - return info.mRotationX; + return node.getRotationX(); case ROTATION_Y: - return info.mRotationY; + return node.getRotationY(); case SCALE_X: - return info.mScaleX; + return node.getScaleX(); case SCALE_Y: - return info.mScaleY; + return node.getScaleY(); case X: - return mView.mLeft + info.mTranslationX; + return mView.mLeft + node.getTranslationX(); case Y: - return mView.mTop + info.mTranslationY; + return mView.mTop + node.getTranslationY(); case ALPHA: - return info.mAlpha; + return mView.mTransformationInfo.mAlpha; } return 0; } @@ -1093,7 +1083,7 @@ public class ViewPropertyAnimator { // Shouldn't happen, but just to play it safe return; } - boolean useDisplayListProperties = mView.mDisplayList != null; + boolean useRenderNodeProperties = mView.mRenderNode != null; // alpha requires slightly different treatment than the other (transform) properties. // The logic in setAlpha() is not simply setting mAlpha, plus the invalidation @@ -1101,7 +1091,7 @@ public class ViewPropertyAnimator { // We track what kinds of properties are set, and how alpha is handled when it is // set, and perform the invalidation steps appropriately. boolean alphaHandled = false; - if (!useDisplayListProperties) { + if (!useRenderNodeProperties) { mView.invalidateParentCaches(); } float fraction = animation.getAnimatedFraction(); @@ -1123,8 +1113,7 @@ public class ViewPropertyAnimator { } } if ((propertyMask & TRANSFORM_MASK) != 0) { - mView.mTransformationInfo.mMatrixDirty = true; - if (!useDisplayListProperties) { + if (!useRenderNodeProperties) { mView.mPrivateFlags |= View.PFLAG_DRAWN; // force another invalidation } } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index e0e523e..a35c28e 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -1465,8 +1465,8 @@ public final class ViewRootImpl implements ViewParent, mWidth, mHeight); } mResizeBuffer.prepare(mWidth, mHeight, false); - RenderNode layerDisplayList = mResizeBuffer.startRecording(); - HardwareCanvas layerCanvas = layerDisplayList.start(mWidth, mHeight); + RenderNode layerRenderNode = mResizeBuffer.startRecording(); + HardwareCanvas layerCanvas = layerRenderNode.start(mWidth, mHeight); final int restoreCount = layerCanvas.save(); int yoff; @@ -1484,9 +1484,9 @@ public final class ViewRootImpl implements ViewParent, mTranslator.translateCanvas(layerCanvas); } - RenderNode displayList = mView.mDisplayList; - if (displayList != null && displayList.isValid()) { - layerCanvas.drawDisplayList(displayList, null, + RenderNode renderNode = mView.mRenderNode; + if (renderNode != null && renderNode.isValid()) { + layerCanvas.drawDisplayList(renderNode, null, RenderNode.FLAG_CLIP_CHILDREN); } else { mView.draw(layerCanvas); @@ -1499,9 +1499,9 @@ public final class ViewRootImpl implements ViewParent, com.android.internal.R.integer.config_mediumAnimTime); layerCanvas.restoreToCount(restoreCount); - layerDisplayList.end(mAttachInfo.mHardwareRenderer, layerCanvas); - layerDisplayList.setCaching(true); - layerDisplayList.setLeftTopRightBottom(0, 0, mWidth, mHeight); + layerRenderNode.end(mAttachInfo.mHardwareRenderer, layerCanvas); + layerRenderNode.setCaching(true); + layerRenderNode.setLeftTopRightBottom(0, 0, mWidth, mHeight); mTempRect.set(0, 0, mWidth, mHeight); mResizeBuffer.endRecording(mTempRect); mAttachInfo.mHardwareRenderer.flushLayerUpdates(); @@ -2178,9 +2178,9 @@ public final class ViewRootImpl implements ViewParent, * @hide */ void outputDisplayList(View view) { - RenderNode displayList = view.getDisplayList(); - if (displayList != null) { - displayList.output(); + RenderNode renderNode = view.getDisplayList(); + if (renderNode != null) { + renderNode.output(); } } @@ -5218,10 +5218,10 @@ public final class ViewRootImpl implements ViewParent, } private static void getGfxInfo(View view, int[] info) { - RenderNode displayList = view.mDisplayList; + RenderNode renderNode = view.mRenderNode; info[0]++; - if (displayList != null) { - info[1] += 0; /* TODO: Memory used by display lists */ + if (renderNode != null) { + info[1] += 0; /* TODO: Memory used by RenderNodes (properties + DisplayLists) */ } if (view instanceof ViewGroup) { diff --git a/core/java/android/widget/ActionMenuPresenter.java b/core/java/android/widget/ActionMenuPresenter.java index 1f405a7..e4575e5 100644 --- a/core/java/android/widget/ActionMenuPresenter.java +++ b/core/java/android/widget/ActionMenuPresenter.java @@ -640,16 +640,6 @@ public class ActionMenuPresenter extends BaseMenuPresenter } @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) { - // Fill available height - heightMeasureSpec = MeasureSpec.makeMeasureSpec( - MeasureSpec.getSize(heightMeasureSpec), MeasureSpec.EXACTLY); - } - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - } - - @Override public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(info); info.setCanOpenPopup(true); diff --git a/core/java/android/widget/ActionMenuView.java b/core/java/android/widget/ActionMenuView.java index 32c7086..3975edf 100644 --- a/core/java/android/widget/ActionMenuView.java +++ b/core/java/android/widget/ActionMenuView.java @@ -20,6 +20,7 @@ import android.content.res.Configuration; import android.util.AttributeSet; import android.view.Gravity; import android.view.Menu; +import android.view.MenuItem; import android.view.View; import android.view.ViewDebug; import android.view.ViewGroup; @@ -27,6 +28,7 @@ import android.view.accessibility.AccessibilityEvent; import com.android.internal.view.menu.ActionMenuItemView; import com.android.internal.view.menu.MenuBuilder; import com.android.internal.view.menu.MenuItemImpl; +import com.android.internal.view.menu.MenuPresenter; import com.android.internal.view.menu.MenuView; /** @@ -50,6 +52,8 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo private int mMinCellSize; private int mGeneratedItemPadding; + private OnMenuItemClickListener mOnMenuItemClickListener; + public ActionMenuView(Context context) { this(context, null); } @@ -78,6 +82,10 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo } } + public void setOnMenuItemClickListener(OnMenuItemClickListener listener) { + mOnMenuItemClickListener = listener; + } + @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // If we've been given an exact size to match, apply special formatting during layout. @@ -96,11 +104,11 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo mMenu.onItemsChanged(true); } - if (mFormatItems) { + final int childCount = getChildCount(); + if (mFormatItems && childCount > 0) { onMeasureExactFormat(widthMeasureSpec, heightMeasureSpec); } else { // Previous measurement at exact format may have set margins - reset them. - final int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { final View child = getChildAt(i); final LayoutParams lp = (LayoutParams) child.getLayoutParams(); @@ -559,9 +567,11 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo if (mMenu == null) { final Context context = getContext(); mMenu = new MenuBuilder(context); + mMenu.setCallback(new MenuBuilderCallback()); mPresenter = new ActionMenuPresenter(context); - mPresenter.initForMenu(context, mMenu); mPresenter.setMenuView(this); + mPresenter.setCallback(new ActionMenuPresenterCallback()); + mMenu.addMenuPresenter(mPresenter); } return mMenu; @@ -591,6 +601,44 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo return false; } + /** + * Interface responsible for receiving menu item click events if the items themselves + * do not have individual item click listeners. + */ + public interface OnMenuItemClickListener { + /** + * This method will be invoked when a menu item is clicked if the item itself did + * not already handle the event. + * + * @param item {@link MenuItem} that was clicked + * @return <code>true</code> if the event was handled, <code>false</code> otherwise. + */ + public boolean onMenuItemClick(MenuItem item); + } + + private class MenuBuilderCallback implements MenuBuilder.Callback { + @Override + public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) { + return mOnMenuItemClickListener != null && + mOnMenuItemClickListener.onMenuItemClick(item); + } + + @Override + public void onMenuModeChange(MenuBuilder menu) { + } + } + + private class ActionMenuPresenterCallback implements ActionMenuPresenter.Callback { + @Override + public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) { + } + + @Override + public boolean onOpenSubMenu(MenuBuilder subMenu) { + return false; + } + } + /** @hide */ public interface ActionMenuChildView { public boolean needsDividerBefore(); diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index 333e631..14e7951 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -25,6 +25,7 @@ import android.text.InputFilter; import android.text.SpannableString; import com.android.internal.util.ArrayUtils; +import com.android.internal.util.GrowingArrayUtils; import com.android.internal.widget.EditableInputConnection; import android.R; @@ -1341,7 +1342,7 @@ public class Editor { if (layout instanceof DynamicLayout) { if (mTextDisplayLists == null) { - mTextDisplayLists = new TextDisplayList[ArrayUtils.idealObjectArraySize(0)]; + mTextDisplayLists = ArrayUtils.emptyArray(TextDisplayList.class); } DynamicLayout dynamicLayout = (DynamicLayout) layout; @@ -1441,10 +1442,7 @@ public class Editor { } // No available index found, the pool has to grow - int newSize = ArrayUtils.idealIntArraySize(length + 1); - TextDisplayList[] displayLists = new TextDisplayList[newSize]; - System.arraycopy(mTextDisplayLists, 0, displayLists, 0, length); - mTextDisplayLists = displayLists; + mTextDisplayLists = GrowingArrayUtils.append(mTextDisplayLists, length, null); return length; } diff --git a/core/java/android/widget/ListPopupWindow.java b/core/java/android/widget/ListPopupWindow.java index 64953f8..b47177a 100644 --- a/core/java/android/widget/ListPopupWindow.java +++ b/core/java/android/widget/ListPopupWindow.java @@ -24,6 +24,7 @@ import android.database.DataSetObserver; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.os.Handler; +import android.os.SystemClock; import android.text.TextUtils; import android.util.AttributeSet; import android.util.IntProperty; @@ -1225,6 +1226,15 @@ public class ListPopupWindow { forwarding = onTouchForwarded(event) || !onForwardingStopped(); } else { forwarding = onTouchObserved(event) && onForwardingStarted(); + + if (forwarding) { + // Make sure we cancel any ongoing source event stream. + final long now = SystemClock.uptimeMillis(); + final MotionEvent e = MotionEvent.obtain(now, now, MotionEvent.ACTION_CANCEL, + 0.0f, 0.0f, 0); + mSrc.onTouchEvent(e); + e.recycle(); + } } mForwarding = forwarding; diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index 0d3df51..f7d20b53 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -1516,6 +1516,75 @@ public class RemoteViews implements Parcelable, Filter { } /** + * Helper action to set a color filter on a compound drawable on a TextView. Supports relative + * (s/t/e/b) or cardinal (l/t/r/b) arrangement. + */ + private class TextViewDrawableColorFilterAction extends Action { + public TextViewDrawableColorFilterAction(int viewId, boolean isRelative, int index, + int color, PorterDuff.Mode mode) { + this.viewId = viewId; + this.isRelative = isRelative; + this.index = index; + this.color = color; + this.mode = mode; + } + + public TextViewDrawableColorFilterAction(Parcel parcel) { + viewId = parcel.readInt(); + isRelative = (parcel.readInt() != 0); + index = parcel.readInt(); + color = parcel.readInt(); + mode = readPorterDuffMode(parcel); + } + + private PorterDuff.Mode readPorterDuffMode(Parcel parcel) { + int mode = parcel.readInt(); + if (mode >= 0 && mode < PorterDuff.Mode.values().length) { + return PorterDuff.Mode.values()[mode]; + } else { + return PorterDuff.Mode.CLEAR; + } + } + + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(TAG); + dest.writeInt(viewId); + dest.writeInt(isRelative ? 1 : 0); + dest.writeInt(index); + dest.writeInt(color); + dest.writeInt(mode.ordinal()); + } + + @Override + public void apply(View root, ViewGroup rootParent, OnClickHandler handler) { + final TextView target = (TextView) root.findViewById(viewId); + if (target == null) return; + Drawable[] drawables = isRelative + ? target.getCompoundDrawablesRelative() + : target.getCompoundDrawables(); + if (index < 0 || index >= 4) { + throw new IllegalStateException("index must be in range [0, 3]."); + } + Drawable d = drawables[index]; + if (d != null) { + d.mutate(); + d.setColorFilter(color, mode); + } + } + + public String getActionName() { + return "TextViewDrawableColorFilterAction"; + } + + final boolean isRelative; + final int index; + final int color; + final PorterDuff.Mode mode; + + public final static int TAG = 17; + } + + /** * Simple class used to keep track of memory usage in a RemoteViews. * */ @@ -1686,6 +1755,9 @@ public class RemoteViews implements Parcelable, Filter { case SetRemoteViewsAdapterList.TAG: mActions.add(new SetRemoteViewsAdapterList(parcel)); break; + case TextViewDrawableColorFilterAction.TAG: + mActions.add(new TextViewDrawableColorFilterAction(parcel)); + break; default: throw new ActionException("Tag " + tag + " not found"); } @@ -1921,6 +1993,28 @@ public class RemoteViews implements Parcelable, Filter { } /** + * Equivalent to applying a color filter on one of the drawables in + * {@link android.widget.TextView#getCompoundDrawablesRelative()}. + * + * @param viewId The id of the view whose text should change. + * @param index The index of the drawable in the array of + * {@link android.widget.TextView#getCompoundDrawablesRelative()} to set the color + * filter on. Must be in [0, 3]. + * @param color The color of the color filter. See + * {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)}. + * @param mode The mode of the color filter. See + * {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)}. + * @hide + */ + public void setTextViewCompoundDrawablesRelativeColorFilter(int viewId, + int index, int color, PorterDuff.Mode mode) { + if (index < 0 || index >= 4) { + throw new IllegalArgumentException("index must be in range [0, 3]."); + } + addAction(new TextViewDrawableColorFilterAction(viewId, true, index, color, mode)); + } + + /** * Equivalent to calling ImageView.setImageResource * * @param viewId The id of the view whose drawable should change diff --git a/core/java/android/widget/SearchView.java b/core/java/android/widget/SearchView.java index 1eedc5d..d8a6867 100644 --- a/core/java/android/widget/SearchView.java +++ b/core/java/android/widget/SearchView.java @@ -1171,8 +1171,8 @@ public class SearchView extends LinearLayout implements CollapsibleActionView { || !mOnQueryChangeListener.onQueryTextSubmit(query.toString())) { if (mSearchable != null) { launchQuerySearch(KeyEvent.KEYCODE_UNKNOWN, null, query.toString()); - setImeVisibility(false); } + setImeVisibility(false); dismissSuggestions(); } } diff --git a/core/java/android/widget/SpellChecker.java b/core/java/android/widget/SpellChecker.java index 1cda631..595f023 100644 --- a/core/java/android/widget/SpellChecker.java +++ b/core/java/android/widget/SpellChecker.java @@ -35,6 +35,7 @@ import android.view.textservice.TextInfo; import android.view.textservice.TextServicesManager; import com.android.internal.util.ArrayUtils; +import com.android.internal.util.GrowingArrayUtils; import java.text.BreakIterator; import java.util.Locale; @@ -105,9 +106,9 @@ public class SpellChecker implements SpellCheckerSessionListener { mTextView = textView; // Arbitrary: these arrays will automatically double their sizes on demand - final int size = ArrayUtils.idealObjectArraySize(1); - mIds = new int[size]; - mSpellCheckSpans = new SpellCheckSpan[size]; + final int size = 1; + mIds = ArrayUtils.newUnpaddedIntArray(size); + mSpellCheckSpans = new SpellCheckSpan[mIds.length]; setLocale(mTextView.getSpellCheckerLocale()); @@ -184,17 +185,9 @@ public class SpellChecker implements SpellCheckerSessionListener { if (mIds[i] < 0) return i; } - if (mLength == mSpellCheckSpans.length) { - final int newSize = mLength * 2; - int[] newIds = new int[newSize]; - SpellCheckSpan[] newSpellCheckSpans = new SpellCheckSpan[newSize]; - System.arraycopy(mIds, 0, newIds, 0, mLength); - System.arraycopy(mSpellCheckSpans, 0, newSpellCheckSpans, 0, mLength); - mIds = newIds; - mSpellCheckSpans = newSpellCheckSpans; - } - - mSpellCheckSpans[mLength] = new SpellCheckSpan(); + mIds = GrowingArrayUtils.append(mIds, mLength, 0); + mSpellCheckSpans = GrowingArrayUtils.append( + mSpellCheckSpans, mLength, new SpellCheckSpan()); mLength++; return mLength - 1; } diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 687036c..5e4c143 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -5163,12 +5163,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener final int width = mRight - mLeft; final int padding = getCompoundPaddingLeft() + getCompoundPaddingRight(); final float dx = mLayout.getLineRight(0) - (width - padding); - canvas.translate(isLayoutRtl ? -dx : +dx, 0.0f); + canvas.translate(layout.getParagraphDirection(0) * dx, 0.0f); } if (mMarquee != null && mMarquee.isRunning()) { final float dx = -mMarquee.getScroll(); - canvas.translate(isLayoutRtl ? -dx : +dx, 0.0f); + canvas.translate(layout.getParagraphDirection(0) * dx, 0.0f); } } @@ -5182,8 +5182,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } if (mMarquee != null && mMarquee.shouldDrawGhost()) { - final int dx = (int) mMarquee.getGhostOffset(); - canvas.translate(isLayoutRtl ? -dx : dx, 0.0f); + final float dx = mMarquee.getGhostOffset(); + canvas.translate(layout.getParagraphDirection(0) * dx, 0.0f); layout.draw(canvas, highlight, mHighlightPaint, cursorOffsetVertical); } diff --git a/core/java/android/widget/TimePickerDelegate.java b/core/java/android/widget/TimePickerDelegate.java index c9a9894..79256e5 100644 --- a/core/java/android/widget/TimePickerDelegate.java +++ b/core/java/android/widget/TimePickerDelegate.java @@ -152,11 +152,11 @@ class TimePickerDelegate extends TimePicker.AbstractTimePickerDelegate implement final int headerBackgroundColor = a.getColor( R.styleable.TimePicker_headerBackgroundColor, 0); - a.recycle(); - final int layoutResourceId = a.getResourceId( R.styleable.TimePicker_internalLayout, R.layout.time_picker_holo); + a.recycle(); + final LayoutInflater inflater = (LayoutInflater) mContext.getSystemService( Context.LAYOUT_INFLATER_SERVICE); diff --git a/core/java/android/widget/Toolbar.java b/core/java/android/widget/Toolbar.java new file mode 100644 index 0000000..075feba --- /dev/null +++ b/core/java/android/widget/Toolbar.java @@ -0,0 +1,1048 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package android.widget; + +import android.annotation.NonNull; +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.drawable.Drawable; +import android.os.Parcel; +import android.os.Parcelable; +import android.text.TextUtils; +import android.util.AttributeSet; +import android.view.Gravity; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewDebug; +import android.view.ViewGroup; +import com.android.internal.R; + +import java.util.ArrayList; +import java.util.List; + +/** + * A standard toolbar for use within application content. + * + * <p>A Toolbar is a generalization of {@link android.app.ActionBar action bars} for use + * within application layouts. While an action bar is traditionally part of an + * {@link android.app.Activity Activity's} opaque window decor controlled by the framework, + * a Toolbar may be placed at any arbitrary level of nesting within a view hierarchy. + * An application may choose to designate a Toolbar as the action bar for an Activity + * using the {@link android.app.Activity#setActionBar(Toolbar) setActionBar()} method.</p> + * + * <p>Toolbar supports a more focused feature set than ActionBar. From start to end, a toolbar + * may contain a combination of the following optional elements: + * + * <ul> + * <li><em>A navigation button.</em> This may be an Up arrow, navigation menu toggle, close, + * collapse, done or another glyph of the app's choosing. This button should always be used + * to access other navigational destinations within the container of the Toolbar and + * its signified content or otherwise leave the current context signified by the Toolbar.</li> + * <li><em>A branded logo image.</em> This may extend to the height of the bar and can be + * arbitrarily wide.</li> + * <li><em>A title and subtitle.</em> The title should be a signpost for the Toolbar's current + * position in the navigation hierarchy and the content contained there. The subtitle, + * if present should indicate any extended information about the current content. + * If an app uses a logo image it should strongly consider omitting a title and subtitle.</li> + * <li><em>One or more custom views.</em> The application may add arbitrary child views + * to the Toolbar. They will appear at this position within the layout. If a child view's + * {@link LayoutParams} indicates a {@link Gravity} value of + * {@link Gravity#CENTER_HORIZONTAL CENTER_HORIZONTAL} the view will attempt to center + * within the available space remaining in the Toolbar after all other elements have been + * measured.</li> + * <li><em>An {@link ActionMenuView action menu}.</em> The menu of actions will pin to the + * end of the Toolbar offering a few + * <a href="http://developer.android.com/design/patterns/actionbar.html#ActionButtons"> + * frequent, important or typical</a> actions along with an optional overflow menu for + * additional actions.</li> + * </ul> + * </p> + * + * <p>In modern Android UIs developers should lean more on a visually distinct color scheme for + * toolbars than on their application icon. The use of application icon plus title as a standard + * layout is discouraged on API 21 devices and newer.</p> + */ +public class Toolbar extends ViewGroup { + private ActionMenuView mMenuView; + private TextView mTitleTextView; + private TextView mSubtitleTextView; + private ImageButton mNavButtonView; + private ImageView mLogoView; + + private int mTitleTextAppearance; + private int mSubtitleTextAppearance; + private int mTitleMarginStart; + private int mTitleMarginEnd; + private int mTitleMarginTop; + private int mTitleMarginBottom; + + private int mGravity = Gravity.START | Gravity.CENTER_VERTICAL; + + private CharSequence mTitleText; + private CharSequence mSubtitleText; + + // Clear me after use. + private final ArrayList<View> mTempViews = new ArrayList<View>(); + + private OnMenuItemClickListener mOnMenuItemClickListener; + + private final ActionMenuView.OnMenuItemClickListener mMenuViewItemClickListener = + new ActionMenuView.OnMenuItemClickListener() { + @Override + public boolean onMenuItemClick(MenuItem item) { + if (mOnMenuItemClickListener != null) { + return mOnMenuItemClickListener.onMenuItemClick(item); + } + return false; + } + }; + + public Toolbar(Context context) { + this(context, null); + } + + public Toolbar(Context context, AttributeSet attrs) { + this(context, attrs, com.android.internal.R.attr.toolbarStyle); + } + + public Toolbar(Context context, AttributeSet attrs, int defStyleAttr) { + this(context, attrs, defStyleAttr, 0); + } + + public Toolbar(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + + final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Toolbar, + defStyleAttr, defStyleRes); + + mTitleTextAppearance = a.getResourceId(R.styleable.Toolbar_titleTextAppearance, 0); + mSubtitleTextAppearance = a.getResourceId(R.styleable.Toolbar_subtitleTextAppearance, 0); + mGravity = a.getInteger(R.styleable.Toolbar_gravity, mGravity); + mTitleMarginStart = mTitleMarginEnd = Math.max(0, a.getDimensionPixelOffset( + R.styleable.Toolbar_titleMargins, -1)); + + final int marginStart = a.getDimensionPixelOffset(R.styleable.Toolbar_titleMarginStart, -1); + if (marginStart >= 0) { + mTitleMarginStart = marginStart; + } + + final int marginEnd = a.getDimensionPixelOffset(R.styleable.Toolbar_titleMarginEnd, -1); + if (marginEnd >= 0) { + mTitleMarginEnd = marginEnd; + } + + final int marginTop = a.getDimensionPixelOffset(R.styleable.Toolbar_titleMarginTop, -1); + if (marginTop >= 0) { + mTitleMarginTop = marginTop; + } + + final int marginBottom = a.getDimensionPixelOffset(R.styleable.Toolbar_titleMarginBottom, + -1); + if (marginBottom >= 0) { + mTitleMarginBottom = marginBottom; + } + + final CharSequence title = a.getText(R.styleable.Toolbar_title); + if (!TextUtils.isEmpty(title)) { + setTitle(title); + } + + final CharSequence subtitle = a.getText(R.styleable.Toolbar_subtitle); + if (!TextUtils.isEmpty(subtitle)) { + setSubtitle(title); + } + a.recycle(); + } + + /** + * Set a logo drawable from a resource id. + * + * <p>This drawable should generally take the place of title text. The logo cannot be + * clicked. Apps using a logo should also supply a description using + * {@link #setLogoDescription(int)}.</p> + * + * @param resId ID of a drawable resource + */ + public void setLogo(int resId) { + setLogo(getContext().getDrawable(resId)); + } + + /** + * Set a logo drawable. + * + * <p>This drawable should generally take the place of title text. The logo cannot be + * clicked. Apps using a logo should also supply a description using + * {@link #setLogoDescription(int)}.</p> + * + * @param drawable Drawable to use as a logo + */ + public void setLogo(Drawable drawable) { + if (drawable != null) { + if (mLogoView == null) { + mLogoView = new ImageView(getContext()); + } + if (mLogoView.getParent() == null) { + addSystemView(mLogoView); + } + } else if (mLogoView != null && mLogoView.getParent() != null) { + removeView(mLogoView); + } + if (mLogoView != null) { + mLogoView.setImageDrawable(drawable); + } + } + + /** + * Return the current logo drawable. + * + * @return The current logo drawable + * @see #setLogo(int) + * @see #setLogo(android.graphics.drawable.Drawable) + */ + public Drawable getLogo() { + return mLogoView != null ? mLogoView.getDrawable() : null; + } + + /** + * Set a description of the toolbar's logo. + * + * <p>This description will be used for accessibility or other similar descriptions + * of the UI.</p> + * + * @param resId String resource id + */ + public void setLogoDescription(int resId) { + setLogoDescription(getContext().getText(resId)); + } + + /** + * Set a description of the toolbar's logo. + * + * <p>This description will be used for accessibility or other similar descriptions + * of the UI.</p> + * + * @param description Description to set + */ + public void setLogoDescription(CharSequence description) { + if (!TextUtils.isEmpty(description) && mLogoView == null) { + mLogoView = new ImageView(getContext()); + } + if (mLogoView != null) { + mLogoView.setContentDescription(description); + } + } + + /** + * Return the description of the toolbar's logo. + * + * @return A description of the logo + */ + public CharSequence getLogoDescription() { + return mLogoView != null ? mLogoView.getContentDescription() : null; + } + + /** + * Return the current title displayed in the toolbar. + * + * @return The current title + */ + public CharSequence getTitle() { + return mTitleText; + } + + /** + * Set the title of this toolbar. + * + * <p>A title should be used as the anchor for a section of content. It should + * describe or name the content being viewed.</p> + * + * @param resId Resource ID of a string to set as the title + */ + public void setTitle(int resId) { + setTitle(getContext().getText(resId)); + } + + /** + * Set the title of this toolbar. + * + * <p>A title should be used as the anchor for a section of content. It should + * describe or name the content being viewed.</p> + * + * @param title Title to set + */ + public void setTitle(CharSequence title) { + if (!TextUtils.isEmpty(title)) { + if (mTitleTextView == null) { + final Context context = getContext(); + mTitleTextView = new TextView(context); + mTitleTextView.setTextAppearance(context, mTitleTextAppearance); + } + if (mTitleTextView.getParent() == null) { + addSystemView(mTitleTextView); + } + } else if (mTitleTextView != null && mTitleTextView.getParent() != null) { + removeView(mTitleTextView); + } + if (mTitleTextView != null) { + mTitleTextView.setText(title); + } + mTitleText = title; + } + + /** + * Return the subtitle of this toolbar. + * + * @return The current subtitle + */ + public CharSequence getSubtitle() { + return mSubtitleText; + } + + /** + * Set the subtitle of this toolbar. + * + * <p>Subtitles should express extended information about the current content.</p> + * + * @param resId String resource ID + */ + public void setSubtitle(int resId) { + setSubtitle(getContext().getText(resId)); + } + + /** + * Set the subtitle of this toolbar. + * + * <p>Subtitles should express extended information about the current content.</p> + * + * @param subtitle Subtitle to set + */ + public void setSubtitle(CharSequence subtitle) { + if (!TextUtils.isEmpty(subtitle)) { + if (mSubtitleTextView == null) { + final Context context = getContext(); + mSubtitleTextView = new TextView(context); + mSubtitleTextView.setTextAppearance(context, mSubtitleTextAppearance); + } + if (mSubtitleTextView.getParent() == null) { + addSystemView(mSubtitleTextView); + } + } else if (mSubtitleTextView != null && mSubtitleTextView.getParent() != null) { + removeView(mSubtitleTextView); + } + if (mSubtitleTextView != null) { + mSubtitleTextView.setText(subtitle); + } + mSubtitleText = subtitle; + } + + /** + * Set the icon to use for the toolbar's navigation button. + * + * <p>The navigation button appears at the start of the toolbar if present. Setting an icon + * will make the navigation button visible.</p> + * + * <p>If you use a navigation icon you should also set a description for its action using + * {@link #setNavigationDescription(int)}. This is used for accessibility and tooltips.</p> + * + * @param resId Resource ID of a drawable to set + */ + public void setNavigationIcon(int resId) { + setNavigationIcon(getContext().getDrawable(resId)); + } + + /** + * Set the icon to use for the toolbar's navigation button. + * + * <p>The navigation button appears at the start of the toolbar if present. Setting an icon + * will make the navigation button visible.</p> + * + * <p>If you use a navigation icon you should also set a description for its action using + * {@link #setNavigationDescription(int)}. This is used for accessibility and tooltips.</p> + * + * @param icon Drawable to set + */ + public void setNavigationIcon(Drawable icon) { + if (icon != null) { + ensureNavButtonView(); + if (mNavButtonView.getParent() == null) { + addSystemView(mNavButtonView); + } + } else if (mNavButtonView != null && mNavButtonView.getParent() != null) { + removeView(mNavButtonView); + } + if (mNavButtonView != null) { + mNavButtonView.setImageDrawable(icon); + } + } + + /** + * Return the current drawable used as the navigation icon. + * + * @return The navigation icon drawable + */ + public Drawable getNavigationIcon() { + return mNavButtonView != null ? mNavButtonView.getDrawable() : null; + } + + /** + * Set a description for the navigation button. + * + * <p>This description string is used for accessibility, tooltips and other facilities + * to improve discoverability.</p> + * + * @param resId Resource ID of a string to set + */ + public void setNavigationDescription(int resId) { + setNavigationDescription(getContext().getText(resId)); + } + + /** + * Set a description for the navigation button. + * + * <p>This description string is used for accessibility, tooltips and other facilities + * to improve discoverability.</p> + * + * @param description String to set as the description + */ + public void setNavigationDescription(CharSequence description) { + if (!TextUtils.isEmpty(description)) { + ensureNavButtonView(); + } + if (mNavButtonView != null) { + mNavButtonView.setContentDescription(description); + } + } + + /** + * Set a listener to respond to navigation events. + * + * <p>This listener will be called whenever the user clicks the navigation button + * at the start of the toolbar. An icon must be set for the navigation button to appear.</p> + * + * @param listener Listener to set + * @see #setNavigationIcon(android.graphics.drawable.Drawable) + */ + public void setNavigationOnClickListener(OnClickListener listener) { + ensureNavButtonView(); + mNavButtonView.setOnClickListener(listener); + } + + /** + * Return the Menu shown in the toolbar. + * + * <p>Applications that wish to populate the toolbar's menu can do so from here. To use + * an XML menu resource, use {@link #inflateMenu(int)}.</p> + * + * @return The toolbar's Menu + */ + public Menu getMenu() { + if (mMenuView == null) { + mMenuView = new ActionMenuView(getContext()); + mMenuView.setOnMenuItemClickListener(mMenuViewItemClickListener); + addSystemView(mMenuView); + } + return mMenuView.getMenu(); + } + + private MenuInflater getMenuInflater() { + return new MenuInflater(getContext()); + } + + /** + * Inflate a menu resource into this toolbar. + * + * <p>Inflate an XML menu resource into this toolbar. Existing items in the menu will not + * be modified or removed.</p> + * + * @param resId ID of a menu resource to inflate + */ + public void inflateMenu(int resId) { + getMenuInflater().inflate(resId, getMenu()); + } + + /** + * Set a listener to respond to menu item click events. + * + * <p>This listener will be invoked whenever a user selects a menu item from + * the action buttons presented at the end of the toolbar or the associated overflow.</p> + * + * @param listener Listener to set + */ + public void setOnMenuItemClickListener(OnMenuItemClickListener listener) { + mOnMenuItemClickListener = listener; + } + + private void ensureNavButtonView() { + if (mNavButtonView == null) { + mNavButtonView = new ImageButton(getContext(), null, R.attr.borderlessButtonStyle); + } + } + + private void addSystemView(View v) { + final LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT, + LayoutParams.WRAP_CONTENT); + lp.mViewType = LayoutParams.SYSTEM; + addView(v, lp); + } + + @Override + protected Parcelable onSaveInstanceState() { + SavedState state = new SavedState(super.onSaveInstanceState()); + return state; + } + + @Override + protected void onRestoreInstanceState(Parcelable state) { + final SavedState ss = (SavedState) state; + super.onRestoreInstanceState(ss.getSuperState()); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int width = 0; + int height = 0; + int childState = 0; + + // System views measure first. + + if (shouldLayout(mNavButtonView)) { + measureChildWithMargins(mNavButtonView, widthMeasureSpec, width, heightMeasureSpec, 0); + width += mNavButtonView.getMeasuredWidth() + getHorizontalMargins(mNavButtonView); + height = Math.max(height, mNavButtonView.getMeasuredHeight() + + getVerticalMargins(mNavButtonView)); + childState = combineMeasuredStates(childState, mNavButtonView.getMeasuredState()); + } + + if (shouldLayout(mMenuView)) { + measureChildWithMargins(mMenuView, widthMeasureSpec, width, heightMeasureSpec, 0); + width += mMenuView.getMeasuredWidth() + getHorizontalMargins(mMenuView); + height = Math.max(height, mMenuView.getMeasuredHeight() + + getVerticalMargins(mMenuView)); + childState = combineMeasuredStates(childState, mMenuView.getMeasuredState()); + } + + if (shouldLayout(mLogoView)) { + measureChildWithMargins(mLogoView, widthMeasureSpec, width, heightMeasureSpec, 0); + width += mLogoView.getMeasuredWidth() + getHorizontalMargins(mLogoView); + height = Math.max(height, mLogoView.getMeasuredHeight() + + getVerticalMargins(mLogoView)); + childState = combineMeasuredStates(childState, mLogoView.getMeasuredState()); + } + + int titleWidth = 0; + int titleHeight = 0; + final int titleVertMargins = mTitleMarginTop + mTitleMarginBottom; + final int titleHorizMargins = mTitleMarginStart + mTitleMarginEnd; + if (shouldLayout(mTitleTextView)) { + measureChildWithMargins(mTitleTextView, widthMeasureSpec, width + titleHorizMargins, + heightMeasureSpec, titleVertMargins); + titleWidth = mTitleTextView.getMeasuredWidth() + getHorizontalMargins(mTitleTextView); + titleHeight = mTitleTextView.getMeasuredHeight() + getVerticalMargins(mTitleTextView); + childState = combineMeasuredStates(childState, mTitleTextView.getMeasuredState()); + } + if (shouldLayout(mSubtitleTextView)) { + measureChildWithMargins(mSubtitleTextView, widthMeasureSpec, width + titleHorizMargins, + heightMeasureSpec, titleHeight + titleVertMargins); + titleWidth = Math.max(titleWidth, mSubtitleTextView.getMeasuredWidth() + + getHorizontalMargins(mSubtitleTextView)); + titleHeight += mSubtitleTextView.getMeasuredHeight() + + getVerticalMargins(mSubtitleTextView); + childState = combineMeasuredStates(childState, mSubtitleTextView.getMeasuredState()); + } + + width += titleWidth; + height = Math.max(height, titleHeight); + + final int childCount = getChildCount(); + for (int i = 0; i < childCount; i++) { + final View child = getChildAt(i); + final LayoutParams lp = (LayoutParams) child.getLayoutParams(); + if (lp.mViewType == LayoutParams.SYSTEM || !shouldLayout(child)) { + // We already got all system views above. Skip them and GONE views. + continue; + } + + measureChildWithMargins(child, widthMeasureSpec, width, heightMeasureSpec, 0); + width += child.getMeasuredWidth() + getHorizontalMargins(child); + height = Math.max(height, child.getMeasuredHeight() + getVerticalMargins(child)); + childState = combineMeasuredStates(childState, child.getMeasuredState()); + } + + // Measurement already took padding into account for available space for the children, + // add it in for the final size. + width += getPaddingLeft() + getPaddingRight(); + height += getPaddingTop() + getPaddingBottom(); + + final int measuredWidth = resolveSizeAndState( + Math.max(width, getSuggestedMinimumWidth()), + widthMeasureSpec, childState & MEASURED_STATE_MASK); + final int measuredHeight = resolveSizeAndState( + Math.max(height, getSuggestedMinimumHeight()), + heightMeasureSpec, childState << MEASURED_HEIGHT_STATE_SHIFT); + setMeasuredDimension(measuredWidth, measuredHeight); + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + final boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL; + final int width = getWidth(); + final int height = getHeight(); + final int paddingLeft = getPaddingLeft(); + final int paddingRight = getPaddingRight(); + final int paddingTop = getPaddingTop(); + final int paddingBottom = getPaddingBottom(); + int left = paddingLeft; + int right = width - paddingRight; + + if (shouldLayout(mNavButtonView)) { + if (isRtl) { + right = layoutChildRight(mNavButtonView, right); + } else { + left = layoutChildLeft(mNavButtonView, left); + } + } + + if (shouldLayout(mMenuView)) { + if (isRtl) { + left = layoutChildLeft(mMenuView, left); + } else { + right = layoutChildRight(mMenuView, right); + } + } + + if (shouldLayout(mLogoView)) { + if (isRtl) { + right = layoutChildRight(mLogoView, right); + } else { + left = layoutChildLeft(mLogoView, left); + } + } + + final boolean layoutTitle = shouldLayout(mTitleTextView); + final boolean layoutSubtitle = shouldLayout(mSubtitleTextView); + int titleHeight = 0; + if (layoutTitle) { + final LayoutParams lp = (LayoutParams) mTitleTextView.getLayoutParams(); + titleHeight += lp.topMargin + mTitleTextView.getMeasuredHeight() + lp.bottomMargin; + } + if (layoutSubtitle) { + final LayoutParams lp = (LayoutParams) mSubtitleTextView.getLayoutParams(); + titleHeight += lp.bottomMargin + mTitleTextView.getMeasuredHeight() + lp.bottomMargin; + } + + if (layoutTitle || layoutSubtitle) { + int titleTop; + switch (mGravity & Gravity.VERTICAL_GRAVITY_MASK) { + case Gravity.TOP: + titleTop = getPaddingTop(); + break; + default: + case Gravity.CENTER_VERTICAL: + final View child = layoutTitle ? mTitleTextView : mSubtitleTextView; + final LayoutParams lp = (LayoutParams) child.getLayoutParams(); + final int space = height - paddingTop - paddingBottom; + int spaceAbove = (space - titleHeight) / 2; + if (spaceAbove < lp.topMargin + mTitleMarginTop) { + spaceAbove = lp.topMargin + mTitleMarginTop; + } else { + final int spaceBelow = height - paddingBottom - titleHeight - + spaceAbove - paddingTop; + if (spaceBelow < lp.bottomMargin + mTitleMarginBottom) { + spaceAbove = Math.max(0, spaceAbove - + (lp.bottomMargin + mTitleMarginBottom - spaceBelow)); + } + } + titleTop = paddingTop + spaceAbove; + break; + case Gravity.BOTTOM: + titleTop = height - paddingBottom - titleHeight; + break; + } + if (isRtl) { + int titleRight = right; + int subtitleRight = right; + titleTop += mTitleMarginTop; + if (layoutTitle) { + final LayoutParams lp = (LayoutParams) mTitleTextView.getLayoutParams(); + titleRight -= lp.rightMargin + mTitleMarginStart; + titleTop += lp.topMargin; + final int titleLeft = titleRight - mTitleTextView.getMeasuredWidth(); + final int titleBottom = titleTop + mTitleTextView.getMeasuredHeight(); + mTitleTextView.layout(titleLeft, titleTop, titleRight, titleBottom); + titleRight = titleLeft - lp.leftMargin - mTitleMarginEnd; + titleTop = titleBottom + lp.bottomMargin; + } + if (layoutSubtitle) { + final LayoutParams lp = (LayoutParams) mSubtitleTextView.getLayoutParams(); + subtitleRight -= lp.rightMargin + mTitleMarginStart; + titleTop += lp.topMargin; + final int subtitleLeft = subtitleRight - mSubtitleTextView.getMeasuredWidth(); + final int subtitleBottom = titleTop + mSubtitleTextView.getMeasuredHeight(); + mSubtitleTextView.layout(subtitleLeft, titleTop, subtitleRight, subtitleBottom); + subtitleRight = subtitleRight - lp.leftMargin - mTitleMarginEnd; + titleTop = subtitleBottom + lp.bottomMargin; + } + right = Math.max(titleRight, subtitleRight); + } else { + int titleLeft = left; + int subtitleLeft = left; + titleTop += mTitleMarginTop; + if (layoutTitle) { + final LayoutParams lp = (LayoutParams) mTitleTextView.getLayoutParams(); + titleLeft += lp.leftMargin + mTitleMarginStart; + titleTop += lp.topMargin; + final int titleRight = titleLeft + mTitleTextView.getMeasuredWidth(); + final int titleBottom = titleTop + mTitleTextView.getMeasuredHeight(); + mTitleTextView.layout(titleLeft, titleTop, titleRight, titleBottom); + titleLeft = titleRight + lp.rightMargin + mTitleMarginEnd; + titleTop = titleBottom + lp.bottomMargin; + } + if (layoutSubtitle) { + final LayoutParams lp = (LayoutParams) mSubtitleTextView.getLayoutParams(); + subtitleLeft += lp.leftMargin + mTitleMarginStart; + titleTop += lp.topMargin; + final int subtitleRight = subtitleLeft + mSubtitleTextView.getMeasuredWidth(); + final int subtitleBottom = titleTop + mSubtitleTextView.getMeasuredHeight(); + mSubtitleTextView.layout(subtitleLeft, titleTop, subtitleRight, subtitleBottom); + subtitleLeft = subtitleRight + lp.rightMargin + mTitleMarginEnd; + titleTop = subtitleBottom + lp.bottomMargin; + } + left = Math.max(titleLeft, subtitleLeft); + } + } + + // Get all remaining children sorted for layout. This is all prepared + // such that absolute layout direction can be used below. + + addCustomViewsWithGravity(mTempViews, Gravity.LEFT); + final int leftViewsCount = mTempViews.size(); + for (int i = 0; i < leftViewsCount; i++) { + left = layoutChildLeft(getChildAt(i), left); + } + + addCustomViewsWithGravity(mTempViews, Gravity.RIGHT); + final int rightViewsCount = mTempViews.size(); + for (int i = 0; i < rightViewsCount; i++) { + right = layoutChildRight(getChildAt(i), right); + } + + // Centered views try to center with respect to the whole bar, but views pinned + // to the left or right can push the mass of centered views to one side or the other. + addCustomViewsWithGravity(mTempViews, Gravity.CENTER); + final int centerViewsWidth = getViewListMeasuredWidth(mTempViews); + final int parentCenter = paddingLeft + (width - paddingLeft - paddingRight) / 2; + final int halfCenterViewsWidth = centerViewsWidth / 2; + int centerLeft = parentCenter - halfCenterViewsWidth; + final int centerRight = centerLeft + centerViewsWidth; + if (centerLeft < left) { + centerLeft = left; + } else if (centerRight > right) { + centerLeft -= centerRight - right; + } + + final int centerViewsCount = mTempViews.size(); + for (int i = 0; i < centerViewsCount; i++) { + centerLeft = layoutChildLeft(getChildAt(i), centerLeft); + } + mTempViews.clear(); + } + + private int getViewListMeasuredWidth(List<View> views) { + int width = 0; + final int count = views.size(); + for (int i = 0; i < count; i++) { + final View v = views.get(i); + final LayoutParams lp = (LayoutParams) v.getLayoutParams(); + width += lp.leftMargin + v.getMeasuredWidth() + lp.rightMargin; + } + return width; + } + + private int layoutChildLeft(View child, int left) { + final LayoutParams lp = (LayoutParams) child.getLayoutParams(); + left += lp.leftMargin; + int top = getChildTop(child); + child.layout(left, top, left + child.getMeasuredWidth(), top + child.getMeasuredHeight()); + left += lp.rightMargin; + return left; + } + + private int layoutChildRight(View child, int right) { + final LayoutParams lp = (LayoutParams) child.getLayoutParams(); + right -= lp.rightMargin; + int top = getChildTop(child); + child.layout(right - child.getMeasuredWidth(), top, right, top + child.getMeasuredHeight()); + right -= lp.leftMargin; + return right; + } + + private int getChildTop(View child) { + final LayoutParams lp = (LayoutParams) child.getLayoutParams(); + switch (getChildVerticalGravity(lp.gravity)) { + case Gravity.TOP: + return getPaddingTop(); + + case Gravity.BOTTOM: + return getPaddingBottom() - child.getMeasuredHeight() - lp.bottomMargin; + + default: + case Gravity.CENTER_VERTICAL: + final int paddingTop = getPaddingTop(); + final int paddingBottom = getPaddingBottom(); + final int height = getHeight(); + final int childHeight = child.getMeasuredHeight(); + final int space = height - paddingTop - paddingBottom; + int spaceAbove = (space - childHeight) / 2; + if (spaceAbove < lp.topMargin) { + spaceAbove = lp.topMargin; + } else { + final int spaceBelow = height - paddingBottom - childHeight - + spaceAbove - paddingTop; + if (spaceBelow < lp.bottomMargin) { + spaceAbove = Math.max(0, spaceAbove - (lp.bottomMargin - spaceBelow)); + } + } + return paddingTop + spaceAbove; + } + } + + private int getChildVerticalGravity(int gravity) { + final int vgrav = gravity & Gravity.VERTICAL_GRAVITY_MASK; + switch (vgrav) { + case Gravity.TOP: + case Gravity.BOTTOM: + case Gravity.CENTER_VERTICAL: + return vgrav; + default: + return mGravity & Gravity.VERTICAL_GRAVITY_MASK; + } + } + + /** + * Prepare a list of non-SYSTEM child views. If the layout direction is RTL + * this will be in reverse child order. + * + * @param views List to populate. It will be cleared before use. + * @param gravity Horizontal gravity to match against + */ + private void addCustomViewsWithGravity(List<View> views, int gravity) { + final boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL; + final int childCount = getChildCount(); + final int absGrav = Gravity.getAbsoluteGravity(gravity, getLayoutDirection()); + + views.clear(); + + if (isRtl) { + for (int i = childCount - 1; i >= 0; i--) { + final View child = getChildAt(i); + final LayoutParams lp = (LayoutParams) child.getLayoutParams(); + if (lp.mViewType != LayoutParams.SYSTEM && shouldLayout(child) && + getChildHorizontalGravity(lp.gravity) == absGrav) { + views.add(child); + } + + } + } else { + for (int i = 0; i < childCount; i++) { + final View child = getChildAt(i); + final LayoutParams lp = (LayoutParams) child.getLayoutParams(); + if (lp.mViewType != LayoutParams.SYSTEM && shouldLayout(child) && + getChildHorizontalGravity(lp.gravity) == absGrav) { + views.add(child); + } + } + } + } + + private int getChildHorizontalGravity(int gravity) { + final int ld = getLayoutDirection(); + final int absGrav = Gravity.getAbsoluteGravity(gravity, ld); + final int hGrav = absGrav & Gravity.HORIZONTAL_GRAVITY_MASK; + switch (hGrav) { + case Gravity.LEFT: + case Gravity.RIGHT: + case Gravity.CENTER_HORIZONTAL: + return hGrav; + default: + return ld == LAYOUT_DIRECTION_RTL ? Gravity.RIGHT : Gravity.LEFT; + } + } + + private boolean shouldLayout(View view) { + return view != null && view.getParent() == this && view.getVisibility() != GONE; + } + + private int getHorizontalMargins(View v) { + final MarginLayoutParams mlp = (MarginLayoutParams) v.getLayoutParams(); + return mlp.getMarginStart() + mlp.getMarginEnd(); + } + + private int getVerticalMargins(View v) { + final MarginLayoutParams mlp = (MarginLayoutParams) v.getLayoutParams(); + return mlp.topMargin + mlp.bottomMargin; + } + + @Override + public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) { + return super.generateLayoutParams(attrs); + } + + @Override + protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { + if (p instanceof LayoutParams) { + return new LayoutParams((LayoutParams) p); + } else if (p instanceof MarginLayoutParams) { + return new LayoutParams((MarginLayoutParams) p); + } else { + return new LayoutParams(p); + } + } + + @Override + protected ViewGroup.LayoutParams generateDefaultLayoutParams() { + return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); + } + + @Override + protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { + return super.checkLayoutParams(p) && p instanceof LayoutParams; + } + + private static boolean isCustomView(View child) { + return ((LayoutParams) child.getLayoutParams()).mViewType == LayoutParams.CUSTOM; + } + + /** + * Interface responsible for receiving menu item click events if the items themselves + * do not have individual item click listeners. + */ + public interface OnMenuItemClickListener { + /** + * This method will be invoked when a menu item is clicked if the item itself did + * not already handle the event. + * + * @param item {@link MenuItem} that was clicked + * @return <code>true</code> if the event was handled, <code>false</code> otherwise. + */ + public boolean onMenuItemClick(MenuItem item); + } + + /** + * Layout information for child views of Toolbars. + * + * @attr ref android.R.styleable#Toolbar_LayoutParams_layout_gravity + */ + public static class LayoutParams extends MarginLayoutParams { + /** + * Gravity for the view associated with these LayoutParams. + * + * @see android.view.Gravity + */ + @ViewDebug.ExportedProperty(category = "layout", mapping = { + @ViewDebug.IntToString(from = -1, to = "NONE"), + @ViewDebug.IntToString(from = Gravity.NO_GRAVITY, to = "NONE"), + @ViewDebug.IntToString(from = Gravity.TOP, to = "TOP"), + @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.START, to = "START"), + @ViewDebug.IntToString(from = Gravity.END, to = "END"), + @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"), + @ViewDebug.IntToString(from = Gravity.FILL_HORIZONTAL, to = "FILL_HORIZONTAL"), + @ViewDebug.IntToString(from = Gravity.CENTER, to = "CENTER"), + @ViewDebug.IntToString(from = Gravity.FILL, to = "FILL") + }) + public int gravity = Gravity.NO_GRAVITY; + + static final int CUSTOM = 0; + static final int SYSTEM = 1; + + int mViewType = CUSTOM; + + public LayoutParams(@NonNull Context c, AttributeSet attrs) { + super(c, attrs); + + TypedArray a = c.obtainStyledAttributes(attrs, + com.android.internal.R.styleable.Toolbar_LayoutParams); + gravity = a.getInt( + com.android.internal.R.styleable.Toolbar_LayoutParams_layout_gravity, + Gravity.NO_GRAVITY); + a.recycle(); + } + + public LayoutParams(int width, int height) { + super(width, height); + this.gravity = Gravity.CENTER_VERTICAL | Gravity.START; + } + + public LayoutParams(int width, int height, int gravity) { + super(width, height); + this.gravity = gravity; + } + + public LayoutParams(int gravity) { + this(WRAP_CONTENT, MATCH_PARENT, gravity); + } + + public LayoutParams(LayoutParams source) { + super(source); + + this.gravity = source.gravity; + } + + public LayoutParams(MarginLayoutParams source) { + super(source); + } + + public LayoutParams(ViewGroup.LayoutParams source) { + super(source); + } + } + + static class SavedState extends BaseSavedState { + public SavedState(Parcel source) { + super(source); + } + + public SavedState(Parcelable superState) { + super(superState); + } + + @Override + public void writeToParcel(Parcel out, int flags) { + super.writeToParcel(out, flags); + } + + public static final Creator<SavedState> CREATOR = new Creator<SavedState>() { + + @Override + public SavedState createFromParcel(Parcel source) { + return new SavedState(source); + } + + @Override + public SavedState[] newArray(int size) { + return new SavedState[size]; + } + }; + } +} |