diff options
45 files changed, 1903 insertions, 386 deletions
diff --git a/api/current.xml b/api/current.xml index 96a4d62194f0..953c9f34cc40 100644 --- a/api/current.xml +++ b/api/current.xml @@ -2103,7 +2103,7 @@ type="int" transient="false" volatile="false" - value="16843563" + value="16843567" static="true" final="true" deprecated="not deprecated" @@ -2125,7 +2125,7 @@ type="int" transient="false" volatile="false" - value="16843572" + value="16843576" static="true" final="true" deprecated="not deprecated" @@ -2136,7 +2136,7 @@ type="int" transient="false" volatile="false" - value="16843571" + value="16843575" static="true" final="true" deprecated="not deprecated" @@ -2147,7 +2147,7 @@ type="int" transient="false" volatile="false" - value="16843573" + value="16843577" static="true" final="true" deprecated="not deprecated" @@ -2191,7 +2191,7 @@ type="int" transient="false" volatile="false" - value="16843580" + value="16843584" static="true" final="true" deprecated="not deprecated" @@ -2213,7 +2213,7 @@ type="int" transient="false" volatile="false" - value="16843576" + value="16843580" static="true" final="true" deprecated="not deprecated" @@ -2235,7 +2235,7 @@ type="int" transient="false" volatile="false" - value="16843574" + value="16843578" static="true" final="true" deprecated="not deprecated" @@ -2246,7 +2246,7 @@ type="int" transient="false" volatile="false" - value="16843581" + value="16843585" static="true" final="true" deprecated="not deprecated" @@ -2257,7 +2257,7 @@ type="int" transient="false" volatile="false" - value="16843582" + value="16843586" static="true" final="true" deprecated="not deprecated" @@ -2466,7 +2466,7 @@ type="int" transient="false" volatile="false" - value="16843570" + value="16843574" static="true" final="true" deprecated="not deprecated" @@ -2807,7 +2807,7 @@ type="int" transient="false" volatile="false" - value="16843589" + value="16843593" static="true" final="true" deprecated="not deprecated" @@ -2818,7 +2818,7 @@ type="int" transient="false" volatile="false" - value="16843588" + value="16843592" static="true" final="true" deprecated="not deprecated" @@ -4497,6 +4497,28 @@ visibility="public" > </field> +<field name="fragmentNextEnterAnimation" + type="int" + transient="false" + volatile="false" + value="16843563" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="fragmentNextExitAnimation" + type="int" + transient="false" + volatile="false" + value="16843564" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="fragmentOpenEnterAnimation" type="int" transient="false" @@ -4519,6 +4541,28 @@ visibility="public" > </field> +<field name="fragmentPrevEnterAnimation" + type="int" + transient="false" + volatile="false" + value="16843565" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="fragmentPrevExitAnimation" + type="int" + transient="false" + volatile="false" + value="16843566" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="freezesText" type="int" transient="false" @@ -4996,7 +5040,7 @@ type="int" transient="false" volatile="false" - value="16843579" + value="16843583" static="true" final="true" deprecated="not deprecated" @@ -5117,7 +5161,7 @@ type="int" transient="false" volatile="false" - value="16843566" + value="16843570" static="true" final="true" deprecated="not deprecated" @@ -5128,7 +5172,7 @@ type="int" transient="false" volatile="false" - value="16843564" + value="16843568" static="true" final="true" deprecated="not deprecated" @@ -5139,7 +5183,7 @@ type="int" transient="false" volatile="false" - value="16843565" + value="16843569" static="true" final="true" deprecated="not deprecated" @@ -5502,7 +5546,7 @@ type="int" transient="false" volatile="false" - value="16843575" + value="16843579" static="true" final="true" deprecated="not deprecated" @@ -6778,7 +6822,7 @@ type="int" transient="false" volatile="false" - value="16843568" + value="16843572" static="true" final="true" deprecated="not deprecated" @@ -6822,7 +6866,7 @@ type="int" transient="false" volatile="false" - value="16843590" + value="16843594" static="true" final="true" deprecated="not deprecated" @@ -6833,7 +6877,7 @@ type="int" transient="false" volatile="false" - value="16843584" + value="16843588" static="true" final="true" deprecated="not deprecated" @@ -6921,7 +6965,7 @@ type="int" transient="false" volatile="false" - value="16843592" + value="16843596" static="true" final="true" deprecated="not deprecated" @@ -7735,7 +7779,7 @@ type="int" transient="false" volatile="false" - value="16843585" + value="16843589" static="true" final="true" deprecated="not deprecated" @@ -9044,7 +9088,7 @@ type="int" transient="false" volatile="false" - value="16843569" + value="16843573" static="true" final="true" deprecated="not deprecated" @@ -9066,7 +9110,7 @@ type="int" transient="false" volatile="false" - value="16843567" + value="16843571" static="true" final="true" deprecated="not deprecated" @@ -9165,7 +9209,7 @@ type="int" transient="false" volatile="false" - value="16843583" + value="16843587" static="true" final="true" deprecated="not deprecated" @@ -9396,7 +9440,7 @@ type="int" transient="false" volatile="false" - value="16843578" + value="16843582" static="true" final="true" deprecated="not deprecated" @@ -9781,7 +9825,7 @@ type="int" transient="false" volatile="false" - value="16843586" + value="16843590" static="true" final="true" deprecated="not deprecated" @@ -9858,7 +9902,7 @@ type="int" transient="false" volatile="false" - value="16843587" + value="16843591" static="true" final="true" deprecated="not deprecated" @@ -9902,7 +9946,7 @@ type="int" transient="false" volatile="false" - value="16843591" + value="16843595" static="true" final="true" deprecated="not deprecated" @@ -10320,7 +10364,7 @@ type="int" transient="false" volatile="false" - value="16843577" + value="16843581" static="true" final="true" deprecated="not deprecated" @@ -30872,6 +30916,17 @@ visibility="public" > </field> +<field name="TRANSIT_FRAGMENT_NEXT" + type="int" + transient="false" + volatile="false" + value="4099" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="TRANSIT_FRAGMENT_OPEN" type="int" transient="false" @@ -30883,6 +30938,17 @@ visibility="public" > </field> +<field name="TRANSIT_FRAGMENT_PREV" + type="int" + transient="false" + volatile="false" + value="8196" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="TRANSIT_NONE" type="int" transient="false" @@ -143262,6 +143328,8 @@ </parameter> <parameter name="args" type="android.os.Bundle"> </parameter> +<parameter name="next" type="boolean"> +</parameter> </method> <field name="EXTRA_NO_HEADERS" type="java.lang.String" diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java index da7ba6f547b4..37e72532e45c 100644 --- a/core/java/android/app/FragmentManager.java +++ b/core/java/android/app/FragmentManager.java @@ -1380,6 +1380,12 @@ final class FragmentManagerImpl implements FragmentManager { case FragmentTransaction.TRANSIT_FRAGMENT_CLOSE: rev = FragmentTransaction.TRANSIT_FRAGMENT_OPEN; break; + case FragmentTransaction.TRANSIT_FRAGMENT_NEXT: + rev = FragmentTransaction.TRANSIT_FRAGMENT_PREV; + break; + case FragmentTransaction.TRANSIT_FRAGMENT_PREV: + rev = FragmentTransaction.TRANSIT_FRAGMENT_NEXT; + break; } return rev; @@ -1398,6 +1404,16 @@ final class FragmentManagerImpl implements FragmentManager { ? com.android.internal.R.styleable.FragmentAnimation_fragmentCloseEnterAnimation : com.android.internal.R.styleable.FragmentAnimation_fragmentCloseExitAnimation; break; + case FragmentTransaction.TRANSIT_FRAGMENT_NEXT: + animAttr = enter + ? com.android.internal.R.styleable.FragmentAnimation_fragmentNextEnterAnimation + : com.android.internal.R.styleable.FragmentAnimation_fragmentNextExitAnimation; + break; + case FragmentTransaction.TRANSIT_FRAGMENT_PREV: + animAttr = enter + ? com.android.internal.R.styleable.FragmentAnimation_fragmentPrevEnterAnimation + : com.android.internal.R.styleable.FragmentAnimation_fragmentPrevExitAnimation; + break; } return animAttr; } diff --git a/core/java/android/app/FragmentTransaction.java b/core/java/android/app/FragmentTransaction.java index 09d8d2603166..b00476bb91bd 100644 --- a/core/java/android/app/FragmentTransaction.java +++ b/core/java/android/app/FragmentTransaction.java @@ -106,10 +106,14 @@ public interface FragmentTransaction { public final int TRANSIT_UNSET = -1; /** No animation for transition. */ public final int TRANSIT_NONE = 0; - /** Fragment is being added */ + /** Fragment is being added onto the stack */ public final int TRANSIT_FRAGMENT_OPEN = 1 | TRANSIT_ENTER_MASK; - /** Fragment is being removed */ + /** Fragment is being removed from the stack */ public final int TRANSIT_FRAGMENT_CLOSE = 2 | TRANSIT_EXIT_MASK; + /** Fragment is being added in a 'next' operation*/ + public final int TRANSIT_FRAGMENT_NEXT = 3 | TRANSIT_ENTER_MASK; + /** Fragment is being removed in a 'previous' operation */ + public final int TRANSIT_FRAGMENT_PREV = 4 | TRANSIT_EXIT_MASK; /** * Set specific animation resources to run for the fragments that are diff --git a/core/java/android/preference/PreferenceActivity.java b/core/java/android/preference/PreferenceActivity.java index a6c7d9e23ef7..a59b2f805c9a 100644 --- a/core/java/android/preference/PreferenceActivity.java +++ b/core/java/android/preference/PreferenceActivity.java @@ -893,11 +893,15 @@ public abstract class PreferenceActivity extends ListActivity implements } } - public void switchToHeaderInner(String fragmentName, Bundle args) { + public void switchToHeaderInner(String fragmentName, Bundle args, boolean next) { getFragmentManager().popBackStack(BACK_STACK_PREFS, POP_BACK_STACK_INCLUSIVE); Fragment f = Fragment.instantiate(this, fragmentName, args); - getFragmentManager().openTransaction().replace( - com.android.internal.R.id.prefs, f).commit(); + FragmentTransaction transaction = getFragmentManager().openTransaction(); + transaction.setTransition(next ? + FragmentTransaction.TRANSIT_FRAGMENT_NEXT : + FragmentTransaction.TRANSIT_FRAGMENT_PREV); + transaction.replace(com.android.internal.R.id.prefs, f); + transaction.commit(); } /** @@ -909,7 +913,7 @@ public abstract class PreferenceActivity extends ListActivity implements */ public void switchToHeader(String fragmentName, Bundle args) { setSelectedHeader(null); - switchToHeaderInner(fragmentName, args); + switchToHeaderInner(fragmentName, args, true); } /** @@ -919,7 +923,8 @@ public abstract class PreferenceActivity extends ListActivity implements * @param header The new header to display. */ public void switchToHeader(Header header) { - switchToHeaderInner(header.fragment, header.fragmentArguments); + switchToHeaderInner(header.fragment, header.fragmentArguments, + mHeaders.indexOf(header) > mHeaders.indexOf(mCurHeader)); setSelectedHeader(header); } @@ -979,7 +984,10 @@ public abstract class PreferenceActivity extends ListActivity implements FragmentTransaction transaction = getFragmentManager().openTransaction(); startPreferenceFragment(fragment, transaction); if (push) { + transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN); transaction.addToBackStack(BACK_STACK_PREFS); + } else { + transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_NEXT); } transaction.commit(); } @@ -1001,6 +1009,7 @@ public abstract class PreferenceActivity extends ListActivity implements FragmentTransaction transaction = getFragmentManager().openTransaction(); startPreferenceFragment(f, transaction); transaction.setBreadCrumbTitle(pref.getTitle()); + transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN); transaction.addToBackStack(BACK_STACK_PREFS); transaction.commit(); return true; diff --git a/core/java/com/android/internal/app/AlertController.java b/core/java/com/android/internal/app/AlertController.java index 4a0617c6f67e..fefdcea2b0fe 100644 --- a/core/java/com/android/internal/app/AlertController.java +++ b/core/java/com/android/internal/app/AlertController.java @@ -17,6 +17,9 @@ package com.android.internal.app; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; + +import com.android.internal.R; + import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; @@ -32,10 +35,11 @@ import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.view.ViewGroup.LayoutParams; import android.view.Window; import android.view.WindowManager; -import android.view.ViewGroup.LayoutParams; import android.widget.AdapterView; +import android.widget.AdapterView.OnItemClickListener; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.CheckedTextView; @@ -48,9 +52,6 @@ import android.widget.ListView; import android.widget.ScrollView; import android.widget.SimpleCursorAdapter; import android.widget.TextView; -import android.widget.AdapterView.OnItemClickListener; - -import com.android.internal.R; import java.lang.ref.WeakReference; @@ -403,12 +404,10 @@ public class AlertController { mIconView = (ImageView) mWindow.findViewById(R.id.icon); if (hasTextTitle) { - /* Display the title if a title is supplied, else hide it */ mTitleView = (TextView) mWindow.findViewById(R.id.alertTitle); mTitleView.setText(mTitle); - mIconView.setImageResource(R.drawable.ic_dialog_menu_generic); /* Do this last so that if the user has supplied any * icons we use them instead of the default ones. If the @@ -689,7 +688,7 @@ public class AlertController { public final Context mContext; public final LayoutInflater mInflater; - public int mIconId = -1; + public int mIconId = 0; public Drawable mIcon; public CharSequence mTitle; public View mCustomTitleView; diff --git a/core/res/res/anim/fragment_close_enter.xml b/core/res/res/anim/fragment_close_enter.xml index 53afa2a1f277..7a9a3b909e37 100644 --- a/core/res/res/anim/fragment_close_enter.xml +++ b/core/res/res/anim/fragment_close_enter.xml @@ -19,20 +19,6 @@ <set xmlns:android="http://schemas.android.com/apk/res/android"> <objectAnimator android:interpolator="@anim/decelerate_interpolator" - android:valueFrom="2" - android:valueTo="1" - android:valueType="floatType" - android:propertyName="scaleX" - android:duration="@android:integer/config_mediumAnimTime"/> - <objectAnimator - android:interpolator="@anim/decelerate_interpolator" - android:valueFrom="2" - android:valueTo="1" - android:valueType="floatType" - android:propertyName="scaleY" - android:duration="@android:integer/config_mediumAnimTime"/> - <objectAnimator - android:interpolator="@anim/decelerate_interpolator" android:valueFrom="0" android:valueTo="1" android:valueType="floatType" diff --git a/core/res/res/anim/fragment_close_exit.xml b/core/res/res/anim/fragment_close_exit.xml index 1554a4e57f9f..07435776aac6 100644 --- a/core/res/res/anim/fragment_close_exit.xml +++ b/core/res/res/anim/fragment_close_exit.xml @@ -20,20 +20,6 @@ <objectAnimator android:interpolator="@anim/accelerate_interpolator" android:valueFrom="1" - android:valueTo=".5" - android:valueType="floatType" - android:propertyName="scaleX" - android:duration="@android:integer/config_mediumAnimTime"/> - <objectAnimator - android:interpolator="@anim/accelerate_interpolator" - android:valueFrom="1" - android:valueTo=".5" - android:valueType="floatType" - android:propertyName="scaleY" - android:duration="@android:integer/config_mediumAnimTime"/> - <objectAnimator - android:interpolator="@anim/accelerate_interpolator" - android:valueFrom="1" android:valueTo="0" android:valueType="floatType" android:propertyName="alpha" diff --git a/core/res/res/anim/fragment_next_enter.xml b/core/res/res/anim/fragment_next_enter.xml new file mode 100644 index 000000000000..d2d6ec9318d9 --- /dev/null +++ b/core/res/res/anim/fragment_next_enter.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** Copyright 2010, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<set xmlns:android="http://schemas.android.com/apk/res/android"> + <objectAnimator + android:valueFrom="0" + android:valueTo="1" + android:valueType="floatType" + android:propertyName="alpha" + android:duration="@android:integer/config_longAnimTime"/> + <objectAnimator + android:valueFrom="50" + android:valueTo="0" + android:valueType="floatType" + android:propertyName="translationY" + android:duration="@android:integer/config_longAnimTime"/> +</set>
\ No newline at end of file diff --git a/core/res/res/anim/fragment_next_exit.xml b/core/res/res/anim/fragment_next_exit.xml new file mode 100644 index 000000000000..fbb82d9542cb --- /dev/null +++ b/core/res/res/anim/fragment_next_exit.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** Copyright 2010, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<set xmlns:android="http://schemas.android.com/apk/res/android"> + <objectAnimator + android:valueFrom="1" + android:valueTo="0" + android:valueType="floatType" + android:propertyName="alpha" + android:duration="@android:integer/config_mediumAnimTime"/> + <objectAnimator + android:valueFrom="0" + android:valueTo="-50" + android:valueType="floatType" + android:propertyName="translationY" + android:duration="@android:integer/config_mediumAnimTime"/> +</set>
\ No newline at end of file diff --git a/core/res/res/anim/fragment_open_enter.xml b/core/res/res/anim/fragment_open_enter.xml index 142f60c4cb14..ac6049465373 100644 --- a/core/res/res/anim/fragment_open_enter.xml +++ b/core/res/res/anim/fragment_open_enter.xml @@ -18,20 +18,6 @@ --> <set xmlns:android="http://schemas.android.com/apk/res/android"> <objectAnimator - android:interpolator="@anim/decelerate_interpolator" - android:valueFrom="2" - android:valueTo="1" - android:valueType="floatType" - android:propertyName="scaleX" - android:duration="@android:integer/config_mediumAnimTime"/> - <objectAnimator - android:interpolator="@anim/decelerate_interpolator" - android:valueFrom="2" - android:valueTo="1" - android:valueType="floatType" - android:propertyName="scaleY" - android:duration="@android:integer/config_mediumAnimTime"/> - <objectAnimator android:valueFrom="0" android:valueTo="1" android:valueType="floatType" diff --git a/core/res/res/anim/fragment_open_exit.xml b/core/res/res/anim/fragment_open_exit.xml index 21260b91a3aa..3bf1ad491a08 100644 --- a/core/res/res/anim/fragment_open_exit.xml +++ b/core/res/res/anim/fragment_open_exit.xml @@ -18,20 +18,6 @@ --> <set xmlns:android="http://schemas.android.com/apk/res/android"> <objectAnimator - android:interpolator="@anim/accelerate_interpolator" - android:valueFrom="1" - android:valueTo="2" - android:valueType="floatType" - android:propertyName="scaleX" - android:duration="@android:integer/config_mediumAnimTime"/> - <objectAnimator - android:interpolator="@anim/accelerate_interpolator" - android:valueFrom="1" - android:valueTo="2" - android:valueType="floatType" - android:propertyName="scaleY" - android:duration="@android:integer/config_mediumAnimTime"/> - <objectAnimator android:valueFrom="1" android:valueTo="0" android:valueType="floatType" diff --git a/core/res/res/anim/fragment_prev_enter.xml b/core/res/res/anim/fragment_prev_enter.xml new file mode 100644 index 000000000000..d37afd040ac4 --- /dev/null +++ b/core/res/res/anim/fragment_prev_enter.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** Copyright 2010, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<set xmlns:android="http://schemas.android.com/apk/res/android"> + <objectAnimator + android:valueFrom="0" + android:valueTo="1" + android:valueType="floatType" + android:propertyName="alpha" + android:duration="@android:integer/config_longAnimTime"/> + <objectAnimator + android:valueFrom="-50" + android:valueTo="0" + android:valueType="floatType" + android:propertyName="translationY" + android:duration="@android:integer/config_longAnimTime"/> +</set>
\ No newline at end of file diff --git a/core/res/res/anim/fragment_prev_exit.xml b/core/res/res/anim/fragment_prev_exit.xml new file mode 100644 index 000000000000..a445a4d08b49 --- /dev/null +++ b/core/res/res/anim/fragment_prev_exit.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** Copyright 2010, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<set xmlns:android="http://schemas.android.com/apk/res/android"> + <objectAnimator + android:valueFrom="1" + android:valueTo="0" + android:valueType="floatType" + android:propertyName="alpha" + android:duration="@android:integer/config_mediumAnimTime"/> + <objectAnimator + android:valueFrom="0" + android:valueTo="50" + android:valueType="floatType" + android:propertyName="translationY" + android:duration="@android:integer/config_mediumAnimTime"/> +</set>
\ No newline at end of file diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 8e2b762f0adf..33d3eeb482d3 100755 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -1088,6 +1088,10 @@ <attr name="fragmentOpenExitAnimation" format="reference" /> <attr name="fragmentCloseEnterAnimation" format="reference" /> <attr name="fragmentCloseExitAnimation" format="reference" /> + <attr name="fragmentNextEnterAnimation" format="reference" /> + <attr name="fragmentNextExitAnimation" format="reference" /> + <attr name="fragmentPrevEnterAnimation" format="reference" /> + <attr name="fragmentPrevExitAnimation" format="reference" /> </declare-styleable> <!-- Window animation class attributes. --> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 35f8df56268e..2c3c4fcc76b9 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -1332,6 +1332,10 @@ <public type="attr" name="fragmentOpenExitAnimation" /> <public type="attr" name="fragmentCloseEnterAnimation" /> <public type="attr" name="fragmentCloseExitAnimation" /> + <public type="attr" name="fragmentNextEnterAnimation" /> + <public type="attr" name="fragmentNextExitAnimation" /> + <public type="attr" name="fragmentPrevEnterAnimation" /> + <public type="attr" name="fragmentPrevExitAnimation" /> <public type="attr" name="actionBarSize" /> <public type="attr" name="imeSubtypeLocale" /> <public type="attr" name="imeSubtypeMode" /> diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml index 3dfaf7f6de10..4b5047ed84de 100644 --- a/core/res/res/values/styles.xml +++ b/core/res/res/values/styles.xml @@ -78,6 +78,10 @@ <item name="fragmentOpenExitAnimation">@anim/fragment_open_exit</item> <item name="fragmentCloseEnterAnimation">@anim/fragment_close_enter</item> <item name="fragmentCloseExitAnimation">@anim/fragment_close_exit</item> + <item name="fragmentNextEnterAnimation">@anim/fragment_next_enter</item> + <item name="fragmentNextExitAnimation">@anim/fragment_next_exit</item> + <item name="fragmentPrevEnterAnimation">@anim/fragment_prev_enter</item> + <item name="fragmentPrevExitAnimation">@anim/fragment_prev_exit</item> </style> <!-- Standard animations for a non-full-screen window or activity. --> diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteCursorTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteCursorTest.java index bbffe70a67a3..f6b1d04c8487 100644 --- a/core/tests/coretests/src/android/database/sqlite/SQLiteCursorTest.java +++ b/core/tests/coretests/src/android/database/sqlite/SQLiteCursorTest.java @@ -20,6 +20,7 @@ import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.LargeTest; import android.test.suitebuilder.annotation.SmallTest; import android.util.Log; @@ -97,7 +98,10 @@ public class SQLiteCursorTest extends AndroidTestCase { assertTrue(db.isOpen()); } - @SmallTest + /** + * this test could take a while to execute. so, designate it as LargetTest + */ + @LargeTest public void testFillWindow() { // create schema final String testTable = "testV"; diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteStatementTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteStatementTest.java index 217545f9cf6c..955336af19aa 100644 --- a/core/tests/coretests/src/android/database/sqlite/SQLiteStatementTest.java +++ b/core/tests/coretests/src/android/database/sqlite/SQLiteStatementTest.java @@ -128,7 +128,7 @@ public class SQLiteStatementTest extends AndroidTestCase { * pre-compiled SQL statement id except in during the period of binding the arguments * and executing the SQL statement. */ - @SmallTest + @LargeTest public void testReferenceToPrecompiledStatementId() { mDatabase.execSQL("create table t (i int, j text);"); verifyReferenceToPrecompiledStatementId(false); diff --git a/docs/html/guide/practices/design/responsiveness.jd b/docs/html/guide/practices/design/responsiveness.jd index 2c7633d10c94..b811d1b911be 100644 --- a/docs/html/guide/practices/design/responsiveness.jd +++ b/docs/html/guide/practices/design/responsiveness.jd @@ -19,19 +19,6 @@ page.title=Designing for Responsiveness <p><strong>Figure 1.</strong> An ANR dialog displayed to the user.</p> </div> -<p>It's possible to write code that wins every performance test in the world, but still sends users -in a fiery rage when they try to use the application. These are the applications that aren't -<em>responsive</em> enough — the ones that feel -sluggish, hang or freeze for significant periods, or take too long to process -input. </p> - -<p>In Android, the system guards against applications that are insufficiently responsive for a -period of time by displaying a dialog to the user, called the Application Not Responding (ANR) -dialog. The user can choose to let the application continue, but the user won't appreciate having to -act on this dialog every time he or she uses your application. So it's important to design -responsiveness into your application, so that the system never has cause to display an ANR to the -user. </p> - <p>It's possible to write code that wins every performance test in the world, but still sends users in a fiery rage when they try to use the application. These are the applications that aren't <em>responsive</em> enough — the diff --git a/docs/html/guide/topics/fundamentals/activities.jd b/docs/html/guide/topics/fundamentals/activities.jd new file mode 100644 index 000000000000..b616ab8b0f33 --- /dev/null +++ b/docs/html/guide/topics/fundamentals/activities.jd @@ -0,0 +1,762 @@ +page.title=Activities +parent.title=Application Fundamentals +parent.link=index.html +@jd:body + +<div id="qv-wrapper"> +<div id="qv"> +<h2>Quickview</h2> +<ul> + <li>An activity provides a user interface for a single screen in your application</li> + <li>Activities can move into the background and then be resumed with their state restored</li> +</ul> + +<h2>In this document</h2> +<ol> + <li><a href="#Creating">Creating an Activity</a> + <ol> + <li><a href="#UI">Implementing a user interface</a></li> + <li><a href="#Declaring">Declaring the activity in the manifest</a></li> + </ol> + </li> + <li><a href="#StartingAnActivity">Starting an Activity</a> + <ol> + <li><a href="#StartingAnActivityForResult">Starting an Activity for a Result</a></li> + </ol> + </li> + <li><a href="#Lifecycle">Managing the Activity Lifecycle</a> + <ol> + <li><a href="#ImplementingLifecycleCallbacks">Implementing the lifecycle callbacks</a></li> + <li><a href="#SavingActivityState">Saving activity state</a></li> + <li><a href="#ConfigurationChanges">Handling configuration changes</a></li> + <li><a href="#CoordinatingActivities">Coordinating activities</a></li> + </ol> + </li> +</ol> + +<h2>Key classes</h2> +<ol> + <li>{@link android.app.Activity}</li> +</ol> + +<h2>See also</h2> +<ol> + <li><a href="{@docRoot}resources/tutorials/hello-world.html">Hello World Tutorial</a></li> + <li><a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack">Tasks and Back +Stack</a></li> +</ol> + +</div> +</div> + + + +<p>An {@link android.app.Activity} is an application component that provides a screen with which +users can interact in order to do something, such as dial the phone, take a photo, send an email, or +view a map. Each activity is given a window in which to draw its user interface. The window +typically fills the screen, but may be smaller than the screen and float on top of other +windows.</p> + +<p> An application usually consists of multiple activities that are loosely bound +to each other. Typically, one activity in an application is specified as the "main" activity, which +is presented to the user when launching the application for the first time. Each +activity can then start another activity in order to perform different actions. Each time a new +activity starts, the previous activity is stopped, but the system preserves the activity +in a stack (the "back stack"). When a new activity starts, it is pushed onto the back stack and +takes user focus. The back stack abides to the basic "last in, first out" queue mechanism, +so, when the user is done with the current activity and presses the BACK key, it +is popped from the stack (and destroyed) and the previous activity resumes. (The back stack is +discussed more in the <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks +and Back Stack</a> document.)</p> + +<p>When an activity is stopped because a new activity starts, it is notified of this change in state +through the activity's lifecycle callback methods. +There are several callback methods that an activity might receive, due to a change in its +state—whether the system is creating it, stopping it, resuming it, or destroying it—and +each callback provides you the opportunity to perform specific work that's +appropriate to that state change. For instance, when stopped, your activity should release any +large objects, such as network or database connections. When the activity resumes, you can +reacquire the necessary resources and resume actions that were interrupted. These state transitions +are all part of the activity lifecycle.</p> + +<p>The rest of this document discusses the basics of how to build and use an activity, +including a complete discussion of how the activity lifecycle works, so you can properly manage +the transition between various activity states.</p> + + + +<h2 id="Creating">Creating an Activity</h2> + +<p>To create an activity, you must create a subclass of {@link android.app.Activity} (or +an existing subclass of it). In your subclass, you need to implement callback methods that the +system calls when the activity transitions between various states of its lifecycle, such as when +the activity is being created, stopped, resumed, or destroyed. The two most important callback +methods are:</p> + +<dl> + <dt>{@link android.app.Activity#onCreate onCreate()}</dt> + <dd>You must implement this method. The system calls this when creating your + activity. Within your implementation, you should initialize the essential components of your +activity. + Most importantly, this is where you must call {@link android.app.Activity#setContentView + setContentView()} to define the layout for the activity's user interface.</dd> + <dt>{@link android.app.Activity#onPause onPause()}</dt> + <dd>The system calls this method as the first indication that the user is leaving your +activity (though it does not always mean the activity is being destroyed). This is usually where you +should commit any changes that should be persisted beyond the current user session (because +the user might not come back).</dd> +</dl> + +<p>There are several other lifecycle callback methods that you should use in order to provide a +fluid user experience between activities and handle unexpected interuptions that cause your activity +to be stopped and even destroyed. All of the lifecycle callback methods are discussed later, in +the section about <a href="#Lifecycle">Managing the Activity Lifecycle</a>.</p> + + + +<h3 id="UI">Implementing a user interface</h3> + +<p> The user interface for an activity is provided by a hierarchy of views—objects derived +from the {@link android.view.View} class. Each view controls a particular rectangular space +within the activity's window and can respond to user interaction. For example, a view might be a +button that initiates an action when the user touches it.</p> + +<p>Android provides a number of ready-made views that you can use to design and organize your +layout. "Widgets" are views that provide a visual (and interactive) elements for the screen, such +as a button, text field, checkbox, or just an image. "Layouts" are views derived from {@link +android.view.ViewGroup} that provide a unique layout model for its child views, such as a linear +layout, a grid layout, or relative layout. You can also subclass the {@link android.view.View} and +{@link android.view.ViewGroup} classes (or existing subclasses) to create your own widgets and +layouts and apply them to your activity layout.</p> + +<p>The most common way to define a layout using views is with an XML layout file saved in your +application resources. This way, you can maintain the design of your user interface separately from +the source code that defines the activity's behavior. You can set the layout as the UI for your +activity with {@link android.app.Activity#setContentView(int) setContentView()}, passing the +resource ID for the layout. However, you can also create new {@link android.view.View}s in your +activity code and build a view hierarchy by inserting new {@link +android.view.View}s into a {@link android.view.ViewGroup}, then use that layout by passing the root +{@link android.view.ViewGroup} to {@link android.app.Activity#setContentView(View) +setContentView()}.</p> + +<p>For information about creating a user interface, see the <a +href="{@docRoot}guide/topics/ui/index.html">User Interface</a> documentation.</p> + + + +<h3 id="Declaring">Declaring the activity in the manifest</h3> + +<p>You must declare your activity in the manifest file in order for it to +be accessible to the system. To decalare your activity, open your manifest file and add an <a +href="{@docRoot}guide/topics/manifest/activity-element.html">{@code <activity>}</a> element +as a child of the <a +href="{@docRoot}guide/topics/manifest/application-element.html">{@code <application>}</a> +element. For example:</p> + +<pre> +<manifest ... > + <application ... > + <activity android:name=".ExampleActivity" /> + ... + </application ... > + ... +</manifest > +</pre> + +<p>There are several other attributes that you can include in this element, to define properties +such as the label for the activity, an icon for the activity, or a theme to style the activity's +UI. See the <a +href="{@docRoot}guide/topics/manifest/activity-element.html">{@code <activity>}</a> element +reference for more information about available attributes.</p> + + +<h4>Using intent filters</h4> + +<p>An <a href="{@docRoot}guide/topics/manifest/activity-element.html">{@code +<activity>}</a> element can also specify various intent filters—using the <a +href="{@docRoot}guide/topics/manifest/intent-filter-element.html">{@code +<intent-filter>}</a> element—in order to declare how other application components may +activate it.</p> + +<p>When you create a new application using the Android SDK tools, the stub activity +that's created for you automatically includes an intent filter that declares the activity +responds to the "main" action and should be placed in the "launcher" category. The intent filter +looks like this:</p> + +<pre> +<activity android:name=".ExampleActivity" android:icon="@drawable/app_icon"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> +</activity> +</pre> + +<p>The <a href="{@docRoot}guide/topics/manifest/action-element.html">{@code +<action>}</a> element specifies that this is the "main" entry point to the application. The <a +href="{@docRoot}guide/topics/manifest/category-element.html">{@code +<category>}</a> element specifies that this activity should be listed in the +system's application launcher (to allow users to launch this activity).</p> + +<p>If you intend for your application to be self-contained and not allow other applications to +activate its activities, then you don't need any other intent filters. Only one activity should +have the "main" action and "launcher" category, as in the previous example. Activities that +you don't want to make available to other applications should have no intent filters and you can +start them yourself using explicit intents (as discussed in the following section).</p> + +<p>However, if you want your activity to respond to implicit intents that are delivered from +other applications (and your own), then you must define additional intent filters for your +activity. For each type of intent to which you want to respond, you must include an <a +href="{@docRoot}guide/topics/manifest/intent-filter-element.html">{@code +<intent-filter>}</a> that includes an +<a href="{@docRoot}guide/topics/manifest/action-element.html">{@code +<action>}</a> element and, optionally, a <a +href="{@docRoot}guide/topics/manifest/category-element.html">{@code +<category>}</a> element and/or a <a +href="{@docRoot}guide/topics/manifest/data-element.html">{@code +<data>}</a> element. These elements specify the type of intent to which your activity can +respond.</p> + +<p>For more information about how your activities can respond to intents, see the <a +href="{@docRoot}guide/topics/intents/intents-filters.html">Intents and Intent Filters</a> +document.</p> + + + +<h2 id="StartingAnActivity">Starting an Activity</h2> + +<p>You can start another activity by calling {@link android.app.Activity#startActivity + startActivity()}, passing it an {@link android.content.Intent} that describes the activity you + want to start. The intent specifies either the exact activity you want to start or describes the + type of action you want to perform (and the system selects the appropriate activity for you, +which + can even be from a different application). An intent can also carry small amounts of data to be + used by the activity that is started.</p> + +<p>When working within your own application, you'll often need to simply launch a known activity. + You can do so by creating an intent that explicitly defines the activity you want to start, +using the class name. For example, here's how one activity starts another activity named {@code +SignInActivity}:</p> + +<pre> +Intent intent = new Intent(this, SignInActivity.class); +startActivity(intent); +</pre> + +<p>However, your application might also want to perform some action, such as send an email, text + message, or status update, using data from your activity. In this case, your application might + not have its own activities to perform such actions, so you can instead leverage the activities + provided by other applications on the device, which can perform the actions for you. This is where +intents are really valuable—you can create an intent that describes an action you want to +perform and the system + launches the appropriate activity from another application. If there are + multiple activities that can handle the intent, then the user can select which one to use. For + example, if you want to allow the user to send an email message, you can create the + following intent:</p> + +<pre> +Intent intent = new Intent(Intent.ACTION_SEND); +intent.putExtra(Intent.EXTRA_EMAIL, recipientArray); +startActivity(intent); +</pre> + +<p>The {@link android.content.Intent#EXTRA_EMAIL} extra added to the intent is a string array of + email addresses to which the email should be sent. When an email application responds to this + intent, it reads the string array provided in the extra and places them in the "to" field of the + email composition form. In this situation, the email application's activity starts and when the + user is done, your activity resumes.</p> + + + + +<h3 id="StartingAnActivityForResult">Starting an activity for a result</h3> + +<p>Sometimes, you might want to receive a result from the activity that you start. In that case, + start the activity by calling {@link android.app.Activity#startActivityForResult + startActivityForResult()} (instead of {@link android.app.Activity#startActivity + startActivity()}). To then receive the result from the subsequent +activity, implement the {@link android.app.Activity#onActivityResult onActivityResult()} callback + method. When the subsequent activity is done, it returns a result in an {@link +android.content.Intent} to your {@link android.app.Activity#onActivityResult onActivityResult()} +method.</p> + +<p>For example, perhaps you want the user to pick one of their contacts, so your activity can +do something with the information in that contact. Here's how you can create such an intent and +handle the result:</p> + +<pre> +private void pickContact() { + // Create an intent to "pick" a contact, as defined by the content provider URI + Intent intent = new Intent(Intent.ACTION_PICK, Contacts.CONTENT_URI); + startActivityForResult(intent, PICK_CONTACT_REQUEST); +} + +@Override +protected void onActivityResult(int requestCode, int resultCode, Intent data) { + // If the request went well (OK) and the request was PICK_CONTACT_REQUEST + if (resultCode == Activity.RESULT_OK && requestCode == PICK_CONTACT_REQUEST) { + // Perform a query to the contact's content provider for the contact's name + Cursor cursor = getContentResolver().query(data.getData(), + new String[] {Contacts.DISPLAY_NAME}, null, null, null); + if (cursor.moveToFirst()) { // True if the cursor is not empty + int columnIndex = cursor.getColumnIndex(Contacts.DISPLAY_NAME); + String name = cursor.getString(columnIndex); + // Do something with the selected contact's name... + } + } +} +</pre> + +<p>This example shows the basic logic you should use in your {@link +android.app.Activity#onActivityResult onActivityResult()} method in order to handle an +activity result. The first condition checks whether the request was successful—if it was, then +the {@code resultCode} will be {@link android.app.Activity#RESULT_OK}—and whether the request +to which this result is responding is known—in this case, the {@code requestCode} matches the +second parameter sent with {@link android.app.Activity#startActivityForResult +startActivityForResult()}. From there, the code handles the activity result by querying the +data returned in an {@link android.content.Intent} (the {@code data} parameter).</p> + +<p>What happens is, a {@link +android.content.ContentResolver} performs a query against a content provider, which returns a +{@link android.database.Cursor} that allows the queried data to be read. For more information, see +the <a +href="{@docRoot}guide/topics/providers/content-providers.html">Content Providers</a> document.</p> + +<p>For more information about using intents, see the <a +href="{@docRoot}guide/topics/intents/intents-filters.html">Intents and Intent +Filters</a> document.</p> + + +<h2 id="ShuttingDown">Shutting Down an Activity</h2> + +<p>You can shut down an activity by calling its {@link android.app.Activity#finish +finish()} method. You can also shut down a separate activity that you previously started by calling +{@link android.app.Activity#finishActivity finishActivity()}.</p> + +<p class="note"><strong>Note:</strong> In most cases, you should not explicitly finish an activity +using these methods. As discussed in the following section about the activity lifecycle, the +Android system manages the life of an activity for you, so you do not need to finish your own +activities. Calling these methods could adversely affect the expected user +experience and should only be used when you absolutely do not want the user to return to this +instance of the activity.</p> + + +<h2 id="Lifecycle">Managing the Activity Lifecycle</h2> + +<p>Managing the lifecycle of your activities by implementing callback methods is +crucial to developing a strong +and flexible application. The lifecycle of an activity is directly affected by its association with +other activities, its task and back stack.</p> + +<p>An activity can exist in essentially three states:</p> + +<dl> + <dt><i>Resumed</i></dt> + <dd>The activity is in the foreground of the screen and has user focus. (This state is +also sometimes referred to as "running".)</dd> + + <dt><i>Paused</i></dt> + <dd>Another activity is in the foreground and has focus, but this one is still visible. That is, +another activity is visible on top of this one and that activity is partially transparent or doesn't +cover the entire screen. A paused activity is completely alive (the {@link android.app.Activity} +object is retained in memory, it maintains all state and member information, and remains attached to +the window manager), but can be killed by the system in extremely low memory situations.</dd> + + <dt><i>Stopped</i></dt> + <dd>The activity is completely obscured by another activity (the activity is now in the +"background"). A stopped activity is also still alive (the {@link android.app.Activity} +object is retained in memory, it maintains all state and member information, but is <em>not</em> +attached to the window manager). However, it is no longer visible to the user and it +can be killed by the system when memory is needed elsewhere.</dd> +</dl> + +<p>If an activity is paused or stopped, the system can drop it from memory either by asking it to +finish (calling its {@link android.app.Activity#finish finish()} method), or simply killing its +process. When the activity is opened again (after being finished or killed), it must be created all +over.</p> + + + +<h3 id="ImplementingLifecycleCallbacks">Implementing the lifecycle callbacks</h3> + +<p>When an activity transitions into and out of the different states described above, it is notified +through various callback methods. All of the callback methods are hooks that you +can override to do appropriate work when the state of your activity changes. The following skeleton +activity includes each of the fundamental lifecycle methods:</p> + + +<pre> +public class ExampleActivity extends Activity { + @Override + public void {@link android.app.Activity#onCreate onCreate}(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + // The activity is being created. + } + @Override + protected void {@link android.app.Activity#onStart onStart()} { + super.onStart(); + // The activity is about to become visible. + } + @Override + protected void {@link android.app.Activity#onResume onResume()} { + super.onResume(); + // The activity has become visible (it is now "resumed"). + } + @Override + protected void {@link android.app.Activity#onPause onPause()} { + super.onPause(); + // Another activity is taking focus (this activity is about to be "paused"). + } + @Override + protected void {@link android.app.Activity#onStop onStop()} { + super.onStop(); + // The activity is no longer visible (it is now "stopped") + } + @Override + protected void {@link android.app.Activity#onDestroy onDestroy()} { + super.onDestroy(); + // The activity is about to be destroyed. + } +} +</pre> + +<p class="note"><strong>Note:</strong> Your implementation of these lifecycle methods must +always call the superclass implementation before doing any work, as shown in the examples above.</p> + +<p>Taken together, these methods define the entire lifecycle of an activity. By implementing these +methods, you can monitor three nested loops in the activity lifecycle: </p> + +<ul> +<li>The <b>entire lifetime</b> of an activity happens between the call to {@link +android.app.Activity#onCreate onCreate()} and the call to {@link +android.app.Activity#onDestroy}. Your activity should perform setup of +"global" state (such as defining layout) in {@link android.app.Activity#onCreate onCreate()}, and +release all remaining resources in {@link android.app.Activity#onDestroy}. For example, if your +activity has a thread running in the background to download data from the network, it might create +that thread in {@link android.app.Activity#onCreate onCreate()} and then stop the thread in {@link +android.app.Activity#onDestroy}.</li> + +<li><p>The <b>visible lifetime</b> of an activity happens between the call to {@link +android.app.Activity#onStart onStart()} and the call to {@link +android.app.Activity#onStop onStop()}. During this time, the user can see the activity +on-screen and interact with it. For example, {@link android.app.Activity#onStop onStop()} is called +when a new activity starts and this one is no longer visible. Between these two methods, you can +maintain resources that are needed to show the activity to the user. For example, you can register a +{@link android.content.BroadcastReceiver} in {@link +android.app.Activity#onStart onStart()} to monitor changes that impact your UI, and unregister +it in {@link android.app.Activity#onStop onStop()} when the user can no longer see what you are +displaying. The system might call {@link android.app.Activity#onStart onStart()} and {@link +android.app.Activity#onStop onStop()} multiple times during the entire lifetime of the activity, as +the activity alternates between being visible and hidden to the user.</p></li> + +<li><p>The <b>foreground lifetime</b> of an activity happens between the call to {@link +android.app.Activity#onResume onResume()} and the call to {@link android.app.Activity#onPause +onPause()}. During this time, the activity is in front of all other activities on screen and has +user input focus. An activity can frequently transition in and out of the foreground—for +example, {@link android.app.Activity#onPause onPause()} is called when the device goes to sleep or +when a dialog appears. Because this state can transition often, the code in these two methods should +be fairly lightweight in order to avoid slow transitions that make the user wait.</p></li> +</ul> + +<p>Figure 1 illustrates these loops and the paths an activity might take between states. +The rectangles represent the callback methods you can implement to perform operations when +the activity transitions between states. <p> + +<img src="{@docRoot}images/activity_lifecycle.png" alt="" /> +<p class="img-caption"><strong>Figure 1.</strong> The activity lifecycle.</p> + +<p>The same lifecycle callback methods are listed in table 1, which describes each of the callback +methods in more detail and locates each one within the +activity's overall lifecycle, including whether the system can kill the activity after the +callback method completes.</p> + +<p class="table-caption"><strong>Table 1.</strong> A summary of the activity lifecycle's +callback methods.</p> + +<table border="2" width="85%" frame="hsides" rules="rows"> +<colgroup align="left" span="3"></colgroup> +<colgroup align="left"></colgroup> +<colgroup align="center"></colgroup> +<colgroup align="center"></colgroup> + +<thead> +<tr><th colspan="3">Method</th> <th>Description</th> <th>Killable after?</th> <th>Next</th></tr> +</thead> + +<tbody> +<tr> + <td colspan="3" align="left"><code>{@link android.app.Activity#onCreate onCreate()}</code></td> + <td>Called when the activity is first created. + This is where you should do all of your normal static set up — + create views, bind data to lists, and so on. This method is passed + a Bundle object containing the activity's previous state, if that + state was captured (see <a href="#actstate">Saving Activity State</a>, + later). + <p>Always followed by {@code onStart()}.</p></td> + <td align="center">No</td> + <td align="center">{@code onStart()}</td> +</tr> + +<tr> + <td rowspan="5" style="border-left: none; border-right: none;"> </td> + <td colspan="2" align="left"><code>{@link android.app.Activity#onRestart +onRestart()}</code></td> + <td>Called after the activity has been stopped, just prior to it being + started again. + <p>Always followed by {@code onStart()}</p></td> + <td align="center">No</td> + <td align="center">{@code onStart()}</td> +</tr> + +<tr> + <td colspan="2" align="left"><code>{@link android.app.Activity#onStart onStart()}</code></td> + <td>Called just before the activity becomes visible to the user. + <p>Followed by {@code onResume()} if the activity comes + to the foreground, or {@code onStop()} if it becomes hidden.</p></td> + <td align="center">No</td> + <td align="center">{@code onResume()} <br/>or<br/> {@code onStop()}</td> +</tr> + +<tr> + <td rowspan="2" style="border-left: none;"> </td> + <td align="left"><code>{@link android.app.Activity#onResume onResume()}</code></td> + <td>Called just before the activity starts + interacting with the user. At this point the activity is at + the top of the activity stack, with user input going to it. + <p>Always followed by {@code onPause()}.</p></td> + <td align="center">No</td> + <td align="center">{@code onPause()}</td> +</tr> + +<tr> + <td align="left"><code>{@link android.app.Activity#onPause onPause()}</code></td> + <td>Called when the system is about to start resuming another + activity. This method is typically used to commit unsaved changes to + persistent data, stop animations and other things that may be consuming + CPU, and so on. It should do whatever it does very quickly, because + the next activity will not be resumed until it returns. + <p>Followed either by {@code onResume()} if the activity + returns back to the front, or by {@code onStop()} if it becomes + invisible to the user.</td> + <td align="center"><strong style="color:#800000">Yes</strong></td> + <td align="center">{@code onResume()} <br/>or<br/> {@code onStop()}</td> +</tr> + +<tr> + <td colspan="2" align="left"><code>{@link android.app.Activity#onStop onStop()}</code></td> + <td>Called when the activity is no longer visible to the user. This + may happen because it is being destroyed, or because another activity + (either an existing one or a new one) has been resumed and is covering it. + <p>Followed either by {@code onRestart()} if + the activity is coming back to interact with the user, or by + {@code onDestroy()} if this activity is going away.</p></td> + <td align="center"><strong style="color:#800000">Yes</strong></td> + <td align="center">{@code onRestart()} <br/>or<br/> {@code onDestroy()}</td> +</tr> + +<tr> + <td colspan="3" align="left"><code>{@link android.app.Activity#onDestroy +onDestroy()}</code></td> + <td>Called before the activity is destroyed. This is the final call + that the activity will receive. It could be called either because the + activity is finishing (someone called <code>{@link android.app.Activity#finish + finish()}</code> on it), or because the system is temporarily destroying this + instance of the activity to save space. You can distinguish + between these two scenarios with the <code>{@link + android.app.Activity#isFinishing isFinishing()}</code> method.</td> + <td align="center"><strong style="color:#800000">Yes</strong></td> + <td align="center"><em>nothing</em></td> +</tr> +</tbody> +</table> + +<p>The column labeled "Killable after?" indicates whether or not the system can +kill the process hosting the activity at any time <em>after the method returns</em>, without +executing another line of the activity's code. Three methods are marked "yes": ({@link +android.app.Activity#onPause +onPause()}, {@link android.app.Activity#onStop onStop()}, and {@link android.app.Activity#onDestroy +onDestroy()}). Because {@link android.app.Activity#onPause onPause()} is the first +of the three, once the activity is created, {@link android.app.Activity#onPause onPause()} is the +last method that's guaranteed to be called before the process <em>can</em> be killed—if +the system must recover memory in an emergency, then {@link +android.app.Activity#onStop onStop()} and {@link android.app.Activity#onDestroy onDestroy()} might +not be called. Therefore, you should use {@link android.app.Activity#onPause onPause()} to write +crucial persistent data (such as user edits) to storage. However, you should be selective about +what information must be retained during {@link android.app.Activity#onPause onPause()}, because any +blocking procedures in this method block the transition to the next activity and slow the user +experience.</p> + +<p> Methods that are marked "No" in the <b>Killable</b> column protect the process hosting the +activity from being killed from the moment they are called. Thus, an activity is killable +from the time {@link android.app.Activity#onPause onPause()} returns to the time +{@link android.app.Activity#onResume onResume()} is called. It will not again be killable until +{@link android.app.Activity#onPause onPause()} is again called and returns. </p> + +<p class="note"><strong>Note:</strong> An activity that's not technically "killable" by this +definition in table 1 might still be killed by the system—but that would happen only in +extreme circumstances when there is no other recourse. When an activity might be killed is +discussed more in the <a +href="{@docRoot}guide/topics/fundamentals/processes-and-threading.html">Processes and +Threading</a> document.</p> + + +<h3 id="SavingActivityState">Saving activity state</h3> + +<p>The introduction to <a href="Lifecycle">Managing the Activity Lifecycle</a> briefly mentions that +when an activity is paused or stopped, the state of the activity is retained. This is true because +the {@link android.app.Activity} object is still held in memory when it is paused or +stopped—all information about its members and current state is still alive. Thus, any changes +the user made within the activity are retained in memory, so that when the activity returns to the +foreground (when it "resumes"), those changes are still there.</p> + +<div class="figure" style="width:615px"> +<img src="{@docRoot}images/fundamentals/restore_instance.png" alt="" /> +<p class="img-caption"><strong>Figure 2.</strong> The two ways in which an activity returns to user +focus with its state intact: either the activity is stopped, then resumed and the activity state +remains intact (left), or the activity is destroyed, then recreated and the activity must restore +the previous activity state (right).</p> +</div> + +<p>However, when the system destroys an activity in order to recover memory, the {@link +android.app.Activity} object is destroyed, so the system cannot simply resume it with its state +intact. Instead, the system must recreate the {@link android.app.Activity} object if the user +navigates back to it. Yet, the user is unaware +that the system destroyed the activity and recreated it and, thus, probably +expects the activity to be exactly as it was. In this situation, you can ensure that +important information about the activity state is preserved by implementing an additional +callback method that allows you to save information about the state of your activity and then +restore it when the the system recreates the activity.</p> + +<p>The callback method in which you can save information about the current state of your activity is +{@link android.app.Activity#onSaveInstanceState onSaveInstanceState()}. The system calls this method +before making the activity vulnerable to being destroyed and passes it +a {@link android.os.Bundle} object. The {@link android.os.Bundle} is where you can store +state information about the activity as name-value pairs, using methods such as {@link +android.os.Bundle#putString putString()}. Then, if the system kills your activity's +process and the user navigates back to your activity, the system passes the {@link +android.os.Bundle} to {@link android.app.Activity#onCreate onCreate()} so you can restore the +activity state you saved during {@link android.app.Activity#onSaveInstanceState +onSaveInstanceState()}. If there is no state information to restore, then the {@link +android.os.Bundle} passed to {@link android.app.Activity#onCreate onCreate()} is null.</p> + +<p class="note"><strong>Note:</strong> There's no guarantee that {@link +android.app.Activity#onSaveInstanceState onSaveInstanceState()} will be called before your +activity is destroyed, because there are cases in which it won't be necessary to save the state +(such as when the user leaves your activity using the BACK key, because the user is explicitly +closing the activity). If the method is called, it is always called before {@link +android.app.Activity#onStop onStop()} and possibly before {@link android.app.Activity#onPause +onPause()}.</p> + +<p>However, even if you do nothing and do not implement {@link +android.app.Activity#onSaveInstanceState onSaveInstanceState()}, some of the activity state is +restored by the {@link android.app.Activity} class's default implementation of {@link +android.app.Activity#onSaveInstanceState onSaveInstanceState()}. Specifically, the default +implementation calls {@link +android.view.View#onSaveInstanceState onSaveInstanceState()} for every {@link android.view.View} +in the layout, which allows each view to provide information about itself +that should be saved. Almost every widget in the Android framework implements this method as +appropriate, such that any visible changes to the UI are automatically saved and restored when your +activity is recreated. For example, the {@link android.widget.EditText} widget saves any text +entered by the user and the {@link android.widget.CheckBox} widget saves whether it's checked or +not. The only work required by you is to provide a unique ID (with the <a +href="{@docRoot}guide/topics/resources/layout-resource.html#idvalue">{@code android:id}</a> +attribute) for each widget you want to save its state. If a widget does not have an ID, then it +cannot save its state.</p> + +<div class="sidebox-wrapper"> +<div class="sidebox"> +<p>You can also explicitly stop a view in your layout from saving its state by setting the +{@link android.R.attr#saveEnabled android:saveEnabled} attribute to {@code "false"} or by calling +the {@link android.view.View#setSaveEnabled setSaveEnabled()} method. Usually, you should not +disable this, but you might if you want to restore the state of the activity UI differently.</p> +</div> +</div> + +<p>Although the default implementation of {@link +android.app.Activity#onSaveInstanceState onSaveInstanceState()} saves useful information about +your activity's UI, you still might need to override it to save additional information. +For example, you might need to save member values that changed during the activity's life (which +might correlate to values restored in the UI, but the members that hold those UI values are not +restored, by default).</p> + +<p>Because the default implementation of {@link +android.app.Activity#onSaveInstanceState onSaveInstanceState()} helps save the state of the UI, if +you override the method in order to save additional state information, you should always call the +superclass implementation of {@link android.app.Activity#onSaveInstanceState onSaveInstanceState()} +before doing any work.</p> + +<p class="note"><strong>Note:</strong> Because {@link android.app.Activity#onSaveInstanceState +onSaveInstanceState()} is not guaranteed +to be called, you should use it only to record the transient state of the activity (the state of +the UI)—you should never use it to store persistent data. Instead, you should use {@link +android.app.Activity#onPause onPause()} to store persistent data (such as data that should be saved +to a database) when the user leaves the activity.</p> + +<p>A good way to test your application's ability to restore its state is to simply rotate the +device so that the screen orientation changes. When the screen orientation changes, the system +destroys and recreates the activity in order to apply alternative resources that might be available +for the new orientation. For this reason alone, it's very important that your activity +completely restores its state when it is recreated, because users regularly rotate the screen while +using applications.</p> + + +<h3 id="ConfigurationChanges">Handling configuration changes</h3> + +<p>Some device configurations can change during runtime (such as screen orientation, keyboard +availability, and language). When such a change occurs, Android restarts the running Activity +({@link android.app.Activity#onDestroy} is called, followed immediately by {@link +android.app.Activity#onCreate onCreate()}). The restart behavior is +designed to help your application adapt to new configurations by automatically reloading your +application with alternative resources that you've provided. If you design your activity to +properly handle this event, it will be more resilient to unexpected events in the activity +lifecycle.</p> + +<p>The best way to handle a configuration change, such as a change in the screen orientation, is + to simply preserve the state of your application using {@link + android.app.Activity#onSaveInstanceState onSaveInstanceState()} and {@link +android.app.Activity#onRestoreInstanceState onRestoreInstanceState()} (or {@link +android.app.Activity#onCreate onCreate()}), as discussed in the previous section.</p> + +<p>For a detailed discussion about configuration changes that happen at runtime and how you should +handle them, read <a href="{@docRoot}guide/topics/resources/runtime-changes.html">Handling +Runtime Changes</a>.</p> + + + +<h3 id="CoordinatingActivities">Coordinating activities</h3> + + <p>When one activity starts another, they both experience lifecycle transitions. The first activity +pauses and stops (though, it won't stop if it's still visible in the background), while the other +activity is created. In case these activities share data saved to disc or elsewhere, it's important +to understand that the first activity is not completely stopped before the second one is created. +Rather, the process of starting the second one overlaps with the process of stopping the first +one.</p> + +<p>The order of lifecycle callbacks is well defined, particularly when the two activities are in the +same process and one is starting the other. Here's the order of operations that occur when Activity +A starts Acivity B: </p> + +<ol> +<li>Activity A's {@link android.app.Activity#onPause onPause()} method executes.</li> + +<li>Activity B's {@link android.app.Activity#onCreate onCreate()}, {@link +android.app.Activity#onStart onStart()}, and {@link android.app.Activity#onResume onResume()} +methods execute in sequence. (Activity B now has user focus.)</li> + +<li>Then, if Activity A is no longer visible on screen, its {@link +android.app.Activity#onStop onStop()} method executes.</li> +</ol> + + <p>This predictable sequence of lifecycle callbacks allows you to manage the transition of +information from one activity to another. For example, if you must write to a database when the +first activity stops so that the following activity can read it, then you should write to the +database during {@link android.app.Activity#onPause onPause()} instead of during {@link +android.app.Activity#onStop onStop()}.</p> + + +<h2>Beginner's Path</h2> + +<p>For more information about how Android maintains a history of activities and +enables user multitasking, continue with the <b><a +href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back +Stack</a></b> document.</p> diff --git a/docs/html/guide/topics/fundamentals/tasks-and-back-stack.jd b/docs/html/guide/topics/fundamentals/tasks-and-back-stack.jd new file mode 100644 index 000000000000..47dc5471d3e5 --- /dev/null +++ b/docs/html/guide/topics/fundamentals/tasks-and-back-stack.jd @@ -0,0 +1,568 @@ +page.title=Tasks and Back Stack +parent.title=Application Fundamentals +parent.link=index.html +@jd:body + +<div id="qv-wrapper"> +<div id="qv"> +<h2>Quickview</h2> +<ul> + <li>All activities belong to a task</li> + <li>A task contains a collection of activities in the order in which the user interacts with +them</li> + <li>Tasks can move to the background and retain the state of each activity in order for the user +to perform other tasks without loosing their work</li> +</ul> + +<h2>In this document</h2> +<ol> +<li><a href="#ActivityState">Saving Activity State</a></li></li> +<li><a href="#ManagingTasks">Managing Tasks</a> + <ol> + <li><a href="#TaskLaunchModes">Defining launch modes</a></li> + <li><a href="#Affinities">Handling affinities</a></li> + <li><a href="#Clearing">Clearing the back stack</a></li> + <li><a href="#Starting">Starting a task</a></li> + </ol> +</li> +</ol> + +<h2>See also</h2> +<ol> + <li><a><a href="{@docRoot}videos/index.html#v=fL6gSd4ugSI">Application Lifecycle video</a></li> + <li><a +href="{@docRoot}guide/topics/manifest/activity-element.html">{@code <activity>} manifest +element</a></li> +</ol> +</div> +</div> + + +<p>An application usually contains multiple <a +href="{@docRoot}guide/topics/fundamentals/activities.html">activities</a>. Each activity +should be designed around a specific kind of action the user can perform and can start other +activities. For example, an email application might have one activity to show a list of new email. +When the user selects an email, a new activity opens to view that email.</p> + +<p>An activity can even start activities that exist in other applications on the device. For +example, if your application wants to send an email, you can define an intent to perform a "send" +action and include some data, such as an email address and a message. An activity from another +application that declares itself to handle this kind of intent then opens. In this case, the intent +is to send an email, so an email application's "compose" activity starts (if multiple activities +support the same intent, then the system lets the user select which one to use). When the email is +sent, your activity resumes and it seems as if the email activity was part of your application. Even +though the activities may be from different applications, Android maintains this seamless user +experience by keeping both activities in the same <em>task</em>.</p> + +<p>A task is a collection of activities that users interact with +when performing a certain job. The activities are arranged in a stack (the "back stack"), in the +order in which each activity is opened.</p> + +<!-- SAVE FOR WHEN THE FRAGMENT DOC IS ADDED +<div class="sidebox-wrapper"> +<div class="sidebox"> +<h3>Adding fragments to a task's back stack</h3> + +<p>Your activity can also include {@link android.app.Fragment}s to the back stack. For example, +suppose you have a two-pane layout using fragments, one of which is a list view (fragment A) and the +other being a layout to display an item from the list (fragment B). When the user selects an item +from the list, fragment B is replaced by a new fragment (fragment C). In this case, it might be +desireable for the user to navigate back to reveal fragment B, using the BACK key.</p> +<p>In order to add fragment B to the back stack so that this is possible, you must call {@link +android.app.FragmentTransaction#addToBackStack addToBackStack()} before you {@link +android.app.FragmentTransaction#commit()} the transaction that replaces fragment B with fragment +C.</p> +<p>For more information about using fragments and adding them to the back stack, see the {@link +android.app.Fragment} class documentation.</p> + +</div> +</div> +--> + +<p>The device Home screen is the starting place for most tasks. When the user touches an icon in the +application +launcher (or a shortcut on the Home screen), that application's task comes to the foreground. If no +task exists for the application (the application has not been used recently), then a new task +is created and the "main" activity for that application opens as the root activity in the stack.</p> + +<p>When the current activity starts another, the new activity is pushed on the top of the stack and +takes focus. The previous activity remains in the stack, but is stopped. When an activity +stops, the system retains the current state of its user interface. When the user presses the BACK +key, the current activity is popped from the top of the stack (the activity is destroyed) and the +previous activity resumes (the previous state of its UI is restored). Activities in the stack are +never rearranged, only pushed and popped from the stack—pushed onto the stack when started by +the current activity and popped off when the user leaves it using the BACK key. As such, the back +stack operates as a "last in, first out" object structure. Figure 1 visualizes +this behavior with a timeline showing the progress between activities along with the current back +stack at each point in time.</p> + +<img src="{@docRoot}images/fundamentals/diagram_backstack.png" alt="" /> +<p class="img-caption"><strong>Figure 1.</strong> A representation of how each new activity in a +task adds an item to the back stack. When the user presses the BACK key, the current activity is +destroyed and the previous activity resumes.</p> + + +<p>If the user continues to press BACK, then each activity in the stack is popped off to reveal the +previous one, until the user returns to the Home screen (or to whichever activity was running when +the task began). When all activities are removed from the stack, the task no longer exists.</p> + +<div class="figure" style="width:369px"> +<img src="{@docRoot}images/fundamentals/diagram_multitasking.png" alt="" /> <p +class="img-caption"><strong>Figure 2.</strong> Two tasks: Task A is in the background, waiting +to be resumed, while Task B receives user interaction in the foreground.</p> +</div> +<div class="figure" style="width:178px"> + <img src="{@docRoot}images/fundamentals/diagram_multiple_instances.png" alt="" /> <p +class="img-caption"><strong>Figure 3.</strong> A single activity is instantiated multiple times.</p> +</div> + +<p>A task is a cohesive unit that can move to the "background" when users begin a new task or go +to the Home screen, via the HOME key. While in the background, all the activities in the task are +stopped, but the back stack for the task remains intact—the task has simply lost focus while +another task takes place, as shown in figure 2. A task can then return to the "foreground" so users +can pick up where they left off. Suppose, for example, that the current task (Task A) has three +activities in its stack—two under the current activity. The user presses the HOME key, then +starts a new application from the application launcher. When the Home screen appears, Task A goes +into the background. When the new application starts, the system starts a task for that application +(Task B) with its own stack of activities. After interacting with +that application, the user returns Home again and selects the application that originally +started Task A. Now, Task A comes to the +foreground—all three activities in its stack are intact and the activity at the top of the +stack resumes. At +this point, the user can also switch back to Task B by going Home and selecting the application icon +that started that task (or by touching and holding the HOME key to reveal recent tasks and selecting +one). This is an example of multitasking on Android.</p> + +<p class="note"><strong>Note:</strong> Multiple tasks can be held in the background at once. +However, if the user is running many background tasks at the same time, the system might begin +destroying background activities in order to recover memory, causing the activity states to be lost. +See the following section about <a href="#ActivityState">Activity state</a>.</p> + +<p>Because the activities in the back stack are never rearranged, if your application allows +users to start a particular activity from more than one activity, a new instance of +that activity is created and popped onto the stack (rather than bringing any previous instance of +the activity to the top). As such, one activity in your application might be instantiated multiple +times (even from different tasks), as shown in figure 3. As such, if the user navigates backward +using the BACK key, each instance of the activity is revealed in the order they were opened (each +with their own UI state). However, you can modify this behavior if you do not want an activity to be +instantiated more than once. How to do so is discussed in the later section about <a +href="#ManagingTasks">Managing Tasks</a>.</p> + + +<p>To summarize the default behavior for activities and tasks:</p> + +<ul> + <li>When Activity A starts Activity B, Activity A is stopped, but the system retains its state +(such as scroll position and text entered into forms). +If the user presses the BACK key while in Activity B, Activity A resumes with its state +restored.</li> + <li>When the user leaves a task by pressing the HOME key, the current activity is stopped and +its task goes into the background. The system retains the state of every activity in the task. If +the user later resumes the task by selecting the launcher icon that began the task, the task comes +to the foreground and resumes the activity at the top of the stack.</li> + <li>If the user presses the BACK key, the current activity is popped from the stack and +destroyed. The previous activity in the stack is resumed. When an activity is destroyed, the system +<em>does not</em> retain the activity's state.</li> + <li>Activities can be instantiated multiple times, even from other tasks.</li> +</ul> + + +<h2 id="ActivityState">Saving Activity State</h2> + +<p>As discussed above, the system's default behavior preserves the state of an activity when it is +stopped. This way, when users navigate back to a previous activity, its user interface appears +the way they left it. However, you can—and <strong>should</strong>—proactively retain +the state of your activities using callback methods, in case the activity is destroyed and must +be recreated.</p> + +<p>When the system stops one of your activities (such as when a new activity starts or the task +moves to the background), the system might destroy that activity completely if it needs to recover +system memory. When this happens, information about the activity state is lost. If this happens, the +system still +knows that the activity has a place in the back stack, but when the activity is brought to the +top of the stack the system must recreate it (rather than resume it). In order to +avoid loosing the user's work, you should proactively retain it by implementing the {@link +android.app.Activity#onSaveInstanceState onSaveInstanceState()} callback +methods in your activity.</p> + +<p>For more information about how to save your activity state, see the <a +href="{@docRoot}guide/topics/fundamentals/activities.html#SavingActivityState">Activities</a> +document.</p> + + + +<h2 id="ManagingTasks">Managing Tasks</h2> + +<p>The way Android manages tasks and the back stack, as described above—by placing all +activities started in succession in the same task and in a "last in, first out" stack—works +great for most applications and you shouldn't have to worry about how your activities are associated +with tasks or how they exist in the back stack. However, you might decide that you want to interrupt +the normal behavior. Perhaps you want an activity in your application to begin a new task when it is +started (instead of being placed within the current task); or, when you start an activity, you want +to bring forward an existing instance of it (instead of creating a new +instance on top of the back stack); or, you want your back stack to be cleared of all +activitiesstart an activity except for the root activity when the user leaves the task.</p> + +<p>You can do these things and more, with attributes in the +<a href="{@docRoot}guide/topics/manifest/activity-element.html">{@code +<activity>}</a> manifest element and with flags in the intent that you pass to {@link +android.app.Activity#startActivity startActivity()}.</p> + +<p>In this regard, the the principal <a +href="{@docRoot}guide/topics/manifest/activity-element.html">{@code <activity>}</a> +attributes you can use are:</p> + +<ul class="nolist"> + <li><a href="{@docRoot}guide/topics/manifest/activity-element.html#aff">{@code +taskAffinity}</a></li> + <li><a href="{@docRoot}guide/topics/manifest/activity-element.html#lmode">{@code +launchMode}</a></li> + <li><a href="{@docRoot}guide/topics/manifest/activity-element.html#reparent">{@code +allowTaskReparenting}</a></li> + <li><a href="{@docRoot}guide/topics/manifest/activity-element.html#clear">{@code +clearTaskOnLaunch}</a></li> + <li><a href="{@docRoot}guide/topics/manifest/activity-element.html#always">{@code +alwaysRetainTaskState}</a></li> + <li><a href="{@docRoot}guide/topics/manifest/activity-element.html#finish">{@code +finishOnTaskLaunch}</a></li> +</ul> + +<p>And the principal intent flags you can use are:</p> + +<ul class="nolist"> + <li>{@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK}</li> + <li>{@link android.content.Intent#FLAG_ACTIVITY_CLEAR_TOP}</li> + <li>{@link android.content.Intent#FLAG_ACTIVITY_SINGLE_TOP}</li> +</ul> + +<p>In the following sections, you'll see how you can use these manifest attributes and intent +flags to define how activities are associated with tasks and how the behave in the back stack.</p> + + +<p class="caution"><strong>Caution:</strong> Most applications should not interrupt the default +behavior for activities and tasks. If you determine that it's necessary for your activity to modify +the default behaviors, use caution and be sure to test the usability of the activity during +launch and when navigating back to it from other activities and tasks with the BACK key. Be sure +to test for navigation behaviors that might conflict with the user's expected behavior.</p> + + +<h3 id="TaskLaunchModes">Defining launch modes</h3> + +<p>Launch modes allow you to define how a new instance of an activity is associated with the +current task. You can define different launch modes in two ways:</p> +<ul class="nolist"> + <li><a href="#ManifestForTasks">Using the manifest file</a> + <p>When you declare an activity in your manifest file, you can specify how the activity +should associate with tasks when it starts.</li> + <li><a href="#IntentFlagsForTasks">Using Intent flags</a> + <p>When you call {@link android.app.Activity#startActivity startActivity()}, +you can include a flag in the {@link android.content.Intent} that declares how (or +whether) the new activity should associate with the current task.</p></li> +</ul> + +<p>As such, if Activity A starts Activity B, Activity B can define in its manifest how it +should associate with the current task (if at all) and Activity A can also request how Activity +B should associate with current task. If both activities define how Activity B +should associate with a task, then Activity A's request (as defined in the intent) is honored +over Activity B's request (as defined in its manifest).</p> + +<p class="note"><strong>Note:</strong> Some the launch modes available in the manifest +are not available as flags for an intent and, likewise, some launch modes available as flags +for an intent cannot be defined in the manifest.</p> + + +<h4 id="ManifestForTasks">Using the manifest file</h4> + +<p>When declaring an activity in your manifest file, you can specify how the activity should +associate with a task using the <a +href="{@docRoot}guide/topics/manifest/activity-element.html">{@code <activity>}</a> +element's <a href="{@docRoot}guide/topics/manifest/activity-element.html#lmode">{@code +launchMode}</a> attribute.</p> + +<p>The <a href="{@docRoot}guide/topics/manifest/activity-element.html#lmode">{@code +launchMode}</a> attribute specifies an instruction on how the activity should be launched into a +task. There are four different launch modes you can assign to the +<code><a href="{@docRoot}guide/topics/manifest/activity-element.html#lmode">launchMode</a></code> +attribute:</p> + +<dl> +<dt>{@code "standard"} (the default mode)</dt> + <dd>Default. The system creates a new instance of the activity in the task from +which it was started and routes the intent to it. The activity can be instantiated multiple times, +each instance can belong to different tasks, and one task can have multiple instances.</dd> +<dt>{@code "singleTop"}</dt> + <dd>If an instance of the activity already exists at the top of the current task, the system +routes the intent to that instance through a call to its {@link +android.app.Activity#onNewIntent onNewIntent()} method, rather than creating a new instance of the +activity. The activity can be instantiated multiple times, each instance can +belong to different tasks, and one task can have multiple instances (but only if the the +activity at the top of the back stack is <em>not</em> an existing instance of the activity). + <p>For example, suppose a task's back stack consists of root activity A with activities B, C, +and D on top (the stack is A-B-C-D; D is on top). An intent arrives for an activity of type D. +If D has the default {@code "standard"} launch mode, a new instance of the class is launched and the +stack becomes A-B-C-D-D. However, if D's launch mode is {@code "singleTop"}, the existing instance +of D is deliverd the intent through {@link +android.app.Activity#onNewIntent onNewIntent()}, because it's at the top of the stack—the +stack remains A-B-C-D. However, if an intent arrives for an activity of type B, then a new +instance of B is added to the stack, even if its launch mode is {@code "singleTop"}.</p> + <p class="note"><strong>Note:</strong> When a new instance of an activity is created, +the user can press the BACK key to return to the previous activity. But when an existing instance of +an activity handles a new intent, the user cannot press the BACK key to return to the state of +the activity before the new intent arrived in {@link android.app.Activity#onNewIntent +onNewIntent()}.</p> +</dd> + +<dt>{@code "singleTask"}</dt> + <dd>The system creates a new task and instantiates the activity at the root of the new task. +However, if an instance of the activity already exists in a separate task, the system routes the +intent to the existing instance through a call to its {@link +android.app.Activity#onNewIntent onNewIntent()} method, rather than creating a new instance. Only +one instance of the activity can exist at a time. + <p class="note"><strong>Note:</strong> Although the activity starts in a new task, the +BACK key still returns the user to the previous activity.</p></dd> +<dt>{@code "singleInstance"}.</dt> + <dd>Same as {@code "singleTask"}, except that the system doesn't launch any other activities into +the task holding the instance. The activity is always the single and only member of its task; +any activities started by this one open in a separate task.</dd> +</dl> + + +<p>As another example, the Android Browser application declares that the web browser activity should +always open in its own task—by specifying the {@code singleTask} launch mode in the <a +href="{@docRoot}guide/topics/manifest/activity-element.html">{@code <activity>}</a> element. +This means that if your application issues an +intent to open the Android Browser, its activity is <em>not</em> placed in the same +task as your application. Instead, either a new task starts for the Browser or, if the Browser +already has a task running in the background, that task is brought forward to handle the new +intent.</p> + +<p>Regardless of whether an activity starts in a new task or in the same task as the activity that +started it, the BACK key always takes the user to the previous activity. However, if you +start an activity from your task (Task A) that specifies the {@code singleTask} launch mode, then +that activity might have an instance in the background that belongs to a task with its own back +stack (Task B). In this +case, when Task B is brought forward to handle a new intent, the BACK key first navigates +backward through the activities in Task B before returning to +the top-most activity in Task A. Figure 4 visualizes this type of scenario.</p> + +<img src="{@docRoot}images/fundamentals/diagram_backstack_singletask_multiactivity.png" alt="" /> +<p class="img-caption"><strong>Figure 4.</strong> A representation of how an activity with +launch mode "singleTask" is added to the back stack. If the activity is already a part of a +background task with its own back stack (Task B), then the entire back stack also comes +forward, on top of the current task (Task A).</p> + +<p>For more information about using launch modes in the manifest file, see the +<code><a href="{@docRoot}guide/topics/manifest/activity-element.html"><activity></a></code> +element documentation, where the {@code launchMode} attribute and the accepted values are +discussed more.</p> + +<p class="note"><strong>Note:</strong> The behaviors that you specify for your activity with the <a +href="{@docRoot}guide/topics/manifest/activity-element.html#lmode">{@code launchMode}</a> attribute +can be overriden by flags included with the intent that start your activity, as discussed in the +next section.</p> + + + +<h4 id="#IntentFlagsForTasks">Using Intent flags</h4> + +<p>When starting an activity, you can modify the default association of an activity to its task +by including flags in the intent that you deliver to {@link +android.app.Activity#startActivity startActivity()}. The flags you can use to modify the +default behavior are:</p> + +<p> + <dt>{@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK}</dt> + <dd>Start the activity in a new task. If a task is already running for the activity you are now +starting, that task is brought to the foreground with its last state restored and the activity +receives the new intent in {@link android.app.Activity#onNewIntent onNewIntent()}. + <p>This produces the same behavior as the {@code "singleTask"} <a +href="{@docRoot}guide/topics/manifest/activity-element.html#lmode">{@code launchMode}</a> value, +discussed in the previous section.</p></dd> + <dt>{@link android.content.Intent#FLAG_ACTIVITY_SINGLE_TOP}</dt> + <dd>If the activity being started is the current activity (at the top of the back stack), then +the existing instance receives a call to {@link android.app.Activity#onNewIntent onNewIntent()}, +instead of creating a new instance of the activity. + <p>This produces the same behavior as the {@code "singleTop"} <a +href="{@docRoot}guide/topics/manifest/activity-element.html#lmode">{@code launchMode}</a> value, +discussed in the previous section.</p></dd> + <dt>{@link android.content.Intent#FLAG_ACTIVITY_CLEAR_TOP}</dt> + <dd>If the activity being started is already running in the current task, then instead +of launching a new instance of that activity, all of the other activities on top of it are +destroyed and this intent is delivered to the resumed instance of the activity (now on top), +through {@link android.app.Activity#onNewIntent onNewIntent()}). + <p>There is no value for the <a +href="{@docRoot}guide/topics/manifest/activity-element.html#lmode">{@code launchMode}</a> +attribute that produces this behavior.</p> + <p>{@code FLAG_ACTIVITY_CLEAR_TOP} is most often used in conjunction with {@code +FLAG_ACTIVITY_NEW_TASK}. When used together, these flags are a way of locating an existing activity +in another task and putting it in a position where it can respond to the intent. </p> + <p class="note"><strong>Note:</strong> If the launch mode of the designated activity is {@code +"standard"}, it too is removed from the stack and a new instance is launched in its place to handle +the incoming intent. That's because a new instance is always created for a new intent when the +launch mode is {@code "standard"}. </p> +</dd> +</dl> + + + + + +<h3 id="Affinities">Handling affinities</h3> + +<p>The <em>affinity</em> indicates which task an activity prefers to belong to. By default, all the +activities from the same application have an affinity for each other. So, by default, all +activities in the same application prefer to be in the same task. However, you can modify +the default affinity for an activity. Activities defined in +different applications can share an affinity, or activities defined in the same application can be +assigned different task affinities.</p> + +<p>You can modify the affinity for any given activity with the <a +href="{@docRoot}guide/topics/manifest/activity-element.html#aff">{@code taskAffinity}</a> attribute +of the <a href="{@docRoot}guide/topics/manifest/activity-element.html">{@code <activity>}</a> +element.</p> + +<p>The <a +href="{@docRoot}guide/topics/manifest/activity-element.html#aff">{@code taskAffinity}</a> +attribute takes a string value, which must be unique from the default package name +declared in the <a href="{@docRoot}guide/topics/manifest/manifest-element.html">{@code +<manifest>}</a> element, because the system uses that name to identify the default task +affinity for the application.</p> + +<p>The affinity comes into play in two circumstances:</p> +<ul> + <li>When the intent that launches an activity contains the {@link +android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag. + +<p>A new activity is, by default, launched into the task of the activity +that called {@link android.app.Activity#startActivity startActivity()}. It's pushed onto the same +back stack as the caller. However, if the intent passed to {@link +android.app.Activity#startActivity startActivity()} contains the {@link +android.content.Intent#FLAG_ACTIVITY_NEW_TASK} +flag, the system looks for a different task to house the new activity. Often, it's a new task. +However, it doesn't have to be. If there's already an existing task with the same affinity as the +new activity, the activity is launched into that task. If not, it begins a new task.</p> + +<p>If this flag causes an activity to begin a new task and the user presses the HOME key to leave +it, there must be some way for the user to navigate back to the task. Some entities (such as the +notification manager) always start activities in an external task, never as part of their own, so +they always put {@code FLAG_ACTIVITY_NEW_TASK} in the intents they pass to {@link +android.app.Activity#startActivity startActivity()}. If you have an activity that can be invoked by +an external entity that might use this flag, take care that the user has a independent way to get +back to the task that's started, such as with a launcher icon (the root activity of the task +has a {@link android.content.Intent#CATEGORY_LAUNCHER} intent filter; see the <a +href="#Starting">Starting a task</a> section below).</p> +</li> + + <li>When an activity has its <a +href="{@docRoot}guide/topics/manifest/activity-element.html#reparent">{@code +allowTaskReparenting}</a> attribute set to {@code "true"}. + <p>In this case, the activity can move from the task it starts to the task it has an affinity +for, when that task comes to the foreground.</p> + <p>For example, suppose that an activity that reports weather conditions in selected cities is +defined as part of a travel application. It has the same affinity as other activities in the same +application (the default application affinity) and it allows re-parenting with this attribute. +When one of your activities starts the weather reporter activity, it initially belongs to the same +task as your activity. However, when the travel application's task comes to the foreground, the +weather reporter activity is reassigned to that task and displayed within it.</p> +</li> +</ul> + +<p class="note"><strong>Tip:</strong> If an {@code .apk} file contains more than one "application" +from the user's point of view, you probably want to use the <a +href="{@docRoot}guide/topics/manifest/activity-element.html#aff">{@code taskAffinity}</a> +attribute to assign different affinities to the activities associated with each "application".</p> + + + +<h3 id="Clearing">Clearing the back stack</h3> + +<p>If the user leaves a task for a long time, the system clears the task of all activities except +the root activity. When the user returns to the task again, only the root activity is restored. +The system behaves this way, because, after an extended amount of time, users likely have abandoned +what they were doing before and are returning to the task to begin something new. </p> + +<p>There are some activity attributes that you can use to modify this behavior: </p> + +<dl> +<dt><code><a +href="{@docRoot}guide/topics/manifest/activity-element.html#always">alwaysRetainTaskState</a></code> +</dt> +<dd>If this attribute is set to {@code "true"} in the root activity of a task, +the default behavior just described does not happen. +The task retains all activities in its stack even after a long period.</dd> + +<dt><code><a +href="{@docRoot}guide/topics/manifest/activity-element.html#clear">clearTaskOnLaunch</a></code></dt> +<dd>If this attribute is set to {@code "true"} in the root activity of a task, +the stack is cleared down to the root activity whenever the user leaves the task +and returns to it. In other words, it's the opposite of <a +href="{@docRoot}guide/topics/manifest/activity-element.html#always">{@code +alwaysRetainTaskState}</a>. The user always returns to the task in its +initial state, even after a leaving the task for only a moment.</dd> + +<dt><code><a +href="{@docRoot}guide/topics/manifest/activity-element.html#finish">finishOnTaskLaunch</a></code> +</dt> +<dd>This attribute is like <a +href="{@docRoot}guide/topics/manifest/activity-element.html#clear">{@code clearTaskOnLaunch}</a>, +but it operates on a +single activity, not an entire task. It can also cause any activity to go +away, including the root activity. When it's set to {@code "true"}, the +activity remains part of the task only for the current session. If the user +leaves and then returns to the task, it is no longer present.</dd> +</dl> + + + + +<h3 id="Starting">Starting a task</h3> + +<p>You can set up an activity as the entry point for a task by giving it an intent filter with +{@code "android.intent.action.MAIN"} as the specified action and {@code +"android.intent.category.LAUNCHER"} as the specified category. For example:</p> + +<pre> +<activity ... > + <intent-filter ... > + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + ... +</activity> +</pre> + +<p>An intent filter of this kind causes an icon and label for the +activity to be displayed in the application launcher, giving users a way to launch the activity and +to return to the task that it creates any time after it has been launched. +</p> + +<p>This second ability is important: Users must be able to leave a task and then come back to it +later using this activity launcher. For this reason, the two <a href="#LaunchModes">launch +modes</a> that mark activities as always initiating a task, {@code "singleTask"} and "{@code +"singleInstance"}, should be used only when the activity has an {@link +android.content.Intent#ACTION_MAIN} +and a {@link android.content.Intent#CATEGORY_LAUNCHER} +filter. Imagine, for example, what could happen if the filter is missing: An intent launches a +{@code "singleTask"} activity, initiating a new task, and the user spends some time working in +that task. The user then presses the HOME key. The task is now sent to the background and not +visible. Because it is not represented in the application launcher, the user has no way to return to +the task. +</p> + +<p>For those cases where you don't want the user to be able to return to an activity, set the + <code><a +href="{@docRoot}guide/topics/manifest/activity-element.html"><activity></a></code> element's +<a href="{@docRoot}guide/topics/manifest/activity-element.html#finish">{@code +finishOnTaskLaunch}</a> to {@code "true"} (see <a +href="#Clearing">Clearing the stack</a>).</p> + + + + +<h2>Beginner's Path</h2> + +<p>For more information about how to use intents to +activate other application components and publish the intents to which your components +respond, continue with the <b><a +href="{@docRoot}guide/topics/intents/intents-filters.html">Intents and Intent +Filters</a></b> document.</p> diff --git a/docs/html/images/fundamentals/diagram_backstack.png b/docs/html/images/fundamentals/diagram_backstack.png Binary files differnew file mode 100644 index 000000000000..2c6e33fe2697 --- /dev/null +++ b/docs/html/images/fundamentals/diagram_backstack.png diff --git a/docs/html/images/fundamentals/diagram_backstack_singletask_multiactivity.png b/docs/html/images/fundamentals/diagram_backstack_singletask_multiactivity.png Binary files differnew file mode 100644 index 000000000000..d6a21d773e18 --- /dev/null +++ b/docs/html/images/fundamentals/diagram_backstack_singletask_multiactivity.png diff --git a/docs/html/images/fundamentals/diagram_multiple_instances.png b/docs/html/images/fundamentals/diagram_multiple_instances.png Binary files differnew file mode 100644 index 000000000000..380e7788d424 --- /dev/null +++ b/docs/html/images/fundamentals/diagram_multiple_instances.png diff --git a/docs/html/images/fundamentals/diagram_multitasking.png b/docs/html/images/fundamentals/diagram_multitasking.png Binary files differnew file mode 100644 index 000000000000..b8c7b45a192a --- /dev/null +++ b/docs/html/images/fundamentals/diagram_multitasking.png diff --git a/docs/html/images/fundamentals/restore_instance.png b/docs/html/images/fundamentals/restore_instance.png Binary files differnew file mode 100644 index 000000000000..fa428a7c9934 --- /dev/null +++ b/docs/html/images/fundamentals/restore_instance.png diff --git a/docs/html/images/home/market-intl.png b/docs/html/images/home/market-intl.png Binary files differnew file mode 100644 index 000000000000..2bb22f1f5d07 --- /dev/null +++ b/docs/html/images/home/market-intl.png diff --git a/docs/html/index.jd b/docs/html/index.jd index f37a122b46d5..049df62dacb2 100644 --- a/docs/html/index.jd +++ b/docs/html/index.jd @@ -11,12 +11,14 @@ home=true </div><!-- end homeTitle --> <div id="announcement-block"> <!-- total max width is 520px --> - <img src="{@docRoot}images/home/io-logo.png" alt="Google IO -2010" width="200" height="41" style="padding:22px 12px;"/> + <img src="{@docRoot}images/home/market-intl.png" alt="Android +Market" width="104" height="120" style="padding:10px 60px 5px" /> <div id="announcement" style="width:295px"> -<p>Thanks to everyone who visited us at Google I/O in San Francisco! Stay tuned for -videos and slides from the Android sessions, which will be posted at the Google I/O web site.</p><p><a -href="http://code.google.com/events/io/2010/sessions.html#Android">Learn more »</a></p> +<p>We're pleased to announce that paid apps are available in more locations of the world! Developers +from 20 more locations can now sell paid apps on Android Market. Users in more locations will also +soon be able to purchase apps.</p><p><a +href="http://android-developers.blogspot.com/2010/09/more-countries-more-sellers-more-buyers.html"> +Learn more »</a></p> </div> <!-- end annoucement --> </div> <!-- end annoucement-block --> </div><!-- end topAnnouncement --> diff --git a/include/utils/ZipFileRO.h b/include/utils/ZipFileRO.h index e1ff780ab9e0..3c1f3caf2ef8 100644 --- a/include/utils/ZipFileRO.h +++ b/include/utils/ZipFileRO.h @@ -14,13 +14,19 @@ * limitations under the License. */ -// -// Read-only access to Zip archives, with minimal heap allocation. -// -// This is similar to the more-complete ZipFile class, but no attempt -// has been made to make them interchangeable. This class operates under -// a very different set of assumptions and constraints. -// +/* + * Read-only access to Zip archives, with minimal heap allocation. + * + * This is similar to the more-complete ZipFile class, but no attempt + * has been made to make them interchangeable. This class operates under + * a very different set of assumptions and constraints. + * + * One such assumption is that if you're getting file descriptors for + * use with this class as a child of a fork() operation, you must be on + * a pread() to guarantee correct operation. This is because pread() can + * atomically read at a file offset without worrying about a lock around an + * lseek() + read() pair. + */ #ifndef __LIBS_ZIPFILERO_H #define __LIBS_ZIPFILERO_H @@ -55,6 +61,10 @@ typedef void* ZipEntryRO; * the record structure. However, this requires a private mapping of * every page that the Central Directory touches. Easier to tuck a copy * of the string length into the hash table entry. + * + * NOTE: If this is used on file descriptors inherited from a fork() operation, + * you must be on a platform that implements pread() to guarantee correctness + * on the shared file descriptors. */ class ZipFileRO { public: diff --git a/libs/binder/CursorWindow.cpp b/libs/binder/CursorWindow.cpp index 20b27c9eb948..bdd4dd6505cd 100644 --- a/libs/binder/CursorWindow.cpp +++ b/libs/binder/CursorWindow.cpp @@ -141,10 +141,12 @@ uint32_t CursorWindow::alloc(size_t requestedSize, bool aligned) size = requestedSize + padding; if (size > freeSpace()) { - LOGE("need to grow: mSize = %d, size = %d, freeSpace() = %d, numRows = %d", mSize, size, freeSpace(), mHeader->numRows); + LOGV("need to grow: mSize = %d, size = %d, freeSpace() = %d, numRows = %d", mSize, size, + freeSpace(), mHeader->numRows); // Only grow the window if the first row doesn't fit if (mHeader->numRows > 1) { -LOGE("not growing since there are already %d row(s), max size %d", mHeader->numRows, mMaxSize); + LOGV("not growing since there are already %d row(s), max size %d", mHeader->numRows, + mMaxSize); return 0; } diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp index adf6ee2757b0..701df8358964 100644 --- a/libs/hwui/TextureCache.cpp +++ b/libs/hwui/TextureCache.cpp @@ -18,6 +18,8 @@ #include <GLES2/gl2.h> +#include <SkCanvas.h> + #include <utils/threads.h> #include "TextureCache.h" @@ -192,8 +194,12 @@ void TextureCache::generateTexture(SkBitmap* bitmap, Texture* texture, bool rege // decoding happened texture->blend = !bitmap->isOpaque(); break; + case SkBitmap::kIndex8_Config: + uploadPalettedTexture(resize, bitmap, texture->width, texture->height); + texture->blend = false; + break; default: - LOGW("Unsupported bitmap config"); + LOGW("Unsupported bitmap config: %d", bitmap->getConfig()); break; } @@ -204,6 +210,20 @@ void TextureCache::generateTexture(SkBitmap* bitmap, Texture* texture, bool rege glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); } +void TextureCache::uploadPalettedTexture(bool resize, SkBitmap* bitmap, + uint32_t width, uint32_t height) { + SkBitmap rgbaBitmap; + rgbaBitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height); + rgbaBitmap.allocPixels(); + rgbaBitmap.eraseColor(0); + + SkCanvas canvas(rgbaBitmap); + canvas.drawBitmap(*bitmap, 0.0f, 0.0f, NULL); + + uploadToTexture(resize, GL_RGBA, rgbaBitmap.rowBytesAsPixels(), height, + GL_UNSIGNED_BYTE, rgbaBitmap.getPixels()); +} + void TextureCache::uploadToTexture(bool resize, GLenum format, GLsizei width, GLsizei height, GLenum type, const GLvoid * data) { if (resize) { diff --git a/libs/hwui/TextureCache.h b/libs/hwui/TextureCache.h index 7cf66d928536..34c5455673ac 100644 --- a/libs/hwui/TextureCache.h +++ b/libs/hwui/TextureCache.h @@ -79,6 +79,7 @@ private: */ void generateTexture(SkBitmap* bitmap, Texture* texture, bool regenerate = false); + void uploadPalettedTexture(bool resize, SkBitmap* bitmap, uint32_t width, uint32_t height); void uploadToTexture(bool resize, GLenum format, GLsizei width, GLsizei height, GLenum type, const GLvoid * data); diff --git a/libs/rs/Android.mk b/libs/rs/Android.mk index 37c418bec7e9..05c1a48417a9 100644 --- a/libs/rs/Android.mk +++ b/libs/rs/Android.mk @@ -83,7 +83,6 @@ LOCAL_SRC_FILES:= \ rsElement.cpp \ rsFileA3D.cpp \ rsFont.cpp \ - rsLight.cpp \ rsLocklessFifo.cpp \ rsObjectBase.cpp \ rsMatrix.cpp \ diff --git a/libs/rs/RenderScript.h b/libs/rs/RenderScript.h index 13ae1fb7182e..66e27f31d1e1 100644 --- a/libs/rs/RenderScript.h +++ b/libs/rs/RenderScript.h @@ -40,7 +40,6 @@ typedef void * RsSampler; typedef void * RsScript; typedef void * RsMesh; typedef void * RsType; -typedef void * RsLight; typedef void * RsObjectBase; typedef void * RsProgram; @@ -242,7 +241,6 @@ enum RsA3DClassID { RS_A3D_CLASS_ID_PROGRAM_STORE, RS_A3D_CLASS_ID_SAMPLER, RS_A3D_CLASS_ID_ANIMATION, - RS_A3D_CLASS_ID_LIGHT, RS_A3D_CLASS_ID_ADAPTER_1D, RS_A3D_CLASS_ID_ADAPTER_2D, RS_A3D_CLASS_ID_SCRIPT_C diff --git a/libs/rs/RenderScriptEnv.h b/libs/rs/RenderScriptEnv.h index c83ece452382..b82eaf1d6c59 100644 --- a/libs/rs/RenderScriptEnv.h +++ b/libs/rs/RenderScriptEnv.h @@ -13,8 +13,6 @@ typedef void * RsMesh; typedef void * RsType; typedef void * RsProgramFragment; typedef void * RsProgramStore; -typedef void * RsLight; - typedef struct { float m[16]; diff --git a/libs/rs/rsContext.cpp b/libs/rs/rsContext.cpp index 493a0926c972..30add62dbfb5 100644 --- a/libs/rs/rsContext.cpp +++ b/libs/rs/rsContext.cpp @@ -188,6 +188,9 @@ void Context::timerInit() mTimeFrame = mTimeLast; mTimeLastFrame = mTimeLast; mTimerActive = RS_TIMER_INTERNAL; + mAverageFPSFrameCount = 0; + mAverageFPSStartTime = mTimeLast; + mAverageFPS = 0; timerReset(); } @@ -195,6 +198,16 @@ void Context::timerFrame() { mTimeLastFrame = mTimeFrame; mTimeFrame = getTime(); + // Update average fps + const uint64_t averageFramerateInterval = 1000 * 1000000; + mAverageFPSFrameCount ++; + uint64_t inverval = mTimeFrame - mAverageFPSStartTime; + if(inverval >= averageFramerateInterval) { + inverval = inverval / 1000000; + mAverageFPS = (mAverageFPSFrameCount * 1000) / inverval; + mAverageFPSFrameCount = 0; + mAverageFPSStartTime = mTimeFrame; + } } void Context::timerSet(Timers tm) @@ -218,12 +231,13 @@ void Context::timerPrint() if (props.mLogTimes) { - LOGV("RS: Frame (%i), Script %2.1f (%i), Clear & Swap %2.1f (%i), Idle %2.1f (%lli), Internal %2.1f (%lli)", + LOGV("RS: Frame (%i), Script %2.1f (%i), Clear & Swap %2.1f (%i), Idle %2.1f (%lli), Internal %2.1f (%lli), Avg fps: %u", mTimeMSLastFrame, 100.0 * mTimers[RS_TIMER_SCRIPT] / total, mTimeMSLastScript, 100.0 * mTimers[RS_TIMER_CLEAR_SWAP] / total, mTimeMSLastSwap, 100.0 * mTimers[RS_TIMER_IDLE] / total, mTimers[RS_TIMER_IDLE] / 1000000, - 100.0 * mTimers[RS_TIMER_INTERNAL] / total, mTimers[RS_TIMER_INTERNAL] / 1000000); + 100.0 * mTimers[RS_TIMER_INTERNAL] / total, mTimers[RS_TIMER_INTERNAL] / 1000000, + mAverageFPS); } } @@ -255,17 +269,17 @@ static bool getProp(const char *str) void Context::displayDebugStats() { char buffer[128]; - sprintf(buffer, "Frame %i ms, Script %i ms", mTimeMSLastFrame, mTimeMSLastScript); + sprintf(buffer, "Avg fps %u, Frame %i ms, Script %i ms", mAverageFPS, mTimeMSLastFrame, mTimeMSLastScript); float oldR, oldG, oldB, oldA; mStateFont.getFontColor(&oldR, &oldG, &oldB, &oldA); uint32_t bufferLen = strlen(buffer); float shadowCol = 0.1f; mStateFont.setFontColor(shadowCol, shadowCol, shadowCol, 1.0f); - mStateFont.renderText(buffer, bufferLen, 5, getHeight() - 5); + mStateFont.renderText(buffer, bufferLen, 5, getHeight() - 6); mStateFont.setFontColor(1.0f, 0.7f, 0.0f, 1.0f); - mStateFont.renderText(buffer, bufferLen, 4, getHeight() - 6); + mStateFont.renderText(buffer, bufferLen, 4, getHeight() - 7); mStateFont.setFontColor(oldR, oldG, oldB, oldA); } diff --git a/libs/rs/rsContext.h b/libs/rs/rsContext.h index dabe19675c74..8a8b8a8e7955 100644 --- a/libs/rs/rsContext.h +++ b/libs/rs/rsContext.h @@ -30,7 +30,6 @@ #include "rsAllocation.h" #include "rsAdapter.h" #include "rsSampler.h" -#include "rsLight.h" #include "rsFont.h" #include "rsProgramFragment.h" #include "rsProgramStore.h" @@ -94,7 +93,6 @@ public: ProgramStoreState mStateFragmentStore; ProgramRasterState mStateRaster; ProgramVertexState mStateVertex; - LightState mStateLight; VertexArrayState mStateVertexArray; FontState mStateFont; @@ -296,6 +294,9 @@ private: uint32_t mTimeMSLastFrame; uint32_t mTimeMSLastScript; uint32_t mTimeMSLastSwap; + uint32_t mAverageFPSFrameCount; + uint64_t mAverageFPSStartTime; + uint32_t mAverageFPS; }; } diff --git a/libs/rs/rsContextHostStub.h b/libs/rs/rsContextHostStub.h index 06298e85eda1..aa0205d2a8fe 100644 --- a/libs/rs/rsContextHostStub.h +++ b/libs/rs/rsContextHostStub.h @@ -30,7 +30,6 @@ #include "rsAllocation.h" #include "rsAdapter.h" #include "rsSampler.h" -#include "rsLight.h" #include "rsProgramFragment.h" #include "rsProgramStore.h" #include "rsProgramRaster.h" @@ -68,7 +67,6 @@ public: ProgramStoreState mStateFragmentStore; //ProgramRasterState mStateRaster; //ProgramVertexState mStateVertex; - LightState mStateLight; VertexArrayState mStateVertexArray; //ScriptCState mScriptC; diff --git a/libs/rs/rsFileA3D.cpp b/libs/rs/rsFileA3D.cpp index 893598f06b11..c90edc2b35f8 100644 --- a/libs/rs/rsFileA3D.cpp +++ b/libs/rs/rsFileA3D.cpp @@ -278,9 +278,6 @@ ObjectBase *FileA3D::initializeFromEntry(size_t index) { case RS_A3D_CLASS_ID_ANIMATION: entry->mRsObj = Animation::createFromStream(mRSC, mReadStream); break; - case RS_A3D_CLASS_ID_LIGHT: - entry->mRsObj = Light::createFromStream(mRSC, mReadStream); - break; case RS_A3D_CLASS_ID_ADAPTER_1D: entry->mRsObj = Adapter1D::createFromStream(mRSC, mReadStream); break; diff --git a/libs/rs/rsFont.cpp b/libs/rs/rsFont.cpp index cc2b76addf65..b9de7e1d44d2 100644 --- a/libs/rs/rsFont.cpp +++ b/libs/rs/rsFont.cpp @@ -825,7 +825,7 @@ void FontState::setFontColor(float r, float g, float b, float a) { mConstants.mFontColor[3] = a; mConstants.mGamma = 1.0f; - const int32_t luminance = (r * 2.0f + g * 5.0f + b) / 8.0f; + const float luminance = (r * 2.0f + g * 5.0f + b) / 8.0f; if (luminance <= mBlackThreshold) { mConstants.mGamma = mBlackGamma; } else if (luminance >= mWhiteThreshold) { diff --git a/libs/rs/rsLight.cpp b/libs/rs/rsLight.cpp deleted file mode 100644 index eab9a077ef89..000000000000 --- a/libs/rs/rsLight.cpp +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_RS_BUILD_FOR_HOST -#include "rsContext.h" -#include <GLES/gl.h> -#else -#include "rsContextHostStub.h" -#include <OpenGL/gl.h> -#endif //ANDROID_RS_BUILD_FOR_HOST - -using namespace android; -using namespace android::renderscript; - - -Light::Light(Context *rsc, bool isLocal, bool isMono) : ObjectBase(rsc) -{ - mAllocFile = __FILE__; - mAllocLine = __LINE__; - mIsLocal = isLocal; - mIsMono = isMono; - - mPosition[0] = 0; - mPosition[1] = 0; - mPosition[2] = 1; - mPosition[3] = 0; - - mColor[0] = 1.f; - mColor[1] = 1.f; - mColor[2] = 1.f; - mColor[3] = 1.f; -} - -Light::~Light() -{ -} - -void Light::setPosition(float x, float y, float z) -{ - mPosition[0] = x; - mPosition[1] = y; - mPosition[2] = z; -} - -void Light::setColor(float r, float g, float b) -{ - mColor[0] = r; - mColor[1] = g; - mColor[2] = b; -} - -void Light::setupGL(uint32_t num) const -{ - glLightfv(GL_LIGHT0 + num, GL_DIFFUSE, mColor); - glLightfv(GL_LIGHT0 + num, GL_SPECULAR, mColor); - glLightfv(GL_LIGHT0 + num, GL_POSITION, mPosition); -} - -void Light::serialize(OStream *stream) const -{ - -} - -Light *Light::createFromStream(Context *rsc, IStream *stream) -{ - return NULL; -} - -//////////////////////////////////////////// - -LightState::LightState() -{ - clear(); -} - -LightState::~LightState() -{ -} - -void LightState::clear() -{ - mIsLocal = false; - mIsMono = false; -} - - -//////////////////////////////////////////////////// -// - -namespace android { -namespace renderscript { - -void rsi_LightBegin(Context *rsc) -{ - rsc->mStateLight.clear(); -} - -void rsi_LightSetLocal(Context *rsc, bool isLocal) -{ - rsc->mStateLight.mIsLocal = isLocal; -} - -void rsi_LightSetMonochromatic(Context *rsc, bool isMono) -{ - rsc->mStateLight.mIsMono = isMono; -} - -RsLight rsi_LightCreate(Context *rsc) -{ - Light *l = new Light(rsc, rsc->mStateLight.mIsLocal, - rsc->mStateLight.mIsMono); - l->incUserRef(); - return l; -} - -void rsi_LightSetColor(Context *rsc, RsLight vl, float r, float g, float b) -{ - Light *l = static_cast<Light *>(vl); - l->setColor(r, g, b); -} - -void rsi_LightSetPosition(Context *rsc, RsLight vl, float x, float y, float z) -{ - Light *l = static_cast<Light *>(vl); - l->setPosition(x, y, z); -} - - - -} -} diff --git a/libs/rs/rsLight.h b/libs/rs/rsLight.h deleted file mode 100644 index bd58979bc931..000000000000 --- a/libs/rs/rsLight.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_LIGHT_H -#define ANDROID_LIGHT_H - - -#include "rsObjectBase.h" - -// --------------------------------------------------------------------------- -namespace android { -namespace renderscript { - - -// An element is a group of Components that occupies one cell in a structure. -class Light : public ObjectBase -{ -public: - Light(Context *, bool isLocal, bool isMono); - virtual ~Light(); - - // Values, mutable after creation. - void setPosition(float x, float y, float z); - void setColor(float r, float g, float b); - - void setupGL(uint32_t num) const; - virtual void serialize(OStream *stream) const; - virtual RsA3DClassID getClassId() const { return RS_A3D_CLASS_ID_LIGHT; } - static Light *createFromStream(Context *rsc, IStream *stream); - -protected: - float mColor[4]; - float mPosition[4]; - bool mIsLocal; - bool mIsMono; -}; - - -class LightState { -public: - LightState(); - ~LightState(); - - void clear(); - - bool mIsMono; - bool mIsLocal; -}; - - -} -} -#endif //ANDROID_LIGHT_H - diff --git a/libs/rs/rsVertexArray.h b/libs/rs/rsVertexArray.h index bd76d871117d..dea7d41a1290 100644 --- a/libs/rs/rsVertexArray.h +++ b/libs/rs/rsVertexArray.h @@ -62,7 +62,6 @@ public: } void add(const Attrib &, uint32_t stride); - //void addLegacy(uint32_t type, uint32_t size, uint32_t stride, bool normalized, uint32_t offset); void add(uint32_t type, uint32_t size, uint32_t stride, bool normalized, uint32_t offset, const char *name); void setupGL2(const Context *rsc, class VertexArrayState *, ShaderCache *) const; @@ -89,7 +88,7 @@ public: } } -#endif //ANDROID_LIGHT_H +#endif //ANDROID_VERTEX_ARRAY_H diff --git a/libs/utils/ZipFileRO.cpp b/libs/utils/ZipFileRO.cpp index bee86b2bbae0..9b1f82fab97f 100644 --- a/libs/utils/ZipFileRO.cpp +++ b/libs/utils/ZipFileRO.cpp @@ -508,6 +508,36 @@ bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen, unsigned char lfhBuf[kLFHLen]; +#ifdef HAVE_PREAD + /* + * This file descriptor might be from zygote's preloaded assets, + * so we need to do an pread() instead of a lseek() + read() to + * guarantee atomicity across the processes with the shared file + * descriptors. + */ + ssize_t actual = + TEMP_FAILURE_RETRY(pread(mFd, lfhBuf, sizeof(lfhBuf), localHdrOffset)); + + if (actual != sizeof(lfhBuf)) { + LOGW("failed reading lfh from offset %ld\n", localHdrOffset); + return false; + } + + if (get4LE(lfhBuf) != kLFHSignature) { + LOGW("didn't find signature at start of lfh; wanted: offset=%ld data=0x%08x; " + "got: data=0x%08lx\n", + localHdrOffset, kLFHSignature, get4LE(lfhBuf)); + return false; + } +#else /* HAVE_PREAD */ + /* + * For hosts don't have pread() we cannot guarantee atomic reads from + * an offset in a file. Android should never run on those platforms. + * File descriptors inherited from a fork() share file offsets and + * there would be nothing to protect from two different processes + * calling lseek() concurrently. + */ + { AutoMutex _l(mFdLock); @@ -517,7 +547,7 @@ bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen, } ssize_t actual = - TEMP_FAILURE_RETRY(read(mFd, lfhBuf, sizeof(lfhBuf))); + TEMP_FAILURE_RETRY(read(mFd, lfhBuf, sizeof(lfhBuf))); if (actual != sizeof(lfhBuf)) { LOGW("failed reading lfh from offset %ld\n", localHdrOffset); return false; @@ -531,6 +561,7 @@ bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen, return false; } } +#endif /* HAVE_PREAD */ off_t dataOffset = localHdrOffset + kLFHLen + get2LE(lfhBuf + kLFHNameLen) + get2LE(lfhBuf + kLFHExtraLen); diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp index 546df476bd39..90b1aab304ee 100644 --- a/media/libstagefright/MPEG4Writer.cpp +++ b/media/libstagefright/MPEG4Writer.cpp @@ -40,6 +40,7 @@ namespace android { static const int64_t kMax32BitFileSize = 0x007fffffffLL; static const uint8_t kNalUnitTypeSeqParamSet = 0x07; static const uint8_t kNalUnitTypePicParamSet = 0x08; +static const int64_t kVideoMediaTimeAdjustPeriodTimeUs = 10000000LL; // 10s class MPEG4Writer::Track { public: @@ -148,6 +149,28 @@ private: int64_t mPreviousTrackTimeUs; int64_t mTrackEveryTimeDurationUs; + // Has the media time adjustment for video started? + bool mIsMediaTimeAdjustmentOn; + // The time stamp when previous media time adjustment period starts + int64_t mPrevMediaTimeAdjustTimestampUs; + // Number of vidoe frames whose time stamp may be adjusted + int64_t mMediaTimeAdjustNumFrames; + // The sample number when previous meida time adjustmnet period starts + int64_t mPrevMediaTimeAdjustSample; + // The total accumulated drift time within a period of + // kVideoMediaTimeAdjustPeriodTimeUs. + int64_t mTotalDriftTimeToAdjustUs; + // The total accumalated drift time since the start of the recording + // excluding the current time adjustment period + int64_t mPrevTotalAccumDriftTimeUs; + + // Update the audio track's drift information. + void updateDriftTime(const sp<MetaData>& meta); + + // Adjust the time stamp of the video track according to + // the drift time information from the audio track. + void adjustMediaTime(int64_t *timestampUs); + static void *ThreadWrapper(void *me); status_t threadEntry(); @@ -319,7 +342,7 @@ int64_t MPEG4Writer::estimateMoovBoxSize(int32_t bitRate) { size = MAX_MOOV_BOX_SIZE; } - LOGI("limits: %lld/%lld bytes/us, bit rate: %d bps and the estimated" + LOGV("limits: %lld/%lld bytes/us, bit rate: %d bps and the estimated" " moov size %lld bytes", mMaxFileSizeLimitBytes, mMaxFileDurationLimitUs, bitRate, size); return factor * size; @@ -346,7 +369,7 @@ status_t MPEG4Writer::start(MetaData *param) { // If file size is set to be larger than the 32 bit file // size limit, treat it as an error. if (mMaxFileSizeLimitBytes > kMax32BitFileSize) { - LOGW("32-bi file size limit (%lld bytes) too big. " + LOGW("32-bit file size limit (%lld bytes) too big. " "It is changed to %lld bytes", mMaxFileSizeLimitBytes, kMax32BitFileSize); mMaxFileSizeLimitBytes = kMax32BitFileSize; @@ -1149,6 +1172,12 @@ status_t MPEG4Writer::Track::start(MetaData *params) { mNumStscTableEntries = 0; mNumSttsTableEntries = 0; mMdatSizeBytes = 0; + mIsMediaTimeAdjustmentOn = false; + mPrevMediaTimeAdjustTimestampUs = 0; + mMediaTimeAdjustNumFrames = 0; + mPrevMediaTimeAdjustSample = 0; + mTotalDriftTimeToAdjustUs = 0; + mPrevTotalAccumDriftTimeUs = 0; pthread_create(&mThread, &attr, ThreadWrapper, this); pthread_attr_destroy(&attr); @@ -1437,6 +1466,145 @@ status_t MPEG4Writer::Track::makeAVCCodecSpecificData( return OK; } +/* +* The video track's media time adjustment for real-time applications +* is described as follows: +* +* First, the media time adjustment is done for every period of +* kVideoMediaTimeAdjustPeriodTimeUs. kVideoMediaTimeAdjustPeriodTimeUs +* is currently a fixed value chosen heuristically. The value of +* kVideoMediaTimeAdjustPeriodTimeUs should not be very large or very small +* for two considerations: on one hand, a relatively large value +* helps reduce large fluctuation of drift time in the audio encoding +* path; while on the other hand, a relatively small value helps keep +* restoring synchronization in audio/video more frequently. Note for the +* very first period of kVideoMediaTimeAdjustPeriodTimeUs, there is +* no media time adjustment for the video track. +* +* Second, the total accumulated audio track time drift found +* in a period of kVideoMediaTimeAdjustPeriodTimeUs is distributed +* over a stream of incoming video frames. The number of video frames +* affected is determined based on the number of recorded video frames +* within the past kVideoMediaTimeAdjustPeriodTimeUs period. +* We choose to distribute the drift time over only a portion +* (rather than all) of the total number of recorded video frames +* in order to make sure that the video track media time adjustment is +* completed for the current period before the next video track media +* time adjustment period starts. Currently, the portion chosen is a +* half (0.5). +* +* Last, various additional checks are performed to ensure that +* the actual audio encoding path does not have too much drift. +* In particular, 1) we want to limit the average incremental time +* adjustment for each video frame to be less than a threshold +* for a single period of kVideoMediaTimeAdjustPeriodTimeUs. +* Currently, the threshold is set to 5 ms. If the average incremental +* media time adjustment for a video frame is larger than the +* threshold, the audio encoding path has too much time drift. +* 2) We also want to limit the total time drift in the audio +* encoding path to be less than a threshold for a period of +* kVideoMediaTimeAdjustPeriodTimeUs. Currently, the threshold +* is 0.5% of kVideoMediaTimeAdjustPeriodTimeUs. If the time drift of +* the audio encoding path is larger than the threshold, the audio +* encoding path has too much time drift. We treat the large time +* drift of the audio encoding path as errors, since there is no +* way to keep audio/video in synchronization for real-time +* applications if the time drift is too large unless we drop some +* video frames, which has its own problems that we don't want +* to get into for the time being. +*/ +void MPEG4Writer::Track::adjustMediaTime(int64_t *timestampUs) { + if (*timestampUs - mPrevMediaTimeAdjustTimestampUs >= + kVideoMediaTimeAdjustPeriodTimeUs) { + + LOGV("New media time adjustment period at %lld us", *timestampUs); + mIsMediaTimeAdjustmentOn = true; + mMediaTimeAdjustNumFrames = + (mNumSamples - mPrevMediaTimeAdjustSample) >> 1; + + mPrevMediaTimeAdjustTimestampUs = *timestampUs; + mPrevMediaTimeAdjustSample = mNumSamples; + int64_t totalAccumDriftTimeUs = mOwner->getDriftTimeUs(); + mTotalDriftTimeToAdjustUs = + totalAccumDriftTimeUs - mPrevTotalAccumDriftTimeUs; + + mPrevTotalAccumDriftTimeUs = totalAccumDriftTimeUs; + + // Check on incremental adjusted time per frame + int64_t adjustTimePerFrameUs = + mTotalDriftTimeToAdjustUs / mMediaTimeAdjustNumFrames; + + if (adjustTimePerFrameUs < 0) { + adjustTimePerFrameUs = -adjustTimePerFrameUs; + } + if (adjustTimePerFrameUs >= 5000) { + LOGE("Adjusted time per video frame is %lld us", + adjustTimePerFrameUs); + CHECK(!"Video frame time adjustment is too large!"); + } + + // Check on total accumulated time drift within a period of + // kVideoMediaTimeAdjustPeriodTimeUs. + int64_t driftPercentage = (mTotalDriftTimeToAdjustUs * 1000) + / kVideoMediaTimeAdjustPeriodTimeUs; + + if (driftPercentage < 0) { + driftPercentage = -driftPercentage; + } + if (driftPercentage > 5) { + LOGE("Audio track has time drift %lld us over %lld us", + mTotalDriftTimeToAdjustUs, + kVideoMediaTimeAdjustPeriodTimeUs); + + CHECK(!"The audio track media time drifts too much!"); + } + + } + + if (mIsMediaTimeAdjustmentOn) { + if (mNumSamples - mPrevMediaTimeAdjustSample <= + mMediaTimeAdjustNumFrames) { + + // Do media time incremental adjustment + int64_t incrementalAdjustTimeUs = + (mTotalDriftTimeToAdjustUs * + (mNumSamples - mPrevMediaTimeAdjustSample)) + / mMediaTimeAdjustNumFrames; + + *timestampUs += + (incrementalAdjustTimeUs + mPrevTotalAccumDriftTimeUs); + + LOGV("Incremental video frame media time adjustment: %lld us", + (incrementalAdjustTimeUs + mPrevTotalAccumDriftTimeUs)); + } else { + // Within the remaining adjustment period, + // no incremental adjustment is needed. + *timestampUs += + (mTotalDriftTimeToAdjustUs + mPrevTotalAccumDriftTimeUs); + + LOGV("Fixed video frame media time adjustment: %lld us", + (mTotalDriftTimeToAdjustUs + mPrevTotalAccumDriftTimeUs)); + } + } +} + +/* + * Updates the drift time from the audio track so that + * the video track can get the updated drift time information + * from the file writer. The fluctuation of the drift time of the audio + * encoding path is smoothed out with a simple filter by giving a larger + * weight to more recently drift time. The filter coefficients, 0.5 and 0.5, + * are heuristically determined. + */ +void MPEG4Writer::Track::updateDriftTime(const sp<MetaData>& meta) { + int64_t driftTimeUs = 0; + if (meta->findInt64(kKeyDriftTime, &driftTimeUs)) { + int64_t prevDriftTimeUs = mOwner->getDriftTimeUs(); + int64_t timeUs = (driftTimeUs + prevDriftTimeUs) >> 1; + mOwner->setDriftTimeUs(timeUs); + } +} + status_t MPEG4Writer::Track::threadEntry() { int32_t count = 0; const int64_t interleaveDurationUs = mOwner->interleaveDuration(); @@ -1587,24 +1755,16 @@ status_t MPEG4Writer::Track::threadEntry() { timestampUs -= previousPausedDurationUs; CHECK(timestampUs >= 0); - if (mIsRealTimeRecording && !mIsAudio) { - // The minor adjustment on the timestamp is heuristic/experimental - // We are adjusting the timestamp to reduce the fluctuation of the duration - // of neighboring samples. This in turn helps reduce the track header size, - // especially, the number of entries in the "stts" box. - if (mNumSamples > 1) { - int64_t currDriftTimeUs = mOwner->getDriftTimeUs(); - int64_t durationUs = timestampUs + currDriftTimeUs - lastTimestampUs; - int64_t diffUs = (durationUs > lastDurationUs) - ? durationUs - lastDurationUs - : lastDurationUs - durationUs; - if (diffUs <= 5000) { // XXX: Magic number 5ms - timestampUs = lastTimestampUs + lastDurationUs; - } else { - timestampUs += currDriftTimeUs; - } + + // Media time adjustment for real-time applications + if (mIsRealTimeRecording) { + if (mIsAudio) { + updateDriftTime(meta_data); + } else { + adjustMediaTime(×tampUs); } } + CHECK(timestampUs >= 0); if (mNumSamples > 1) { if (timestampUs <= lastTimestampUs) { @@ -1656,12 +1816,6 @@ status_t MPEG4Writer::Track::threadEntry() { lastDurationUs = timestampUs - lastTimestampUs; lastDurationTicks = currDurationTicks; lastTimestampUs = timestampUs; - if (mIsRealTimeRecording && mIsAudio) { - int64_t driftTimeUs = 0; - if (meta_data->findInt64(kKeyDriftTime, &driftTimeUs)) { - mOwner->setDriftTimeUs(driftTimeUs); - } - } if (isSync != 0) { addOneStssTableEntry(mNumSamples); @@ -1735,6 +1889,9 @@ status_t MPEG4Writer::Track::threadEntry() { mReachedEOS = true; LOGI("Received total/0-length (%d/%d) buffers and encoded %d frames. - %s", count, nZeroLengthFrames, mNumSamples, mIsAudio? "audio": "video"); + if (mIsAudio) { + LOGI("Audio track drift time: %lld us", mOwner->getDriftTimeUs()); + } if (err == ERROR_END_OF_STREAM) { return OK; |