diff options
27 files changed, 754 insertions, 308 deletions
diff --git a/api/current.xml b/api/current.xml index 5cad414..ef49855 100644 --- a/api/current.xml +++ b/api/current.xml @@ -28065,13 +28065,22 @@ </parameter> </method> </class> -<interface name="FragmentManager" +<class name="FragmentManager" + extends="java.lang.Object" abstract="true" static="false" final="false" deprecated="not deprecated" visibility="public" > +<constructor name="FragmentManager" + type="android.app.FragmentManager" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</constructor> <method name="addOnBackStackChangedListener" return="void" abstract="true" @@ -28262,7 +28271,7 @@ visibility="public" > </field> -</interface> +</class> <interface name="FragmentManager.BackStackEntry" abstract="true" static="true" @@ -28323,13 +28332,22 @@ > </method> </interface> -<interface name="FragmentTransaction" +<class name="FragmentTransaction" + extends="java.lang.Object" abstract="true" static="false" final="false" deprecated="not deprecated" visibility="public" > +<constructor name="FragmentTransaction" + type="android.app.FragmentTransaction" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</constructor> <method name="add" return="android.app.FragmentTransaction" abstract="true" @@ -28401,6 +28419,17 @@ visibility="public" > </method> +<method name="commitAllowingStateLoss" + return="int" + abstract="true" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="disallowAddToBackStack" return="android.app.FragmentTransaction" abstract="true" @@ -28686,7 +28715,7 @@ visibility="public" > </field> -</interface> +</class> <class name="Instrumentation" extends="java.lang.Object" abstract="false" @@ -30281,13 +30310,22 @@ </parameter> </method> </class> -<interface name="LoaderManager" +<class name="LoaderManager" + extends="java.lang.Object" abstract="true" static="false" final="false" deprecated="not deprecated" visibility="public" > +<constructor name="LoaderManager" + type="android.app.LoaderManager" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</constructor> <method name="getLoader" return="android.content.Loader<D>" abstract="true" @@ -30348,7 +30386,7 @@ <parameter name="id" type="int"> </parameter> </method> -</interface> +</class> <interface name="LoaderManager.LoaderCallbacks" abstract="true" static="true" @@ -30392,7 +30430,7 @@ abstract="true" static="false" final="false" - deprecated="not deprecated" + deprecated="deprecated" visibility="public" > <implements name="android.content.Loader.OnLoadCompleteListener"> diff --git a/core/java/android/app/BackStackRecord.java b/core/java/android/app/BackStackRecord.java index c75777d..e9b6869 100644 --- a/core/java/android/app/BackStackRecord.java +++ b/core/java/android/app/BackStackRecord.java @@ -155,7 +155,7 @@ final class BackStackState implements Parcelable { /** * @hide Entry of an operation on the fragment back stack. */ -final class BackStackRecord implements FragmentTransaction, +final class BackStackRecord extends FragmentTransaction implements FragmentManager.BackStackEntry, Runnable { static final String TAG = "BackStackEntry"; @@ -417,6 +417,14 @@ final class BackStackRecord implements FragmentTransaction, } public int commit() { + return commitInternal(false); + } + + public int commitAllowingStateLoss() { + return commitInternal(true); + } + + int commitInternal(boolean allowStateLoss) { if (mCommitted) throw new IllegalStateException("commit already called"); if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Commit: " + this); mCommitted = true; @@ -425,10 +433,10 @@ final class BackStackRecord implements FragmentTransaction, } else { mIndex = -1; } - mManager.enqueueAction(this); + mManager.enqueueAction(this, allowStateLoss); return mIndex; } - + public void run() { if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Run: " + this); diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 5ce4cd6..ba301e9 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -1182,6 +1182,13 @@ class ContextImpl extends Context { /* package */ static DropBoxManager createDropBoxManager() { IBinder b = ServiceManager.getService(DROPBOX_SERVICE); IDropBoxManagerService service = IDropBoxManagerService.Stub.asInterface(b); + if (service == null) { + // Don't return a DropBoxManager that will NPE upon use. + // This also avoids caching a broken DropBoxManager in + // getDropBoxManager during early boot, before the + // DROPBOX_SERVICE is registered. + return null; + } return new DropBoxManager(service); } diff --git a/core/java/android/app/DialogFragment.java b/core/java/android/app/DialogFragment.java index 8e2389b..cbecc21 100644 --- a/core/java/android/app/DialogFragment.java +++ b/core/java/android/app/DialogFragment.java @@ -256,6 +256,10 @@ public class DialogFragment extends Fragment * the fragment. */ public void dismiss() { + dismissInternal(false); + } + + void dismissInternal(boolean allowStateLoss) { if (mDialog != null) { mDialog.dismiss(); mDialog = null; @@ -271,7 +275,7 @@ public class DialogFragment extends Fragment ft.commit(); } } - + public Dialog getDialog() { return mDialog; } @@ -353,7 +357,11 @@ public class DialogFragment extends Fragment public void onDismiss(DialogInterface dialog) { if (!mRemoved) { - dismiss(); + // Note: we need to use allowStateLoss, because the dialog + // dispatches this asynchronously so we can receive the call + // after the activity is paused. Worst case, when the user comes + // back to the activity they see the dialog again. + dismissInternal(true); } } diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java index 512ca16..11de6a6 100644 --- a/core/java/android/app/FragmentManager.java +++ b/core/java/android/app/FragmentManager.java @@ -40,7 +40,7 @@ import java.util.ArrayList; * Interface for interacting with {@link Fragment} objects inside of an * {@link Activity} */ -public interface FragmentManager { +public abstract class FragmentManager { /** * Representation of an entry on the fragment back stack, as created * with {@link FragmentTransaction#addToBackStack(String) @@ -96,7 +96,7 @@ public interface FragmentManager { * in the state, and if changes are made after the state is saved then they * will be lost.</p> */ - public FragmentTransaction openTransaction(); + public abstract FragmentTransaction openTransaction(); /** * Finds a fragment that was identified by the given id either when inflated @@ -106,7 +106,7 @@ public interface FragmentManager { * on the back stack associated with this ID are searched. * @return The fragment if found or null otherwise. */ - public Fragment findFragmentById(int id); + public abstract Fragment findFragmentById(int id); /** * Finds a fragment that was identified by the given tag either when inflated @@ -116,7 +116,7 @@ public interface FragmentManager { * on the back stack are searched. * @return The fragment if found or null otherwise. */ - public Fragment findFragmentByTag(String tag); + public abstract Fragment findFragmentByTag(String tag); /** * Flag for {@link #popBackStack(String, int)} @@ -132,7 +132,7 @@ public interface FragmentManager { * Pop the top state off the back stack. Returns true if there was one * to pop, else false. */ - public boolean popBackStack(); + public abstract boolean popBackStack(); /** * Pop the last fragment transition from the manager's fragment @@ -143,7 +143,7 @@ public interface FragmentManager { * the named state itself is popped. If null, only the top state is popped. * @param flags Either 0 or {@link #POP_BACK_STACK_INCLUSIVE}. */ - public boolean popBackStack(String name, int flags); + public abstract boolean popBackStack(String name, int flags); /** * Pop all back stack states up to the one with the given identifier. @@ -155,29 +155,29 @@ public interface FragmentManager { * the named state itself is popped. * @param flags Either 0 or {@link #POP_BACK_STACK_INCLUSIVE}. */ - public boolean popBackStack(int id, int flags); + public abstract boolean popBackStack(int id, int flags); /** * Return the number of entries currently in the back stack. */ - public int countBackStackEntries(); + public abstract int countBackStackEntries(); /** * Return the BackStackEntry at index <var>index</var> in the back stack; * entries start index 0 being the bottom of the stack. */ - public BackStackEntry getBackStackEntry(int index); + public abstract BackStackEntry getBackStackEntry(int index); /** * Add a new listener for changes to the fragment back stack. */ - public void addOnBackStackChangedListener(OnBackStackChangedListener listener); + public abstract void addOnBackStackChangedListener(OnBackStackChangedListener listener); /** * Remove a listener that was previously added with * {@link #addOnBackStackChangedListener(OnBackStackChangedListener)}. */ - public void removeOnBackStackChangedListener(OnBackStackChangedListener listener); + public abstract void removeOnBackStackChangedListener(OnBackStackChangedListener listener); /** * Put a reference to a fragment in a Bundle. This Bundle can be @@ -189,7 +189,7 @@ public interface FragmentManager { * @param key The name of the entry in the bundle. * @param fragment The Fragment whose reference is to be stored. */ - public void putFragment(Bundle bundle, String key, Fragment fragment); + public abstract void putFragment(Bundle bundle, String key, Fragment fragment); /** * Retrieve the current Fragment instance for a reference previously @@ -200,7 +200,7 @@ public interface FragmentManager { * @return Returns the current Fragment instance that is associated with * the given reference. */ - public Fragment getFragment(Bundle bundle, String key); + public abstract Fragment getFragment(Bundle bundle, String key); /** * Print the FragmentManager's state into the given stream. @@ -210,7 +210,7 @@ public interface FragmentManager { * @param writer A PrintWriter to which the dump is to be set. * @param args additional arguments to the dump request. */ - public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args); + public abstract void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args); } final class FragmentManagerState implements Parcelable { @@ -252,7 +252,7 @@ final class FragmentManagerState implements Parcelable { /** * Container for fragments associated with an activity. */ -final class FragmentManagerImpl implements FragmentManager { +final class FragmentManagerImpl extends FragmentManager { static final boolean DEBUG = true; static final String TAG = "FragmentManager"; @@ -849,8 +849,8 @@ final class FragmentManagerImpl implements FragmentManager { return null; } - public void enqueueAction(Runnable action) { - if (mStateSaved) { + public void enqueueAction(Runnable action, boolean allowStateLoss) { + if (!allowStateLoss && mStateSaved) { throw new IllegalStateException( "Can not perform this action after onSaveInstanceState"); } @@ -991,7 +991,7 @@ final class FragmentManagerImpl implements FragmentManager { bss.popFromBackStack(true); reportBackStackChanged(); } - }); + }, false); } else { int index = -1; if (name != null || id >= 0) { @@ -1042,7 +1042,7 @@ final class FragmentManagerImpl implements FragmentManager { } reportBackStackChanged(); } - }); + }, false); } return true; } diff --git a/core/java/android/app/FragmentTransaction.java b/core/java/android/app/FragmentTransaction.java index 19da763..dc4acbe 100644 --- a/core/java/android/app/FragmentTransaction.java +++ b/core/java/android/app/FragmentTransaction.java @@ -3,16 +3,16 @@ package android.app; /** * API for performing a set of Fragment operations. */ -public interface FragmentTransaction { +public abstract class FragmentTransaction { /** * Calls {@link #add(int, Fragment, String)} with a 0 containerViewId. */ - public FragmentTransaction add(Fragment fragment, String tag); + public abstract FragmentTransaction add(Fragment fragment, String tag); /** * Calls {@link #add(int, Fragment, String)} with a null tag. */ - public FragmentTransaction add(int containerViewId, Fragment fragment); + public abstract FragmentTransaction add(int containerViewId, Fragment fragment); /** * Add a fragment to the activity state. This fragment may optionally @@ -29,12 +29,12 @@ public interface FragmentTransaction { * * @return Returns the same FragmentTransaction instance. */ - public FragmentTransaction add(int containerViewId, Fragment fragment, String tag); + public abstract FragmentTransaction add(int containerViewId, Fragment fragment, String tag); /** * Calls {@link #replace(int, Fragment, String)} with a null tag. */ - public FragmentTransaction replace(int containerViewId, Fragment fragment); + public abstract FragmentTransaction replace(int containerViewId, Fragment fragment); /** * Replace an existing fragment that was added to a container. This is @@ -52,7 +52,7 @@ public interface FragmentTransaction { * * @return Returns the same FragmentTransaction instance. */ - public FragmentTransaction replace(int containerViewId, Fragment fragment, String tag); + public abstract FragmentTransaction replace(int containerViewId, Fragment fragment, String tag); /** * Remove an existing fragment. If it was added to a container, its view @@ -62,7 +62,7 @@ public interface FragmentTransaction { * * @return Returns the same FragmentTransaction instance. */ - public FragmentTransaction remove(Fragment fragment); + public abstract FragmentTransaction remove(Fragment fragment); /** * Hides an existing fragment. This is only relevant for fragments whose @@ -73,7 +73,7 @@ public interface FragmentTransaction { * * @return Returns the same FragmentTransaction instance. */ - public FragmentTransaction hide(Fragment fragment); + public abstract FragmentTransaction hide(Fragment fragment); /** * Hides a previously hidden fragment. This is only relevant for fragments whose @@ -84,55 +84,55 @@ public interface FragmentTransaction { * * @return Returns the same FragmentTransaction instance. */ - public FragmentTransaction show(Fragment fragment); + public abstract FragmentTransaction show(Fragment fragment); /** * @return <code>true</code> if this transaction contains no operations, * <code>false</code> otherwise. */ - public boolean isEmpty(); + public abstract boolean isEmpty(); /** * Bit mask that is set for all enter transitions. */ - public final int TRANSIT_ENTER_MASK = 0x1000; + public static final int TRANSIT_ENTER_MASK = 0x1000; /** * Bit mask that is set for all exit transitions. */ - public final int TRANSIT_EXIT_MASK = 0x2000; + public static final int TRANSIT_EXIT_MASK = 0x2000; /** Not set up for a transition. */ - public final int TRANSIT_UNSET = -1; + public static final int TRANSIT_UNSET = -1; /** No animation for transition. */ - public final int TRANSIT_NONE = 0; + public static final int TRANSIT_NONE = 0; /** Fragment is being added onto the stack */ - public final int TRANSIT_FRAGMENT_OPEN = 1 | TRANSIT_ENTER_MASK; + public static final int TRANSIT_FRAGMENT_OPEN = 1 | TRANSIT_ENTER_MASK; /** Fragment is being removed from the stack */ - public final int TRANSIT_FRAGMENT_CLOSE = 2 | TRANSIT_EXIT_MASK; + public static 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; + public static 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; + public static final int TRANSIT_FRAGMENT_PREV = 4 | TRANSIT_EXIT_MASK; /** * Set specific animation resources to run for the fragments that are * entering and exiting in this transaction. */ - public FragmentTransaction setCustomAnimations(int enter, int exit); + public abstract FragmentTransaction setCustomAnimations(int enter, int exit); /** * Select a standard transition animation for this transaction. May be * one of {@link #TRANSIT_NONE}, {@link #TRANSIT_FRAGMENT_OPEN}, * or {@link #TRANSIT_FRAGMENT_CLOSE} */ - public FragmentTransaction setTransition(int transit); + public abstract FragmentTransaction setTransition(int transit); /** * Set a custom style resource that will be used for resolving transit * animations. */ - public FragmentTransaction setTransitionStyle(int styleRes); + public abstract FragmentTransaction setTransitionStyle(int styleRes); /** * Add this transaction to the back stack. This means that the transaction @@ -141,7 +141,7 @@ public interface FragmentTransaction { * * @param name An optional name for this back stack state, or null. */ - public FragmentTransaction addToBackStack(String name); + public abstract FragmentTransaction addToBackStack(String name); /** * Returns true if this FragmentTransaction is allowed to be added to the back @@ -150,14 +150,14 @@ public interface FragmentTransaction { * * @return True if {@link #addToBackStack(String)} is permitted on this transaction. */ - public boolean isAddToBackStackAllowed(); + public abstract boolean isAddToBackStackAllowed(); /** * Disallow calls to {@link #addToBackStack(String)}. Any future calls to * addToBackStack will throw {@link IllegalStateException}. If addToBackStack * has already been called, this method will throw IllegalStateException. */ - public FragmentTransaction disallowAddToBackStack(); + public abstract FragmentTransaction disallowAddToBackStack(); /** * Set the full title to show as a bread crumb when this transaction @@ -165,14 +165,14 @@ public interface FragmentTransaction { * * @param res A string resource containing the title. */ - public FragmentTransaction setBreadCrumbTitle(int res); + public abstract FragmentTransaction setBreadCrumbTitle(int res); /** * Like {@link #setBreadCrumbTitle(int)} but taking a raw string; this * method is <em>not</em> recommended, as the string can not be changed * later if the locale changes. */ - public FragmentTransaction setBreadCrumbTitle(CharSequence text); + public abstract FragmentTransaction setBreadCrumbTitle(CharSequence text); /** * Set the short title to show as a bread crumb when this transaction @@ -180,23 +180,39 @@ public interface FragmentTransaction { * * @param res A string resource containing the title. */ - public FragmentTransaction setBreadCrumbShortTitle(int res); + public abstract FragmentTransaction setBreadCrumbShortTitle(int res); /** * Like {@link #setBreadCrumbShortTitle(int)} but taking a raw string; this * method is <em>not</em> recommended, as the string can not be changed * later if the locale changes. */ - public FragmentTransaction setBreadCrumbShortTitle(CharSequence text); + public abstract FragmentTransaction setBreadCrumbShortTitle(CharSequence text); /** - * Schedules a commit of this transaction. Note that the commit does + * Schedules a commit of this transaction. The commit does * not happen immediately; it will be scheduled as work on the main thread * to be done the next time that thread is ready. * + * <p class="note">A transaction can only be committed with this method + * prior to its containing activity saving its state. If the commit is + * attempted after that point, an exception will be thrown. This is + * because the state after the commit can be lost if the activity needs to + * be restored from its state. See {@link #commitAllowingStateLoss()} for + * situations where it may be okay to lose the commit.</p> + * * @return Returns the identifier of this transaction's back stack entry, * if {@link #addToBackStack(String)} had been called. Otherwise, returns * a negative number. */ - public int commit(); + public abstract int commit(); + + /** + * Like {@link #commit} but allows the commit to be executed after an + * activity's state is saved. This is dangerous because the commit can + * be lost if the activity needs to later be restored from its state, so + * this should only be used for cases where it is okay for the UI state + * to change unexpectedly on the user. + */ + public abstract int commitAllowingStateLoss(); } diff --git a/core/java/android/app/LoaderManager.java b/core/java/android/app/LoaderManager.java index 4d4ea9a..7ae4b95 100644 --- a/core/java/android/app/LoaderManager.java +++ b/core/java/android/app/LoaderManager.java @@ -25,7 +25,7 @@ import android.util.SparseArray; * Interface associated with an {@link Activity} or {@link Fragment} for managing * one or more {@link android.content.Loader} instances associated with it. */ -public interface LoaderManager { +public abstract class LoaderManager { /** * Callback interface for a client to interact with the manager. */ @@ -66,7 +66,7 @@ public interface LoaderManager { * be called immediately (inside of this function), so you must be prepared * for this to happen. */ - public <D> Loader<D> initLoader(int id, Bundle args, + public abstract <D> Loader<D> initLoader(int id, Bundle args, LoaderManager.LoaderCallbacks<D> callback); /** @@ -77,22 +77,22 @@ public interface LoaderManager { * its work. The callback will be delivered before the old loader * is destroyed. */ - public <D> Loader<D> restartLoader(int id, Bundle args, + public abstract <D> Loader<D> restartLoader(int id, Bundle args, LoaderManager.LoaderCallbacks<D> callback); /** * Stops and removes the loader with the given ID. */ - public void stopLoader(int id); + public abstract void stopLoader(int id); /** * Return the Loader with the given id or null if no matching Loader * is found. */ - public <D> Loader<D> getLoader(int id); + public abstract <D> Loader<D> getLoader(int id); } -class LoaderManagerImpl implements LoaderManager { +class LoaderManagerImpl extends LoaderManager { static final String TAG = "LoaderManagerImpl"; static final boolean DEBUG = true; diff --git a/core/java/android/app/LoaderManagingFragment.java b/core/java/android/app/LoaderManagingFragment.java index af71170..f0f5856 100644 --- a/core/java/android/app/LoaderManagingFragment.java +++ b/core/java/android/app/LoaderManagingFragment.java @@ -26,7 +26,10 @@ import java.util.HashMap; * * @param <D> The type of data returned by the Loader. If you're using multiple Loaders with * different return types use Object and case the results. + * + * @deprecated This was an old design, it will be removed before Honeycomb ships. */ +@Deprecated public abstract class LoaderManagingFragment<D> extends Fragment implements Loader.OnLoadCompleteListener<D> { private boolean mStarted = false; diff --git a/core/java/android/net/SSLCertificateSocketFactory.java b/core/java/android/net/SSLCertificateSocketFactory.java index b822b27..b8a1113 100644 --- a/core/java/android/net/SSLCertificateSocketFactory.java +++ b/core/java/android/net/SSLCertificateSocketFactory.java @@ -16,13 +16,10 @@ package android.net; -import com.android.internal.net.DomainNameValidator; - import android.os.SystemProperties; import android.util.Config; import android.util.Log; - import java.io.IOException; import java.net.InetAddress; import java.net.Socket; diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java index ab5173e..25d868f 100644 --- a/core/java/android/os/StrictMode.java +++ b/core/java/android/os/StrictMode.java @@ -600,6 +600,14 @@ public final class StrictMode { } } + // Sets up CloseGuard in Dalvik/libcore + private static void setCloseGuardEnabled(boolean enabled) { + if (!(CloseGuard.getReporter() instanceof AndroidBlockGuardPolicy)) { + CloseGuard.setReporter(new AndroidCloseGuardReporter()); + } + CloseGuard.setEnabled(enabled); + } + private static class StrictModeNetworkViolation extends BlockGuard.BlockGuardPolicyException { public StrictModeNetworkViolation(int policyMask) { super(policyMask, DETECT_NETWORK); @@ -1026,6 +1034,12 @@ public final class StrictMode { } } + private static class AndroidCloseGuardReporter implements CloseGuard.Reporter { + public void report (String message, Throwable allocationSite) { + onVmPolicyViolation(message, allocationSite); + } + } + /** * Called from Parcel.writeNoException() */ @@ -1051,7 +1065,7 @@ public final class StrictMode { */ public static void setVmPolicy(final VmPolicy policy) { sVmPolicyMask = policy.mask; - CloseGuard.setEnabled(vmClosableObjectLeaksEnabled()); + setCloseGuardEnabled(vmClosableObjectLeaksEnabled()); } /** @@ -1099,6 +1113,13 @@ public final class StrictMode { * @hide */ public static void onSqliteObjectLeaked(String message, Throwable originStack) { + onVmPolicyViolation(message, originStack); + } + + /** + * @hide + */ + public static void onVmPolicyViolation(String message, Throwable originStack) { if ((sVmPolicyMask & PENALTY_LOG) != 0) { Log.e(TAG, message, originStack); } diff --git a/core/java/android/preference/PreferenceActivity.java b/core/java/android/preference/PreferenceActivity.java index c1ee0cf..0ce69ad 100644 --- a/core/java/android/preference/PreferenceActivity.java +++ b/core/java/android/preference/PreferenceActivity.java @@ -216,8 +216,6 @@ public abstract class PreferenceActivity extends ListActivity implements Header mappedHeader = findBestMatchingHeader(mCurHeader, mHeaders); if (mappedHeader != null) { setSelectedHeader(mappedHeader); - } else { - switchToHeader(null); } } } break; diff --git a/core/java/android/view/FallbackEventHandler.java b/core/java/android/view/FallbackEventHandler.java new file mode 100644 index 0000000..dd68d89 --- /dev/null +++ b/core/java/android/view/FallbackEventHandler.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 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. + */ + +package android.view; + +/** + * @hide + */ +public interface FallbackEventHandler { + public void setView(View v); + public void preDispatchKeyEvent(KeyEvent event); + public boolean dispatchKeyEvent(KeyEvent event); +} + diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java index c7c2071..5b18715 100644 --- a/core/java/android/view/ViewRoot.java +++ b/core/java/android/view/ViewRoot.java @@ -59,6 +59,7 @@ import android.view.accessibility.AccessibilityManager; import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputMethodManager; import android.widget.Scroller; +import com.android.internal.policy.PolicyManager; import com.android.internal.view.BaseSurfaceHolder; import com.android.internal.view.IInputMethodCallback; import com.android.internal.view.IInputMethodSession; @@ -160,6 +161,7 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn InputChannel mInputChannel; InputQueue.Callback mInputQueueCallback; InputQueue mInputQueue; + FallbackEventHandler mFallbackEventHandler; final Rect mTempRect; // used in the transaction to not thrash the heap. final Rect mVisRect; // used to retrieve visible rect of focused view. @@ -273,6 +275,7 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn mAttachInfo = new View.AttachInfo(sWindowSession, mWindow, this, this); mViewConfiguration = ViewConfiguration.get(context); mDensity = context.getResources().getDisplayMetrics().densityDpi; + mFallbackEventHandler = PolicyManager.makeNewFallbackEventHandler(context); } public static void addFirstDrawHandler(Runnable callback) { @@ -325,6 +328,7 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn synchronized (this) { if (mView == null) { mView = view; + mFallbackEventHandler.setView(view); mWindowAttributes.copyFrom(attrs); attrs = mWindowAttributes; @@ -386,6 +390,7 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn mView = null; mAttachInfo.mRootView = null; mInputChannel = null; + mFallbackEventHandler.setView(null); unscheduleTraversals(); throw new RuntimeException("Adding window failed", e); } finally { @@ -404,6 +409,7 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn mView = null; mAttachInfo.mRootView = null; mAdded = false; + mFallbackEventHandler.setView(null); unscheduleTraversals(); switch (res) { case WindowManagerImpl.ADD_BAD_APP_TOKEN: @@ -2422,8 +2428,13 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn if (Config.LOGV) { captureKeyLog("captureDispatchKeyEvent", event); } + mFallbackEventHandler.preDispatchKeyEvent(event); boolean keyHandled = mView.dispatchKeyEvent(event); + if (!keyHandled) { + mFallbackEventHandler.dispatchKeyEvent(event); + } + if (!keyHandled && isDown) { int direction = 0; switch (event.getKeyCode()) { diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java index 4b7c071..95678c6 100644 --- a/core/java/android/widget/PopupWindow.java +++ b/core/java/android/widget/PopupWindow.java @@ -787,6 +787,8 @@ public class PopupWindow { p.gravity = gravity; p.x = x; p.y = y; + if (mHeightMode < 0) p.height = mLastHeight = mHeightMode; + if (mWidthMode < 0) p.width = mLastWidth = mWidthMode; invokePopup(p); } diff --git a/core/java/com/android/internal/policy/IPolicy.java b/core/java/com/android/internal/policy/IPolicy.java index 73db0b7..d08b3b4 100644 --- a/core/java/com/android/internal/policy/IPolicy.java +++ b/core/java/com/android/internal/policy/IPolicy.java @@ -17,6 +17,7 @@ package com.android.internal.policy; import android.content.Context; +import android.view.FallbackEventHandler; import android.view.LayoutInflater; import android.view.Window; import android.view.WindowManagerPolicy; @@ -33,4 +34,6 @@ public interface IPolicy { public LayoutInflater makeNewLayoutInflater(Context context); public WindowManagerPolicy makeNewWindowManager(); + + public FallbackEventHandler makeNewFallbackEventHandler(Context context); } diff --git a/core/java/com/android/internal/policy/PolicyManager.java b/core/java/com/android/internal/policy/PolicyManager.java index 4ed5a14..5274e54 100644 --- a/core/java/com/android/internal/policy/PolicyManager.java +++ b/core/java/com/android/internal/policy/PolicyManager.java @@ -17,6 +17,7 @@ package com.android.internal.policy; import android.content.Context; +import android.view.FallbackEventHandler; import android.view.LayoutInflater; import android.view.Window; import android.view.WindowManagerPolicy; @@ -65,4 +66,8 @@ public final class PolicyManager { public static WindowManagerPolicy makeNewWindowManager() { return sPolicy.makeNewWindowManager(); } + + public static FallbackEventHandler makeNewFallbackEventHandler(Context context) { + return sPolicy.makeNewFallbackEventHandler(context); + } } diff --git a/core/res/res/layout/preference_child.xml b/core/res/res/layout/preference_child.xml index 8975ed6..d6f1182 100644 --- a/core/res/res/layout/preference_child.xml +++ b/core/res/res/layout/preference_child.xml @@ -21,21 +21,25 @@ android:minHeight="?android:attr/listPreferredItemHeight" android:gravity="center_vertical" android:paddingRight="?android:attr/scrollbarSize"> - + + <View + android:layout_width="@dimen/preference_widget_width" + android:layout_height="match_parent" /> + <RelativeLayout android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginLeft="20dip" + android:layout_marginLeft="16dip" android:layout_marginRight="6dip" android:layout_marginTop="6dip" android:layout_marginBottom="6dip" android:layout_weight="1"> - + <TextView android:id="@+android:id/title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:singleLine="true" - android:textAppearance="?android:attr/textAppearanceLarge" + android:textAppearance="?android:attr/textAppearanceMedium" android:ellipsize="marquee" android:fadingEdge="horizontal" /> @@ -45,11 +49,11 @@ android:layout_below="@android:id/title" android:layout_alignLeft="@android:id/title" android:textAppearance="?android:attr/textAppearanceSmall" - android:maxLines="2" - android:textColor="?android:attr/textColorSecondary" /> + android:textColor="?android:attr/textColorSecondary" + android:maxLines="4" /> </RelativeLayout> - + <!-- Preference should place its actual preference widget here. --> <LinearLayout android:id="@+android:id/widget_frame" android:layout_width="wrap_content" diff --git a/libs/rs/java/tests/src/com/android/rs/test/RSTestCore.java b/libs/rs/java/tests/src/com/android/rs/test/RSTestCore.java index 8054779..d2ce6c8 100644 --- a/libs/rs/java/tests/src/com/android/rs/test/RSTestCore.java +++ b/libs/rs/java/tests/src/com/android/rs/test/RSTestCore.java @@ -64,6 +64,7 @@ public class RSTestCore { unitTests.add(new UT_primitives(this, mRes)); unitTests.add(new UT_rsdebug(this, mRes)); unitTests.add(new UT_rstypes(this, mRes)); + unitTests.add(new UT_vector_array(this, mRes)); unitTests.add(new UT_fp_mad(this, mRes)); /* unitTests.add(new UnitTest(null, "<Pass>", 1)); diff --git a/libs/rs/java/tests/src/com/android/rs/test/UT_vector_array.java b/libs/rs/java/tests/src/com/android/rs/test/UT_vector_array.java new file mode 100644 index 0000000..615ce14 --- /dev/null +++ b/libs/rs/java/tests/src/com/android/rs/test/UT_vector_array.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 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. + */ + +package com.android.rs.test; + +import android.content.res.Resources; +import android.renderscript.*; + +public class UT_vector_array extends UnitTest { + private Resources mRes; + + protected UT_vector_array(RSTestCore rstc, Resources res) { + super(rstc, "Vector Array"); + mRes = res; + } + + public void run() { + RenderScript pRS = RenderScript.create(); + ScriptC_vector_array s = new ScriptC_vector_array(pRS, mRes, R.raw.vector_array); + pRS.mMessageCallback = mRsMessage; + s.invoke_vector_array_test(); + pRS.finish(); + waitForMessage(); + pRS.destroy(); + } +} + diff --git a/libs/rs/java/tests/src/com/android/rs/test/vector_array.rs b/libs/rs/java/tests/src/com/android/rs/test/vector_array.rs new file mode 100644 index 0000000..f924ae4 --- /dev/null +++ b/libs/rs/java/tests/src/com/android/rs/test/vector_array.rs @@ -0,0 +1,51 @@ +// 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. + +#include "shared.rsh" + +#pragma rs export_func(vector_array_test) + +typedef struct { + float3 arr[2]; +} float3Struct; + +float3Struct f; + +bool size_test() { + bool failed = false; + int expectedSize = 2 * 3 * (int) sizeof(float); + int actualSize = (int) sizeof(f); + + rsDebug("Size of struct { float3 arr[2]; } (expected):", expectedSize); + rsDebug("Size of struct { float3 arr[2]; } (actual) :", actualSize); + + if (expectedSize != actualSize) { + failed = true; + } + + return failed; +} + +void vector_array_test() { + bool failed = false; + failed |= size_test(); + + if (failed) { + rsSendToClientBlocking(RS_MSG_TEST_FAILED); + } + else { + rsSendToClientBlocking(RS_MSG_TEST_PASSED); + } +} + diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index b23dcde..b84a2c2 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -27,10 +27,12 @@ import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.RemoteException; +import android.os.SystemClock; import android.os.ServiceManager; import android.provider.Settings; import android.util.Log; import android.view.KeyEvent; +import android.view.VolumePanel; import java.util.Iterator; import java.util.HashMap; @@ -45,6 +47,7 @@ public class AudioManager { private final Context mContext; private final Handler mHandler; + private long mVolumeKeyUpTime; private static String TAG = "AudioManager"; private static boolean DEBUG = false; @@ -358,6 +361,71 @@ public class AudioManager { } /** + * @hide + */ + public void preDispatchKeyEvent(int keyCode, int stream) { + /* + * If the user hits another key within the play sound delay, then + * cancel the sound + */ + if (keyCode != KeyEvent.KEYCODE_VOLUME_DOWN && keyCode != KeyEvent.KEYCODE_VOLUME_UP + && keyCode != KeyEvent.KEYCODE_VOLUME_MUTE + && mVolumeKeyUpTime + VolumePanel.PLAY_SOUND_DELAY + > SystemClock.uptimeMillis()) { + /* + * The user has hit another key during the delay (e.g., 300ms) + * since the last volume key up, so cancel any sounds. + */ + adjustSuggestedStreamVolume(AudioManager.ADJUST_SAME, + stream, AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE); + } + } + + /** + * @hide + */ + public void handleKeyDown(int keyCode, int stream) { + switch (keyCode) { + case KeyEvent.KEYCODE_VOLUME_UP: + case KeyEvent.KEYCODE_VOLUME_DOWN: + /* + * Adjust the volume in on key down since it is more + * responsive to the user. + */ + adjustSuggestedStreamVolume( + keyCode == KeyEvent.KEYCODE_VOLUME_UP + ? AudioManager.ADJUST_RAISE + : AudioManager.ADJUST_LOWER, + stream, + AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE); + break; + case KeyEvent.KEYCODE_VOLUME_MUTE: + // TODO: Actually handle MUTE. + break; + } + } + + /** + * @hide + */ + public void handleKeyUp(int keyCode, int stream) { + switch (keyCode) { + case KeyEvent.KEYCODE_VOLUME_UP: + case KeyEvent.KEYCODE_VOLUME_DOWN: + /* + * Play a sound. This is done on key up since we don't want the + * sound to play when a user holds down volume down to mute. + */ + adjustSuggestedStreamVolume(ADJUST_SAME, stream, FLAG_PLAY_SOUND); + mVolumeKeyUpTime = SystemClock.uptimeMillis(); + break; + case KeyEvent.KEYCODE_VOLUME_MUTE: + // TODO: Actually handle MUTE. + break; + } + } + + /** * Adjusts the volume of a particular stream by one step in a direction. * <p> * This method should only be used by applications that replace the platform-wide diff --git a/policy/src/com/android/internal/policy/impl/PhoneFallbackEventHandler.java b/policy/src/com/android/internal/policy/impl/PhoneFallbackEventHandler.java new file mode 100644 index 0000000..a8dd76c --- /dev/null +++ b/policy/src/com/android/internal/policy/impl/PhoneFallbackEventHandler.java @@ -0,0 +1,288 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.policy.impl; + +import android.app.KeyguardManager; +import android.app.SearchManager; +import android.content.ActivityNotFoundException; +import android.content.Context; +import android.content.Intent; +import android.content.res.Configuration; +import android.media.AudioManager; +import android.telephony.TelephonyManager; +import android.util.EventLog; +import android.util.Slog; +import android.view.View; +import android.view.HapticFeedbackConstants; +import android.view.FallbackEventHandler; +import android.view.KeyEvent; + +public class PhoneFallbackEventHandler implements FallbackEventHandler { + static String TAG = "PhoneFallbackEventHandler"; + + Context mContext; + View mView; + + AudioManager mAudioManager; + KeyguardManager mKeyguardManager; + SearchManager mSearchManager; + TelephonyManager mTelephonyManager; + + public PhoneFallbackEventHandler(Context context) { + mContext = context; + } + + public void setView(View v) { + mView = v; + } + + public void preDispatchKeyEvent(KeyEvent event) { + getAudioManager().preDispatchKeyEvent(event.getKeyCode(), + AudioManager.USE_DEFAULT_STREAM_TYPE); + } + + public boolean dispatchKeyEvent(KeyEvent event) { + + final int action = event.getAction(); + final int keyCode = event.getKeyCode(); + + if (action == KeyEvent.ACTION_DOWN) { + return onKeyDown(keyCode, event); + } else { + return onKeyUp(keyCode, event); + } + } + + boolean onKeyDown(int keyCode, KeyEvent event) { + /* **************************************************************************** + * HOW TO DECIDE WHERE YOUR KEY HANDLING GOES. + * See the comment in PhoneWindow.onKeyDown + * ****************************************************************************/ + final KeyEvent.DispatcherState dispatcher = mView.getKeyDispatcherState(); + + switch (keyCode) { + case KeyEvent.KEYCODE_VOLUME_UP: + case KeyEvent.KEYCODE_VOLUME_DOWN: + case KeyEvent.KEYCODE_VOLUME_MUTE: { + getAudioManager().handleKeyDown(keyCode, AudioManager.USE_DEFAULT_STREAM_TYPE); + return true; + } + + + case KeyEvent.KEYCODE_MEDIA_PLAY: + case KeyEvent.KEYCODE_MEDIA_PAUSE: + case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: + /* Suppress PLAY/PAUSE toggle when phone is ringing or in-call + * to avoid music playback */ + if (getTelephonyManager().getCallState() != TelephonyManager.CALL_STATE_IDLE) { + return true; // suppress key event + } + case KeyEvent.KEYCODE_MUTE: + case KeyEvent.KEYCODE_HEADSETHOOK: + case KeyEvent.KEYCODE_MEDIA_STOP: + case KeyEvent.KEYCODE_MEDIA_NEXT: + case KeyEvent.KEYCODE_MEDIA_PREVIOUS: + case KeyEvent.KEYCODE_MEDIA_REWIND: + case KeyEvent.KEYCODE_MEDIA_RECORD: + case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: { + Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON, null); + intent.putExtra(Intent.EXTRA_KEY_EVENT, event); + mContext.sendOrderedBroadcast(intent, null); + return true; + } + + case KeyEvent.KEYCODE_CALL: { + if (getKeyguardManager().inKeyguardRestrictedInputMode() || dispatcher == null) { + break; + } + if (event.getRepeatCount() == 0) { + dispatcher.startTracking(event, this); + } else if (event.isLongPress() && dispatcher.isTracking(event)) { + dispatcher.performedLongPress(event); + mView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); + // launch the VoiceDialer + Intent intent = new Intent(Intent.ACTION_VOICE_COMMAND); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + try { + sendCloseSystemWindows(); + mContext.startActivity(intent); + } catch (ActivityNotFoundException e) { + startCallActivity(); + } + } + return true; + } + + case KeyEvent.KEYCODE_CAMERA: { + if (getKeyguardManager().inKeyguardRestrictedInputMode() || dispatcher == null) { + break; + } + if (event.getRepeatCount() == 0) { + dispatcher.startTracking(event, this); + } else if (event.isLongPress() && dispatcher.isTracking(event)) { + dispatcher.performedLongPress(event); + mView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); + sendCloseSystemWindows(); + // Broadcast an intent that the Camera button was longpressed + Intent intent = new Intent(Intent.ACTION_CAMERA_BUTTON, null); + intent.putExtra(Intent.EXTRA_KEY_EVENT, event); + mContext.sendOrderedBroadcast(intent, null); + } + return true; + } + + case KeyEvent.KEYCODE_SEARCH: { + if (getKeyguardManager().inKeyguardRestrictedInputMode() || dispatcher == null) { + break; + } + if (event.getRepeatCount() == 0) { + dispatcher.startTracking(event, this); + } else if (event.isLongPress() && dispatcher.isTracking(event)) { + Configuration config = mContext.getResources().getConfiguration(); + if (config.keyboard == Configuration.KEYBOARD_NOKEYS + || config.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES) { + // launch the search activity + Intent intent = new Intent(Intent.ACTION_SEARCH_LONG_PRESS); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + try { + mView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); + sendCloseSystemWindows(); + getSearchManager().stopSearch(); + mContext.startActivity(intent); + // Only clear this if we successfully start the + // activity; otherwise we will allow the normal short + // press action to be performed. + dispatcher.performedLongPress(event); + return true; + } catch (ActivityNotFoundException e) { + // Ignore + } + } + } + break; + } + } + return false; + } + + boolean onKeyUp(int keyCode, KeyEvent event) { + Slog.d(TAG, "up " + keyCode); + final KeyEvent.DispatcherState dispatcher = mView.getKeyDispatcherState(); + if (dispatcher != null) { + dispatcher.handleUpEvent(event); + } + + switch (keyCode) { + case KeyEvent.KEYCODE_VOLUME_UP: + case KeyEvent.KEYCODE_VOLUME_DOWN: + case KeyEvent.KEYCODE_VOLUME_MUTE: { + if (!event.isCanceled()) { + AudioManager audioManager = (AudioManager)mContext.getSystemService( + Context.AUDIO_SERVICE); + if (audioManager != null) { + getAudioManager().handleKeyUp(keyCode, + AudioManager.USE_DEFAULT_STREAM_TYPE); + } + } + return true; + } + + case KeyEvent.KEYCODE_HEADSETHOOK: + case KeyEvent.KEYCODE_MUTE: + case KeyEvent.KEYCODE_MEDIA_PLAY: + case KeyEvent.KEYCODE_MEDIA_PAUSE: + case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: + case KeyEvent.KEYCODE_MEDIA_STOP: + case KeyEvent.KEYCODE_MEDIA_NEXT: + case KeyEvent.KEYCODE_MEDIA_PREVIOUS: + case KeyEvent.KEYCODE_MEDIA_REWIND: + case KeyEvent.KEYCODE_MEDIA_RECORD: + case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: { + Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON, null); + intent.putExtra(Intent.EXTRA_KEY_EVENT, event); + mContext.sendOrderedBroadcast(intent, null); + return true; + } + + case KeyEvent.KEYCODE_CAMERA: { + if (getKeyguardManager().inKeyguardRestrictedInputMode()) { + break; + } + if (event.isTracking() && !event.isCanceled()) { + // Add short press behavior here if desired + } + return true; + } + + case KeyEvent.KEYCODE_CALL: { + if (getKeyguardManager().inKeyguardRestrictedInputMode()) { + break; + } + if (event.isTracking() && !event.isCanceled()) { + startCallActivity(); + } + return true; + } + } + return false; + } + + void startCallActivity() { + sendCloseSystemWindows(); + Intent intent = new Intent(Intent.ACTION_CALL_BUTTON); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + try { + mContext.startActivity(intent); + } catch (ActivityNotFoundException e) { + Slog.w(TAG, "No activity found for android.intent.action.CALL_BUTTON."); + } + } + + SearchManager getSearchManager() { + if (mSearchManager == null) { + mSearchManager = (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE); + } + return mSearchManager; + } + + TelephonyManager getTelephonyManager() { + if (mTelephonyManager == null) { + mTelephonyManager = (TelephonyManager)mContext.getSystemService( + Context.TELEPHONY_SERVICE); + } + return mTelephonyManager; + } + + KeyguardManager getKeyguardManager() { + if (mKeyguardManager == null) { + mKeyguardManager = (KeyguardManager)mContext.getSystemService(Context.KEYGUARD_SERVICE); + } + return mKeyguardManager; + } + + AudioManager getAudioManager() { + if (mAudioManager == null) { + mAudioManager = (AudioManager)mContext.getSystemService(Context.AUDIO_SERVICE); + } + return mAudioManager; + } + + void sendCloseSystemWindows() { + PhoneWindowManager.sendCloseSystemWindows(mContext, null); + } +} + diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java index cd88821..1bded54 100644 --- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java @@ -36,7 +36,6 @@ import com.android.internal.widget.ActionBarContextView; import com.android.internal.widget.ActionBarView; import android.app.KeyguardManager; -import android.app.SearchManager; import android.content.ActivityNotFoundException; import android.content.Context; import android.content.Intent; @@ -52,7 +51,6 @@ import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; import android.os.SystemClock; -import android.telephony.TelephonyManager; import android.util.AndroidRuntimeException; import android.util.Config; import android.util.EventLog; @@ -61,7 +59,6 @@ import android.util.SparseArray; import android.util.TypedValue; import android.view.ActionMode; import android.view.Gravity; -import android.view.HapticFeedbackConstants; import android.view.InputQueue; import android.view.KeyCharacterMap; import android.view.KeyEvent; @@ -170,12 +167,9 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { private int mVolumeControlStreamType = AudioManager.USE_DEFAULT_STREAM_TYPE; private long mVolumeKeyUpTime; - private KeyguardManager mKeyguardManager = null; - - private SearchManager mSearchManager = null; + private AudioManager mAudioManager; + private KeyguardManager mKeyguardManager; - private TelephonyManager mTelephonyManager = null; - public PhoneWindow(Context context) { super(context); mLayoutInflater = LayoutInflater.from(context); @@ -1223,6 +1217,21 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { * @see android.view.KeyEvent */ protected boolean onKeyDown(int featureId, int keyCode, KeyEvent event) { + /* **************************************************************************** + * HOW TO DECIDE WHERE YOUR KEY HANDLING GOES. + * + * If your key handling must happen before the app gets a crack at the event, + * it goes in PhoneWindowManager. + * + * If your key handling should happen in all windows, and does not depend on + * the state of the current application, other than that the current + * application can override the behavior by handling the event itself, it + * should go in PhoneFallbackEventHandler. + * + * Only if your handling depends on the window, and the fact that it has + * a DecorView, should it go here. + * ****************************************************************************/ + final KeyEvent.DispatcherState dispatcher = mDecor != null ? mDecor.getKeyDispatcherState() : null; //Log.i(TAG, "Key down: repeat=" + event.getRepeatCount() @@ -1232,68 +1241,11 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { case KeyEvent.KEYCODE_VOLUME_UP: case KeyEvent.KEYCODE_VOLUME_DOWN: case KeyEvent.KEYCODE_VOLUME_MUTE: { - AudioManager audioManager = (AudioManager) getContext().getSystemService( - Context.AUDIO_SERVICE); - if (audioManager != null) { - /* - * Adjust the volume in on key down since it is more - * responsive to the user. - */ - // TODO: Actually handle MUTE. - audioManager.adjustSuggestedStreamVolume( - keyCode == KeyEvent.KEYCODE_VOLUME_UP - ? AudioManager.ADJUST_RAISE - : AudioManager.ADJUST_LOWER, - mVolumeControlStreamType, - AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE); - } - return true; - } - - - case KeyEvent.KEYCODE_MEDIA_PLAY: - case KeyEvent.KEYCODE_MEDIA_PAUSE: - case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: - /* Suppress PLAY/PAUSE toggle when phone is ringing or in-call - * to avoid music playback */ - if (mTelephonyManager == null) { - mTelephonyManager = (TelephonyManager) getContext().getSystemService( - Context.TELEPHONY_SERVICE); - } - if (mTelephonyManager != null && - mTelephonyManager.getCallState() != TelephonyManager.CALL_STATE_IDLE) { - return true; // suppress key event - } - case KeyEvent.KEYCODE_MUTE: - case KeyEvent.KEYCODE_HEADSETHOOK: - case KeyEvent.KEYCODE_MEDIA_STOP: - case KeyEvent.KEYCODE_MEDIA_NEXT: - case KeyEvent.KEYCODE_MEDIA_PREVIOUS: - case KeyEvent.KEYCODE_MEDIA_REWIND: - case KeyEvent.KEYCODE_MEDIA_RECORD: - case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: { - Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON, null); - intent.putExtra(Intent.EXTRA_KEY_EVENT, event); - getContext().sendOrderedBroadcast(intent, null); - return true; - } - - case KeyEvent.KEYCODE_CAMERA: { - if (getKeyguardManager().inKeyguardRestrictedInputMode() - || dispatcher == null) { - break; - } - if (event.getRepeatCount() == 0) { - dispatcher.startTracking(event, this); - } else if (event.isLongPress() && dispatcher.isTracking(event)) { - dispatcher.performedLongPress(event); - mDecor.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); - sendCloseSystemWindows(); - // Broadcast an intent that the Camera button was longpressed - Intent intent = new Intent(Intent.ACTION_CAMERA_BUTTON, null); - intent.putExtra(Intent.EXTRA_KEY_EVENT, event); - getContext().sendOrderedBroadcast(intent, null); - } + // Similar code is in PhoneFallbackEventHandler in case the window + // doesn't have one of these. In this case, we execute it here and + // eat the event instead, because we have mVolumeControlStreamType + // and they don't. + getAudioManager().handleKeyDown(keyCode, mVolumeControlStreamType); return true; } @@ -1310,84 +1262,24 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { return true; } - case KeyEvent.KEYCODE_CALL: { - if (getKeyguardManager().inKeyguardRestrictedInputMode() - || dispatcher == null) { - break; - } - if (event.getRepeatCount() == 0) { - dispatcher.startTracking(event, this); - } else if (event.isLongPress() && dispatcher.isTracking(event)) { - dispatcher.performedLongPress(event); - mDecor.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); - // launch the VoiceDialer - Intent intent = new Intent(Intent.ACTION_VOICE_COMMAND); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - try { - sendCloseSystemWindows(); - getContext().startActivity(intent); - } catch (ActivityNotFoundException e) { - startCallActivity(); - } - } - return true; - } - - case KeyEvent.KEYCODE_SEARCH: { - if (getKeyguardManager().inKeyguardRestrictedInputMode() - || dispatcher == null) { - break; - } - if (event.getRepeatCount() == 0) { - dispatcher.startTracking(event, this); - } else if (event.isLongPress() && dispatcher.isTracking(event)) { - Configuration config = getContext().getResources().getConfiguration(); - if (config.keyboard == Configuration.KEYBOARD_NOKEYS - || config.hardKeyboardHidden - == Configuration.HARDKEYBOARDHIDDEN_YES) { - // launch the search activity - Intent intent = new Intent(Intent.ACTION_SEARCH_LONG_PRESS); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - try { - mDecor.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); - sendCloseSystemWindows(); - getSearchManager().stopSearch(); - getContext().startActivity(intent); - // Only clear this if we successfully start the - // activity; otherwise we will allow the normal short - // press action to be performed. - dispatcher.performedLongPress(event); - return true; - } catch (ActivityNotFoundException e) { - // Ignore - } - } - } - break; - } } return false; } - /** - * @return A handle to the keyguard manager. - */ private KeyguardManager getKeyguardManager() { if (mKeyguardManager == null) { - mKeyguardManager = (KeyguardManager) getContext().getSystemService(Context.KEYGUARD_SERVICE); + mKeyguardManager = (KeyguardManager) getContext().getSystemService( + Context.KEYGUARD_SERVICE); } return mKeyguardManager; } - - /** - * @return A handle to the search manager. - */ - private SearchManager getSearchManager() { - if (mSearchManager == null) { - mSearchManager = (SearchManager) getContext().getSystemService(Context.SEARCH_SERVICE); + + AudioManager getAudioManager() { + if (mAudioManager == null) { + mAudioManager = (AudioManager)getContext().getSystemService(Context.AUDIO_SERVICE); } - return mSearchManager; + return mAudioManager; } /** @@ -1409,22 +1301,11 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { case KeyEvent.KEYCODE_VOLUME_UP: case KeyEvent.KEYCODE_VOLUME_DOWN: case KeyEvent.KEYCODE_VOLUME_MUTE: { - if (!event.isCanceled()) { - AudioManager audioManager = (AudioManager) getContext().getSystemService( - Context.AUDIO_SERVICE); - if (audioManager != null) { - /* - * Play a sound. This is done on key up since we don't want the - * sound to play when a user holds down volume down to mute. - */ - // TODO: Actually handle MUTE. - audioManager.adjustSuggestedStreamVolume( - AudioManager.ADJUST_SAME, - mVolumeControlStreamType, - AudioManager.FLAG_PLAY_SOUND); - mVolumeKeyUpTime = SystemClock.uptimeMillis(); - } - } + // Similar code is in PhoneFallbackEventHandler in case the window + // doesn't have one of these. In this case, we execute it here and + // eat the event instead, because we have mVolumeControlStreamType + // and they don't. + getAudioManager().handleKeyUp(keyCode, mVolumeControlStreamType); return true; } @@ -1452,43 +1333,6 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { break; } - case KeyEvent.KEYCODE_HEADSETHOOK: - case KeyEvent.KEYCODE_MUTE: - case KeyEvent.KEYCODE_MEDIA_PLAY: - case KeyEvent.KEYCODE_MEDIA_PAUSE: - case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: - case KeyEvent.KEYCODE_MEDIA_STOP: - case KeyEvent.KEYCODE_MEDIA_NEXT: - case KeyEvent.KEYCODE_MEDIA_PREVIOUS: - case KeyEvent.KEYCODE_MEDIA_REWIND: - case KeyEvent.KEYCODE_MEDIA_RECORD: - case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: { - Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON, null); - intent.putExtra(Intent.EXTRA_KEY_EVENT, event); - getContext().sendOrderedBroadcast(intent, null); - return true; - } - - case KeyEvent.KEYCODE_CAMERA: { - if (getKeyguardManager().inKeyguardRestrictedInputMode()) { - break; - } - if (event.isTracking() && !event.isCanceled()) { - // Add short press behavior here if desired - } - return true; - } - - case KeyEvent.KEYCODE_CALL: { - if (getKeyguardManager().inKeyguardRestrictedInputMode()) { - break; - } - if (event.isTracking() && !event.isCanceled()) { - startCallActivity(); - } - return true; - } - case KeyEvent.KEYCODE_SEARCH: { /* * Do this in onKeyUp since the Search key is also used for @@ -1507,17 +1351,6 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { return false; } - private void startCallActivity() { - sendCloseSystemWindows(); - Intent intent = new Intent(Intent.ACTION_CALL_BUTTON); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - try { - getContext().startActivity(intent); - } catch (ActivityNotFoundException e) { - Log.w(TAG, "No activity found for android.intent.action.CALL_BUTTON."); - } - } - @Override protected void onActive() { } @@ -1719,26 +1552,6 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { final int action = event.getAction(); final boolean isDown = action == KeyEvent.ACTION_DOWN; - /* - * If the user hits another key within the play sound delay, then - * cancel the sound - */ - if (keyCode != KeyEvent.KEYCODE_VOLUME_DOWN && keyCode != KeyEvent.KEYCODE_VOLUME_UP - && keyCode != KeyEvent.KEYCODE_VOLUME_MUTE - && mVolumeKeyUpTime + VolumePanel.PLAY_SOUND_DELAY - > SystemClock.uptimeMillis()) { - /* - * The user has hit another key during the delay (e.g., 300ms) - * since the last volume key up, so cancel any sounds. - */ - AudioManager audioManager = (AudioManager) getContext().getSystemService( - Context.AUDIO_SERVICE); - if (audioManager != null) { - audioManager.adjustSuggestedStreamVolume(AudioManager.ADJUST_SAME, - mVolumeControlStreamType, AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE); - } - } - if (isDown && (event.getRepeatCount() == 0)) { // First handle chording of panel key: if a panel key is held // but not released, try to execute a shortcut in it. diff --git a/policy/src/com/android/internal/policy/impl/Policy.java b/policy/src/com/android/internal/policy/impl/Policy.java index 17f3e91..a490729 100644 --- a/policy/src/com/android/internal/policy/impl/Policy.java +++ b/policy/src/com/android/internal/policy/impl/Policy.java @@ -18,6 +18,10 @@ package com.android.internal.policy.impl; import android.content.Context; import android.util.Log; +import android.view.FallbackEventHandler; +import android.view.LayoutInflater; +import android.view.Window; +import android.view.WindowManagerPolicy; import com.android.internal.policy.IPolicy; import com.android.internal.policy.impl.PhoneLayoutInflater; @@ -55,15 +59,19 @@ public class Policy implements IPolicy { } } - public PhoneWindow makeNewWindow(Context context) { + public Window makeNewWindow(Context context) { return new PhoneWindow(context); } - public PhoneLayoutInflater makeNewLayoutInflater(Context context) { + public LayoutInflater makeNewLayoutInflater(Context context) { return new PhoneLayoutInflater(context); } - public PhoneWindowManager makeNewWindowManager() { + public WindowManagerPolicy makeNewWindowManager() { return new PhoneWindowManager(); } + + public FallbackEventHandler makeNewFallbackEventHandler(Context context) { + return new PhoneFallbackEventHandler(context); + } } diff --git a/services/java/com/android/server/DevicePolicyManagerService.java b/services/java/com/android/server/DevicePolicyManagerService.java index 081bd30..68aa8e3 100644 --- a/services/java/com/android/server/DevicePolicyManagerService.java +++ b/services/java/com/android/server/DevicePolicyManagerService.java @@ -175,7 +175,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { out.endTag(null, "min-password-nonletter"); } } - if (maximumTimeToUnlock != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) { + if (maximumTimeToUnlock != 0) { out.startTag(null, "max-time-to-unlock"); out.attribute(null, "value", Long.toString(maximumTimeToUnlock)); out.endTag(null, "max-time-to-unlock"); diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java index 90cdb4b..c74a27c 100644 --- a/services/java/com/android/server/WindowManagerService.java +++ b/services/java/com/android/server/WindowManagerService.java @@ -86,6 +86,7 @@ import android.os.PowerManager; import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.StrictMode; import android.os.SystemClock; import android.os.SystemProperties; import android.os.TokenWatcher; @@ -944,6 +945,11 @@ public class WindowManagerService extends IWindowManager.Stub notifyAll(); } + // For debug builds, log event loop stalls to dropbox for analysis. + if (StrictMode.conditionallyEnableDebugLogging()) { + Slog.i(TAG, "Enabled StrictMode logging for WMThread's Looper"); + } + Looper.loop(); } } @@ -981,6 +987,11 @@ public class WindowManagerService extends IWindowManager.Stub notifyAll(); } + // For debug builds, log event loop stalls to dropbox for analysis. + if (StrictMode.conditionallyEnableDebugLogging()) { + Slog.i(TAG, "Enabled StrictMode for PolicyThread's Looper"); + } + Looper.loop(); } } @@ -2725,7 +2736,10 @@ public class WindowManagerService extends IWindowManager.Stub displayed = !win.isVisibleLw(); if (win.mExiting) { win.mExiting = false; - win.mAnimation = null; + if (win.mAnimation != null) { + win.mAnimation.cancel(); + win.mAnimation = null; + } } if (win.mDestroying) { win.mDestroying = false; @@ -6892,6 +6906,7 @@ public class WindowManagerService extends IWindowManager.Stub if (mAnimation != null) { mAnimating = true; mLocalAnimating = false; + mAnimation.cancel(); mAnimation = null; } } @@ -7163,6 +7178,7 @@ public class WindowManagerService extends IWindowManager.Stub // starting window, so there is no need for it to also // be doing its own stuff. if (mAnimation != null) { + mAnimation.cancel(); mAnimation = null; // Make sure we clean up the animation. mAnimating = true; @@ -7208,7 +7224,11 @@ public class WindowManagerService extends IWindowManager.Stub if (DEBUG_ANIM) Slog.v( TAG, "Finished animation in " + this + " @ " + currentTime); - mAnimation = null; + + if (mAnimation != null) { + mAnimation.cancel(); + mAnimation = null; + } //WindowManagerService.this.dump(); } mHasLocalTransformation = false; @@ -7237,6 +7257,7 @@ public class WindowManagerService extends IWindowManager.Stub // clear it and make sure we run the cleanup code. mAnimating = true; mLocalAnimating = true; + mAnimation.cancel(); mAnimation = null; } @@ -7251,7 +7272,10 @@ public class WindowManagerService extends IWindowManager.Stub mAnimating = false; mLocalAnimating = false; - mAnimation = null; + if (mAnimation != null) { + mAnimation.cancel(); + mAnimation = null; + } mAnimLayer = mLayer; if (mIsImWindow) { mAnimLayer += mInputMethodAnimLayerAdjustment; diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 7905dc6..ba8d7d2 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -1349,6 +1349,11 @@ public final class ActivityManagerService extends ActivityManagerNative } } + // For debug builds, log event loop stalls to dropbox for analysis. + if (StrictMode.conditionallyEnableDebugLogging()) { + Slog.i(TAG, "Enabled StrictMode logging for AThread's Looper"); + } + Looper.loop(); } } |