diff options
18 files changed, 2090 insertions, 842 deletions
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java index 4605ea02ecd1..085c26b74880 100644 --- a/core/java/android/database/sqlite/SQLiteDatabase.java +++ b/core/java/android/database/sqlite/SQLiteDatabase.java @@ -276,17 +276,11 @@ public class SQLiteDatabase extends SQLiteClosable { * invoked. * * this cache's max size is settable by calling the method - * (@link #setMaxSqlCacheSize(int)}). - */ - // guarded by itself - /* package */ final LruCache<String, SQLiteCompiledSql> mCompiledQueries = - new LruCache<String, SQLiteCompiledSql>(DEFAULT_SQL_CACHE_SIZE) { - @Override - protected void entryEvicted(String key, SQLiteCompiledSql value) { - verifyLockOwner(); - value.releaseIfNotInUse(); - } - }; + * (@link #setMaxSqlCacheSize(int)}. + */ + // guarded by this + private LruCache<String, SQLiteCompiledSql> mCompiledQueries; + /** * absolute max value that can be set by {@link #setMaxSqlCacheSize(int)} * size of each prepared-statement is between 1K - 6K, depending on the complexity of the @@ -1979,6 +1973,7 @@ public class SQLiteDatabase extends SQLiteClosable { if (path == null) { throw new IllegalArgumentException("path should not be null"); } + setMaxSqlCacheSize(DEFAULT_SQL_CACHE_SIZE); mFlags = flags; mPath = path; mSlowQueryThreshold = SystemProperties.getInt(LOG_SLOW_QUERIES_PROPERTY, -1); @@ -1991,7 +1986,7 @@ public class SQLiteDatabase extends SQLiteClosable { mConnectionNum = connectionNum; /* sqlite soft heap limit http://www.sqlite.org/c3ref/soft_heap_limit64.html * set it to 4 times the default cursor window size. - * TODO what is an appropriate value, considring the WAL feature which could burn + * TODO what is an appropriate value, considering the WAL feature which could burn * a lot of memory with many connections to the database. needs testing to figure out * optimal value for this. */ @@ -2145,59 +2140,55 @@ public class SQLiteDatabase extends SQLiteClosable { * the new {@link SQLiteCompiledSql} object is NOT inserted into the cache (i.e.,the current * mapping is NOT replaced with the new mapping). */ - /* package */ void addToCompiledQueries(String sql, SQLiteCompiledSql compiledStatement) { - synchronized(mCompiledQueries) { - // don't insert the new mapping if a mapping already exists - if (mCompiledQueries.get(sql) != null) { - return; - } + /* package */ synchronized void addToCompiledQueries( + String sql, SQLiteCompiledSql compiledStatement) { + // don't insert the new mapping if a mapping already exists + if (mCompiledQueries.get(sql) != null) { + return; + } - int maxCacheSz = (mConnectionNum == 0) ? mCompiledQueries.maxSize() : - mParentConnObj.mCompiledQueries.maxSize(); - - if (SQLiteDebug.DEBUG_SQL_CACHE) { - boolean printWarning = (mConnectionNum == 0) - ? (!mCacheFullWarning && mCompiledQueries.size() == maxCacheSz) - : (!mParentConnObj.mCacheFullWarning && - mParentConnObj.mCompiledQueries.size() == maxCacheSz); - if (printWarning) { - /* - * cache size of {@link #mMaxSqlCacheSize} is not enough for this app. - * log a warning. - * chances are it is NOT using ? for bindargs - or cachesize is too small. - */ - Log.w(TAG, "Reached MAX size for compiled-sql statement cache for database " + - getPath() + ". Use setMaxSqlCacheSize() to increase cachesize. "); - mCacheFullWarning = true; - Log.d(TAG, "Here are the SQL statements in Cache of database: " + mPath); - for (String s : mCompiledQueries.snapshot().keySet()) { - Log.d(TAG, "Sql statement in Cache: " + s); - } + int maxCacheSz = (mConnectionNum == 0) ? mCompiledQueries.maxSize() : + mParentConnObj.mCompiledQueries.maxSize(); + + if (SQLiteDebug.DEBUG_SQL_CACHE) { + boolean printWarning = (mConnectionNum == 0) + ? (!mCacheFullWarning && mCompiledQueries.size() == maxCacheSz) + : (!mParentConnObj.mCacheFullWarning && + mParentConnObj.mCompiledQueries.size() == maxCacheSz); + if (printWarning) { + /* + * cache size is not enough for this app. log a warning. + * chances are it is NOT using ? for bindargs - or cachesize is too small. + */ + Log.w(TAG, "Reached MAX size for compiled-sql statement cache for database " + + getPath() + ". Use setMaxSqlCacheSize() to increase cachesize. "); + mCacheFullWarning = true; + Log.d(TAG, "Here are the SQL statements in Cache of database: " + mPath); + for (String s : mCompiledQueries.snapshot().keySet()) { + Log.d(TAG, "Sql statement in Cache: " + s); } } - /* add the given SQLiteCompiledSql compiledStatement to cache. - * no need to worry about the cache size - because {@link #mCompiledQueries} - * self-limits its size to {@link #mMaxSqlCacheSize}. - */ - mCompiledQueries.put(sql, compiledStatement); } + /* add the given SQLiteCompiledSql compiledStatement to cache. + * no need to worry about the cache size - because {@link #mCompiledQueries} + * self-limits its size. + */ + mCompiledQueries.put(sql, compiledStatement); } /** package-level access for testing purposes */ - /* package */ void deallocCachedSqlStatements() { - synchronized (mCompiledQueries) { - for (SQLiteCompiledSql compiledSql : mCompiledQueries.snapshot().values()) { - compiledSql.releaseSqlStatement(); - } - mCompiledQueries.evictAll(); + /* package */ synchronized void deallocCachedSqlStatements() { + for (SQLiteCompiledSql compiledSql : mCompiledQueries.snapshot().values()) { + compiledSql.releaseSqlStatement(); } + mCompiledQueries.evictAll(); } /** * From the compiledQueries cache, returns the compiled-statement-id for the given SQL. * Returns null, if not found in the cache. */ - /* package */ SQLiteCompiledSql getCompiledStatementForSql(String sql) { + /* package */ synchronized SQLiteCompiledSql getCompiledStatementForSql(String sql) { return mCompiledQueries.get(sql); } @@ -2216,44 +2207,55 @@ public class SQLiteDatabase extends SQLiteClosable { * the value set with previous setMaxSqlCacheSize() call. */ public void setMaxSqlCacheSize(int cacheSize) { - synchronized(mCompiledQueries) { + synchronized (this) { + LruCache<String, SQLiteCompiledSql> oldCompiledQueries = mCompiledQueries; if (cacheSize > MAX_SQL_CACHE_SIZE || cacheSize < 0) { - throw new IllegalStateException("expected value between 0 and " + MAX_SQL_CACHE_SIZE); - } else if (cacheSize < mCompiledQueries.maxSize()) { - throw new IllegalStateException("cannot set cacheSize to a value less than the value " + - "set with previous setMaxSqlCacheSize() call."); + throw new IllegalStateException( + "expected value between 0 and " + MAX_SQL_CACHE_SIZE); + } else if (oldCompiledQueries != null && cacheSize < oldCompiledQueries.maxSize()) { + throw new IllegalStateException("cannot set cacheSize to a value less than the " + + "value set with previous setMaxSqlCacheSize() call."); + } + mCompiledQueries = new LruCache<String, SQLiteCompiledSql>(cacheSize) { + @Override + protected void entryEvicted(String key, SQLiteCompiledSql value) { + verifyLockOwner(); + value.releaseIfNotInUse(); + } + }; + if (oldCompiledQueries != null) { + for (Map.Entry<String, SQLiteCompiledSql> entry + : oldCompiledQueries.snapshot().entrySet()) { + mCompiledQueries.put(entry.getKey(), entry.getValue()); + } } - mCompiledQueries.setMaxSize(cacheSize); } } - /* package */ boolean isInStatementCache(String sql) { - synchronized (mCompiledQueries) { - return mCompiledQueries.get(sql) != null; - } + /* package */ synchronized boolean isInStatementCache(String sql) { + return mCompiledQueries.get(sql) != null; } - /* package */ void releaseCompiledSqlObj(String sql, SQLiteCompiledSql compiledSql) { - synchronized (mCompiledQueries) { - if (mCompiledQueries.get(sql) == compiledSql) { - // it is in cache - reset its inUse flag - compiledSql.release(); - } else { - // it is NOT in cache. finalize it. - compiledSql.releaseSqlStatement(); - } + /* package */ synchronized void releaseCompiledSqlObj( + String sql, SQLiteCompiledSql compiledSql) { + if (mCompiledQueries.get(sql) == compiledSql) { + // it is in cache - reset its inUse flag + compiledSql.release(); + } else { + // it is NOT in cache. finalize it. + compiledSql.releaseSqlStatement(); } } - private int getCacheHitNum() { + private synchronized int getCacheHitNum() { return mCompiledQueries.hitCount(); } - private int getCacheMissNum() { + private synchronized int getCacheMissNum() { return mCompiledQueries.missCount(); } - private int getCachesize() { + private synchronized int getCachesize() { return mCompiledQueries.size(); } diff --git a/core/java/android/net/DhcpInfoInternal.java b/core/java/android/net/DhcpInfoInternal.java index 6e981dfea5ee..73966692d03f 100644 --- a/core/java/android/net/DhcpInfoInternal.java +++ b/core/java/android/net/DhcpInfoInternal.java @@ -44,12 +44,14 @@ public class DhcpInfoInternal { } private int convertToInt(String addr) { - try { - InetAddress inetAddress = NetworkUtils.numericToInetAddress(addr); - if (inetAddress instanceof Inet4Address) { - return NetworkUtils.inetAddressToInt(inetAddress); - } - } catch (IllegalArgumentException e) {} + if (addr != null) { + try { + InetAddress inetAddress = NetworkUtils.numericToInetAddress(addr); + if (inetAddress instanceof Inet4Address) { + return NetworkUtils.inetAddressToInt(inetAddress); + } + } catch (IllegalArgumentException e) {} + } return 0; } @@ -80,19 +82,17 @@ public class DhcpInfoInternal { LinkProperties p = new LinkProperties(); p.addLinkAddress(makeLinkAddress()); if (TextUtils.isEmpty(gateway) == false) { - p.setGateway(NetworkUtils.numericToInetAddress(gateway)); - } else { - Log.e(TAG, "makeLinkProperties with empty gateway!"); + p.addGateway(NetworkUtils.numericToInetAddress(gateway)); } if (TextUtils.isEmpty(dns1) == false) { p.addDns(NetworkUtils.numericToInetAddress(dns1)); } else { - Log.e(TAG, "makeLinkProperties with empty dns1!"); + Log.d(TAG, "makeLinkProperties with empty dns1!"); } if (TextUtils.isEmpty(dns2) == false) { p.addDns(NetworkUtils.numericToInetAddress(dns2)); } else { - Log.e(TAG, "makeLinkProperties with empty dns2!"); + Log.d(TAG, "makeLinkProperties with empty dns2!"); } return p; } diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java index f1545eab4686..b6e9751a520b 100644 --- a/core/java/android/net/LinkProperties.java +++ b/core/java/android/net/LinkProperties.java @@ -31,7 +31,24 @@ import java.util.Collections; /** * Describes the properties of a network link. - * TODO - consider adding optional fields like Apn and ApnType + * + * A link represents a connection to a network. + * It may have multiple addresses and multiple gateways, + * multiple dns servers but only one http proxy. + * + * Because it's a single network, the dns's + * are interchangeable and don't need associating with + * particular addresses. The gateways similarly don't + * need associating with particular addresses. + * + * A dual stack interface works fine in this model: + * each address has it's own prefix length to describe + * the local network. The dns servers all return + * both v4 addresses and v6 addresses regardless of the + * address family of the server itself (rfc4213) and we + * don't care which is used. The gateways will be + * selected based on the destination address and the + * source address has no relavence. * @hide */ public class LinkProperties implements Parcelable { @@ -39,7 +56,7 @@ public class LinkProperties implements Parcelable { String mIfaceName; private Collection<LinkAddress> mLinkAddresses; private Collection<InetAddress> mDnses; - private InetAddress mGateway; + private Collection<InetAddress> mGateways; private ProxyProperties mHttpProxy; public LinkProperties() { @@ -52,7 +69,7 @@ public class LinkProperties implements Parcelable { mIfaceName = source.getInterfaceName(); mLinkAddresses = source.getLinkAddresses(); mDnses = source.getDnses(); - mGateway = source.getGateway(); + mGateways = source.getGateways(); mHttpProxy = new ProxyProperties(source.getHttpProxy()); } } @@ -89,11 +106,11 @@ public class LinkProperties implements Parcelable { return Collections.unmodifiableCollection(mDnses); } - public void setGateway(InetAddress gateway) { - mGateway = gateway; + public void addGateway(InetAddress gateway) { + mGateways.add(gateway); } - public InetAddress getGateway() { - return mGateway; + public Collection<InetAddress> getGateways() { + return Collections.unmodifiableCollection(mGateways); } public void setHttpProxy(ProxyProperties proxy) { @@ -107,7 +124,7 @@ public class LinkProperties implements Parcelable { mIfaceName = null; mLinkAddresses = new ArrayList<LinkAddress>(); mDnses = new ArrayList<InetAddress>(); - mGateway = null; + mGateways = new ArrayList<InetAddress>(); mHttpProxy = null; } @@ -131,10 +148,12 @@ public class LinkProperties implements Parcelable { for (InetAddress addr : mDnses) dns += addr.getHostAddress() + ","; dns += "] "; + String gateways = "Gateways: ["; + for (InetAddress gw : mGateways) gateways += gw.getHostAddress() + ","; + gateways += "] "; String proxy = (mHttpProxy == null ? "" : "HttpProxy: " + mHttpProxy.toString() + " "); - String gateway = (mGateway == null ? "" : "Gateway: " + mGateway.getHostAddress() + " "); - return ifaceName + linkAddresses + gateway + dns + proxy; + return ifaceName + linkAddresses + gateways + dns + proxy; } /** @@ -152,12 +171,12 @@ public class LinkProperties implements Parcelable { for(InetAddress d : mDnses) { dest.writeByteArray(d.getAddress()); } - if (mGateway != null) { - dest.writeByte((byte)1); - dest.writeByteArray(mGateway.getAddress()); - } else { - dest.writeByte((byte)0); + + dest.writeInt(mGateways.size()); + for(InetAddress gw : mGateways) { + dest.writeByteArray(gw.getAddress()); } + if (mHttpProxy != null) { dest.writeByte((byte)1); dest.writeParcelable(mHttpProxy, flags); @@ -192,10 +211,11 @@ public class LinkProperties implements Parcelable { netProp.addDns(InetAddress.getByAddress(in.createByteArray())); } catch (UnknownHostException e) { } } - if (in.readByte() == 1) { + addressCount = in.readInt(); + for (int i=0; i<addressCount; i++) { try { - netProp.setGateway(InetAddress.getByAddress(in.createByteArray())); - } catch (UnknownHostException e) {} + netProp.addGateway(InetAddress.getByAddress(in.createByteArray())); + } catch (UnknownHostException e) { } } if (in.readByte() == 1) { netProp.setHttpProxy((ProxyProperties)in.readParcelable(null)); diff --git a/core/java/android/util/LruCache.java b/core/java/android/util/LruCache.java index 6ee09cb443ad..212205087de4 100644 --- a/core/java/android/util/LruCache.java +++ b/core/java/android/util/LruCache.java @@ -155,25 +155,6 @@ public class LruCache<K, V> { } /** - * Sets the maximum size of this cache. Decreasing the maximum size may - * evict entries from this cache. - * - * @hide - * - * @param maxSize for caches that do not override {@link #sizeOf}, this is - * the maximum number of entries in the cache. For all other caches, - * this is the maximum sum of the sizes of the entries in this cache. - */ - public synchronized final void setMaxSize(int maxSize) { - if (maxSize <= 0) { - throw new IllegalArgumentException("maxSize <= 0"); - } - - trimToSize(maxSize); - this.maxSize = maxSize; - } - - /** * Called for entries that have reached the tail of the least recently used * queue and are be removed. The default implementation does nothing. */ diff --git a/core/java/android/view/VolumePanel.java b/core/java/android/view/VolumePanel.java index a67ce170a701..89b7aaadd2d8 100644 --- a/core/java/android/view/VolumePanel.java +++ b/core/java/android/view/VolumePanel.java @@ -342,11 +342,10 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie if (LOGD) Log.d(TAG, "onVolumeChanged(streamType: " + streamType + ", flags: " + flags + ")"); - if (mActiveStreamType == -1) { - reorderSliders(streamType); - } - if ((flags & AudioManager.FLAG_SHOW_UI) != 0) { + if (mActiveStreamType == -1) { + reorderSliders(streamType); + } onShowVolumeChanged(streamType, flags); } diff --git a/core/java/android/webkit/WebTextView.java b/core/java/android/webkit/WebTextView.java index 6e1a6fcba4bb..492cb8024efc 100644 --- a/core/java/android/webkit/WebTextView.java +++ b/core/java/android/webkit/WebTextView.java @@ -67,7 +67,8 @@ import junit.framework.Assert; * to overlay html textfields (and textareas) to use our standard * text editing. */ -/* package */ class WebTextView extends AutoCompleteTextView { +/* package */ class WebTextView extends AutoCompleteTextView + implements AdapterView.OnItemClickListener { static final String LOGTAG = "webtextview"; @@ -558,6 +559,27 @@ import junit.framework.Assert; mFromFocusChange = false; } + // AdapterView.OnItemClickListener implementation + + @Override + public void onItemClick(AdapterView<?> parent, View view, int position, long id) { + if (id == 0 && position == 0) { + // Blank out the text box while we wait for WebCore to fill the form. + replaceText(""); + WebSettings settings = mWebView.getSettings(); + if (mAutoFillProfileIsSet) { + // Call a webview method to tell WebCore to autofill the form. + mWebView.autoFillForm(mQueryId); + } else { + // There is no autofill profile setup yet and the user has + // elected to try and set one up. Call through to the + // embedder to action that. + mWebView.getWebChromeClient().setupAutoFill( + mHandler.obtainMessage(AUTOFILL_FORM)); + } + } + } + @Override protected void onScrollChanged(int l, int t, int oldl, int oldt) { super.onScrollChanged(l, t, oldl, oldt); @@ -814,33 +836,16 @@ import junit.framework.Assert; setInputType(getInputType() | EditorInfo.TYPE_TEXT_FLAG_AUTO_COMPLETE); adapter.setTextView(this); - } - super.setAdapter(adapter); - if (mAutoFillable) { - setOnItemClickListener(new AdapterView.OnItemClickListener() { - @Override - public void onItemClick(AdapterView<?> parent, View view, int position, long id) { - if (id == 0 && position == 0) { - // Blank out the text box while we wait for WebCore to fill the form. - replaceText(""); - WebSettings settings = mWebView.getSettings(); - if (mAutoFillProfileIsSet) { - // Call a webview method to tell WebCore to autofill the form. - mWebView.autoFillForm(mQueryId); - } else { - // There is no autofill profile setup yet and the user has - // elected to try and set one up. Call through to the - // embedder to action that. - mWebView.getWebChromeClient().setupAutoFill( - mHandler.obtainMessage(AUTOFILL_FORM)); - } - } - } - }); + if (mAutoFillable) { + setOnItemClickListener(this); + } else { + setOnItemClickListener(null); + } + showDropDown(); } else { - setOnItemClickListener(null); + dismissDropDown(); } - showDropDown(); + super.setAdapter(adapter); } /** @@ -858,6 +863,7 @@ import junit.framework.Assert; /** * {@inheritDoc} */ + @Override public View getView(int position, View convertView, ViewGroup parent) { TextView tv = (TextView) super.getView(position, convertView, parent); diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java index 21f1bfc29d51..1ecf10389178 100644 --- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java +++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java @@ -303,7 +303,7 @@ public class AccessPointParserHelper { if (!InetAddress.isNumeric(gwAddr)) { throw new SAXException(); } - mLinkProperties.setGateway(InetAddress.getByName(gwAddr)); + mLinkProperties.addGateway(InetAddress.getByName(gwAddr)); } catch (UnknownHostException e) { throw new SAXException(); } diff --git a/docs/html/guide/guide_toc.cs b/docs/html/guide/guide_toc.cs index 27a4b89e24f3..2274735283c4 100644 --- a/docs/html/guide/guide_toc.cs +++ b/docs/html/guide/guide_toc.cs @@ -237,9 +237,15 @@ <li><a href="<?cs var:toroot ?>guide/topics/graphics/opengl.html"> <span class="en">3D with OpenGL</span> </a></li> + <li><a href="<?cs var:toroot ?>guide/topics/graphics/renderscript.html"> + <span class="en">3D with Renderscript</span> + </a><span class="new">new!</span></li> <li><a href="<?cs var:toroot ?>guide/topics/graphics/animation.html"> - <span class="en">Animation</span> + <span class="en">Property Animation</span> </a><span class="new">new!</span></li> + <li><a href="<?cs var:toroot ?>guide/topics/graphics/view-animation.html"> + <span class="en">View Animation</span> + </a></li> </ul> </li> <li><a href="<?cs var:toroot ?>guide/topics/media/index.html"> diff --git a/docs/html/guide/topics/graphics/animation.jd b/docs/html/guide/topics/graphics/animation.jd index 83a4e1d30662..cd74efa4d539 100644 --- a/docs/html/guide/topics/graphics/animation.jd +++ b/docs/html/guide/topics/graphics/animation.jd @@ -1,40 +1,37 @@ -page.title=Animation +page.title=Property Animation @jd:body - <div id="qv-wrapper"> + + <div id="qv-wrapper"> <div id="qv"> <h2>In this document</h2> <ol> - <li> - <a href="#property-animation">Property Animation</a> - + <li><a href="#what">What is Property Animation?</a> <ol> + <li><a href="#how">How property animation works</a></li> + </ol> + </li> + <li><a href="#value-animator">Animating with ValueAnimator</a></li> <li><a href="#object-animator">Animating with ObjectAnimator</a></li> - <li><a href="#type-evaluator">Using a TypeEvaluator</a></li> - - <li><a href="#interpolators">Using interpolators</a></li> + <li><a href="#choreography">Choreographing Multiple Animations with + AnimatorSet</a></li> + + <li><a href="#listeners">Animation Listeners</a></li> - <li><a href="#keyframes">Specifying keyframes</a></li> + <li><a href="#type-evaluator">Using a TypeEvaluator</a></li> - <li><a href="#choreography">Choreographing multiple animations with AnimatorSet</a></li> + <li><a href="#interpolators">Using Interpolators</a></li> - <li><a href="#declaring-xml">Declaring animations in XML</a></li> - </ol> - </li> + <li><a href="#keyframes">Specifying Keyframes</a></li> + <li><a href="#layout">Animating Layout Changes to ViewGroups</a></li> - <li> - <a href="#view-animation">View Animation</a> + <li><a href="#views">Animating Views</a></li> - <ol> - <li><a href="#tween-animation">Tween animation</a></li> - - <li><a href="#frame-animation">Frame animation</a></li> - </ol> - </li> - </ol> + <li><a href="#declaring-xml">Declaring Animations in XML</a></li> + </ol> <h2>Key classes</h2> @@ -52,201 +49,504 @@ page.title=Animation <h2>Related samples</h2> <ol> - <li><a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/animation/index.html">API Demos</a></li> + <li><a href= + "{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/animation/index.html">API + Demos</a></li> </ol> - </div> </div> - <p>The Android system provides a flexible animation system that allows you to animate - almost anything, either programmatically or declaratively with XML. There are two - animation systems that you can choose from: <a href="#property-animation">property - animation</a> and <a href="#view-animation">view animation</a>. You can use whichever - system that matches your needs, but use only one system for each object that you - are animating.</p> - - <h2 id="property-animation">Property Animation</h2> - - <p>Introduced in Android 3.0, the property animation system allows you to animate - object properties of any type. <code>int</code>, <code>float</code>, - and hexadecimal color values are supported by default. You can animate any other type by telling the - system how to calculate the values for that given type.</p> + <p>Introduced in Android 3.0, the property animation system is a robust framework that allows you + to animate almost anything. Property animation is not confined to objects drawn on the screen. + You can define an animation to change any object property over time, regardless of whether it + draws to the screen or not.The property animation system also has a few advantages over the view + animation system, which makes it more flexible to use.</p> - <p>The property animation system allows you to define many aspects of an animation, - such as:</p> + <p>The view animation system provides the capability to only animate View objects, so if + you wanted to animate non-View objects, you had to implement your own code to do so. The view + animation system also was constrained in the fact that it only exposed a few aspects of a View + object to animate, such as the scaling and rotation of a View but not the background color for + instance.</p> + + <p>Another disadvantage of the view animation system is that it only modified where the + View was drawn, and not the actual View itself. For instance, if you animated a button to move + across the screen, the button draws correctly, but the actual location where you can click the + button does not change, so you have to implement your own logic to handle this. With the property + animation system, these constraints are completely removed, and you can animate any property of + any object, including View objects, and the object itself is actually modified.</p> + + <p>The view animation system, however, takes less time to setup and requires less code to write. + If view animation accomplishes everything that you need to do, or if your existing code already + works the way you want, there is no need to use the property animation system.</p> + + <h2 id="what">What is Property Animation?</h2> + A property animation changes a property's (a field in + an object) value over a specified length of time. To animate something, you specify the + object property that you want to animate, such as an object's position on the screen, how long + you want to animate it for, and what values you want to animate between. </p> + + <p>The property animation system lets you define the following characteristics of an + animation:</p> <ul> - <li>Duration</li> + <li>Duration: You can specify the duration of an animation. The default length is 300 ms.</li> - <li>Repeat amount and behavior</li> + <li>Time interpolation: You can specify how the values for the property are calculated as a + function of the animation's current elapsed time.</li> - <li>Type of time interpolation</li> + <li>Repeat count and behavior: You can specify whether or not to have an animation repeat when + it reaches the end of a duration and how many times to repeat the animation. You can also + specify whether you want the animation to play back in reverse. Setting it to reverse plays + the animation forwards then backwards repeatedly, until the number of repeats is reached.</li> - <li>Animator sets to play animations together, sequentially, or after specified - delays</li> + <li>Animator sets: You can group animations into logical sets that play together or + sequentially or after specified delays.</li> - <li>Frame refresh delay</li> - + <li>Frame refresh delay: You can specify how often to refresh frames of your animation. The + default is set to refresh every 10 ms, but the speed in which your application can refresh frames is + ultimately dependent on how busy the system is overall and how fast the system can service the underlying timer.</li> </ul> - <p>Most of the property animation system's features can be found in - {@link android.animation android.animation}. Because the - <a href="#view-animation">view animation</a> system already - defines many interpolators in {@link android.view.animation android.view.animation}, - you will use those to define your animation's interpolation in the property animation - system as well. + <h3 id="how">How the property animation system works</h3> + + <p>First, let's go over how an animation works with a simple example. Figure 1 depicts a + hypothetical object that is animated with its <code>x</code> property, which represents its + horizontal location on a screen. The duration of the animation is set to 40 ms and the distance + to travel is 40 pixels. Every 10 ms, which is the default frame refresh rate, the object moves + horizontally by 10 pixels. At the end of 40ms, the animation stops, and the object ends at + horizontal position 40. This is an example of an animation with linear interpolation, meaning the + object moves at a constant speed.</p><img src="{@docRoot}images/animation/animation-linear.png"> + + <p class="img-caption"><strong>Figure 1.</strong> Example of a linear animation</p> + + <p>You can also specify animations to have a non-linear interpolation. Figure 2 illustrates a + hypothetical object that accelerates at the beginning of the animation, and decelerates at the + end of the animation. The object still moves 40 pixels in 40 ms, but non-linearly. In the + beginning, this animation accelerates up to the halfway point then decelerates from the + halfway point until the end of the animation. As Figure 2 shows, the distance traveled + at the beginning and end of the animation is less than in the middle.</p><img src= + "{@docRoot}images/animation/animation-nonlinear.png"> + + <p class="img-caption"><strong>Figure 2.</strong> Example of a non-linear animation</p> + + <p>Let's take a detailed look at how the important components of the property animation system + would calculate animations like the ones illustrated above. Figure 3 depicts how the main classes + work with one another.</p><img src="{@docRoot}images/animation/valueanimator.png"> + + <p class="img-caption"><strong>Figure 3.</strong> How animations are calculated</p> + + <p>The {@link android.animation.ValueAnimator} object keeps track of your animation's timing, + such as how long the animation has been running, and the current value of the property that it is + animating.</p> + + <p>The {@link android.animation.ValueAnimator} encapsulates a {@link + android.animation.TimeInterpolator}, which defines animation interpolation, and a {@link + android.animation.TypeEvaluator}, which defines how to calculate values for the property being + animated. For example, in Figure 2, the {@link android.animation.TimeInterpolator} used would be + {@link android.view.animation.AccelerateDecelerateInterpolator} and the {@link + android.animation.TypeEvaluator} would be {@link android.animation.IntEvaluator}.</p> + + <p>To start an animation, create a {@link android.animation.ValueAnimator} and give it the + starting and ending values for the property that you want to animate, along with the duration of + the animation. When you call {@link android.animation.ValueAnimator#start start()} the animation + begins. During the whole animation, the {@link android.animation.ValueAnimator} calculates an <em>elapsed fraction</em> + between 0 and 1, based on the duration of the animation and how much time has elapsed. The + elapsed fraction represents the percentage of time that the animation has completed, 0 meaning 0% + and 1 meaning 100%. For example, in Figure 1, the elapsed fraction at t = 10 ms would be .25 + because the total duration is t = 40 ms.</p> + + <p>When the {@link android.animation.ValueAnimator} is done calculating an elapsed fraction, it + calls the {@link android.animation.TimeInterpolator} that is currently set, to calculate an + <em>interpolated fraction</em>. An interpolated fraction maps the elapsed fraction to a new + fraction that takes into account the time interpolation that is set. For example, in Figure 2, + because the animation slowly accelerates, the interpolated fraction, about .15, is less than the + elapsed fraction, .25, at t = 10 ms. In Figure 1, the interpolated fraction is always the same as + the elapsed fraction.</p> + + <p>When the interpolated fraction is calculated, {@link android.animation.ValueAnimator} calls + the appropriate {@link android.animation.TypeEvaluator}, to calculate the value of the + property that you are animating, based on the interpolated fraction, the starting value, and the + ending value of the animation. For example, in Figure 2, the interpolated fraction was .15 at t = + 10 ms, so the value for the property at that time would be .15 X (40 - 0), or 6.</p> + + <!-- <p>When the final value is calculated, the {@link android.animation.ValueAnimator} calls the + {@link android.animation.ValueAnimator.AnimatorUpdateListener#onAnimationUpdate + onAnimationUpdate()} method. Implement this callback to obtain the property value by + calling {@link android.animation.ValueAnimator#getAnimatedValue getAnimatedValue()} and set the + value for the property in the object that you are animating. Setting the property doesn't redraw + the object on the screen, so you need to call {@link + android.view.View#invalidate invalidate()} to refresh the View that the object + resides in. If the object is actually a View object, then the system calls {@link + android.view.View#invalidate invalidate()} when the property is changed. + The system redraws the window and the {@link android.animation.ValueAnimator} + repeats the process.</p>--> + + <p>The <code>com.example.android.apis.animation</code> package in the <a href= + "{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/animation/index.html">API + Demos</a> sample project provides many examples on how to use the property + animation system.</p> + + <h2>API Overview</h2> + + <p>You can find most of the property animation system's APIs in {@link android.animation + android.animation}. Because the view animation system already + defines many interpolators in {@link android.view.animation android.view.animation}, you can use + those interpolators in the property animation system as well. The following tables describe the main + components of the property animation system.</p> + + <p>The {@link android.animation.Animator} class provides the basic structure for creating + animations. You normally do not use this class directly as it only provides minimal + functionality that must be extended to fully support animating values. The following + subclasses extend {@link android.animation.Animator}: </p> + <p class="table-caption"><strong>Table 1.</strong> Animators</p> + <table> + <tr> + <th>Class</th> + + <th>Description</th> + </tr> + + <tr> + <td>{@link android.animation.ValueAnimator}</td> + + <td>The main timing engine for property animation that also computes the values for the + property to be animated. It has all of the core functionality that calculates animation + values and contains the timing details of each animation, information about whether an + animation repeats, listeners that receive update events, and the ability to set custom + types to evaluate. There are two pieces to animating properties: calculating the animated + values and setting those values on the object and property that is being animated. {@link + android.animation.ValueAnimator} does not carry out the second piece, so you must listen + for updates to values calculated by the {@link android.animation.ValueAnimator} and + modify the objects that you want to animate with your own logic. See the section about + <a href="#value-animator">Animating with ValueAnimator</a> for more information.</td> + </tr> + + <tr> + <td>{@link android.animation.ObjectAnimator}</td> + + <td>A subclass of {@link android.animation.ValueAnimator} that allows you to set a target + object and object property to animate. This class updates the property accordingly when + it computes a new value for the animation. You want to use + {@link android.animation.ObjectAnimator} most of the time, + because it makes the process of animating values on target objects much easier. However, + you sometimes want to use {@link android.animation.ValueAnimator} directly because {@link + android.animation.ObjectAnimator} has a few more restrictions, such as requiring specific + acessor methods to be present on the target object.</td> + </tr> + + <tr> + <td>{@link android.animation.AnimatorSet}</td> + + <td>Provides a mechanism to group animations together so that they run in + relation to one another. You can set animations to play together, sequentially, or after + a specified delay. See the section about <a href="#choreography">Choreographing multiple + animations with Animator Sets</a> for more information.</td> + </tr> + </table> - <p>The following items are the main components of the property animation system:</p> + + <p>Evaluators tell the property animation system how to calculate values for a given + property. They take the timing data that is provided by an {@link android.animation.Animator} + class, the animation's start and end value, and calculate the animated values of the property + based on this data. The property animation system provides the following evaluators:</p> + <p class="table-caption"><strong>Table 2.</strong> Evaluators</p> + <table> + <tr> + <th>Class/Interface</th> + + <th>Description</th> + </tr> + + <tr> + <td>{@link android.animation.IntEvaluator}</td> + + <td>The default evaluator to calculate values for <code>int</code> properties.</td> + </tr> + + <tr> + <td>{@link android.animation.FloatEvaluator}</td> + + <td>The default evaluator to calculate values for <code>float</code> properties.</td> + </tr> + + <tr> + <td>{@link android.animation.ArgbEvaluator}</td> - <dl> - <dt><strong>Animators</strong></dt> + <td>The default evaluator to calculate values for color properties that are represented + as hexidecimal values.</td> + </tr> - <dd> - The {@link android.animation.Animator} class provides the basic structure for - creating animations. You normally do not use this class directly as it only provides - minimal functionality that must be extended to fully support animating values. - The following subclasses extend {@link android.animation.Animator}, which you might find more useful: + <tr> + <td>{@link android.animation.TypeEvaluator}</td> - <ul> - <li>{@link android.animation.ValueAnimator} is the main timing engine for - property animation and computes the values for the property to be animated. - {@link android.animation.ValueAnimator} only computes the animation values and is - not aware of the specific object and property that is being animated or what the - values might be used for. You must listen for updates to values calculated by the - {@link android.animation.ValueAnimator} and process the data with your own logic. - See the section about <a href="#value-animator">Animating with ValueAnimator</a> - for more information.</li> - - <li>{@link android.animation.ObjectAnimator} is a subclass of {@link - android.animation.ValueAnimator} and allows you to set a target object and object - property to animate. This class is aware of the object and property to be - animated, and updates the property accordingly when it computes a new value for - the animation. See the section about <a href="#object-animator"> - Animating with ObjectAnimator</a> for more information.</li> - - <li>{@link android.animation.AnimatorSet} provides a mechanism to group - animations together so that they are rendered in relation to one another. You can - set animations to play together, sequentially, or after a specified delay. - See the section about <a href="#choreography"> - Choreographing multiple animations with Animator Sets</a> for more information.</li> - </ul> - </dd> - - <dt><strong>Evaluators</strong></dt> - - <dd> - <p>If you are animating an object property that is <em>not</em> an <code>int</code>, - <code>float</code>, or color, implement the {@link android.animation.TypeEvaluator} - interface to specify how to compute the object property's animated values. You give - a {@link android.animation.TypeEvaluator} the timing data that is provided by an - {@link android.animation.Animator} class, the animation's start and end value, and - provide logic that computes the animated values of the property based on this data.</p> - - <p>You can also specify a custom {@link android.animation.TypeEvaluator} for - <code>int</code>, <code>float</code>, and color values as well, if you want to - process those types differently than the default behavior.</p> - - <p>See <a href="#type-evaluator">Using a TypeEvaluator</a> for more information on - how to write a custom evaluator.</p> - </dd> - - <dt><strong>Interpolators</strong></dt> - - <dd> - <p>A time interpolator defines how specific values in an animation are calculated - as a function of time. For example, you can specify animations to happen linearly - across the whole animation, meaning the animation moves evenly the entire time, or - you can specify animations to use non-linear time, for example, using acceleration - or deceleration at the beginning or end of the animation.</p> - - <p>The Android system provides a set of common interpolators in - {@link android.view.animation android.view.animation}. If none of these suits your needs, you - can implement the {@link android.animation.TimeInterpolator} interface and create - your own. See <a href="#interpolators">Using interpolators</a> for more information on - how to write a custom interpolator.</p> - </dd> - </dl> - - - <p>The <code>com.example.android.apis.animation</code> package in the <a href= - "{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/animation/index.html"> - API Demos</a> sample project also provides a good overview and many examples on how to - use the property animation system.</p> - - - <h3>How the property animation system calculates animated values</h3> - - <p>When you call {@link android.animation.ValueAnimator#start start()} to begin an animation, - the {@link android.animation.ValueAnimator} calculates - an <em>elapsed fraction</em> between 0 and 1, based on the duration of the animation - and how much time has elapsed. The elapsed fraction represents the percentage of time - that the animation has completed, 0 meaning 0% and 1 meaning 100%. The Animator then - calls the {@link android.animation.TimeInterpolator} that is currently set, - to calculate an <em>eased fraction</em>, - which is a modified value of the elapsed fraction that takes into account the interpolator that - is set (time interpolation is often referred to as <em>easing</em>). The eased fraction - is the final value that is used to animate the property.</p> - - <p>Once the eased fraction is calculated, {@link android.animation.ValueAnimator} calls - the appropriate {@link android.animation.TypeEvaluator} to calculate the final value of - the property that you are animating, based on the eased fraction, the starting value, - and ending value of the animation.</p> - - <h3 id="value-animator">Animating with ValueAnimator</h3> - - <p>The {@link android.animation.ValueAnimator} class lets you animate values of some - type for the duration of an animation by specifying a set of <code>int</code>, - <code>float</code>, or color values to animate over and the duration of the animation. - You obtain a {@link android.animation.ValueAnimator} by calling one of its factory - methods: {@link android.animation.ValueAnimator#ofInt ofInt()}, - {@link android.animation.ValueAnimator#ofFloat ofFloat()}, - or {@link android.animation.ValueAnimator#ofObject ofObject()}. For example:</p> - - <pre>ValueAnimator animation = ValueAnimator.ofFloat(0f, 1f); + <td>An interface that allows you to create your own evaluator. If you are animating an + object property that is <em>not</em> an <code>int</code>, <code>float</code>, or color, + you must implement the {@link android.animation.TypeEvaluator} interface to specify how + to compute the object property's animated values. You can also specify a custom {@link + android.animation.TypeEvaluator} for <code>int</code>, <code>float</code>, and color + values as well, if you want to process those types differently than the default behavior. + See the section about <a href="#type-evaluator">Using a TypeEvaluator</a> for more + information on how to write a custom evaluator.</td> + </tr> + </table> + + + + + <p>A time interpolator defines how specific values in an animation are calculated as a + function of time. For example, you can specify animations to happen linearly across the whole + animation, meaning the animation moves evenly the entire time, or you can specify animations + to use non-linear time, for example, accelerating at the beginning and decelerating at the + end of the animation. Table 3 describes the interpolators that are contained in {@link + android.view.animation android.view.animation}. If none of the provided interpolators suits + your needs, implement the {@link android.animation.TimeInterpolator} interface and create your own. See <a href= + "#interpolators">Using interpolators</a> for more information on how to write a custom + interpolator.</p> + <p class="table-caption"><strong>Table 3.</strong> Interpolators</p> + <table> + <tr> + <th>Class/Interface</th> + + <th>Description</th> + </tr> + + <tr> + <td>{@link android.view.animation.AccelerateDecelerateInterpolator}</td> + + <td>An interpolator whose rate of change starts and ends slowly but accelerates + through the middle.</td> + </tr> + + <tr> + <td>{@link android.view.animation.AccelerateInterpolator}</td> + + <td>An interpolator whose rate of change starts out slowly and then + accelerates.</td> + </tr> + + <tr> + <td>{@link android.view.animation.AnticipateInterpolator}</td> + + <td>An interpolator whose change starts backward then flings forward.</td> + </tr> + + <tr> + <td>{@link android.view.animation.AnticipateOvershootInterpolator}</td> + + <td>An interpolator whose change starts backward, flings forward and overshoots + the target value, then finally goes back to the final value.</td> + </tr> + + <tr> + <td>{@link android.view.animation.BounceInterpolator}</td> + + <td>An interpolator whose change bounces at the end.</td> + </tr> + + <tr> + <td>{@link android.view.animation.CycleInterpolator}</td> + + <td>An interpolator whose animation repeats for a specified number of cycles.</td> + </tr> + + <tr> + <td>{@link android.view.animation.DecelerateInterpolator}</td> + + <td>An interpolator whose rate of change starts out quickly and and then + decelerates.</td> + </tr> + + <tr> + <td>{@link android.view.animation.LinearInterpolator}</td> + + <td>An interpolator whose rate of change is constant.</td> + </tr> + + <tr> + <td>{@link android.view.animation.OvershootInterpolator}</td> + + <td>An interpolator whose change flings forward and overshoots the last value then + comes back.</td> + </tr> + + <tr> + <td>{@link android.animation.TimeInterpolator}</td> + + <td>An interface that allows you to implement your own interpolator.</td> + </tr> + </table> + + <h2 id="value-animator">Animating with ValueAnimator</h2> + + <p>The {@link android.animation.ValueAnimator} class lets you animate values of some type for the + duration of an animation by specifying a set of <code>int</code>, <code>float</code>, or color + values to animate through. You obtain a {@link android.animation.ValueAnimator} by calling one of + its factory methods: {@link android.animation.ValueAnimator#ofInt ofInt()}, {@link + android.animation.ValueAnimator#ofFloat ofFloat()}, or {@link + android.animation.ValueAnimator#ofObject ofObject()}. For example:</p> + <pre> +ValueAnimator animation = ValueAnimator.ofFloat(0f, 1f); animation.setDuration(1000); animation.start(); </pre> - <p>In this code, the {@link android.animation.ValueAnimator} starts - calculating the values of the animation, between 0 and 1, for - a duration of 1000 ms, when the <code>start()</code> method runs.</p> + <p>In this code, the {@link android.animation.ValueAnimator} starts calculating the values of the + animation, between 0 and 1, for a duration of 1000 ms, when the <code>start()</code> method + runs.</p> <p>You can also specify a custom type to animate by doing the following:</p> - - <pre>ValueAnimator animation = ValueAnimator.ofObject(new MyTypeEvaluator(), startPropertyValue, endPropertyValue); + <pre> +ValueAnimator animation = ValueAnimator.ofObject(new MyTypeEvaluator(), startPropertyValue, endPropertyValue); animation.setDuration(1000); animation.start(); </pre> - <p>In this code, the {@link android.animation.ValueAnimator} starts - calculating the values of the animation, between <code>startPropertyValue</code> and - <code>endPropertyValue</code> using the logic supplied by <code>MyTypeEvaluator</code> - for a duration of 1000 ms, when the {@link android.animation.ValueAnimator#start start()} - method runs.</p> + <p>In this code, the {@link android.animation.ValueAnimator} starts calculating the values of the + animation, between <code>startPropertyValue</code> and <code>endPropertyValue</code> using the + logic supplied by <code>MyTypeEvaluator</code> for a duration of 1000 ms, when the {@link + android.animation.ValueAnimator#start start()} method runs.</p> + + <p>The previous code snippets, however, has no real effect on an object, because the {@link + android.animation.ValueAnimator} does not operate on objects or properties directly. The most likely thing + that you want to do is modify the objects that you want to animate with these calculated values. You do + this by defining listeners in the {@link android.animation.ValueAnimator} to appropriately handle important events + during the animation's lifespan, such as frame updates. When implementing the listeners, you can + obtain the calculated value for that specific frame refresh by calling {@link + android.animation.ValueAnimator#getAnimatedValue getAnimatedValue()}. For more information on listeners, + see the section about <a href="#listeners">Animation Listeners</a>. + + <h2 id="object-animator">Animating with ObjectAnimator</h2> + + <p>The {@link android.animation.ObjectAnimator} is a subclass of the {@link + android.animation.ValueAnimator} (discussed in the previous section) and combines the timing + engine and value computation of {@link android.animation.ValueAnimator} with the ability to + animate a named property of a target object. This makes animating any object much easier, as you + no longer need to implement the {@link android.animation.ValueAnimator.AnimatorUpdateListener}, + because the animated property updates automatically.</p> + + <p>Instantiating an {@link android.animation.ObjectAnimator} is similar to a {@link + android.animation.ValueAnimator}, but you also specify the object and the name of that object's property (as + a String) along with the values to animate between:</p> + <pre> +ObjectAnimator anim = ObjectAnimator.ofFloat(foo, "alpha", 0f, 1f); +anim.setDuration(1000); +anim.start(); +</pre> - <p>The previous code snippets, however, do not affect an object, because the {@link - android.animation.ValueAnimator} does not operate on objects or properties directly. To - use the results of a {@link android.animation.ValueAnimator}, you must define listeners - in the {@link android.animation.ValueAnimator} to appropriately handle important events - during the animation's lifespan, such as frame updates. You can implement the following - interfaces to create listeners for {@link android.animation.ValueAnimator}:</p> + <p>To have the {@link android.animation.ObjectAnimator} update properties correctly, you must do + the following:</p> + + <ul> + <li>The object property that you are animating must have a setter function (in camel case) in the form of + <code>set<propertyName>()</code>. Because the {@link android.animation.ObjectAnimator} + automatically updates the property during animation, it must be able to access the property + with this setter method. For example, if the property name is <code>foo</code>, you need to + have a <code>setFoo()</code> method. If this setter method does not exist, you have three + options: + + <ul> + <li>Add the setter method to the class if you have the rights to do so.</li> + + <li>Use a wrapper class that you have rights to change and have that wrapper receive the + value with a valid setter method and forward it to the original object.</li> + + <li>Use {@link android.animation.ValueAnimator} instead.</li> + </ul> + </li> + + <li>If you specify only one value for the <code>values...</code> parameter in one of the {@link + android.animation.ObjectAnimator} factory methods, it is assumed to be the ending value of the + animation. Therefore, the object property that you are animating must have a getter function + that is used to obtain the starting value of the animation. The getter function must be in the + form of <code>get<propertyName>()</code>. For example, if the property name is + <code>foo</code>, you need to have a <code>getFoo()</code> method.</li> + + <li>The getter (if needed) and setter methods of the property that you are animating must + operate on the same type as the starting and ending values that you specify to {@link + android.animation.ObjectAnimator}. For example, you must have + <code>targetObject.setPropName(float)</code> and <code>targetObject.getPropName(float)</code> + if you construct the following {@link android.animation.ObjectAnimator}: + <pre> +ObjectAnimator.ofFloat(targetObject, "propName", 1f) +</pre> + </li> + + <li>Depending on what property or object you are animating, you might need to call the {@link + android.view.View#invalidate invalidate()} method on a View force the screen to redraw itself with the + updated animated values. You do this in the + {@link android.animation.ValueAnimator.AnimatorUpdateListener#onAnimationUpdate onAnimationUpdate()} + callback. For example, animating the color property of a Drawable object only cause updates to the + screen when that object redraws itself. All of the property setters on View, such as + {@link android.view.View#setAlpha setAlpha()} and {@link android.view.View#setTranslationX setTranslationX()} + invalidate the View properly, so you do not need to invalidate the View when calling these + methods with new values. For more information on listeners, see the section about <a href="#listeners">Animation Listeners</a>. + </li> + </ul> + + <h2 id="choreography">Choreographing Multiple Animations with AnimatorSet</h2> + + <p>In many cases, you want to play an animation that depends on when another animation starts or + finishes. The Android system lets you bundle animations together into an {@link + android.animation.AnimatorSet}, so that you can specify whether to start animations + simultaneously, sequentially, or after a specified delay. You can also nest {@link + android.animation.AnimatorSet} objects within each other.</p> + + <p>The following sample code taken from the <a href= + "{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/animation/BouncingBalls.html">Bouncing + Balls</a> sample (modified for simplicity) plays the following {@link android.animation.Animator} + objects in the following manner:</p> + + <ol> + <li>Plays <code>bounceAnim</code>.</li> + + <li>Plays <code>squashAnim1</code>, <code>squashAnim2</code>, <code>stretchAnim1</code>, and + <code>stretchAnim2</code> at the same time.</li> + + <li>Plays <code>bounceBackAnim</code>.</li> + + <li>Plays <code>fadeAnim</code>.</li> + </ol> + <pre> +AnimatorSet bouncer = new AnimatorSet(); +bouncer.play(bounceAnim).before(squashAnim1); +bouncer.play(squashAnim1).with(squashAnim2); +bouncer.play(squashAnim1).with(stretchAnim1); +bouncer.play(squashAnim1).with(stretchAnim2); +bouncer.play(bounceBackAnim).after(stretchAnim2); +ValueAnimator fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f); +fadeAnim.setDuration(250); +AnimatorSet animatorSet = new AnimatorSet(); +animatorSet.play(bouncer).before(fadeAnim); +animatorSet.start(); +</pre> + + <p>For a more complete example on how to use animator sets, see the <a href= + "{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/animation/BouncingBalls.html">Bouncing + Balls</a> sample in APIDemos.</p> + +<h2 id="listeners">Animation Listeners</h2> +<p> +You can listen for important events during an animation's duration with the listeners described below. +</p> <ul> <li>{@link android.animation.Animator.AnimatorListener} <ul> - <li>{@link android.animation.Animator.AnimatorListener#onAnimationStart - onAnimationStart()} - Called when the animation starts</li> + <li>{@link android.animation.Animator.AnimatorListener#onAnimationStart onAnimationStart()} + - Called when the animation starts.</li> - <li>{@link android.animation.Animator.AnimatorListener#onAnimationEnd - onAnimationEnd()} - Called when the animation ends.</li> + <li>{@link android.animation.Animator.AnimatorListener#onAnimationEnd onAnimationEnd()} - + Called when the animation ends.</li> <li>{@link android.animation.Animator.AnimatorListener#onAnimationRepeat onAnimationRepeat()} - Called when the animation repeats itself.</li> <li>{@link android.animation.Animator.AnimatorListener#onAnimationCancel - onAnimationCancel()} - Called when the animation is canceled.</li> + onAnimationCancel()} - Called when the animation is canceled. A cancelled animation + also calls {@link android.animation.Animator.AnimatorListener#onAnimationEnd onAnimationEnd()}, + regardless of how they were ended.</li> </ul> </li> @@ -254,167 +554,173 @@ animation.start(); <ul> <li> - <p>{@link - android.animation.ValueAnimator.AnimatorUpdateListener#onAnimationUpdate - onAnimationUpdate()} - called on every frame of the animation. - Listen to this event to use the calculated values generated by - {@link android.animation.ValueAnimator} during an animation. To use the value, - query the {@link android.animation.ValueAnimator} object passed into the event - to get the current animated value with the - {@link android.animation.ValueAnimator#getAnimatedValue getAnimatedValue()} method.</p> - - <p>If you are animating your own custom object (not View objects), this - callback must also call the {@link android.view.View#invalidate invalidate()} - method to force a redraw of the screen. If you are animating View objects, - {@link android.view.View#invalidate invalidate()} is automatically called when - a property of the View is changed.</p> + <p>{@link android.animation.ValueAnimator.AnimatorUpdateListener#onAnimationUpdate + onAnimationUpdate()} - called on every frame of the animation. Listen to this event to + use the calculated values generated by {@link android.animation.ValueAnimator} during an + animation. To use the value, query the {@link android.animation.ValueAnimator} object + passed into the event to get the current animated value with the {@link + android.animation.ValueAnimator#getAnimatedValue getAnimatedValue()} method. Implementing this + listener is required if you use {@link android.animation.ValueAnimator}. </p> + + <p> + Depending on what property or object you are animating, you might need to call + {@link android.view.View#invalidate invalidate()} on a View to force that area of the + screen to redraw itself with the new animated values. For example, animating the + color property of a Drawable object only cause updates to the screen when that object + redraws itself. All of the property setters on View, + such as {@link android.view.View#setAlpha setAlpha()} and + {@link android.view.View#setTranslationX setTranslationX()} invalidate the View + properly, so you do not need to invalidate the View when calling these methods with new values. + </p> + </li> </ul> - - <p>You can extend the {@link android.animation.AnimatorListenerAdapter} class - instead of implementing the {@link android.animation.Animator.AnimatorListener} - interface, if you do not want to implement all of the methods of the {@link - android.animation.Animator.AnimatorListener} interface. The {@link - android.animation.AnimatorListenerAdapter} class provides empty implementations of the - methods that you can choose to override.</p> </li> </ul> +<p>You can extend the {@link android.animation.AnimatorListenerAdapter} class instead of +implementing the {@link android.animation.Animator.AnimatorListener} interface, if you do not +want to implement all of the methods of the {@link android.animation.Animator.AnimatorListener} +interface. The {@link android.animation.AnimatorListenerAdapter} class provides empty +implementations of the methods that you can choose to override.</p> <p>For example, the <a href= - "{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/animation/BouncingBalls.html"> - Bouncing Balls</a> sample in the API demos creates an {@link - android.animation.AnimatorListenerAdapter} for just the {@link - android.animation.Animator.AnimatorListener#onAnimationEnd onAnimationEnd()} + "{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/animation/BouncingBalls.html">Bouncing + Balls</a> sample in the API demos creates an {@link android.animation.AnimatorListenerAdapter} + for just the {@link android.animation.Animator.AnimatorListener#onAnimationEnd onAnimationEnd()} callback:</p> - - <pre>ValueAnimator fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f); + <pre> +ValueAnimatorAnimator fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f); fadeAnim.setDuration(250); fadeAnim.addListener(new AnimatorListenerAdapter() { public void onAnimationEnd(Animator animation) { balls.remove(((ObjectAnimator)animation).getTarget()); -}</pre> +} +</pre> - <h3 id="object-animator">Animating with ObjectAnimator</h3> - <p>The {@link android.animation.ObjectAnimator} is a subclass of the {@link - android.animation.ValueAnimator} (discussed in the previous section) - and combines the timing engine and value computation - of {@link android.animation.ValueAnimator} with the ability to animate a named property - of a target object. This makes animating any object much easier, as you no longer need - to implement the {@link android.animation.ValueAnimator.AnimatorUpdateListener}, because - the animated property updates automatically.</p> + <h2 id="layout">Animating Layout Changes to ViewGroups</h2> - <p>Instantiating an {@link android.animation.ObjectAnimator} is similar to a {@link - android.animation.ValueAnimator}, but you also specify the object and that object's - property (as a String) that you want to animate:</p> - <pre>ObjectAnimator anim = ObjectAnimator.ofFloat(foo, "alpha", 0f, 1f); -anim.setDuration(1000); -anim.start();</pre> + <p>The property animation system provides the capability to animate changes to ViewGroup objects + as well as provide an easy way to animate View objects themselves.</p> - <p>To have the {@link android.animation.ObjectAnimator} update properties correctly, - you must do the following:</p> + <p>You can animate layout changes within a ViewGroup with the {@link + android.animation.LayoutTransition} class. Views inside a ViewGroup can go through an appearing + and disappearing animation when you add them to or remove them from a ViewGroup or when you call + a View's {@link android.view.View#setVisibility setVisibility()} method with {@link + android.view.View#VISIBLE}, android.view.View#INVISIBLE}, or {@link android.view.View#GONE}. The remaining Views in the + ViewGroup can also animate into their new positions when you add or remove Views. You can define + the following animations in a {@link android.animation.LayoutTransition} object by calling {@link + android.animation.LayoutTransition#setAnimator setAnimator()} and passing in an {@link + android.animation.Animator} object with one of the following {@link + android.animation.LayoutTransition} constants:</p> <ul> - <li>The object property that you are animating must have a setter function in the - form of <code>set<propertyName>()</code>. Because the {@link - android.animation.ObjectAnimator} automatically updates the property during - animation, it must be able to access the property with this setter method. For - example, if the property name is <code>foo</code>, you need to have a - <code>setFoo()</code> method. If this setter method does not exist, you have three - options: + <li><code>APPEARING</code> - A flag indicating the animation that runs on items that are + appearing in the container.</li> - <ul> - <li>Add the setter method to the class if you have the rights to do so.</li> + <li><code>CHANGE_APPEARING</code> - A flag indicating the animation that runs on items that are + changing due to a new item appearing in the container.</li> - <li>Use a wrapper class that you have rights to change and have that wrapper - receive the value with a valid setter method and forward it to the original - object.</li> + <li><code>DISAPPEARING</code> - A flag indicating the animation that runs on items that are + disappearing from the container.</li> - <li>Use {@link android.animation.ValueAnimator} instead.</li> - </ul> - </li> + <li><code>CHANGE_DISAPPEARING</code> - A flag indicating the animation that runs on items that + are changing due to an item disappearing from the container.</li> + </ul> - <li>If you specify only one value for the <code>values...</code> parameter, - in one of the {@link android.animation.ObjectAnimator} factory methods, it is assumed to be - the ending value of the animation. Therefore, the object property that you are - animating must have a getter function that is used to obtain the starting value of - the animation. The getter function must be in the form of - <code>get<propertyName>()</code>. For example, if the property name is - <code>foo</code>, you need to have a <code>getFoo()</code> method.</li> + <p>You can define your own custom animations for these four types of events to customize the look + of your layout transitions or just tell the animation system to use the default animations.</p> + + <p>The <a href= + "{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/animation/LayoutAnimations.html"> + LayoutAnimations</a> sample in API Demos shows you how to define animations for layout + transitions and then set the animations on the View objects that you want to animate.</p> + + <p>The <a href= + "{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/animation/LayoutAnimationsByDefault.html"> + LayoutAnimationsByDefault</a> and its corresponding <a href= + "{@docRoot}resources/samples/ApiDemos/res/layout/layout_animations_by_default.html">layout_animations_by_default.xml</a> + layout resource file show you how to enable the default layout transitions for ViewGroups in XML. + The only thing that you need to do is to set the <code>android:animateLayoutchanges</code> + attribute to <code>true</code> for the ViewGroup. For example:</p> + <pre> +<LinearLayout + android:orientation="vertical" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:id="@+id/verticalContainer" + android:animateLayoutChanges="true" /> +</pre> - <li>The getter (if needed) and setter methods of the property that you are animating must - return the same type as the starting and ending values that you specify to {@link - android.animation.ObjectAnimator}. For example, you must have - <code>targetObject.setPropName(float)</code> and - <code>targetObject.getPropName(float)</code> if you construct the following {@link - android.animation.ObjectAnimator}: - <pre>ObjectAnimator.ofFloat(targetObject, "propName", 1f)</pre> - </li> - </ul> + <p>Setting this attribute to true automatically animates Views that are added or removed from the + ViewGroup as well as the remaining Views in the ViewGroup.</p> - <h3 id="type-evaluator">Using a TypeEvaluator</h3> + <h2 id="type-evaluator">Using a TypeEvaluator</h2> - <p>If you want to animate a type that is unknown to the Android system, - you can create your own evaluator by implementing the {@link - android.animation.TypeEvaluator} interface. The types that are known by the Android - system are <code>int</code>, <code>float</code>, or a color, which are supported by the - {@link android.animation.IntEvaluator}, {@link android.animation.FloatEvaluator}, and - {@link android.animation.ArgbEvaluator} type evaluators.</p> + <p>If you want to animate a type that is unknown to the Android system, you can create your own + evaluator by implementing the {@link android.animation.TypeEvaluator} interface. The types that + are known by the Android system are <code>int</code>, <code>float</code>, or a color, which are + supported by the {@link android.animation.IntEvaluator}, {@link + android.animation.FloatEvaluator}, and {@link android.animation.ArgbEvaluator} type + evaluators.</p> <p>There is only one method to implement in the {@link android.animation.TypeEvaluator} - interface, the {@link android.animation.TypeEvaluator#evaluate evaluate()} method. - This allows the animator that you are using to return an - appropriate value for your animated property at the current point of the animation. The - {@link android.animation.FloatEvaluator} class demonstrates how to do this:</p> - <pre>public class FloatEvaluator implements TypeEvaluator { + interface, the {@link android.animation.TypeEvaluator#evaluate evaluate()} method. This allows + the animator that you are using to return an appropriate value for your animated property at the + current point of the animation. The {@link android.animation.FloatEvaluator} class demonstrates + how to do this:</p> + <pre> +public class FloatEvaluator implements TypeEvaluator { public Object evaluate(float fraction, Object startValue, Object endValue) { float startFloat = ((Number) startValue).floatValue(); return startFloat + fraction * (((Number) endValue).floatValue() - startFloat); } -}</pre> - - <p class="note"><strong>Note:</strong> When {@link android.animation.ValueAnimator} (or - {@link android.animation.ObjectAnimator}) runs, it calculates a current elapsed - fraction of the animation (a value between 0 and 1) and then calculates an eased - version of that depending on what interpolator that you are using. The eased fraction - is what your {@link android.animation.TypeEvaluator} receives through the <code>fraction</code> - parameter, so you do not have to take into account the interpolator - when calculating animated values.</p> - - <h3 id="interpolators">Using interpolators</h3> - - <p>An interpolator define how specific values in an animation are - calculated as a function of time. For example, you can specify animations to happen - linearly across the whole animation, meaning the animation moves evenly the entire - time, or you can specify animations to use non-linear time, for example, using - acceleration or deceleration at the beginning or end of the animation.</p> - - <p>Interpolators in the animation system receive a fraction from Animators that represent the elapsed time - of the animation. Interpolators modify this fraction to coincide with the type of - animation that it aims to provide. The Android system provides a set of common - interpolators in the {@link android.view.animation android.view.animation package}. If - none of these suit your needs, you can implement the {@link - android.animation.TimeInterpolator} interface and create your own.</p> +} +</pre> + + <p class="note"><strong>Note:</strong> When {@link android.animation.ValueAnimator} (or {@link + android.animation.ObjectAnimator}) runs, it calculates a current elapsed fraction of the + animation (a value between 0 and 1) and then calculates an interpolated version of that depending + on what interpolator that you are using. The interpolated fraction is what your {@link + android.animation.TypeEvaluator} receives through the <code>fraction</code> parameter, so you do + not have to take into account the interpolator when calculating animated values.</p> + + <h2 id="interpolators">Using Interpolators</h2> + + <p>An interpolator define how specific values in an animation are calculated as a function of + time. For example, you can specify animations to happen linearly across the whole animation, + meaning the animation moves evenly the entire time, or you can specify animations to use + non-linear time, for example, using acceleration or deceleration at the beginning or end of the + animation.</p> + + <p>Interpolators in the animation system receive a fraction from Animators that represent the + elapsed time of the animation. Interpolators modify this fraction to coincide with the type of + animation that it aims to provide. The Android system provides a set of common interpolators in + the {@link android.view.animation android.view.animation package}. If none of these suit your + needs, you can implement the {@link android.animation.TimeInterpolator} interface and create your + own.</p> <p>As an example, how the default interpolator {@link android.view.animation.AccelerateDecelerateInterpolator} and the {@link - android.view.animation.LinearInterpolator} calculate eased fractions are compared below. The {@link - android.view.animation.LinearInterpolator} has no effect on the elapsed fraction, - because a linear interpolation is calculated the same way as the elapsed fraction. The - {@link android.view.animation.AccelerateDecelerateInterpolator} accelerates into the - animation and decelerates out of it. The following methods define the logic for these - interpolators:</p> + android.view.animation.LinearInterpolator} calculate interpolated fractions are compared below. + The {@link android.view.animation.LinearInterpolator} has no effect on the elapsed fraction. The {@link + android.view.animation.AccelerateDecelerateInterpolator} accelerates into the animation and + decelerates out of it. The following methods define the logic for these interpolators:</p> <p><strong>AccelerateDecelerateInterpolator</strong></p> - <pre>public float getInterpolation(float input) { + <pre> +public float getInterpolation(float input) { return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f; -}</pre> +} +</pre> <p><strong>LinearInterpolator</strong></p> - <pre>public float getInterpolation(float input) { + <pre> +public float getInterpolation(float input) { return input; -}</pre> +} +</pre> <p>The following table represents the approximate values that are calculated by these interpolators for an animation that lasts 1000ms:</p> @@ -423,9 +729,9 @@ anim.start();</pre> <tr> <th>ms elapsed</th> - <th>Elapsed fraction/Eased fraction (Linear)</th> + <th>Elapsed fraction/Interpolated fraction (Linear)</th> - <th>Eased fraction (Accelerate/Decelerate)</th> + <th>Interpolated fraction (Accelerate/Decelerate)</th> </tr> <tr> @@ -477,152 +783,167 @@ anim.start();</pre> </tr> </table> - <p>As the table shows, the {@link android.view.animation.LinearInterpolator} changes - the values at the same speed, .2 for every 200ms that passes. The {@link - android.view.animation.AccelerateDecelerateInterpolator} changes the values faster than - {@link android.view.animation.LinearInterpolator} between 200ms and 600ms and slower - between 600ms and 1000ms.</p> - - <h3 id="keyframes">Specifying keyframes</h3> - - <p>A {@link android.animation.Keyframe} object consists of a time/value pair that lets - you define a specific state at a specific time of an animation. Each keyframe can also - have its own interpolator to control the behavior of the animation in the interval - between the previous keyframe's time and the time of this keyframe.</p> - - <p>To instantiate a {@link android.animation.Keyframe} object, you must use one of the - factory methods, {@link android.animation.Keyframe#ofInt ofInt()}, {@link - android.animation.Keyframe#ofFloat ofFloat()}, or {@link - android.animation.Keyframe#ofObject ofObject()} to obtain the appropriate type of - {@link android.animation.Keyframe}. You then call the {@link - android.animation.PropertyValuesHolder#ofKeyframe ofKeyframe()} factory method to - obtain a {@link android.animation.PropertyValuesHolder} object. Once you have the - object, you can obtain an animator by passing in the {@link - android.animation.PropertyValuesHolder} object and the object to animate. The following - code snippet demonstrates how to do this:</p> - <pre>Keyframe kf0 = Keyframe.ofFloat(0f, 0f); -Keyframe kf1 = Keyframe.ofFloat(.9999f, 360f); + <p>As the table shows, the {@link android.view.animation.LinearInterpolator} changes the values + at the same speed, .2 for every 200ms that passes. The {@link + android.view.animation.AccelerateDecelerateInterpolator} changes the values faster than {@link + android.view.animation.LinearInterpolator} between 200ms and 600ms and slower between 600ms and + 1000ms.</p> + + <h2 id="keyframes">Specifying Keyframes</h2> + + <p>A {@link android.animation.Keyframe} object consists of a time/value pair that lets you define + a specific state at a specific time of an animation. Each keyframe can also have its own + interpolator to control the behavior of the animation in the interval between the previous + keyframe's time and the time of this keyframe.</p> + + <p>To instantiate a {@link android.animation.Keyframe} object, you must use one of the factory + methods, {@link android.animation.Keyframe#ofInt ofInt()}, {@link + android.animation.Keyframe#ofFloat ofFloat()}, or {@link android.animation.Keyframe#ofObject + ofObject()} to obtain the appropriate type of {@link android.animation.Keyframe}. You then call + the {@link android.animation.PropertyValuesHolder#ofKeyframe ofKeyframe()} factory method to + obtain a {@link android.animation.PropertyValuesHolder} object. Once you have the object, you can + obtain an animator by passing in the {@link android.animation.PropertyValuesHolder} object and + the object to animate. The following code snippet demonstrates how to do this:</p> + <pre> +Keyframe kf0 = Keyframe.ofFloat(0f, 0f); +Keyframe kf1 = Keyframe.ofFloat(.5f, 360f); Keyframe kf2 = Keyframe.ofFloat(1f, 0f); PropertyValuesHolder pvhRotation = PropertyValuesHolder.ofKeyframe("rotation", kf0, kf1, kf2); ObjectAnimator rotationAnim = ObjectAnimator.ofPropertyValuesHolder(target, pvhRotation) rotationAnim.setDuration(5000ms); </pre> -<p>For a more complete example on how to use keyframes, see the <a href= -"{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/animation/MultiPropertyAnimation.html"> + + <p>For a more complete example on how to use keyframes, see the <a href= + "{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/animation/MultiPropertyAnimation.html"> MultiPropertyAnimation</a> sample in APIDemos.</p> - <h3 id="choreography">Choreographing multiple animations with AnimatorSet</h3> + <h2 id="views">Animating Views</h2> + + <p>The property animation system allow streamlined animation of View objects and offerse + a few advantages over the view animation system. The view + animation system transformed View objects by changing the way that they were drawn. This was + handled in the container of each View, because the View itself had no properties to manipulate. + This resulted in the View being animated, but caused no change in the View object itself. This + led to behavior such as an object still existing in its original location, even though it was + drawn on a different location on the screen. In Android 3.0, new properties and the corresponding + getter and setter methods were added to eliminate this drawback.</p> + <p>The property animation system + can animate Views on the screen by changing the actual properties in the View objects. In + addition, Views also automatically call the {@link android.view.View#invalidate invalidate()} + method to refresh the screen whenever its properties are changed. The new properties in the {@link + android.view.View} class that facilitate property animations are:</p> - <p>In many cases, you want to play an animation that depends on when another animation - starts or finishes. The Android system lets you bundle animations together into an - {@link android.animation.AnimatorSet}, so that you can specify whether to start animations - simultaneously, sequentially, or after a specified delay. You can also nest {@link - android.animation.AnimatorSet} objects within each other.</p> + <ul> + <li><code>translationX</code> and <code>translationY</code>: These properties control where the + View is located as a delta from its left and top coordinates which are set by its layout + container.</li> - <p>The following sample code taken from the <a href= - "{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/animation/BouncingBalls.html"> - Bouncing Balls</a> sample (modified for simplicity) plays the following - {@link android.animation.Animator} objects in the following manner:</p> + <li><code>rotation</code>, <code>rotationX</code>, and <code>rotationY</code>: These properties + control the rotation in 2D (<code>rotation</code> property) and 3D around the pivot point.</li> - <ol> - <li>Plays <code>bounceAnim</code>.</li> + <li><code>scaleX</code> and <code>scaleY</code>: These properties control the 2D scaling of a + View around its pivot point.</li> - <li>Plays <code>squashAnim1</code>, <code>squashAnim2</code>, - <code>stretchAnim1</code>, and <code>stretchAnim2</code> at the same time.</li> + <li><code>pivotX</code> and <code>pivotY</code>: These properties control the location of the + pivot point, around which the rotation and scaling transforms occur. By default, the pivot + point is located at the center of the object.</li> - <li>Plays <code>bounceBackAnim</code>.</li> + <li><code>x</code> and <code>y</code>: These are simple utility properties to describe the + final location of the View in its container, as a sum of the left and top values and + translationX and translationY values.</li> - <li>Plays <code>fadeAnim</code>.</li> - </ol> - <pre>AnimatorSet bouncer = new AnimatorSet(); -bouncer.play(bounceAnim).before(squashAnim1); -bouncer.play(squashAnim1).with(squashAnim2); -bouncer.play(squashAnim1).with(stretchAnim1); -bouncer.play(squashAnim1).with(stretchAnim2); -bouncer.play(bounceBackAnim).after(stretchAnim2); -ValueAnimator fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f); -fadeAnim.setDuration(250); -AnimatorSet animatorSet = new AnimatorSet(); -animatorSet.play(bouncer).before(fadeAnim); -animatorSet.start(); + <li><code>alpha</code>: Represents the alpha transparency on the View. This value is 1 (opaque) + by default, with a value of 0 representing full transparency (not visible).</li> + </ul> + + <p>To animate a property of a View object, such as its color or rotation value, all you need to + do is create a property animator and specify the View property that you want to + animate. For example:</p> + <pre> +ObjectAnimator.ofFloat(myView, "rotation", 0f, 360f); </pre> - <p>For a more complete example on how to use animator sets, see the <a href= - "{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/animation/BouncingBalls.html"> - Bouncing Balls</a> sample in APIDemos.</p> +For more information on creating animators, see the sections on animating with +<a href="#value-animator">ValueAnimator</a> and <a href="#object-animator">ObjectAnimator</a> - <h3 id="declaring-xml">Declaring animations in XML</h3> + <h2 id="declaring-xml">Declaring Animations in XML</h2> - <p>As with <a href="#view-animation">view animation</a>, you can declare property animations with - XML instead of doing it programmatically. The following Android classes also have XML - declaration support with the following XML tags:</p> + <p>The property animation system lets you declare property animations with XML instead of doing + it programmatically. The following Android classes have XML declaration support with the + following XML tags:</p> <ul> <li>{@link android.animation.ValueAnimator} - <code><animator></code></li> <li>{@link android.animation.ObjectAnimator} - <code><objectAnimator></code></li> - <li>{@link android.animation.AnimatorSet} - <code><AnimatorSet></code></li> + <li>{@link android.animation.AnimatorSet} - <code><set></code></li> </ul> <p>Both <code><animator></code> ({@link android.animation.ValueAnimator}) and - <code><objectAnimator></code> ({@link android.animation.ObjectAnimator}) have the - following attributes:</p> + <code><objectAnimator></code> ({@link android.animation.ObjectAnimator}) have the following + attributes:</p> - <dl> - <dt><code>android:duration</code></dt> - <dd>The number of milliseconds that the animation runs.</dd> - - <dt><code>android:valueFrom</code> and <code>android:valueTo</code></dt> - <dd>The values being animated - between. These are restricted to numbers (<code>float</code> or <code>int</code>) in - XML. They can be <code>float</code>, <code>int</code>, or any kind of - <code>Object</code> when creating animations programmatically.</dd> - - <dt><code>android:valueType</code></dt> - <dd>Set to either <code>"floatType"</code> or <code>"intType"</code>.</dd> - - <dt><code>android:startDelay</code></dt> - <dd>The delay, in milliseconds, before the animation begins - playing (after calling {@link android.animation.ValueAnimator#start start()}).</dd> - - <dt><code>android:repeatCount</code></dt> - <dd>How many times to repeat an animation. Set to - <code>"-1"</code> for infinite repeating or to a positive integer. For example, a value of - <code>"1"</code> means that the animation is repeated once after the initial run of the - animation, so the animation plays a total of two times. The default value is - <code>"0"</code>.</dd> - - <dt><code>android:repeatMode</code></dt> - <dd>How an animation behaves when it reaches the end of the - animation. <code>android:repeatCount</code> must be set to a positive integer or - <code>"-1"</code> for this attribute to have an effect. Set to <code>"reverse"</code> to - have the animation reverse direction with each iteration or <code>"repeat"</code> to - have the animation loop from the beginning each time.</dd> + <dl> + <dt><code>android:duration</code></dt> + + <dd>The number of milliseconds that the animation runs. The default is 300 ms.</dd> + + <dt><code>android:valueFrom</code> and <code>android:valueTo</code></dt> + + <dd>The values being animated between. These are restricted to numbers (<code>float</code> or + <code>int</code>) and color values (such as #00ff00). They can be <code>float</code>, <code>int</code>, colors, + or any kind of <code>Object</code> when creating animations programmatically.</dd> + + <dt><code>android:valueType</code></dt> + + <dd>Set to either <code>"floatType"</code> or <code>"intType"</code>. The default is + <code>"floatType"</code> unless you specify something else or if the <code>valuesFrom</code> + and <code>valuesTo</code> values are colors.</dd> + + <dt><code>android:startDelay</code></dt> + + <dd>The delay, in milliseconds, before the animation begins playing (after calling {@link + android.animation.ValueAnimator#start start()}).</dd> + + <dt><code>android:repeatCount</code></dt> + + <dd>How many times to repeat an animation. Set to <code>"-1"</code> to infinitely repeat or + to a positive integer. For example, a value of <code>"1"</code> means that the animation is + repeated once after the initial run of the animation, so the animation plays a total of two + times. The default value is <code>"0"</code>, which means no repetition.</dd> + + <dt><code>android:repeatMode</code></dt> + + <dd>How an animation behaves when it reaches the end of the animation. + <code>android:repeatCount</code> must be set to a positive integer or <code>"-1"</code> for + this attribute to have an effect. Set to <code>"reverse"</code> to have the animation reverse + direction with each iteration or <code>"repeat"</code> to have the animation loop from the + beginning each time.</dd> </dl> - + <p>The <code>objectAnimator</code> ({@link android.animation.ObjectAnimator}) element has the - additional attribute <code>propertyName</code>, that lets you specify the name of the - property being animated. The <code>objectAnimator</code> element does not expose a - <code>target</code> attribute, however, so you cannot set the object to animate in the - XML declaration. You have to inflate the XML resource by calling - {@link android.animation.AnimatorInflater#loadAnimator loadAnimator()} and call - {@link android.animation.ObjectAnimator#setTarget setTarget()} to set the target object, before calling - {@link android.animation.ObjectAnimator#start start()}.</p> + additional attribute <code>propertyName</code>, that lets you specify the name of the property + being animated. The <code>objectAnimator</code> element does not expose a <code>target</code> + attribute, however, so you cannot set the object to animate in the XML declaration. You have to + inflate the XML resource by calling {@link android.animation.AnimatorInflater#loadAnimator + loadAnimator()} and call {@link android.animation.ObjectAnimator#setTarget setTarget()} to set + the target object unlike the underlying {@link android.animation.ObjectAnimator}, + before calling {@link android.animation.ObjectAnimator#start start()}.</p> <p>The <code>set</code> element ({@link android.animation.AnimatorSet}) exposes a single - attribute, <code>ordering</code>. Set this attribute to <code>together</code> (default) - to play all the animations in this set at once. Set this attribute to - <code>sequentially</code> to play the animations in the order they are declared.</p> - - <p>You can specify nested <code>set</code> tags to further group animations together. - The animations that you want to group together should be children of the - <code>set</code> tag and can define their own <code>ordering</code> attribute.</p> - - <p>As an example, this XML code creates an {@link android.animation.AnimatorSet} object - that animates x and y at the same time (<code>together</code> is the default ordering - when nothing is specified), then runs an animation that fades an object out:</p> - <pre><set android:ordering="sequentially"> + attribute, <code>ordering</code>. Set this attribute to <code>together</code> (default) to play + all the animations in this set at once. Set this attribute to <code>sequentially</code> to play + the animations in the order they are declared.</p> + + <p>You can specify nested <code>set</code> tags to further group animations together. The + animations that you want to group together should be children of the <code>set</code> tag and can + define their own <code>ordering</code> attribute.</p> + + <p>As an example, this XML code creates an {@link android.animation.AnimatorSet} object that + animates x and y at the same time, then runs an animation that fades an object out:</p> + <pre> +<set android:ordering="sequentially"> <set> <objectAnimator android:propertyName="x" @@ -639,190 +960,11 @@ animatorSet.start(); android:propertyName="alpha" android:duration="500" android:valueTo="0f"/> -</set></pre> - - <p>In order to run this animation, you must inflate the XML resources in your code to - an {@link android.animation.AnimatorSet} object, and then set the target objects for all of - the animations before starting the animation set. Calling {@link - android.animation.AnimatorSet#setTarget setTarget()} sets a single target object for - all children of the {@link android.animation.AnimatorSet}.</p> - - <h2 id="view-animation">View Animation</h2>You can use View Animation in any View - object to perform tweened animation and frame by frame animation. Tween animation - calculates the animation given information such as the start point, end point, size, - rotation, and other common aspects of an animation. Frame by frame animation lets you - load a series of Drawable resources one after another to create an animation. - - <h3 id="tween-animation">Tween Animation</h3> - - <p>A tween animation can perform a series of simple transformations (position, size, - rotation, and transparency) on the contents of a View object. So, if you have a - {@link android.widget.TextView} object, you can move, rotate, grow, or shrink the text. If it has a background - image, the background image will be transformed along with the text. The {@link - android.view.animation animation package} provides all the classes used in a tween - animation.</p> - - <p>A sequence of animation instructions defines the tween animation, defined by either - XML or Android code. As with defining a layout, an XML file is recommended because it's - more readable, reusable, and swappable than hard-coding the animation. In the example - below, we use XML. (To learn more about defining an animation in your application code, - instead of XML, refer to the {@link android.view.animation.AnimationSet} class and - other {@link android.view.animation.Animation} subclasses.)</p> - - <p>The animation instructions define the transformations that you want to occur, when - they will occur, and how long they should take to apply. Transformations can be - sequential or simultaneous — for example, you can have the contents of a TextView - move from left to right, and then rotate 180 degrees, or you can have the text move and - rotate simultaneously. Each transformation takes a set of parameters specific for that - transformation (starting size and ending size for size change, starting angle and - ending angle for rotation, and so on), and also a set of common parameters (for - instance, start time and duration). To make several transformations happen - simultaneously, give them the same start time; to make them sequential, calculate the - start time plus the duration of the preceding transformation.</p> - - <p>The animation XML file belongs in the <code>res/anim/</code> directory of your - Android project. The file must have a single root element: this will be either a single - <code><alpha></code>, <code><scale></code>, <code><translate></code>, - <code><rotate></code>, interpolator element, or <code><set></code> element - that holds groups of these elements (which may include another - <code><set></code>). By default, all animation instructions are applied - simultaneously. To make them occur sequentially, you must specify the - <code>startOffset</code> attribute, as shown in the example below.</p> - - <p>The following XML from one of the ApiDemos is used to stretch, then simultaneously - spin and rotate a View object.</p> - <pre><set android:shareInterpolator="false"> - <scale - android:interpolator="@android:anim/accelerate_decelerate_interpolator" - android:fromXScale="1.0" - android:toXScale="1.4" - android:fromYScale="1.0" - android:toYScale="0.6" - android:pivotX="50%" - android:pivotY="50%" - android:fillAfter="false" - android:duration="700" /> - <set android:interpolator="@android:anim/decelerate_interpolator"> - <scale - android:fromXScale="1.4" - android:toXScale="0.0" - android:fromYScale="0.6" - android:toYScale="0.0" - android:pivotX="50%" - android:pivotY="50%" - android:startOffset="700" - android:duration="400" - android:fillBefore="false" /> - <rotate - android:fromDegrees="0" - android:toDegrees="-45" - android:toYScale="0.0" - android:pivotX="50%" - android:pivotY="50%" - android:startOffset="700" - android:duration="400" /> - </set> -</set></pre> - - <p>Screen coordinates (not used in this example) are (0,0) at the upper left hand - corner, and increase as you go down and to the right.</p> - - <p>Some values, such as pivotX, can be specified relative to the object itself or - relative to the parent. Be sure to use the proper format for what you want ("50" for - 50% relative to the parent, or "50%" for 50% relative to itself).</p> - - <p>You can determine how a transformation is applied over time by assigning an {@link - android.view.animation.Interpolator}. Android includes several Interpolator subclasses - that specify various speed curves: for instance, {@link - android.view.animation.AccelerateInterpolator} tells a transformation to start slow and - speed up. Each one has an attribute value that can be applied in the XML.</p> - - <p>With this XML saved as <code>hyperspace_jump.xml</code> in the - <code>res/anim/</code> directory of the project, the following code will reference - it and apply it to an {@link android.widget.ImageView} object from the layout.</p> - <pre> -ImageView spaceshipImage = (ImageView) findViewById(R.id.spaceshipImage); -Animation hyperspaceJumpAnimation = AnimationUtils.loadAnimation(this, R.anim.hyperspace_jump); -spaceshipImage.startAnimation(hyperspaceJumpAnimation); -</pre> - - <p>As an alternative to <code>startAnimation()</code>, you can define a starting time - for the animation with <code>{@link android.view.animation.Animation#setStartTime(long) - Animation.setStartTime()}</code>, then assign the animation to the View with - <code>{@link android.view.View#setAnimation(android.view.animation.Animation) - View.setAnimation()}</code>.</p> - - <p>For more information on the XML syntax, available tags and attributes, see <a href= - "{@docRoot}guide/topics/resources/animation-resource.html">Animation Resources</a>.</p> - - <p class="note"><strong>Note:</strong> Regardless of how your animation may move or - resize, the bounds of the View that holds your animation will not automatically adjust - to accommodate it. Even so, the animation will still be drawn beyond the bounds of its - View and will not be clipped. However, clipping <em>will occur</em> if the animation - exceeds the bounds of the parent View.</p> - - <h3 id="frame-animation">Frame Animation</h3> - - <p>This is a traditional animation in the sense that it is created with a sequence of - different images, played in order, like a roll of film. The {@link - android.graphics.drawable.AnimationDrawable} class is the basis for frame - animations.</p> - - <p>While you can define the frames of an animation in your code, using the {@link - android.graphics.drawable.AnimationDrawable} class API, it's more simply accomplished - with a single XML file that lists the frames that compose the animation. Like the tween - animation above, the XML file for this kind of animation belongs in the - <code>res/drawable/</code> directory of your Android project. In this case, the - instructions are the order and duration for each frame of the animation.</p> - - <p>The XML file consists of an <code><animation-list></code> element as the root - node and a series of child <code><item></code> nodes that each define a frame: a - drawable resource for the frame and the frame duration. Here's an example XML file for - a frame-by-frame animation:</p> - <pre> -<animation-list xmlns:android="http://schemas.android.com/apk/res/android" - android:oneshot="true"> - <item android:drawable="@drawable/rocket_thrust1" android:duration="200" /> - <item android:drawable="@drawable/rocket_thrust2" android:duration="200" /> - <item android:drawable="@drawable/rocket_thrust3" android:duration="200" /> -</animation-list> +</set> </pre> - <p>This animation runs for just three frames. By setting the - <code>android:oneshot</code> attribute of the list to <var>true</var>, it will cycle - just once then stop and hold on the last frame. If it is set <var>false</var> then the - animation will loop. With this XML saved as <code>rocket_thrust.xml</code> in the - <code>res/drawable/</code> directory of the project, it can be added as the background - image to a View and then called to play. Here's an example Activity, in which the - animation is added to an {@link android.widget.ImageView} and then animated when the - screen is touched:</p> - <pre>AnimationDrawable rocketAnimation; - -public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.main); - - ImageView rocketImage = (ImageView) findViewById(R.id.rocket_image); - rocketImage.setBackgroundResource(R.drawable.rocket_thrust); - rocketAnimation = (AnimationDrawable) rocketImage.getBackground(); -} - -public boolean onTouchEvent(MotionEvent event) { - if (event.getAction() == MotionEvent.ACTION_DOWN) { - rocketAnimation.start(); - return true; - } - return super.onTouchEvent(event); -}</pre> - - <p>It's important to note that the <code>start()</code> method called on the - AnimationDrawable cannot be called during the <code>onCreate()</code> method of your - Activity, because the AnimationDrawable is not yet fully attached to the window. If you - want to play the animation immediately, without requiring interaction, then you might - want to call it from the <code>{@link - android.app.Activity#onWindowFocusChanged(boolean) onWindowFocusChanged()}</code> - method in your Activity, which will get called when Android brings your window into - focus.</p> - - <p>For more information on the XML syntax, available tags and attributes, see <a href= - "{@docRoot}guide/topics/resources/animation-resource.html">Animation Resources</a>.</p>
\ No newline at end of file + <p>In order to run this animation, you must inflate the XML resources in your code to an {@link + android.animation.AnimatorSet} object, and then set the target objects for all of the animations + before starting the animation set. Calling {@link android.animation.AnimatorSet#setTarget + setTarget()} sets a single target object for all children of the {@link + android.animation.AnimatorSet}.</p>
\ No newline at end of file diff --git a/docs/html/guide/topics/graphics/renderscript.jd b/docs/html/guide/topics/graphics/renderscript.jd new file mode 100644 index 000000000000..0ef8a22d9bbc --- /dev/null +++ b/docs/html/guide/topics/graphics/renderscript.jd @@ -0,0 +1,710 @@ +page.title=3D Rendering and Computation with Renderscript +@jd:body + + <div id="qv-wrapper"> + <div id="qv"> + <h2>In this document</h2> + + <ol> + <li><a href="#overview">Renderscript System Overview</a></li> + + <li> + <a href="#api">API Overview</a> + + <ol> + <li><a href="#native-api">Native Renderscript APIs</a></li> + + <li><a href="#reflective-api">Reflective layer APIs</a></li> + + <li><a href="#graphics-api">Graphics APIs</a></li> + </ol> + </li> + + <li> + <a href="#developing">Developing a Renderscript application</a> + + <ol> + <li><a href="#hello-graphics">The Hello Graphics application</a></li> + </ol> + </li> + </ol> + </div> + </div> + + <p>The Renderscript system offers high performance 3D rendering and mathematical computations at + the native level. The Renderscript APIs are intended for developers who are comfortable with + developing in C (C99 standard) and want to maximize performance in their applications. The + Renderscript system improves performance by running as native code on the device, but it also + features cross-platform functionality. To achieve this, the Android build tools compile your + Renderscript <code>.rs</code> file to intermediate bytecode and package it inside your + application's <code>.apk</code> file. On the device, the bytecode is compiled (just-in-time) to + machine code that is further optimized for the device that it is running on. This eliminates the + need to target a specific architecture during the development process. The compiled code on the + device is cached, so subsequent uses of the Renderscript enabled application do not recompile the + intermediate code.</p> + + <p>The disadvantage of the Renderscript system is that it adds complexity to the development and + debugging processes and is not a substitute for the Android system APIs. It is a portable native + language with pointers and explicit resource management. The target use is for performance + critical code where the existing Android APIs are not sufficient. If what you are rendering or + computing is very simple and does not require much processing power, you should still use the + Android APIs for ease of development. Debugging visibility can be limited, because the + Renderscript system can execute on processors other than the main CPU (such as the GPU), so if + this occurs, debugging becomes more difficult. Remember the tradeoffs between development and + debugging complexity versus performance when deciding to use Renderscript.</p> + + <p>For an example of Renderscript in action, see the 3D carousel view in the Android 3.0 versions + of Google Books and YouTube or install the Renderscript sample applications that are shipped with + the SDK in <code><sdk_root>/platforms/android-3.0/samples</code>.</p> + + <h2 id="overview">Renderscript System Overview</h2> + + <p>The Renderscript system adopts a control and slave architecture where the low-level native + code is controlled by the higher level Android system that runs in the virtual machine (VM). When + you use the Renderscript system, there are three layers of APIs that exist:</p> + + <ul> + <li>The native Renderscript layer consists of the native Renderscript <code>.rs</code> files + that you write to compute mathematical operations, render graphics, or both. This layer does + the intensive computation or graphics rendering and returns the result back to the Android VM + through the reflected layer.</li> + + <li>The reflected layer is a set of generated Android system classes (through reflection) based + on the native layer interface that you define. This layer acts as a bridge between the native + Renderscript layer and the Android system layer. The Android build tools automatically generate + the APIs for this layer during the build process.</li> + + <li>The Android system layer consists of your normal Android APIs along with the Renderscript + APIs in {@link android.renderscript}. This layer handles things such as the Activity lifecycle + management of your application and calls the native Renderscript layer through the reflected + layer.</li> + </ul> + + <p>To fully understand how the Renderscript system works, you must understand how the reflected + layer is generated and how it interacts with the native Renderscript layer and Android system + layer. The reflected layer provides the entry points into the native code, enabling the Android + system code to give high level commands like, "rotate the view" or "filter the bitmap." It + delegates all the heavy lifting to the native layer. To accomplish this, you need to create logic + to hook together all of these layers so that they can correctly communicate.</p> + + <p>At the root of everything is your Renderscript, which is the actual C code that you write and + save to a <code>.rs</code> file in your project. There are two kinds of Renderscripts: compute + and graphics. A compute Renderscript does not do any graphics rendering while a graphics + Renderscript does.</p> + + <p>When you create a Renderscript <code>.rs</code> file, an equivalent, reflective layer class, + {@link android.renderscript.ScriptC}, is generated by the build tools and exposes the native + functions to the Android system. This class is named + <code><em>ScriptC_renderscript_filename</em></code>. The following list describes the major + components of your native Renderscript code that is reflected:</p> + + <ul> + <li>The non-static functions in your Renderscript (<code>.rs</code> file) are reflected into + <code><em>ScriptC_renderscript_filename</em></code> of type {@link + android.renderscript.ScriptC}.</li> + + <li>Any non-static, global Renderscript variables are reflected into + <code><em>ScriptC_renderscript_filename</em></code>. + Accessor methods are generated, so the Android system layer can access the values. + The <code>get()</code> method comes with a one-way communication restriction. + The Android system layer always caches the last value that is set and returns that during a call to get. + If the native Renderscript code has changed the value, the change does propagate back to the Android system layer + for efficiency. If the global variables are initialized in the native Renderscript code, those values are used + to initialize the Android system versions. If global variables are marked as <code>const</code>, + then a <code>set()</code> method is not generated. + </li> + + <li>Structs are reflected into their own classes, one for each struct, into a class named + <code>ScriptField_<em>struct_name</em></code> of type {@link + android.renderscript.Script.FieldBase}.</li> + + <li>Global pointers have a special property. They provide attachment points where the Android system can attach allocations. + If the global pointer is a user defined structure type, it must be a type that is legal for reflection (primitives + or Renderscript data types). The Android system can call the reflected class to allocate memory and + optionally populate data, then attach it to the Renderscript. + For arrays of basic types, the procedure is similar, except a reflected class is not needed. + Renderscripts should not directly set the exported global pointers.</li> + </ul> + + <p>The Android system also has a corresponding Renderscript context object, {@link + android.renderscript.RenderScript} (for a compute Renderscript) or {@link + android.renderscript.RenderScriptGL} (for a graphics Renderscript). This context object allows + you to bind to the reflected Renderscript class, so that the Renderscript context knows what its + corresponding native Renderscript is. If you have a graphics Renderscript context, you can also + specify a variety of Programs (stages in the graphics pipeline) to tweek how your graphics are + rendered. A graphics Renderscript context also needs a surface to render on, {@link + android.renderscript.RSSurfaceView}, which gets passed into its constructor. When all three of + the layers are connected, the Renderscript system can compute or render graphics.</p> + + <h2 id="api">API overview</h2> + + <p>Renderscript code is compiled and executed in a compact and well defined runtime, which has + access to a limited amount of functions. Renderscript cannot use the NDK or standard C functions, + because these functions are assumed to be running on a standard CPU. The Renderscript runtime + chooses the best processor to execute the code, which may not be the CPU, so it cannot guarantee + support for standard C libraries. What Renderscript does offer is an API that supports intensive + computation with an extensive collection of math APIs. Some key features of the Renderscript APIs + are:</p> + + + <h3 id="native-api">Native Renderscript APIs</h3> + + <p>The Renderscript headers are located in the <code>include</code> and + <code>clang-include</code> directories in the + <code><sdk_root>/platforms/android-3.0/renderscript</code> directory of the Android SDK. + The headers are automatically included for you, except for the graphics specific header, + which you can define as follows:</p> + +<pre>#include "rs_graphics.rsh"</pre> + +<p>Some key features of the native Renderscript libraries include: + <ul> + <li>A large collection of math functions with both scalar and vector typed overloaded versions + of many common routines. Operations such as adding, multiplying, dot product, and cross product + are available.</li> + <li>Conversion routines for primitive data types and vectors, matrix routines, date and time + routines, and graphics routines.</li> + <li>Logging functions</li> + <li>Graphics rendering functions</li> + <li>Memory allocation request features</li> + <li>Data types and structures to support the Renderscript system such as + Vector types for defining two-, three-, or four-vectors.</li></li> + </ul> + </ul> + + <h3 id="reflective-api">Reflective layer APIs</h3> + + <p>These classes are not generated by the reflection process, and are actually part of the + Android system APIs, but they are mainly used by the reflective layer classes to handle memory + allocation and management for your Renderscript. You normally do not need to be call these classes + directly.</p> + + <p>Because of the constraints of the Renderscript native layer, you cannot do any dynamic + memory allocation in your Renderscript <code>.rs</code> file. + The native Renderscript layer can request memory from the Android system layer, which allocates memory + for you and does reference counting to figure out when to free the memory. A memory allocation + is taken care of by the {@link android.renderscript.Allocation} class and memory is requested + in your Renderscript code with the <code>the rs_allocation</code> type. + All references to Renderscript objects are counted, so when your Renderscript native code + or system code no longer references a particular {@link android.renderscript.Allocation}, it destroys itself. + Alternatively, you can call {@link android.renderscript.Allocation#destroy destroy()} from the + Android system level, which decreases the reference to the {@link android.renderscript.Allocation}. + If no references exist after the decrease, the {@link android.renderscript.Allocation} destroys itself. + The Android system object, which at this point is just an empty shell, is eventually garbage collected. + </p> + + <p>The following classes are mainly used by the reflective layer classes:</p> + + <table> + <tr> + <th>Android Object Type</th> + + <th>Renderscript Native Type</th> + + <th>Description</th> + </tr> + + <tr> + <td>{@link android.renderscript.Element}</td> + + <td>rs_element</td> + + <td> + An {@link android.renderscript.Element} is the most basic element of a memory type. An + element represents one cell of a memory allocation. An element can have two forms: Basic or + Complex. They are typically created from C structures that are used within Renderscript + code and cannot contain pointers or nested arrays. The other common source of elements is + bitmap formats. + + <p>A basic element contains a single component of data of any valid Renderscript data type. + Examples of basic element data types include a single float value, a float4 vector, or a + single RGB-565 color.</p> + + <p>Complex elements contain a list of sub-elements and names that is basically a reflection + of a C struct. You access the sub-elements by name from a script or vertex program. The + most basic primitive type determines the data alignment of the structure. For example, a + float4 vector is alligned to <code>sizeof(float)</code> and not + <code>sizeof(float4)</code>. The ordering of the elements in memory are the order in which + they were added, with each component aligned as necessary.</p> + </td> + </tr> + + <tr> + <td>{@link android.renderscript.Type}</td> + + <td>rs_type</td> + + <td>A Type is an allocation template that consists of an element and one or more dimensions. + It describes the layout of the memory but does not allocate storage for the data that it + describes. A Type consists of five dimensions: X, Y, Z, LOD (level of detail), and Faces (of + a cube map). You can assign the X,Y,Z dimensions to any positive integer value within the + constraints of available memory. A single dimension allocation has an X dimension of greater + than zero while the Y and Z dimensions are zero to indicate not present. For example, an + allocation of x=10, y=1 is considered two dimensional and x=10, y=0 is considered one + dimensional. The LOD and Faces dimensions are booleans to indicate present or not + present.</td> + </tr> + + <tr> + <td>{@link android.renderscript.Allocation}</td> + + <td>rs_allocation</td> + + <td> + An {@link android.renderscript.Allocation} provides the memory for applications. An {@link + android.renderscript.Allocation} allocates memory based on a description of the memory that + is represented by a {@link android.renderscript.Type}. The {@link + android.renderscript.Type} describes an array of {@link android.renderscript.Element}s that + represent the memory to be allocated. Allocations are the primary way data moves into and + out of scripts. + + <p>Memory is user-synchronized and it's possible for allocations to exist in multiple + memory spaces concurrently. For example, if you make a call to the graphics card to load a + bitmap, you give it the bitmap to load from in the system memory. After that call returns, + the graphics memory contains its own copy of the bitmap so you can choose whether or not to + maintain the bitmap in the system memory. If the Renderscript system modifies an allocation + that is used by other targets, it must call {@link android.renderscript#syncAll syncAll()} to push the updates to + the memory. Otherwise, the results are undefined.</p> + + <p>Allocation data is uploaded in one of two primary ways: type checked and type unchecked. + For simple arrays there are <code>copyFrom()</code> functions that take an array from the + Android system code and copy it to the native layer memory store. Both type checked and + unchecked copies are provided. The unchecked variants allow the Android system to copy over + arrays of structures because it not support inherently support structures. For example, if + there is an allocation that is an array n floats, you can copy the data contained in a + float[n] array or a byte[n*4] array.</p> + </td> + </tr> + + <tr> + <td>{@link android.renderscript.Script}</td> + + <td>rs_script</td> + + <td>Renderscript scripts do much of the work in the native layer. This class is generated + from a Renderscript file that has the <code>.rs</code> file extension. This class is named + <code>ScriptC_<em>rendersript_filename</em></code> when it gets generated.</td> + </tr> + </table> + + <h3 id="graphics-api">Graphics API</h3> + + <p>Renderscript provides a number of graphics APIs for hardware-accelerated 3D rendering. The + Renderscript graphics APIs include a stateful context, {@link + android.renderscript.RenderScriptGL} that contains the current rendering state. The primary state + consists of the objects that are attached to the rendering context, which are the graphics Renderscript + and the four program types. The main working function of the graphics Renderscript is the code that is + defined in the <code>root()</code> function. The <code>root()</code> function is called each time the surface goes through a frame + refresh. The four program types mirror a traditional graphical rendering pipeline and are:</p> + + <ul> + <li>Vertex</li> + + <li>Fragment</li> + + <li>Store</li> + + <li>Raster</li> + </ul> + + <p>Graphical scripts have more properties beyond a basic computational script, and they call the + 'rsg'-prefixed functions defined in the <code>rs_graphics.rsh</code> header file. A graphics + Renderscript can also set four pragmas that control the default bindings to the {@link + android.renderscript.RenderScriptGL} context when the script is executing:</p> + + <ul> + <li>stateVertex</li> + + <li>stateFragment</li> + + <li>stateRaster</li> + + <li>stateStore</li> + </ul> + + <p>The possible values are <code>parent</code> or <code>default</code> for each pragma. Using + <code>default</code> says that when a script is executed, the bindings to the graphical context + are the system defaults. Using <code>parent</code> says that the state should be the same as it + is in the calling script. If this is a root script, the parent + state is taken from the bind points as set in the {@link android.renderscript.RenderScriptGL} + bind methods in the control environment (VM environment).</p> + + <p>For example, you can define this at the top of your native Renderscript code:</p> + <pre> +#pragma stateVertex(parent) +#pragma stateStore(parent) +</pre> + + <p>The following table describes the major graphics specific APIs that are available to you:</p> + + <table> + <tr> + <th>Android Object Type</th> + + <th>Renderscript Native Type</th> + + <th>Description</th> + </tr> + + <tr> + <td>{@link android.renderscript.ProgramVertex}</td> + + <td>rs_program_vertex</td> + + <td> + The Renderscript vertex program, also known as a vertex shader, describes the stage in the + graphics pipeline responsible for manipulating geometric data in a user-defined way. The + object is constructed by providing Renderscript with the following data: + + <ul> + <li>An Element describing its varying inputs or attributes</li> + + <li>GLSL shader string that defines the body of the program</li> + + <li>a Type that describes the layout of an Allocation containing constant or uniform + inputs</li> + </ul> + + <p>Once the program is created, bind it to the graphics context. It is then used for all + subsequent draw calls until you bind a new program. If the program has constant inputs, the + user needs to bind an allocation containing those inputs. The allocation’s type must match + the one provided during creation. The Renderscript library then does all the necessary + plumbing to send those constants to the graphics hardware. Varying inputs to the shader, + such as position, normal, and texture coordinates are matched by name between the input + Element and the Mesh object being drawn. The signatures don’t have to be exact or in any + strict order. As long as the input name in the shader matches a channel name and size + available on the mesh, the run-time would take care of connecting the two. Unlike OpenGL, + there is no need to link the vertex and fragment programs.</p> + <p> To bind shader constructs to the Program, declare a struct containing the necessary shader constants in your native Renderscript code. + This struct is generated into a reflected class that you can use as a constant input element + during the Program's creation. It is an easy way to create an instance of this struct as an allocation. + You would then bind this Allocation to the Program and the Renderscript system sends the data that + is contained in the struct to the hardware when necessary. To update shader constants, you change the values + in the Allocation and notify the native Renderscript code of the change.</p> + </td> + </tr> + + <tr> + <td>{@link android.renderscript.ProgramFragment}</td> + + <td>rs_program_fragment</td> + + <td>The Renderscript fragment program, also known as the fragment shader, is responsible for + manipulating pixel data in a user-defined way. It’s constructed from a GLSL shader string + containing the program body, textures inputs, and a Type object describing the constants used + by the program. Like the vertex programs, when an allocation with constant input values is + bound to the shader, its values are sent to the graphics program automatically. Note that the + values inside the allocation are not explicitly tracked. If they change between two draw + calls using the same program object, notify the runtime of that change by calling + rsgAllocationSyncAll so it could send the new values to hardware. Communication between the + vertex and fragment programs is handled internally in the GLSL code. For example, if the + fragment program is expecting a varying input called varTex0, the GLSL code inside the + program vertex must provide it. + <p> To bind shader constructs to the this Program, declare a struct containing the necessary shader constants in your native Renderscript code. + This struct is generated into a reflected class that you can use as a constant input element + during the Program's creation. It is an easy way to create an instance of this struct as an allocation. + You would then bind this Allocation to the Program and the Renderscript system sends the data that + is contained in the struct to the hardware when necessary. To update shader constants, you change the values + in the Allocation and notify the native Renderscript code of the change.</p></td> + </tr> + + <tr> + <td>{@link android.renderscript.ProgramStore}</td> + + <td>rs_program_store</td> + + <td>The Renderscript ProgramStore contains a set of parameters that control how the graphics + hardware writes to the framebuffer. It could be used to enable/disable depth writes and + testing, setup various blending modes for effects like transparency and define write masks + for color components.</td> + </tr> + + <tr> + <td>{@link android.renderscript.ProgramRaster}</td> + + <td>rs_program_raster</td> + + <td>Program raster is primarily used to specify whether point sprites are enabled and to + control the culling mode. By default back faces are culled.</td> + </tr> + + <tr> + <td>{@link android.renderscript.Sampler}</td> + + <td>rs_sampler</td> + + <td>A Sampler object defines how data is extracted from textures. Samplers are bound to + Program objects (currently only a Fragment Program) alongside the texture whose sampling they + control. These objects are used to specify such things as edge clamping behavior, whether + mip-maps are used and the amount of anisotropy required. There may be situations where + hardware limitations prevent the exact behavior from being matched. In these cases, the + runtime attempts to provide the closest possible approximation. For example, the user + requested 16x anisotropy, but only 8x was set because it’s the best available on the + hardware.</td> + </tr> + + <tr> + <td>{@link android.renderscript.Mesh}</td> + + <td>rs_mesh</td> + + <td>A collection of allocations that represent vertex data (positions, normals, texture + coordinates) and index data such as triangles and lines. Vertex data can be interleaved + within one allocation, provided separately as multiple allocation objects, or done as a + combination of the above. The layout of these allocations will be extracted from their + Elements. When a vertex channel name matches an input in the vertex program, Renderscript + automatically connects the two. Moreover, even allocations that cannot be directly mapped to + graphics hardware can be stored as part of the mesh. Such allocations can be used as a + working area for vertex-related computation and will be ignored by the hardware. Parts of the + mesh could be rendered with either explicit index sets or primitive types.</td> + </tr> + + <tr> + <td>{@link android.renderscript.Font}</td> + + <td>rs_font</td> + + <td> + <p>This class gives you a way to draw hardware accelerated text. Internally, the glyphs are + rendered using the Freetype library, and an internal cache of rendered glyph bitmaps is + maintained. Each font object represents a combination of a typeface and point sizes. + Multiple font objects can be created to represent faces such as bold and italic and to + create different font sizes. During creation, the framework determines the device screen's + DPI to ensure proper sizing across multiple configurations.</p> + + <p>Font rendering can impact performance. Even though though the state changes are + transparent to the user, they are happening internally. It is more efficient to render + large batches of text in sequence, and it is also more efficient to render multiple + characters at once instead of one by one.</p> + + <p>Font color and transparency are not part of the font object and can be freely modified + in the script to suit the your needs. Font colors work as a state machine, and every new + call to draw text will use the last color set in the script.</p> + </td> + </tr> + </table> + + + <h2 id="developing">Developing a Renderscript application</h2> + + <p>The basic workflow of developing a Renderscript application is:</p> + + <ol> + <li>Analyze your application's requirements and figure out what you want to develop with + Renderscript. To take full advantage of Renderscript, you want to use it when the computation + or graphics performance you're getting with the normal Android system APIs is + insufficient.</li> + + <li>Design the interface of your Renderscript code and implement it using the native + Renderscript APIs that are included in the Android SDK in + <code><sdk_root>/platforms/android-3.0/renderscript</code>.</li> + + <li>Create an Android project as you would normally, in Eclipse or with the + <code>android</code> tool.</li> + + <li>Place your Renderscript files in <code>src</code> folder of the Android project so that the + build tools can generate the reflective layer classes.</li> + + <li>Create your application, calling the Renderscript through the reflected class layer when + you need to.</li> + + <li>Build, install, and run your application as you would normally.</li> + </ol> + + <p>To see how a simple Renderscript application is put together, see <a href="#hello-world">The + Hello World Renderscript Graphics Application</a>. The SDK also ships with many Renderscript + samples in the<code><sdk_root>/samples/android-3.0/</code> directory.</p> + + <h3 id="hello-graphics">The Hello Graphics Application</h3> + + <p>This small application demonstrates the structure of a simple Renderscript application. You + can model your Renderscript application after the basic structure of this application. You can + find the complete source in the SDK in the + <code><android-sdk>/platforms/android-3.0/samples/HelloWorldRS directory</code>. The + application uses Renderscript to draw the string, "Hello World!" to the screen and redraws the + text whenever the user touches the screen at the location of the touch. This application is only + a demonstration and you should not use the Renderscript system to do something this trivial. The + application contains the following source files:</p> + + <ul> + <li><code>HelloWorld</code>: The main Activity for the application. This class is present to + provide Activity lifecycle management. It mainly delegates work to HelloWorldView, which is the + Renderscript surface that the sample actually draws on.</li> + + <li><code>HelloWorldView</code>: The Renderscript surface that the graphics render on. If you + are using Renderscript for graphics rendering, you must have a surface to render on. If you are + using it for computatational operations only, then you do not need this.</li> + + <li><code>HelloWorldRS</code>: The class that calls the native Renderscript code through high + level entry points that are generated by the Android build tools.</li> + + <li><code>helloworld.rs</code>: The Renderscript native code that draws the text on the + screen.</li> + + <li> + <p>The <code><project_root>/gen</code> directory contains the reflective layer classes + that are generated by the Android build tools. You will notice a + <code>ScriptC_helloworld</code> class, which is the reflective version of the Renderscript + and contains the entry points into the <code>helloworld.rs</code> native code. This file does + not appear until you run a build.</p> + </li> + </ul> + + <p>Each file has its own distinct use. The following section demonstrates in detail how the + sample works:</p> + + <dl> + <dt><code>helloworld.rs</code></dt> + + <dd> + The native Renderscript code is contained in the <code>helloworld.rs</code> file. Every + <code>.rs</code> file must contain two pragmas that define the version of Renderscript + that it is using (1 is the only version for now), and the package name that the reflected + classes should be generated with. For example: +<pre> +#pragma version(1) + +#pragma rs java_package_name(com.my.package.name) +</pre> + <p>An <code>.rs</code> file can also declare two special functions:</p> + + <ul> + <li> + <code>init()</code>: This function is called once for each instance of this Renderscript + file that is loaded on the device, before the script is accessed in any other way by the + Renderscript system. The <code>init()</code> is ideal for doing one time setup after the + machine code is loaded such as initializing complex constant tables. The + <code>init()</code> function for the <code>helloworld.rs</code> script sets the initial + location of the text that is rendered to the screen: + <pre> +void init(){ + gTouchX = 50.0f; + gTouchY = 50.0f; +} +</pre> + </li> + + <li> + <code>root()</code>: This function is the default worker function for this Renderscript + file. For graphics Renderscript applications, like this one, the Renderscript system + expects this function to render the frame that is going to be displayed. It is called + every time the frame refreshes. The <code>root()</code> function for the + <code>helloworld.rs</code> script sets the background color of the frame, the color of + the text, and then draws the text where the user last touched the screen: +<pre> +int root(int launchID) { + // Clear the background color + rsgClearColor(0.0f, 0.0f, 0.0f, 0.0f); + // Tell the runtime what the font color should be + rsgFontColor(1.0f, 1.0f, 1.0f, 1.0f); + // Introduce ourselves to the world by drawing a greeting + // at the position that the user touched on the screen + rsgDrawText("Hello World!", gTouchX, gTouchY); + + // Return value tells RS roughly how often to redraw + // in this case 20 ms + return 20; +} +</pre> + + <p>The return value, <code>20</code>, is the desired frame refresh rate in milliseconds. + The real screen refresh rate depends on the hardware, computation, and rendering + complexity that the <code>root()</code> function has to execute. A value of + <code>0</code> tells the screen to render only once and to only render again when a + change has been made to one of the properties that are being modified by the Renderscript + code.</p> + + <p>Besides the <code>init()</code> and <code>root()</code> functions, you can define the + other native functions, structs, data types, and any other logic for your Renderscript. + You can even define separate header files as <code>.rsh</code> files.</p> + </li> + </ul> + </dd> + + <dt><code>ScriptC_helloworld</code></dt> + + <dd>This class is generated by the Android build tools and is the reflected version of the + <code>helloworld.rs</code> Renderscript. It provides a a high level entry point into the + <code>helloworld.rs</code> native code by defining the corresponding methods that you can call + from Android system APIs.</dd> + + <dt><code>helloworld.bc</code> bytecode</dt> + + <dd>This file is the intermediate, platform-independent bytecode that gets compiled on the + device when the Renderscript application runs. It is generated by the Android build tools and + is packaged with the <code>.apk</code> file and subsequently compiled on the device at runtime. + This file is located in the <code><project_root>/res/raw/</code> directory and is named + <code>rs_filename.bc</code>. You need to bind these files to your Renderscript context before + call any Renderscript code from your Android application. You can reference them in your code + with <code>R.id.rs_filename</code>.</dd> + + <dt><code>HelloWorldView</code> class</dt> + + <dd> + This class represents the Surface View that the Renderscript graphics are drawn on. It does + some administrative tasks in the <code>ensureRenderScript()</code> method that sets up the + Renderscript system. This method creates a {@link android.renderscript.RenderScriptGL} + object, which represents the context of the Renderscript and creates a default surface to + draw on (you can set the surface properties such as alpha and bit depth in the {@link + android.renderscript.RenderScriptGL.SurfaceConfig} class ). When a {@link + android.renderscript.RenderScriptGL} is instantiated, this class calls the + <code>HelloRS</code> class and creates the instance of the actual Renderscript graphics + renderer. + <pre> +// Renderscipt context +private RenderScriptGL mRS; +// Script that does the rendering +private HelloWorldRS mRender; + + private void ensureRenderScript() { + if (mRS == null) { + // Initialize Renderscript with desired surface characteristics. + // In this case, just use the defaults + RenderScriptGL.SurfaceConfig sc = new RenderScriptGL.SurfaceConfig(); + mRS = createRenderScriptGL(sc); + + // Create an instance of the Renderscript that does the rendering + mRender = new HelloWorldRS(); + mRender.init(mRS, getResources()); + } + } +</pre> + + <p>This class also handles the important lifecycle events and relays touch events to the + Renderscript renderer. When a user touches the screen, it calls the renderer, + <code>HelloWorldRS</code> and asks it to draw the text on the screen at the new location.</p> + <pre> +public boolean onTouchEvent(MotionEvent ev) { + // Pass touch events from the system to the rendering script + if (ev.getAction() == MotionEvent.ACTION_DOWN) { + mRender.onActionDown((int)ev.getX(), (int)ev.getY()); + return true; + } + return false; +} +</pre> + </dd> + + <dt><code>HelloWorldRS</code></dt> + + <dd> + This class represents the Renderscript renderer for the <code>HelloWorldView</code> Surface + View. It interacts with the native Renderscript code that is defined in + <code>helloworld.rs</code> through the interfaces exposed by <code>ScriptC_helloworld</code>. + To be able to call the native code, it creates an instance of the Renderscript reflected + class, <code>ScriptC_helloworld</code>. The reflected Renderscript object binds the + Renderscript bytecode (<code>R.raw.helloworld</code>) and the Renderscript context, {@link + android.renderscript.RenderScriptGL}, so the context knows to use the right Renderscript to + render its surface. + <pre> +private Resources mRes; +private RenderScriptGL mRS; +private ScriptC_helloworld mScript; + +private void initRS() { + mScript = new ScriptC_helloworld(mRS, mRes, R.raw.helloworld); + mRS.bindRootScript(mScript); +} +</pre> + </dd> + </dl>
\ No newline at end of file diff --git a/docs/html/guide/topics/graphics/view-animation.jd b/docs/html/guide/topics/graphics/view-animation.jd new file mode 100644 index 000000000000..ad27e1ce18ac --- /dev/null +++ b/docs/html/guide/topics/graphics/view-animation.jd @@ -0,0 +1,190 @@ +page.title=View Animation +@jd:body + + <div id="qv-wrapper"> + <div id="qv"> + <h2>In this document</h2> + + <ol> + <li><a href="#tween-animation">Tween animation</a></li> + <li><a href="#frame-animation">Frame animation</a></li> + </ol> + + </div> + </div> + + You can use View Animation in any View object to + perform tweened animation and frame by frame animation. Tween animation calculates the animation + given information such as the start point, end point, size, rotation, and other common aspects of + an animation. Frame by frame animation lets you load a series of Drawable resources one after + another to create an animation. + + <h2 id="tween-animation">Tween Animation</h2> + + <p>A tween animation can perform a series of simple transformations (position, size, rotation, + and transparency) on the contents of a View object. So, if you have a {@link + android.widget.TextView} object, you can move, rotate, grow, or shrink the text. If it has a + background image, the background image will be transformed along with the text. The {@link + android.view.animation animation package} provides all the classes used in a tween animation.</p> + + <p>A sequence of animation instructions defines the tween animation, defined by either XML or + Android code. As with defining a layout, an XML file is recommended because it's more readable, + reusable, and swappable than hard-coding the animation. In the example below, we use XML. (To + learn more about defining an animation in your application code, instead of XML, refer to the + {@link android.view.animation.AnimationSet} class and other {@link + android.view.animation.Animation} subclasses.)</p> + + <p>The animation instructions define the transformations that you want to occur, when they will + occur, and how long they should take to apply. Transformations can be sequential or simultaneous + - for example, you can have the contents of a TextView move from left to right, and then rotate + 180 degrees, or you can have the text move and rotate simultaneously. Each transformation takes a + set of parameters specific for that transformation (starting size and ending size for size + change, starting angle and ending angle for rotation, and so on), and also a set of common + parameters (for instance, start time and duration). To make several transformations happen + simultaneously, give them the same start time; to make them sequential, calculate the start time + plus the duration of the preceding transformation.</p> + + <p>The animation XML file belongs in the <code>res/anim/</code> directory of your Android + project. The file must have a single root element: this will be either a single + <code><alpha></code>, <code><scale></code>, <code><translate></code>, + <code><rotate></code>, interpolator element, or <code><set></code> element that holds + groups of these elements (which may include another <code><set></code>). By default, all + animation instructions are applied simultaneously. To make them occur sequentially, you must + specify the <code>startOffset</code> attribute, as shown in the example below.</p> + + <p>The following XML from one of the ApiDemos is used to stretch, then simultaneously spin and + rotate a View object.</p> + <pre> +<set android:shareInterpolator="false"> + <scale + android:interpolator="@android:anim/accelerate_decelerate_interpolator" + android:fromXScale="1.0" + android:toXScale="1.4" + android:fromYScale="1.0" + android:toYScale="0.6" + android:pivotX="50%" + android:pivotY="50%" + android:fillAfter="false" + android:duration="700" /> + <set android:interpolator="@android:anim/decelerate_interpolator"> + <scale + android:fromXScale="1.4" + android:toXScale="0.0" + android:fromYScale="0.6" + android:toYScale="0.0" + android:pivotX="50%" + android:pivotY="50%" + android:startOffset="700" + android:duration="400" + android:fillBefore="false" /> + <rotate + android:fromDegrees="0" + android:toDegrees="-45" + android:toYScale="0.0" + android:pivotX="50%" + android:pivotY="50%" + android:startOffset="700" + android:duration="400" /> + </set> +</set> +</pre> + + <p>Screen coordinates (not used in this example) are (0,0) at the upper left hand corner, and + increase as you go down and to the right.</p> + + <p>Some values, such as pivotX, can be specified relative to the object itself or relative to the + parent. Be sure to use the proper format for what you want ("50" for 50% relative to the parent, + or "50%" for 50% relative to itself).</p> + + <p>You can determine how a transformation is applied over time by assigning an {@link + android.view.animation.Interpolator}. Android includes several Interpolator subclasses that + specify various speed curves: for instance, {@link android.view.animation.AccelerateInterpolator} + tells a transformation to start slow and speed up. Each one has an attribute value that can be + applied in the XML.</p> + + <p>With this XML saved as <code>hyperspace_jump.xml</code> in the <code>res/anim/</code> + directory of the project, the following code will reference it and apply it to an {@link + android.widget.ImageView} object from the layout.</p> + <pre> +ImageView spaceshipImage = (ImageView) findViewById(R.id.spaceshipImage); +Animation hyperspaceJumpAnimation = AnimationUtils.loadAnimation(this, R.anim.hyperspace_jump); +spaceshipImage.startAnimation(hyperspaceJumpAnimation); +</pre> + + <p>As an alternative to <code>startAnimation()</code>, you can define a starting time for the + animation with <code>{@link android.view.animation.Animation#setStartTime(long) + Animation.setStartTime()}</code>, then assign the animation to the View with <code>{@link + android.view.View#setAnimation(android.view.animation.Animation) View.setAnimation()}</code>.</p> + + <p>For more information on the XML syntax, available tags and attributes, see <a href= + "{@docRoot}guide/topics/resources/animation-resource.html">Animation Resources</a>.</p> + + <p class="note"><strong>Note:</strong> Regardless of how your animation may move or resize, the + bounds of the View that holds your animation will not automatically adjust to accommodate it. + Even so, the animation will still be drawn beyond the bounds of its View and will not be clipped. + However, clipping <em>will occur</em> if the animation exceeds the bounds of the parent View.</p> + + <h2 id="frame-animation">Frame Animation</h2> + + <p>This is a traditional animation in the sense that it is created with a sequence of different + images, played in order, like a roll of film. The {@link + android.graphics.drawable.AnimationDrawable} class is the basis for frame animations.</p> + + <p>While you can define the frames of an animation in your code, using the {@link + android.graphics.drawable.AnimationDrawable} class API, it's more simply accomplished with a + single XML file that lists the frames that compose the animation. Like the tween animation above, + the XML file for this kind of animation belongs in the <code>res/drawable/</code> directory of + your Android project. In this case, the instructions are the order and duration for each frame of + the animation.</p> + + <p>The XML file consists of an <code><animation-list></code> element as the root node and a + series of child <code><item></code> nodes that each define a frame: a drawable resource for + the frame and the frame duration. Here's an example XML file for a frame-by-frame animation:</p> + <pre> +<animation-list xmlns:android="http://schemas.android.com/apk/res/android" + android:oneshot="true"> + <item android:drawable="@drawable/rocket_thrust1" android:duration="200" /> + <item android:drawable="@drawable/rocket_thrust2" android:duration="200" /> + <item android:drawable="@drawable/rocket_thrust3" android:duration="200" /> +</animation-list> +</pre> + + <p>This animation runs for just three frames. By setting the <code>android:oneshot</code> + attribute of the list to <var>true</var>, it will cycle just once then stop and hold on the last + frame. If it is set <var>false</var> then the animation will loop. With this XML saved as + <code>rocket_thrust.xml</code> in the <code>res/drawable/</code> directory of the project, it can + be added as the background image to a View and then called to play. Here's an example Activity, + in which the animation is added to an {@link android.widget.ImageView} and then animated when the + screen is touched:</p> + <pre> +AnimationDrawable rocketAnimation; + +public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.main); + + ImageView rocketImage = (ImageView) findViewById(R.id.rocket_image); + rocketImage.setBackgroundResource(R.drawable.rocket_thrust); + rocketAnimation = (AnimationDrawable) rocketImage.getBackground(); +} + +public boolean onTouchEvent(MotionEvent event) { + if (event.getAction() == MotionEvent.ACTION_DOWN) { + rocketAnimation.start(); + return true; + } + return super.onTouchEvent(event); +} +</pre> + + <p>It's important to note that the <code>start()</code> method called on the AnimationDrawable + cannot be called during the <code>onCreate()</code> method of your Activity, because the + AnimationDrawable is not yet fully attached to the window. If you want to play the animation + immediately, without requiring interaction, then you might want to call it from the <code>{@link + android.app.Activity#onWindowFocusChanged(boolean) onWindowFocusChanged()}</code> method in your + Activity, which will get called when Android brings your window into focus.</p> + + <p>For more information on the XML syntax, available tags and attributes, see <a href= + "{@docRoot}guide/topics/resources/animation-resource.html">Animation Resources</a>.</p> +</body> +</html> diff --git a/docs/html/guide/topics/media/index.jd b/docs/html/guide/topics/media/index.jd index e355212c9af8..b6d162917ca8 100644 --- a/docs/html/guide/topics/media/index.jd +++ b/docs/html/guide/topics/media/index.jd @@ -148,70 +148,209 @@ myJet.play(); <h2 id="capture">Audio Capture</h2> <p>Audio capture from the device is a bit more complicated than audio/video playback, but still fairly simple:</p> <ol> - <li>Create a new instance of {@link android.media.MediaRecorder - android.media.MediaRecorder} using <code>new</code></li> - <li>Create a new instance of {@link android.content.ContentValues - android.content.ContentValues} and put in some standard properties like - <code>TITLE</code>, <code>TIMESTAMP</code>, and the all important - <code>MIME_TYPE</code></li> - <li>Create a file path for the data to go to (you can use {@link - android.content.ContentResolver android.content.ContentResolver} to - create an entry in the Content database and get it to assign a path - automatically which you can then use)</li> - <li>Set the audio source using {@link android.media.MediaRecorder#setAudioSource - MediaRecorder.setAudioSource()}. You will probably want to use + <li>Create a new instance of {@link android.media.MediaRecorder android.media.MediaRecorder} using <code>new</code></li> + <li>Set the audio source using + {@link android.media.MediaRecorder#setAudioSource MediaRecorder.setAudioSource()}. You will probably want to use <code>MediaRecorder.AudioSource.MIC</code></li> - <li>Set output file format using {@link - android.media.MediaRecorder#setOutputFormat MediaRecorder.setOutputFormat()} + <li>Set output file format using + {@link android.media.MediaRecorder#setOutputFormat MediaRecorder.setOutputFormat()} + </li> + <li>Set output file name using + {@link android.media.MediaRecorder#setOutputFile MediaRecorder.setOutputFile()} </li> <li>Set the audio encoder using {@link android.media.MediaRecorder#setAudioEncoder MediaRecorder.setAudioEncoder()} </li> - <li>Call {@link android.media.MediaRecorder#prepare prepare()} + <li>Call {@link android.media.MediaRecorder#prepare MediaRecorder.prepare()} on the MediaRecorder instance.</li> <li>To start audio capture, call - {@link android.media.MediaRecorder#start start()}. </li> - <li>To stop audio capture, call {@link android.media.MediaRecorder#stop stop()}. + {@link android.media.MediaRecorder#start MediaRecorder.start()}. </li> + <li>To stop audio capture, call {@link android.media.MediaRecorder#stop MediaRecorder.stop()}. <li>When you are done with the MediaRecorder instance, call -{@link android.media.MediaRecorder#release release()} on it. </li> +{@link android.media.MediaRecorder#release MediaRecorder.release()} on it. Calling +{@link android.media.MediaRecorder#release MediaRecorder.release()} is always recommended to +free the resource immediately.</li> </ol> -<h3>Example: Audio Capture Setup and Start</h3> -<p>The example below illustrates how to set up, then start audio capture.</p> +<h3>Example: Record audio and play the recorded audio</h3> +<p>The example class below illustrates how to set up, start and stop audio capture, and to play the recorded audio file.</p> <pre> - recorder = new MediaRecorder(); - ContentValues values = new ContentValues(3); - - values.put(MediaStore.MediaColumns.TITLE, SOME_NAME_HERE); - values.put(MediaStore.MediaColumns.TIMESTAMP, System.currentTimeMillis()); - values.put(MediaStore.MediaColumns.MIME_TYPE, recorder.getMimeContentType()); - - ContentResolver contentResolver = new ContentResolver(); - - Uri base = MediaStore.Audio.INTERNAL_CONTENT_URI; - Uri newUri = contentResolver.insert(base, values); - - if (newUri == null) { - // need to handle exception here - we were not able to create a new - // content entry +/* + * The application needs to have the permission to write to external storage + * if the output file is written to the external storage, and also the + * permission to record audio. These permissions must be set in the + * application's AndroidManifest.xml file, with something like: + * + * <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> + * <uses-permission android:name="android.permission.RECORD_AUDIO" /> + * + */ +package com.android.audiorecordtest; + +import android.app.Activity; +import android.widget.LinearLayout; +import android.os.Bundle; +import android.os.Environment; +import android.view.ViewGroup; +import android.widget.Button; +import android.view.View; +import android.view.View.OnClickListener; +import android.content.Context; +import android.util.Log; +import android.media.MediaRecorder; +import android.media.MediaPlayer; + +import java.io.IOException; + + +public class AudioRecordTest extends Activity +{ + private static final String LOG_TAG = "AudioRecordTest"; + private static String mFileName = null; + + private RecordButton mRecordButton = null; + private MediaRecorder mRecorder = null; + + private PlayButton mPlayButton = null; + private MediaPlayer mPlayer = null; + + private void onRecord(boolean start) { + if (start) { + startRecording(); + } else { + stopRecording(); + } } - - String path = contentResolver.getDataFilePath(newUri); - - // could use setPreviewDisplay() to display a preview to suitable View here - - recorder.setAudioSource(MediaRecorder.AudioSource.MIC); - recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); - recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); - recorder.setOutputFile(path); - - recorder.prepare(); - recorder.start(); -</pre> -<h3>Stop Recording</h3> -<p>Based on the example above, here's how you would stop audio capture. </p> -<pre> - recorder.stop(); - recorder.release(); + + private void onPlay(boolean start) { + if (start) { + startPlaying(); + } else { + stopPlaying(); + } + } + + private void startPlaying() { + mPlayer = new MediaPlayer(); + try { + mPlayer.setDataSource(mFileName); + mPlayer.prepare(); + mPlayer.start(); + } catch (IOException e) { + Log.e(LOG_TAG, "prepare() failed"); + } + } + + private void stopPlaying() { + mPlayer.release(); + mPlayer = null; + } + + private void startRecording() { + mRecorder = new MediaRecorder(); + mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); + mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); + mRecorder.setOutputFile(mFileName); + mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); + + try { + mRecorder.prepare(); + } catch (IOException e) { + Log.e(LOG_TAG, "prepare() failed"); + } + + mRecorder.start(); + } + + private void stopRecording() { + mRecorder.stop(); + mRecorder.release(); + mRecorder = null; + } + + class RecordButton extends Button { + boolean mStartRecording = true; + + OnClickListener clicker = new OnClickListener() { + public void onClick(View v) { + onRecord(mStartRecording); + if (mStartRecording) { + setText("Stop recording"); + } else { + setText("Start recording"); + } + mStartRecording = !mStartRecording; + } + }; + + public RecordButton(Context ctx) { + super(ctx); + setText("Start recording"); + setOnClickListener(clicker); + } + } + + class PlayButton extends Button { + boolean mStartPlaying = true; + + OnClickListener clicker = new OnClickListener() { + public void onClick(View v) { + onPlay(mStartPlaying); + if (mStartPlaying) { + setText("Stop playing"); + } else { + setText("Start playing"); + } + mStartPlaying = !mStartPlaying; + } + }; + + public PlayButton(Context ctx) { + super(ctx); + setText("Start playing"); + setOnClickListener(clicker); + } + } + + public AudioRecordTest() { + mFileName = Environment.getExternalStorageDirectory().getAbsolutePath(); + mFileName += "/audiorecordtest.3gp"; + } + + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + + LinearLayout ll = new LinearLayout(this); + mRecordButton = new RecordButton(this); + ll.addView(mRecordButton, + new LinearLayout.LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT, + 0)); + mPlayButton = new PlayButton(this); + ll.addView(mPlayButton, + new LinearLayout.LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT, + 0)); + setContentView(ll); + } + + @Override + public void onPause() { + super.onPause(); + if (mRecorder != null) { + mRecorder.release(); + mRecorder = null; + } + + if (mPlayer != null) { + mPlayer.release(); + mPlayer = null; + } + } +} </pre> + diff --git a/docs/html/images/animation/animation-linear.png b/docs/html/images/animation/animation-linear.png Binary files differnew file mode 100644 index 000000000000..08bd9fc3dc2c --- /dev/null +++ b/docs/html/images/animation/animation-linear.png diff --git a/docs/html/images/animation/animation-nonlinear.png b/docs/html/images/animation/animation-nonlinear.png Binary files differnew file mode 100644 index 000000000000..31c17120b551 --- /dev/null +++ b/docs/html/images/animation/animation-nonlinear.png diff --git a/docs/html/images/animation/valueanimator.png b/docs/html/images/animation/valueanimator.png Binary files differnew file mode 100644 index 000000000000..6cc2a13bbfca --- /dev/null +++ b/docs/html/images/animation/valueanimator.png diff --git a/media/jni/mediaeditor/VideoEditorMain.cpp b/media/jni/mediaeditor/VideoEditorMain.cpp index 9cd0efe66f2c..1ba5beb34843 100755 --- a/media/jni/mediaeditor/VideoEditorMain.cpp +++ b/media/jni/mediaeditor/VideoEditorMain.cpp @@ -444,7 +444,7 @@ static void jniPreviewProgressCallback (void* cookie, M4OSA_UInt32 msgType, pContext->mOverlayRenderingMode = pContext->pEditSettings->\ pClipList[pCurrEditInfo->clipIndex]->xVSS.MediaRendering; - LOGI("rendering mode %d ", pContext->mOverlayRenderingMode); + LOGV("rendering mode %d ", pContext->mOverlayRenderingMode); } @@ -653,7 +653,7 @@ static int videoEditor_renderPreviewFrame(JNIEnv* pEnv, } for (i = 0; i < uiNumberOfClipsInStoryBoard; i++) { - if (timeMs < (iIncrementedDuration + + if (timeMs <= (iIncrementedDuration + (pContext->pEditSettings->pClipList[i]->uiEndCutTime - pContext->pEditSettings->pClipList[i]->uiBeginCutTime))) { @@ -696,6 +696,7 @@ static int videoEditor_renderPreviewFrame(JNIEnv* pEnv, pContext->pEditSettings->pClipList[iCurrentClipIndex]->ClipProperties.uiVideoHeight, pContext->pEditSettings->pClipList[iCurrentClipIndex]->ClipProperties.uiVideoWidth, (M4OSA_Void **)&frameStr.pBuffer); + tnTimeMs = (M4OSA_UInt32)timeMs; } else { /* Handle 3gp/mp4 Clips here */ /* get thumbnail*/ @@ -1482,6 +1483,7 @@ videoEditor_populateSettings( int nbOverlays = 0; int i,j = 0; int *pOverlayIndex = M4OSA_NULL; + M4OSA_Char* pTempChar = M4OSA_NULL; // Add a code marker (the condition must always be true). ADD_CODE_MARKER_FUN(NULL != pEnv) @@ -1809,20 +1811,63 @@ videoEditor_populateSettings( pContext->mAudioSettings->fileType = pEnv->GetIntField(audioSettingObject,fid); M4OSA_TRACE1_1("fileType = %d",pContext->mAudioSettings->fileType); + + /* free previous allocations , if any */ + if (pContext->mAudioSettings->pFile != NULL) { + M4OSA_free((M4OSA_MemAddr32)pContext->mAudioSettings->pFile); + pContext->mAudioSettings->pFile = M4OSA_NULL; + } + if (pContext->mAudioSettings->pPCMFilePath != NULL) { + M4OSA_free((M4OSA_MemAddr32)pContext->mAudioSettings->pPCMFilePath); + pContext->mAudioSettings->pPCMFilePath = M4OSA_NULL; + } + fid = pEnv->GetFieldID(audioSettingClazz,"pFile","Ljava/lang/String;"); strPath = (jstring)pEnv->GetObjectField(audioSettingObject,fid); - pContext->mAudioSettings->pFile - = (M4OSA_Char*)pEnv->GetStringUTFChars(strPath, M4OSA_NULL); + pTempChar = (M4OSA_Char*)pEnv->GetStringUTFChars(strPath, M4OSA_NULL); + if (pTempChar != NULL) { + pContext->mAudioSettings->pFile = (M4OSA_Char*) M4OSA_malloc( + (M4OSA_UInt32)(strlen((const char*)pTempChar))+1 /* +1 for NULL termination */, 0, + (M4OSA_Char*)"strPath allocation " ); + if (pContext->mAudioSettings->pFile != M4OSA_NULL) { + M4OSA_memcpy((M4OSA_Int8 *)pContext->mAudioSettings->pFile , + (M4OSA_Int8 *)pTempChar , strlen((const char*)pTempChar)); + ((M4OSA_Int8 *)(pContext->mAudioSettings->pFile))[strlen((const char*)pTempChar)] = '\0'; + pEnv->ReleaseStringUTFChars(strPath,(const char *)pTempChar); + } else { + pEnv->ReleaseStringUTFChars(strPath,(const char *)pTempChar); + VIDEOEDIT_LOG_ERROR(ANDROID_LOG_INFO, "VIDEO_EDITOR", + "regenerateAudio() Malloc failed for pContext->mAudioSettings->pFile "); + videoEditJava_checkAndThrowRuntimeException(&needToBeLoaded, pEnv, + M4OSA_TRUE, M4ERR_ALLOC); + goto videoEditor_populateSettings_cleanup; + } + } M4OSA_TRACE1_1("file name = %s",pContext->mAudioSettings->pFile); VIDEOEDIT_LOG_API(ANDROID_LOG_INFO, "VIDEOEDITOR", "regenerateAudio() file name = %s",\ pContext->mAudioSettings->pFile); fid = pEnv->GetFieldID(audioSettingClazz,"pcmFilePath","Ljava/lang/String;"); strPCMPath = (jstring)pEnv->GetObjectField(audioSettingObject,fid); - - pContext->mAudioSettings->pPCMFilePath = - (M4OSA_Char*)pEnv->GetStringUTFChars(strPCMPath, M4OSA_NULL); - + pTempChar = (M4OSA_Char*)pEnv->GetStringUTFChars(strPCMPath, M4OSA_NULL); + if (pTempChar != NULL) { + pContext->mAudioSettings->pPCMFilePath = (M4OSA_Char*) M4OSA_malloc( + (M4OSA_UInt32)(strlen((const char*)pTempChar))+1 /* +1 for NULL termination */, 0, + (M4OSA_Char*)"strPCMPath allocation " ); + if (pContext->mAudioSettings->pPCMFilePath != M4OSA_NULL) { + M4OSA_memcpy((M4OSA_Int8 *)pContext->mAudioSettings->pPCMFilePath , + (M4OSA_Int8 *)pTempChar , strlen((const char*)pTempChar)); + ((M4OSA_Int8 *)(pContext->mAudioSettings->pPCMFilePath))[strlen((const char*)pTempChar)] = '\0'; + pEnv->ReleaseStringUTFChars(strPCMPath,(const char *)pTempChar); + } else { + pEnv->ReleaseStringUTFChars(strPCMPath,(const char *)pTempChar); + VIDEOEDIT_LOG_ERROR(ANDROID_LOG_INFO, "VIDEO_EDITOR", + "regenerateAudio() Malloc failed for pContext->mAudioSettings->pPCMFilePath "); + videoEditJava_checkAndThrowRuntimeException(&needToBeLoaded, pEnv, + M4OSA_TRUE, M4ERR_ALLOC); + goto videoEditor_populateSettings_cleanup; + } + } VIDEOEDIT_LOG_API(ANDROID_LOG_INFO, "VIDEOEDITOR", "pPCMFilePath -- %s ",\ pContext->mAudioSettings->pPCMFilePath); @@ -1849,15 +1894,6 @@ videoEditor_populateSettings( pEnv->SetBooleanField(thiz,fid,regenerateAudio); } - if (strPath != NULL) { - pEnv->ReleaseStringUTFChars(strPath, - (const char *)pContext->mAudioSettings->pFile); - } - if (strPCMPath != NULL) { - pEnv->ReleaseStringUTFChars(strPCMPath, - (const char *)pContext->mAudioSettings->pPCMFilePath); - } - /* Audio mix and duck */ fid = pEnv->GetFieldID(audioSettingClazz,"ducking_threshold","I"); pContext->mAudioSettings->uiInDucking_threshold @@ -1882,6 +1918,7 @@ videoEditor_populateSettings( } else { if (pContext->mAudioSettings != M4OSA_NULL) { pContext->mAudioSettings->pFile = M4OSA_NULL; + pContext->mAudioSettings->pPCMFilePath = M4OSA_NULL; pContext->mAudioSettings->bRemoveOriginal = 0; pContext->mAudioSettings->uiNbChannels = 0; pContext->mAudioSettings->uiSamplingFrequency = 0; @@ -1890,7 +1927,7 @@ videoEditor_populateSettings( pContext->mAudioSettings->uiAddVolume = 0; pContext->mAudioSettings->beginCutMs = 0; pContext->mAudioSettings->endCutMs = 0; - pContext->mAudioSettings->fileType = 0; + pContext->mAudioSettings->fileType = 0; pContext->mAudioSettings->bLoop = 0; pContext->mAudioSettings->uiInDucking_lowVolume = 0; pContext->mAudioSettings->bInDucking_enable = 0; @@ -2504,6 +2541,7 @@ videoEditor_init( (M4OSA_NULL == pContext->mAudioSettings), "not initialized"); pContext->mAudioSettings->pFile = M4OSA_NULL; + pContext->mAudioSettings->pPCMFilePath = M4OSA_NULL; pContext->mAudioSettings->bRemoveOriginal = 0; pContext->mAudioSettings->uiNbChannels = 0; pContext->mAudioSettings->uiSamplingFrequency = 0; @@ -2995,6 +3033,15 @@ videoEditor_release( pContext->mPreviewController = M4OSA_NULL; } + if (pContext->mAudioSettings->pFile != NULL) { + M4OSA_free((M4OSA_MemAddr32)pContext->mAudioSettings->pFile); + pContext->mAudioSettings->pFile = M4OSA_NULL; + } + if (pContext->mAudioSettings->pPCMFilePath != NULL) { + M4OSA_free((M4OSA_MemAddr32)pContext->mAudioSettings->pPCMFilePath); + pContext->mAudioSettings->pPCMFilePath = M4OSA_NULL; + } + // Free the context. if(pContext->mAudioSettings != M4OSA_NULL) { diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java index 26397bbd81c5..8e39a63fe372 100644 --- a/services/java/com/android/server/ConnectivityService.java +++ b/services/java/com/android/server/ConnectivityService.java @@ -1414,13 +1414,13 @@ public class ConnectivityService extends IConnectivityManager.Stub { LinkProperties p = nt.getLinkProperties(); if (p == null) return; String interfaceName = p.getInterfaceName(); - InetAddress defaultGatewayAddr = p.getGateway(); + if (TextUtils.isEmpty(interfaceName)) return; + for (InetAddress gateway : p.getGateways()) { - if ((interfaceName != null) && (defaultGatewayAddr != null )) { - if (!NetworkUtils.addDefaultRoute(interfaceName, defaultGatewayAddr) && DBG) { + if (!NetworkUtils.addDefaultRoute(interfaceName, gateway) && DBG) { NetworkInfo networkInfo = nt.getNetworkInfo(); log("addDefaultRoute for " + networkInfo.getTypeName() + - " (" + interfaceName + "), GatewayAddr=" + defaultGatewayAddr); + " (" + interfaceName + "), GatewayAddr=" + gateway.getHostAddress()); } } } diff --git a/wifi/java/android/net/wifi/WifiConfigStore.java b/wifi/java/android/net/wifi/WifiConfigStore.java index 55d18443ad82..d4117159477b 100644 --- a/wifi/java/android/net/wifi/WifiConfigStore.java +++ b/wifi/java/android/net/wifi/WifiConfigStore.java @@ -445,7 +445,10 @@ class WifiConfigStore { if (iter.hasNext()) { LinkAddress linkAddress = iter.next(); dhcpInfoInternal.ipAddress = linkAddress.getAddress().getHostAddress(); - dhcpInfoInternal.gateway = linkProperties.getGateway().getHostAddress(); + Iterator<InetAddress>gateways = linkProperties.getGateways().iterator(); + if (gateways.hasNext()) { + dhcpInfoInternal.gateway = gateways.next().getHostAddress(); + } dhcpInfoInternal.prefixLength = linkAddress.getNetworkPrefixLength(); Iterator<InetAddress> dnsIterator = linkProperties.getDnses().iterator(); dhcpInfoInternal.dns1 = dnsIterator.next().getHostAddress(); @@ -582,8 +585,7 @@ class WifiConfigStore { out.writeUTF(linkAddr.getAddress().getHostAddress()); out.writeInt(linkAddr.getNetworkPrefixLength()); } - InetAddress gateway = linkProperties.getGateway(); - if (gateway != null) { + for (InetAddress gateway : linkProperties.getGateways()) { out.writeUTF(GATEWAY_KEY); out.writeUTF(gateway.getHostAddress()); } @@ -688,7 +690,7 @@ class WifiConfigStore { in.readUTF()), in.readInt()); linkProperties.addLinkAddress(linkAddr); } else if (key.equals(GATEWAY_KEY)) { - linkProperties.setGateway(InetAddress.getByName(in.readUTF())); + linkProperties.addGateway(InetAddress.getByName(in.readUTF())); } else if (key.equals(DNS_KEY)) { linkProperties.addDns(InetAddress.getByName(in.readUTF())); } else if (key.equals(PROXY_SETTINGS_KEY)) { @@ -999,15 +1001,17 @@ class WifiConfigStore { .getLinkAddresses(); Collection<InetAddress> currentDnses = currentConfig.linkProperties.getDnses(); Collection<InetAddress> newDnses = newConfig.linkProperties.getDnses(); - InetAddress currentGateway = currentConfig.linkProperties.getGateway(); - InetAddress newGateway = newConfig.linkProperties.getGateway(); - - boolean linkAddressesDiffer = !currentLinkAddresses.containsAll(newLinkAddresses) || - (currentLinkAddresses.size() != newLinkAddresses.size()); - boolean dnsesDiffer = !currentDnses.containsAll(newDnses) || - (currentDnses.size() != newDnses.size()); - boolean gatewaysDiffer = (currentGateway == null) || - !currentGateway.equals(newGateway); + Collection<InetAddress> currentGateways = + currentConfig.linkProperties.getGateways(); + Collection<InetAddress> newGateways = newConfig.linkProperties.getGateways(); + + boolean linkAddressesDiffer = + (currentLinkAddresses.size() != newLinkAddresses.size()) || + !currentLinkAddresses.containsAll(newLinkAddresses); + boolean dnsesDiffer = (currentDnses.size() != newDnses.size()) || + !currentDnses.containsAll(newDnses); + boolean gatewaysDiffer = (currentGateways.size() != newGateways.size()) || + !currentGateways.containsAll(newGateways); if ((currentConfig.ipAssignment != newConfig.ipAssignment) || linkAddressesDiffer || @@ -1087,7 +1091,9 @@ class WifiConfigStore { for (LinkAddress linkAddr : config.linkProperties.getLinkAddresses()) { linkProperties.addLinkAddress(linkAddr); } - linkProperties.setGateway(config.linkProperties.getGateway()); + for (InetAddress gateway : config.linkProperties.getGateways()) { + linkProperties.addGateway(gateway); + } for (InetAddress dns : config.linkProperties.getDnses()) { linkProperties.addDns(dns); } |