diff options
143 files changed, 4738 insertions, 1501 deletions
diff --git a/api/current.txt b/api/current.txt index 4d5a0fd2a744..4997ae6a98ee 100644 --- a/api/current.txt +++ b/api/current.txt @@ -207,6 +207,7 @@ package android { field public static final int actionModeCutDrawable = 16843537; // 0x1010311 field public static final int actionModePasteDrawable = 16843539; // 0x1010313 field public static final int actionModeSelectAllDrawable = 16843647; // 0x101037f + field public static final int actionModeStyle = 16843688; // 0x10103a8 field public static final int actionOverflowButtonStyle = 16843510; // 0x10102f6 field public static final int actionProviderClass = 16843677; // 0x101039d field public static final int actionViewClass = 16843516; // 0x10102fc @@ -1823,6 +1824,7 @@ package android.accessibilityservice { method public static java.lang.String feedbackTypeToString(int); method public static java.lang.String flagToString(int); method public boolean getCanRetrieveWindowContent(); + method public java.lang.String getDescription(); method public java.lang.String getId(); method public android.content.pm.ResolveInfo getResolveInfo(); method public java.lang.String getSettingsActivityName(); @@ -16702,6 +16704,7 @@ package android.provider { field public static final java.lang.String SELECTED_INPUT_METHOD_SUBTYPE = "selected_input_method_subtype"; field public static final java.lang.String SETTINGS_CLASSNAME = "settings_classname"; field public static final java.lang.String SYS_PROP_SETTING_VERSION = "sys.settings_secure_version"; + field public static final java.lang.String TOUCH_EXPLORATION_REQUESTED = "touch_exploration_requested"; field public static final java.lang.String TTS_DEFAULT_COUNTRY = "tts_default_country"; field public static final java.lang.String TTS_DEFAULT_LANG = "tts_default_lang"; field public static final java.lang.String TTS_DEFAULT_PITCH = "tts_default_pitch"; @@ -20492,6 +20495,14 @@ package android.util { ctor public Base64OutputStream(java.io.OutputStream, int); } + public final deprecated class Config { + field public static final deprecated boolean DEBUG = false; + field public static final deprecated boolean LOGD = true; + field public static final deprecated boolean LOGV = false; + field public static final deprecated boolean PROFILE = false; + field public static final deprecated boolean RELEASE = true; + } + public class DebugUtils { method public static boolean isObjectSelected(java.lang.Object); } @@ -20731,11 +20742,12 @@ package android.util { method public void set(T, V); } - public class SparseArray { + public class SparseArray implements java.lang.Cloneable { ctor public SparseArray(); ctor public SparseArray(int); method public void append(int, E); method public void clear(); + method public android.util.SparseArray<E> clone(); method public void delete(int); method public E get(int); method public E get(int, E); @@ -20750,11 +20762,12 @@ package android.util { method public E valueAt(int); } - public class SparseBooleanArray { + public class SparseBooleanArray implements java.lang.Cloneable { ctor public SparseBooleanArray(); ctor public SparseBooleanArray(int); method public void append(int, boolean); method public void clear(); + method public android.util.SparseBooleanArray clone(); method public void delete(int); method public boolean get(int); method public boolean get(int, boolean); @@ -20766,11 +20779,12 @@ package android.util { method public boolean valueAt(int); } - public class SparseIntArray { + public class SparseIntArray implements java.lang.Cloneable { ctor public SparseIntArray(); ctor public SparseIntArray(int); method public void append(int, int); method public void clear(); + method public android.util.SparseIntArray clone(); method public void delete(int); method public int get(int); method public int get(int, int); @@ -20917,11 +20931,13 @@ package android.view { method public abstract android.view.Menu getMenu(); method public abstract android.view.MenuInflater getMenuInflater(); method public abstract java.lang.CharSequence getSubtitle(); + method public java.lang.Object getTag(); method public abstract java.lang.CharSequence getTitle(); method public abstract void invalidate(); method public abstract void setCustomView(android.view.View); method public abstract void setSubtitle(java.lang.CharSequence); method public abstract void setSubtitle(int); + method public void setTag(java.lang.Object); method public abstract void setTitle(java.lang.CharSequence); method public abstract void setTitle(int); } @@ -22034,9 +22050,12 @@ package android.view { method public android.graphics.SurfaceTexture getSurfaceTexture(); method public android.view.TextureView.SurfaceTextureListener getSurfaceTextureListener(); method public boolean isAvailable(); + method public android.graphics.Canvas lockCanvas(); + method public android.graphics.Canvas lockCanvas(android.graphics.Rect); method protected final void onDraw(android.graphics.Canvas); method public void setOpaque(boolean); method public void setSurfaceTextureListener(android.view.TextureView.SurfaceTextureListener); + method public void unlockCanvasAndPost(android.graphics.Canvas); } public static abstract interface TextureView.SurfaceTextureListener { @@ -23265,6 +23284,7 @@ package android.view.accessibility { method public java.util.List<android.accessibilityservice.AccessibilityServiceInfo> getInstalledAccessibilityServiceList(); method public void interrupt(); method public boolean isEnabled(); + method public boolean isTouchExplorationEnabled(); method public boolean removeAccessibilityStateChangeListener(android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener); method public void sendAccessibilityEvent(android.view.accessibility.AccessibilityEvent); } @@ -23301,6 +23321,7 @@ package android.view.accessibility { method public boolean isSelected(); method public static android.view.accessibility.AccessibilityNodeInfo obtain(android.view.View); method public static android.view.accessibility.AccessibilityNodeInfo obtain(); + method public static android.view.accessibility.AccessibilityNodeInfo obtain(android.view.accessibility.AccessibilityNodeInfo); method public boolean performAction(int); method public void recycle(); method public void setBoundsInParent(android.graphics.Rect); diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp index dd1c27520218..b42f1c5a10bd 100644 --- a/cmds/stagefright/stagefright.cpp +++ b/cmds/stagefright/stagefright.cpp @@ -805,6 +805,7 @@ int main(int argc, char **argv) { Vector<CodecCapabilities> results; CHECK_EQ(QueryCodecs(omx, kMimeTypes[k], true, // queryDecoders + false, // hwCodecOnly &results), (status_t)OK); for (size_t i = 0; i < results.size(); ++i) { @@ -844,7 +845,12 @@ int main(int argc, char **argv) { for (List<IOMX::ComponentInfo>::iterator it = list.begin(); it != list.end(); ++it) { - printf("%s\n", (*it).mName.string()); + printf("%s\t Roles: ", (*it).mName.string()); + for (List<String8>::iterator itRoles = (*it).mRoles.begin() ; + itRoles != (*it).mRoles.end() ; ++itRoles) { + printf("%s\t", (*itRoles).string()); + } + printf("\n"); } } diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java index 5f40f25176ed..a09607a7f9ad 100644 --- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java +++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java @@ -171,6 +171,11 @@ public class AccessibilityServiceInfo implements Parcelable { private boolean mCanRetrieveWindowContent; /** + * Description of the accessibility service. + */ + private String mDescription; + + /** * Creates a new instance. */ public AccessibilityServiceInfo() { @@ -240,6 +245,8 @@ public class AccessibilityServiceInfo implements Parcelable { mCanRetrieveWindowContent = asAttributes.getBoolean( com.android.internal.R.styleable.AccessibilityService_canRetrieveWindowContent, false); + mDescription = asAttributes.getString( + com.android.internal.R.styleable.AccessibilityService_description); asAttributes.recycle(); } catch (NameNotFoundException e) { throw new XmlPullParserException( "Unable to create context for: " @@ -313,6 +320,18 @@ public class AccessibilityServiceInfo implements Parcelable { } /** + * Description of the accessibility service. + * <p> + * <strong>Statically set from + * {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong> + * </p> + * @return The description. + */ + public String getDescription() { + return mDescription; + } + + /** * {@inheritDoc} */ public int describeContents() { @@ -329,6 +348,7 @@ public class AccessibilityServiceInfo implements Parcelable { parcel.writeParcelable(mResolveInfo, 0); parcel.writeString(mSettingsActivityName); parcel.writeInt(mCanRetrieveWindowContent ? 1 : 0); + parcel.writeString(mDescription); } private void initFromParcel(Parcel parcel) { @@ -341,6 +361,7 @@ public class AccessibilityServiceInfo implements Parcelable { mResolveInfo = parcel.readParcelable(null); mSettingsActivityName = parcel.readString(); mCanRetrieveWindowContent = (parcel.readInt() == 1); + mDescription = parcel.readString(); } @Override diff --git a/core/java/android/animation/LayoutTransition.java b/core/java/android/animation/LayoutTransition.java index 12a4dbb04970..06d18ec37e22 100644 --- a/core/java/android/animation/LayoutTransition.java +++ b/core/java/android/animation/LayoutTransition.java @@ -25,6 +25,7 @@ import android.view.animation.DecelerateInterpolator; import java.util.ArrayList; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; /** @@ -178,14 +179,17 @@ public class LayoutTransition { * the transition. The reason for this is that a further layout event should cause * existing animations to stop where they are prior to starting new animations. So * we cache all of the current animations in this map for possible cancellation on - * another layout event. + * another layout event. LinkedHashMaps are used to preserve the order in which animations + * are inserted, so that we process events (such as setting up start values) in the same order. */ - private final HashMap<View, Animator> pendingAnimations = new HashMap<View, Animator>(); - private final HashMap<View, Animator> currentChangingAnimations = new HashMap<View, Animator>(); - private final HashMap<View, Animator> currentAppearingAnimations = - new HashMap<View, Animator>(); - private final HashMap<View, Animator> currentDisappearingAnimations = + private final HashMap<View, Animator> pendingAnimations = new HashMap<View, Animator>(); + private final LinkedHashMap<View, Animator> currentChangingAnimations = + new LinkedHashMap<View, Animator>(); + private final LinkedHashMap<View, Animator> currentAppearingAnimations = + new LinkedHashMap<View, Animator>(); + private final LinkedHashMap<View, Animator> currentDisappearingAnimations = + new LinkedHashMap<View, Animator>(); /** * This hashmap is used to track the listeners that have been added to the children of @@ -547,7 +551,7 @@ public class LayoutTransition { } /** - * This function sets up runs animations on all of the views that change during layout. + * This function sets up animations on all of the views that change during layout. * For every child in the parent, we create a change animation of the appropriate * type (appearing or disappearing) and ask it to populate its start values from its * target view. We add layout listeners to all child views and listen for changes. For @@ -821,24 +825,24 @@ public class LayoutTransition { */ public void cancel() { if (currentChangingAnimations.size() > 0) { - HashMap<View, Animator> currentAnimCopy = - (HashMap<View, Animator>) currentChangingAnimations.clone(); + LinkedHashMap<View, Animator> currentAnimCopy = + (LinkedHashMap<View, Animator>) currentChangingAnimations.clone(); for (Animator anim : currentAnimCopy.values()) { anim.cancel(); } currentChangingAnimations.clear(); } if (currentAppearingAnimations.size() > 0) { - HashMap<View, Animator> currentAnimCopy = - (HashMap<View, Animator>) currentAppearingAnimations.clone(); + LinkedHashMap<View, Animator> currentAnimCopy = + (LinkedHashMap<View, Animator>) currentAppearingAnimations.clone(); for (Animator anim : currentAnimCopy.values()) { anim.end(); } currentAppearingAnimations.clear(); } if (currentDisappearingAnimations.size() > 0) { - HashMap<View, Animator> currentAnimCopy = - (HashMap<View, Animator>) currentDisappearingAnimations.clone(); + LinkedHashMap<View, Animator> currentAnimCopy = + (LinkedHashMap<View, Animator>) currentDisappearingAnimations.clone(); for (Animator anim : currentAnimCopy.values()) { anim.end(); } @@ -859,8 +863,8 @@ public class LayoutTransition { case CHANGE_APPEARING: case CHANGE_DISAPPEARING: if (currentChangingAnimations.size() > 0) { - HashMap<View, Animator> currentAnimCopy = - (HashMap<View, Animator>) currentChangingAnimations.clone(); + LinkedHashMap<View, Animator> currentAnimCopy = + (LinkedHashMap<View, Animator>) currentChangingAnimations.clone(); for (Animator anim : currentAnimCopy.values()) { anim.cancel(); } @@ -869,8 +873,8 @@ public class LayoutTransition { break; case APPEARING: if (currentAppearingAnimations.size() > 0) { - HashMap<View, Animator> currentAnimCopy = - (HashMap<View, Animator>) currentAppearingAnimations.clone(); + LinkedHashMap<View, Animator> currentAnimCopy = + (LinkedHashMap<View, Animator>) currentAppearingAnimations.clone(); for (Animator anim : currentAnimCopy.values()) { anim.end(); } @@ -879,8 +883,8 @@ public class LayoutTransition { break; case DISAPPEARING: if (currentDisappearingAnimations.size() > 0) { - HashMap<View, Animator> currentAnimCopy = - (HashMap<View, Animator>) currentDisappearingAnimations.clone(); + LinkedHashMap<View, Animator> currentAnimCopy = + (LinkedHashMap<View, Animator>) currentDisappearingAnimations.clone(); for (Animator anim : currentAnimCopy.values()) { anim.end(); } @@ -1113,4 +1117,4 @@ public class LayoutTransition { View view, int transitionType); } -}
\ No newline at end of file +} diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index c6a746b237f1..f6cd866d4e34 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -70,7 +70,7 @@ import android.view.HardwareRenderer; import android.view.View; import android.view.ViewDebug; import android.view.ViewManager; -import android.view.ViewAncestor; +import android.view.ViewRootImpl; import android.view.Window; import android.view.WindowManager; import android.view.WindowManagerImpl; @@ -3558,6 +3558,7 @@ public final class ActivityThread { } final void handleTrimMemory(int level) { + WindowManagerImpl.getDefault().trimMemory(level); } private final void handleBindApplication(AppBindData data) { @@ -4071,7 +4072,7 @@ public final class ActivityThread { sThreadLocal.set(this); mSystemThread = system; if (!system) { - ViewAncestor.addFirstDrawHandler(new Runnable() { + ViewRootImpl.addFirstDrawHandler(new Runnable() { public void run() { ensureJitEnabled(); } @@ -4101,7 +4102,7 @@ public final class ActivityThread { } } - ViewAncestor.addConfigCallback(new ComponentCallbacks() { + ViewRootImpl.addConfigCallback(new ComponentCallbacks() { public void onConfigurationChanged(Configuration newConfig) { synchronized (mPackages) { // We need to apply this change to the resources diff --git a/core/java/android/app/SearchDialog.java b/core/java/android/app/SearchDialog.java index 42eda0212e01..8e2d360b5637 100644 --- a/core/java/android/app/SearchDialog.java +++ b/core/java/android/app/SearchDialog.java @@ -168,6 +168,7 @@ public class SearchDialog extends Dialog { SearchBar searchBar = (SearchBar) findViewById(com.android.internal.R.id.search_bar); searchBar.setSearchDialog(this); mSearchView = (SearchView) findViewById(com.android.internal.R.id.search_view); + mSearchView.setIconified(false); mSearchView.setOnCloseListener(mOnCloseListener); mSearchView.setOnQueryTextListener(mOnQueryChangeListener); mSearchView.setOnSuggestionListener(mOnSuggestionSelectionListener); @@ -633,31 +634,6 @@ public class SearchDialog extends Dialog { } /** - * Overrides the handling of the back key to move back to the previous - * sources or dismiss the search dialog, instead of dismissing the input - * method. - */ - @Override - public boolean dispatchKeyEventPreIme(KeyEvent event) { - if (DBG) - Log.d(LOG_TAG, "onKeyPreIme(" + event + ")"); - if (mSearchDialog != null && event.getKeyCode() == KeyEvent.KEYCODE_BACK) { - KeyEvent.DispatcherState state = getKeyDispatcherState(); - if (state != null) { - if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) { - state.startTracking(event, this); - return true; - } else if (event.getAction() == KeyEvent.ACTION_UP && !event.isCanceled() - && state.isTracking(event)) { - mSearchDialog.onBackPressed(); - return true; - } - } - } - return super.dispatchKeyEventPreIme(event); - } - - /** * Don't allow action modes in a SearchBar, it looks silly. */ @Override diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java index c179b3576211..4c21d049a6fc 100644 --- a/core/java/android/app/Service.java +++ b/core/java/android/app/Service.java @@ -662,12 +662,6 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) { writer.println("nothing to dump"); } - - @Override - protected void finalize() throws Throwable { - super.finalize(); - //Log.i("Service", "Finalizing Service: " + this); - } // ------------------ Internal API ------------------ diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java index 7fd5a7d3dcd0..8472b3149686 100644 --- a/core/java/android/app/WallpaperManager.java +++ b/core/java/android/app/WallpaperManager.java @@ -40,7 +40,7 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.util.DisplayMetrics; import android.util.Log; -import android.view.ViewAncestor; +import android.view.ViewRootImpl; import java.io.FileOutputStream; import java.io.IOException; @@ -592,7 +592,7 @@ public class WallpaperManager { public void setWallpaperOffsets(IBinder windowToken, float xOffset, float yOffset) { try { //Log.v(TAG, "Sending new wallpaper offsets from app..."); - ViewAncestor.getWindowSession(mContext.getMainLooper()).setWallpaperPosition( + ViewRootImpl.getWindowSession(mContext.getMainLooper()).setWallpaperPosition( windowToken, xOffset, yOffset, mWallpaperXStep, mWallpaperYStep); //Log.v(TAG, "...app returning after sending offsets!"); } catch (RemoteException e) { @@ -630,7 +630,7 @@ public class WallpaperManager { int x, int y, int z, Bundle extras) { try { //Log.v(TAG, "Sending new wallpaper offsets from app..."); - ViewAncestor.getWindowSession(mContext.getMainLooper()).sendWallpaperCommand( + ViewRootImpl.getWindowSession(mContext.getMainLooper()).sendWallpaperCommand( windowToken, action, x, y, z, extras, false); //Log.v(TAG, "...app returning after sending offsets!"); } catch (RemoteException e) { @@ -650,7 +650,7 @@ public class WallpaperManager { */ public void clearWallpaperOffsets(IBinder windowToken) { try { - ViewAncestor.getWindowSession(mContext.getMainLooper()).setWallpaperPosition( + ViewRootImpl.getWindowSession(mContext.getMainLooper()).setWallpaperPosition( windowToken, -1, -1, -1, -1); } catch (RemoteException e) { // Ignore. diff --git a/core/java/android/content/ContentService.java b/core/java/android/content/ContentService.java index a2af55817bd5..0e83dc0fb0a2 100644 --- a/core/java/android/content/ContentService.java +++ b/core/java/android/content/ContentService.java @@ -20,17 +20,21 @@ import android.accounts.Account; import android.database.IContentObserver; import android.database.sqlite.SQLiteException; import android.net.Uri; +import android.os.Binder; import android.os.Bundle; import android.os.IBinder; import android.os.Parcel; import android.os.RemoteException; import android.os.ServiceManager; import android.util.Log; +import android.util.SparseIntArray; import android.Manifest; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; import java.util.List; /** @@ -70,6 +74,40 @@ public final class ContentService extends IContentService.Stub { } else { mSyncManager.dump(fd, pw); } + pw.println(); + pw.println("Observer tree:"); + synchronized (mRootNode) { + int[] counts = new int[2]; + final SparseIntArray pidCounts = new SparseIntArray(); + mRootNode.dumpLocked(fd, pw, args, "", " ", counts, pidCounts); + pw.println(); + ArrayList<Integer> sorted = new ArrayList<Integer>(); + for (int i=0; i<pidCounts.size(); i++) { + sorted.add(pidCounts.keyAt(i)); + } + Collections.sort(sorted, new Comparator<Integer>() { + @Override + public int compare(Integer lhs, Integer rhs) { + int lc = pidCounts.get(lhs); + int rc = pidCounts.get(rhs); + if (lc < rc) { + return 1; + } else if (lc > rc) { + return -1; + } + return 0; + } + + }); + for (int i=0; i<sorted.size(); i++) { + int pid = sorted.get(i); + pw.print(" pid "); pw.print(pid); pw.print(": "); + pw.print(pidCounts.get(pid)); pw.println(" observers"); + } + pw.println(); + pw.print(" Total number of nodes: "); pw.println(counts[0]); + pw.print(" Total number of observers: "); pw.println(counts[1]); + } } finally { restoreCallingIdentity(identityToken); } @@ -102,7 +140,8 @@ public final class ContentService extends IContentService.Stub { throw new IllegalArgumentException("You must pass a valid uri and observer"); } synchronized (mRootNode) { - mRootNode.addObserverLocked(uri, observer, notifyForDescendents, mRootNode); + mRootNode.addObserverLocked(uri, observer, notifyForDescendents, mRootNode, + Binder.getCallingUid(), Binder.getCallingPid()); if (false) Log.v(TAG, "Registered observer " + observer + " at " + uri + " with notifyForDescendents " + notifyForDescendents); } @@ -465,12 +504,17 @@ public final class ContentService extends IContentService.Stub { public static final class ObserverNode { private class ObserverEntry implements IBinder.DeathRecipient { public final IContentObserver observer; + public final int uid; + public final int pid; public final boolean notifyForDescendents; private final Object observersLock; - public ObserverEntry(IContentObserver o, boolean n, Object observersLock) { + public ObserverEntry(IContentObserver o, boolean n, Object observersLock, + int _uid, int _pid) { this.observersLock = observersLock; observer = o; + uid = _uid; + pid = _pid; notifyForDescendents = n; try { observer.asBinder().linkToDeath(this, 0); @@ -484,6 +528,16 @@ public final class ContentService extends IContentService.Stub { removeObserverLocked(observer); } } + + public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args, + String name, String prefix, SparseIntArray pidCounts) { + pidCounts.put(pid, pidCounts.get(pid)+1); + pw.print(prefix); pw.print(name); pw.print(": pid="); + pw.print(pid); pw.print(" uid="); + pw.print(uid); pw.print(" target="); + pw.println(Integer.toHexString(System.identityHashCode( + observer != null ? observer.asBinder() : null))); + } } public static final int INSERT_TYPE = 0; @@ -498,6 +552,37 @@ public final class ContentService extends IContentService.Stub { mName = name; } + public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args, + String name, String prefix, int[] counts, SparseIntArray pidCounts) { + String innerName = null; + if (mObservers.size() > 0) { + if ("".equals(name)) { + innerName = mName; + } else { + innerName = name + "/" + mName; + } + for (int i=0; i<mObservers.size(); i++) { + counts[1]++; + mObservers.get(i).dumpLocked(fd, pw, args, innerName, prefix, + pidCounts); + } + } + if (mChildren.size() > 0) { + if (innerName == null) { + if ("".equals(name)) { + innerName = mName; + } else { + innerName = name + "/" + mName; + } + } + for (int i=0; i<mChildren.size(); i++) { + counts[0]++; + mChildren.get(i).dumpLocked(fd, pw, args, innerName, prefix, + counts, pidCounts); + } + } + } + private String getUriSegment(Uri uri, int index) { if (uri != null) { if (index == 0) { @@ -518,15 +603,16 @@ public final class ContentService extends IContentService.Stub { } public void addObserverLocked(Uri uri, IContentObserver observer, - boolean notifyForDescendents, Object observersLock) { - addObserverLocked(uri, 0, observer, notifyForDescendents, observersLock); + boolean notifyForDescendents, Object observersLock, int uid, int pid) { + addObserverLocked(uri, 0, observer, notifyForDescendents, observersLock, uid, pid); } private void addObserverLocked(Uri uri, int index, IContentObserver observer, - boolean notifyForDescendents, Object observersLock) { + boolean notifyForDescendents, Object observersLock, int uid, int pid) { // If this is the leaf node add the observer if (index == countUriSegments(uri)) { - mObservers.add(new ObserverEntry(observer, notifyForDescendents, observersLock)); + mObservers.add(new ObserverEntry(observer, notifyForDescendents, observersLock, + uid, pid)); return; } @@ -539,7 +625,8 @@ public final class ContentService extends IContentService.Stub { for (int i = 0; i < N; i++) { ObserverNode node = mChildren.get(i); if (node.mName.equals(segment)) { - node.addObserverLocked(uri, index + 1, observer, notifyForDescendents, observersLock); + node.addObserverLocked(uri, index + 1, observer, notifyForDescendents, + observersLock, uid, pid); return; } } @@ -547,7 +634,8 @@ public final class ContentService extends IContentService.Stub { // No child found, create one ObserverNode node = new ObserverNode(segment); mChildren.add(node); - node.addObserverLocked(uri, index + 1, observer, notifyForDescendents, observersLock); + node.addObserverLocked(uri, index + 1, observer, notifyForDescendents, + observersLock, uid, pid); } public boolean removeObserverLocked(IContentObserver observer) { diff --git a/core/java/android/content/SearchRecentSuggestionsProvider.java b/core/java/android/content/SearchRecentSuggestionsProvider.java index 3d89e92d4348..e1a8d21f9970 100644 --- a/core/java/android/content/SearchRecentSuggestionsProvider.java +++ b/core/java/android/content/SearchRecentSuggestionsProvider.java @@ -186,6 +186,9 @@ public class SearchRecentSuggestionsProvider extends ContentProvider { mSuggestionProjection = new String [] { "0 AS " + SearchManager.SUGGEST_COLUMN_FORMAT, + "'android.resource://system/" + + com.android.internal.R.drawable.ic_menu_recent_history + "' AS " + + SearchManager.SUGGEST_COLUMN_ICON_1, "display1 AS " + SearchManager.SUGGEST_COLUMN_TEXT_1, "display2 AS " + SearchManager.SUGGEST_COLUMN_TEXT_2, "query AS " + SearchManager.SUGGEST_COLUMN_QUERY, @@ -196,6 +199,9 @@ public class SearchRecentSuggestionsProvider extends ContentProvider { mSuggestionProjection = new String [] { "0 AS " + SearchManager.SUGGEST_COLUMN_FORMAT, + "'android.resource://system/" + + com.android.internal.R.drawable.ic_menu_recent_history + "' AS " + + SearchManager.SUGGEST_COLUMN_ICON_1, "display1 AS " + SearchManager.SUGGEST_COLUMN_TEXT_1, "query AS " + SearchManager.SUGGEST_COLUMN_QUERY, "_id" diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java index 67d200cd40ff..b5486234b224 100644 --- a/core/java/android/hardware/usb/UsbManager.java +++ b/core/java/android/hardware/usb/UsbManager.java @@ -409,9 +409,10 @@ public class UsbManager { /** * Sets the current USB function. + * If function is null, then the current function is set to the default function. * - * @param function name of the USB function - * @param makeDefault true if this should be set as the default + * @param function name of the USB function, or null to restore the default function + * @param makeDefault true if the function should be set as the new default function * * {@hide} */ diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 23b53ae04ba4..34699e26f994 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -2683,6 +2683,13 @@ public final class Settings { public static final String ACCESSIBILITY_ENABLED = "accessibility_enabled"; /** + * If touch exploration is requested. Touch exploration is enabled if it is + * requested by this setting, accessibility is enabled and there is at least + * one enabled accessibility serivce that provides spoken feedback. + */ + public static final String TOUCH_EXPLORATION_REQUESTED = "touch_exploration_requested"; + + /** * List of the enabled accessibility providers. */ public static final String ENABLED_ACCESSIBILITY_SERVICES = diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java index 8fc8b9dbb31f..c51ba2a0a643 100644 --- a/core/java/android/service/wallpaper/WallpaperService.java +++ b/core/java/android/service/wallpaper/WallpaperService.java @@ -51,7 +51,7 @@ import android.view.MotionEvent; import android.view.SurfaceHolder; import android.view.View; import android.view.ViewGroup; -import android.view.ViewAncestor; +import android.view.ViewRootImpl; import android.view.WindowManager; import android.view.WindowManagerImpl; import android.view.WindowManagerPolicy; @@ -650,7 +650,7 @@ public abstract class WallpaperService extends Service { mWindowToken = wrapper.mWindowToken; mSurfaceHolder.setSizeFromLayout(); mInitializing = true; - mSession = ViewAncestor.getWindowSession(getMainLooper()); + mSession = ViewRootImpl.getWindowSession(getMainLooper()); mWindow.setSession(mSession); diff --git a/core/java/android/speech/tts/AudioPlaybackHandler.java b/core/java/android/speech/tts/AudioPlaybackHandler.java index dea708a92170..96864c4555b8 100644 --- a/core/java/android/speech/tts/AudioPlaybackHandler.java +++ b/core/java/android/speech/tts/AudioPlaybackHandler.java @@ -17,6 +17,7 @@ package android.speech.tts; import android.media.AudioFormat; import android.media.AudioTrack; +import android.text.TextUtils; import android.util.Log; import java.util.Iterator; @@ -25,6 +26,7 @@ import java.util.concurrent.atomic.AtomicLong; class AudioPlaybackHandler { private static final String TAG = "TTS.AudioPlaybackHandler"; + private static final boolean DBG_THREADING = false; private static final boolean DBG = false; private static final int MIN_AUDIO_BUFFER_SIZE = 8192; @@ -64,70 +66,105 @@ class AudioPlaybackHandler { * Stops all synthesis for a given {@code token}. If the current token * is currently being processed, an effort will be made to stop it but * that is not guaranteed. + * + * NOTE: This assumes that all other messages in the queue with {@code token} + * have been removed already. + * + * NOTE: Must be called synchronized on {@code AudioPlaybackHandler.this}. */ - synchronized public void stop(MessageParams token) { + private void stop(MessageParams token) { if (token == null) { return; } - removeMessages(token); + if (DBG) Log.d(TAG, "Stopping token : " + token); if (token.getType() == MessageParams.TYPE_SYNTHESIS) { AudioTrack current = ((SynthesisMessageParams) token).getAudioTrack(); if (current != null) { // Stop the current audio track if it's still playing. - // The audio track is thread safe in this regard. + // The audio track is thread safe in this regard. The current + // handleSynthesisDataAvailable call will return soon after this + // call. current.stop(); } + // This is safe because PlaybackSynthesisCallback#stop would have + // been called before this method, and will no longer enqueue any + // audio for this token. + // + // (Even if it did, all it would result in is a warning message). mQueue.add(new ListEntry(SYNTHESIS_DONE, token, HIGH_PRIORITY)); - } else { - final MessageParams current = getCurrentParams(); - - if (current != null) { - if (token.getType() == MessageParams.TYPE_AUDIO) { - ((AudioMessageParams) current).getPlayer().stop(); - } else if (token.getType() == MessageParams.TYPE_SILENCE) { - ((SilenceMessageParams) current).getConditionVariable().open(); - } - } + } else if (token.getType() == MessageParams.TYPE_AUDIO) { + ((AudioMessageParams) token).getPlayer().stop(); + // No cleanup required for audio messages. + } else if (token.getType() == MessageParams.TYPE_SILENCE) { + ((SilenceMessageParams) token).getConditionVariable().open(); + // No cleanup required for silence messages. } } + // ----------------------------------------------------- + // Methods that add and remove elements from the queue. These do not + // need to be synchronized strictly speaking, but they make the behaviour + // a lot more predictable. (though it would still be correct without + // synchronization). + // ----------------------------------------------------- + synchronized public void removePlaybackItems(String callingApp) { + if (DBG_THREADING) Log.d(TAG, "Removing all callback items for : " + callingApp); removeMessages(callingApp); - stop(getCurrentParams()); + + final MessageParams current = getCurrentParams(); + if (current != null && TextUtils.equals(callingApp, current.getCallingApp())) { + stop(current); + } } synchronized public void removeAllItems() { + if (DBG_THREADING) Log.d(TAG, "Removing all items"); removeAllMessages(); stop(getCurrentParams()); } /** + * @return false iff the queue is empty and no queue item is currently + * being handled, true otherwise. + */ + public boolean isSpeaking() { + return (mQueue.peek() != null) || (mCurrentParams != null); + } + + /** * Shut down the audio playback thread. */ synchronized public void quit() { + removeAllMessages(); stop(getCurrentParams()); mQueue.add(new ListEntry(SHUTDOWN, null, HIGH_PRIORITY)); } - void enqueueSynthesisStart(SynthesisMessageParams token) { + synchronized void enqueueSynthesisStart(SynthesisMessageParams token) { + if (DBG_THREADING) Log.d(TAG, "Enqueuing synthesis start : " + token); mQueue.add(new ListEntry(SYNTHESIS_START, token)); } - void enqueueSynthesisDataAvailable(SynthesisMessageParams token) { + synchronized void enqueueSynthesisDataAvailable(SynthesisMessageParams token) { + if (DBG_THREADING) Log.d(TAG, "Enqueuing synthesis data available : " + token); mQueue.add(new ListEntry(SYNTHESIS_DATA_AVAILABLE, token)); } - void enqueueSynthesisDone(SynthesisMessageParams token) { + synchronized void enqueueSynthesisDone(SynthesisMessageParams token) { + if (DBG_THREADING) Log.d(TAG, "Enqueuing synthesis done : " + token); mQueue.add(new ListEntry(SYNTHESIS_DONE, token)); } - void enqueueAudio(AudioMessageParams token) { + synchronized void enqueueAudio(AudioMessageParams token) { + if (DBG_THREADING) Log.d(TAG, "Enqueuing audio : " + token); mQueue.add(new ListEntry(PLAY_AUDIO, token)); } - void enqueueSilence(SilenceMessageParams token) { + synchronized void enqueueSilence(SilenceMessageParams token) { + if (DBG_THREADING) Log.d(TAG, "Enqueuing silence : " + token); mQueue.add(new ListEntry(PLAY_SILENCE, token)); } @@ -172,26 +209,6 @@ class AudioPlaybackHandler { } /* - * Remove all messages from the queue that contain the supplied token. - * Note that the Iterator is thread safe, and other methods can safely - * continue adding to the queue at this point. - */ - synchronized private void removeMessages(MessageParams token) { - if (token == null) { - return; - } - - Iterator<ListEntry> it = mQueue.iterator(); - - while (it.hasNext()) { - final ListEntry current = it.next(); - if (current.mMessage == token) { - it.remove(); - } - } - } - - /* * Atomically clear the queue of all messages. */ synchronized private void removeAllMessages() { @@ -255,6 +272,13 @@ class AudioPlaybackHandler { } private void setCurrentParams(MessageParams p) { + if (DBG_THREADING) { + if (p != null) { + Log.d(TAG, "Started handling :" + p); + } else { + Log.d(TAG, "End handling : " + mCurrentParams); + } + } mCurrentParams = p; } @@ -345,7 +369,7 @@ class AudioPlaybackHandler { private void handleSynthesisDataAvailable(MessageParams msg) { final SynthesisMessageParams param = (SynthesisMessageParams) msg; if (param.getAudioTrack() == null) { - Log.w(TAG, "Error : null audio track in handleDataAvailable."); + Log.w(TAG, "Error : null audio track in handleDataAvailable : " + param); return; } diff --git a/core/java/android/speech/tts/MessageParams.java b/core/java/android/speech/tts/MessageParams.java index 4c1b6d2bcf33..e7d6da3782a6 100644 --- a/core/java/android/speech/tts/MessageParams.java +++ b/core/java/android/speech/tts/MessageParams.java @@ -38,5 +38,10 @@ abstract class MessageParams { return mCallingApp; } + @Override + public String toString() { + return "MessageParams[" + hashCode() + "]"; + } + abstract int getType(); } diff --git a/core/java/android/speech/tts/PlaybackSynthesisCallback.java b/core/java/android/speech/tts/PlaybackSynthesisCallback.java index 04bd74539c59..7dbf1acdaf1f 100644 --- a/core/java/android/speech/tts/PlaybackSynthesisCallback.java +++ b/core/java/android/speech/tts/PlaybackSynthesisCallback.java @@ -90,12 +90,10 @@ class PlaybackSynthesisCallback extends AbstractSynthesisCallback { Log.w(TAG, "stop() called twice"); return; } + // mToken will be null if the engine encounters // an error before it called start(). - if (mToken != null) { - mAudioTrackHandler.stop(mToken); - mToken = null; - } else { + if (mToken == null) { // In all other cases, mAudioTrackHandler.stop() will // result in onComplete being called. mLogger.onWriteData(); @@ -158,7 +156,7 @@ class PlaybackSynthesisCallback extends AbstractSynthesisCallback { } synchronized (mStateLock) { - if (mToken == null) { + if (mToken == null || mStopped) { return TextToSpeech.ERROR; } diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java index 32ca226f9794..5126e482894d 100755 --- a/core/java/android/speech/tts/TextToSpeech.java +++ b/core/java/android/speech/tts/TextToSpeech.java @@ -109,6 +109,11 @@ public class TextToSpeech { /** * Broadcast Action: The TextToSpeech synthesizer has completed processing * of all the text in the speech queue. + * + * Note that this notifies callers when the <b>engine</b> has finished has + * processing text data. Audio playback might not have completed (or even started) + * at this point. If you wish to be notified when this happens, see + * {@link OnUtteranceCompletedListener}. */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_TTS_QUEUE_PROCESSING_COMPLETED = @@ -576,6 +581,14 @@ public class TextToSpeech { service.setCallback(getPackageName(), null); service.stop(getPackageName()); mServiceConnection.disconnect(); + // Context#unbindService does not result in a call to + // ServiceConnection#onServiceDisconnected. As a result, the + // service ends up being destroyed (if there are no other open + // connections to it) but the process lives on and the + // ServiceConnection continues to refer to the destroyed service. + // + // This leads to tons of log spam about SynthThread being dead. + mServiceConnection = null; mCurrentEngine = null; return null; } @@ -796,7 +809,10 @@ public class TextToSpeech { } /** - * Checks whether the TTS engine is busy speaking. + * Checks whether the TTS engine is busy speaking. Note that a speech item is + * considered complete once it's audio data has been sent to the audio mixer, or + * written to a file. There might be a finite lag between this point, and when + * the audio hardware completes playback. * * @return {@code true} if the TTS engine is speaking. */ diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java index 010c155f56dc..1926c92da023 100644 --- a/core/java/android/speech/tts/TextToSpeechService.java +++ b/core/java/android/speech/tts/TextToSpeechService.java @@ -282,6 +282,8 @@ public abstract class TextToSpeechService extends Service { if (current != null) { current.stop(); } + + // The AudioPlaybackHandler will be destroyed by the caller. } /** @@ -337,6 +339,8 @@ public abstract class TextToSpeechService extends Service { } removeCallbacksAndMessages(callingApp); + // This stops writing data to the file / or publishing + // items to the audio playback handler. SpeechItem current = setCurrentSpeechItem(null); if (current != null && TextUtils.equals(callingApp, current.getCallingApp())) { current.stop(); @@ -628,9 +632,7 @@ public abstract class TextToSpeechService extends Service { @Override protected void stopImpl() { - if (mToken != null) { - mAudioPlaybackHandler.stop(mToken); - } + // Do nothing. } } @@ -657,9 +659,7 @@ public abstract class TextToSpeechService extends Service { @Override protected void stopImpl() { - if (mToken != null) { - mAudioPlaybackHandler.stop(mToken); - } + // Do nothing. } } @@ -719,7 +719,7 @@ public abstract class TextToSpeechService extends Service { } public boolean isSpeaking() { - return mSynthHandler.isSpeaking(); + return mSynthHandler.isSpeaking() || mAudioPlaybackHandler.isSpeaking(); } public int stop(String callingApp) { @@ -767,10 +767,6 @@ public abstract class TextToSpeechService extends Service { mCallbacks.setCallback(packageName, cb); } - private boolean isDefault(String lang, String country, String variant) { - return Locale.getDefault().equals(new Locale(lang, country, variant)); - } - private String intern(String in) { // The input parameter will be non null. return in.intern(); diff --git a/core/java/android/util/Config.java b/core/java/android/util/Config.java new file mode 100644 index 000000000000..70dc9aaa0a4c --- /dev/null +++ b/core/java/android/util/Config.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2006 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.util; + +/** + * @deprecated This class is not useful, it just returns the same value for + * all constants, and has always done this. Do not use it. + */ +@Deprecated +public final class Config { + /** @hide */ public Config() {} + + /** + * @deprecated Always false. + */ + @Deprecated + public static final boolean DEBUG = false; + + /** + * @deprecated Always true. + */ + @Deprecated + public static final boolean RELEASE = true; + + /** + * @deprecated Always false. + */ + @Deprecated + public static final boolean PROFILE = false; + + /** + * @deprecated Always false. + */ + @Deprecated + public static final boolean LOGV = false; + + /** + * @deprecated Always true. + */ + @Deprecated + public static final boolean LOGD = true; +} diff --git a/core/java/android/util/JsonReader.java b/core/java/android/util/JsonReader.java index 132b5953fa68..09ce8e41913f 100644 --- a/core/java/android/util/JsonReader.java +++ b/core/java/android/util/JsonReader.java @@ -196,6 +196,12 @@ public final class JsonReader implements Closeable { private int pos = 0; private int limit = 0; + /* + * The offset of the first character in the buffer. + */ + private int bufferStartLine = 1; + private int bufferStartColumn = 1; + private final List<JsonScope> stack = new ArrayList<JsonScope>(); { push(JsonScope.EMPTY_DOCUMENT); @@ -711,6 +717,16 @@ public final class JsonReader implements Closeable { * false. */ private boolean fillBuffer(int minimum) throws IOException { + // Before clobbering the old characters, update where buffer starts + for (int i = 0; i < pos; i++) { + if (buffer[i] == '\n') { + bufferStartLine++; + bufferStartColumn = 1; + } else { + bufferStartColumn++; + } + } + if (limit != pos) { limit -= pos; System.arraycopy(buffer, pos, buffer, 0, limit); @@ -729,6 +745,28 @@ public final class JsonReader implements Closeable { return false; } + private int getLineNumber() { + int result = bufferStartLine; + for (int i = 0; i < pos; i++) { + if (buffer[i] == '\n') { + result++; + } + } + return result; + } + + private int getColumnNumber() { + int result = bufferStartColumn; + for (int i = 0; i < pos; i++) { + if (buffer[i] == '\n') { + result = 1; + } else { + result++; + } + } + return result; + } + private int nextNonWhitespace() throws IOException { while (pos < limit || fillBuffer(1)) { int c = buffer[pos++]; @@ -1107,7 +1145,8 @@ public final class JsonReader implements Closeable { * with this reader's content. */ private IOException syntaxError(String message) throws IOException { - throw new MalformedJsonException(message + " near " + getSnippet()); + throw new MalformedJsonException(message + + " at line " + getLineNumber() + " column " + getColumnNumber()); } private CharSequence getSnippet() { diff --git a/core/java/android/util/JsonWriter.java b/core/java/android/util/JsonWriter.java index 47e84c51dd25..c1e6e400aa93 100644 --- a/core/java/android/util/JsonWriter.java +++ b/core/java/android/util/JsonWriter.java @@ -407,6 +407,11 @@ public final class JsonWriter implements Closeable { * quotation marks except for the characters that must be escaped: * quotation mark, reverse solidus, and the control characters * (U+0000 through U+001F)." + * + * We also escape '\u2028' and '\u2029', which JavaScript interprets + * as newline characters. This prevents eval() from failing with a + * syntax error. + * http://code.google.com/p/google-gson/issues/detail?id=341 */ switch (c) { case '"': @@ -435,6 +440,11 @@ public final class JsonWriter implements Closeable { out.write("\\f"); break; + case '\u2028': + case '\u2029': + out.write(String.format("\\u%04x", (int) c)); + break; + default: if (c <= 0x1F) { out.write(String.format("\\u%04x", (int) c)); diff --git a/core/java/android/util/SparseArray.java b/core/java/android/util/SparseArray.java index 7fc43b92c549..7cf45793fe7e 100644 --- a/core/java/android/util/SparseArray.java +++ b/core/java/android/util/SparseArray.java @@ -23,10 +23,14 @@ import com.android.internal.util.ArrayUtils; * there can be gaps in the indices. It is intended to be more efficient * than using a HashMap to map Integers to Objects. */ -public class SparseArray<E> { +public class SparseArray<E> implements Cloneable { private static final Object DELETED = new Object(); private boolean mGarbage = false; + private int[] mKeys; + private Object[] mValues; + private int mSize; + /** * Creates a new SparseArray containing no mappings. */ @@ -47,6 +51,20 @@ public class SparseArray<E> { mSize = 0; } + @Override + @SuppressWarnings("unchecked") + public SparseArray<E> clone() { + SparseArray<E> clone = null; + try { + clone = (SparseArray<E>) super.clone(); + clone.mKeys = mKeys.clone(); + clone.mValues = mValues.clone(); + } catch (CloneNotSupportedException cnse) { + /* ignore */ + } + return clone; + } + /** * Gets the Object mapped from the specified key, or <code>null</code> * if no such mapping has been made. @@ -59,6 +77,7 @@ public class SparseArray<E> { * Gets the Object mapped from the specified key, or the specified Object * if no such mapping has been made. */ + @SuppressWarnings("unchecked") public E get(int key, E valueIfKeyNotFound) { int i = binarySearch(mKeys, 0, mSize, key); @@ -209,6 +228,7 @@ public class SparseArray<E> { * the value from the <code>index</code>th key-value mapping that this * SparseArray stores. */ + @SuppressWarnings("unchecked") public E valueAt(int index) { if (mGarbage) { gc(); @@ -331,20 +351,4 @@ public class SparseArray<E> { else return ~high; } - - private void checkIntegrity() { - for (int i = 1; i < mSize; i++) { - if (mKeys[i] <= mKeys[i - 1]) { - for (int j = 0; j < mSize; j++) { - Log.e("FAIL", j + ": " + mKeys[j] + " -> " + mValues[j]); - } - - throw new RuntimeException(); - } - } - } - - private int[] mKeys; - private Object[] mValues; - private int mSize; } diff --git a/core/java/android/util/SparseBooleanArray.java b/core/java/android/util/SparseBooleanArray.java index f7799dee6ef7..76c47c6aabad 100644 --- a/core/java/android/util/SparseBooleanArray.java +++ b/core/java/android/util/SparseBooleanArray.java @@ -24,7 +24,7 @@ import com.android.internal.util.ArrayUtils; * there can be gaps in the indices. It is intended to be more efficient * than using a HashMap to map Integers to Booleans. */ -public class SparseBooleanArray { +public class SparseBooleanArray implements Cloneable { /** * Creates a new SparseBooleanArray containing no mappings. */ @@ -45,6 +45,19 @@ public class SparseBooleanArray { mSize = 0; } + @Override + public SparseBooleanArray clone() { + SparseBooleanArray clone = null; + try { + clone = (SparseBooleanArray) super.clone(); + clone.mKeys = mKeys.clone(); + clone.mValues = mValues.clone(); + } catch (CloneNotSupportedException cnse) { + /* ignore */ + } + return clone; + } + /** * Gets the boolean mapped from the specified key, or <code>false</code> * if no such mapping has been made. @@ -227,18 +240,6 @@ public class SparseBooleanArray { return ~high; } - private void checkIntegrity() { - for (int i = 1; i < mSize; i++) { - if (mKeys[i] <= mKeys[i - 1]) { - for (int j = 0; j < mSize; j++) { - Log.e("FAIL", j + ": " + mKeys[j] + " -> " + mValues[j]); - } - - throw new RuntimeException(); - } - } - } - private int[] mKeys; private boolean[] mValues; private int mSize; diff --git a/core/java/android/util/SparseIntArray.java b/core/java/android/util/SparseIntArray.java index 9ab3b53169e3..8d111770e8dd 100644 --- a/core/java/android/util/SparseIntArray.java +++ b/core/java/android/util/SparseIntArray.java @@ -23,7 +23,12 @@ import com.android.internal.util.ArrayUtils; * there can be gaps in the indices. It is intended to be more efficient * than using a HashMap to map Integers to Integers. */ -public class SparseIntArray { +public class SparseIntArray implements Cloneable { + + private int[] mKeys; + private int[] mValues; + private int mSize; + /** * Creates a new SparseIntArray containing no mappings. */ @@ -44,6 +49,19 @@ public class SparseIntArray { mSize = 0; } + @Override + public SparseIntArray clone() { + SparseIntArray clone = null; + try { + clone = (SparseIntArray) super.clone(); + clone.mKeys = mKeys.clone(); + clone.mValues = mValues.clone(); + } catch (CloneNotSupportedException cnse) { + /* ignore */ + } + return clone; + } + /** * Gets the int mapped from the specified key, or <code>0</code> * if no such mapping has been made. @@ -232,20 +250,4 @@ public class SparseIntArray { else return ~high; } - - private void checkIntegrity() { - for (int i = 1; i < mSize; i++) { - if (mKeys[i] <= mKeys[i - 1]) { - for (int j = 0; j < mSize; j++) { - Log.e("FAIL", j + ": " + mKeys[j] + " -> " + mValues[j]); - } - - throw new RuntimeException(); - } - } - } - - private int[] mKeys; - private int[] mValues; - private int mSize; } diff --git a/core/java/android/view/ActionMode.java b/core/java/android/view/ActionMode.java index bfafa987ceda..e9549834b215 100644 --- a/core/java/android/view/ActionMode.java +++ b/core/java/android/view/ActionMode.java @@ -23,6 +23,36 @@ package android.view; * Examples of good action modes include selection modes, search, content editing, etc. */ public abstract class ActionMode { + private Object mTag; + + /** + * Set a tag object associated with this ActionMode. + * + * <p>Like the tag available to views, this allows applications to associate arbitrary + * data with an ActionMode for later reference. + * + * @param tag Tag to associate with this ActionMode + * + * @see #getTag() + */ + public void setTag(Object tag) { + mTag = tag; + } + + /** + * Retrieve the tag object associated with this ActionMode. + * + * <p>Like the tag available to views, this allows applications to associate arbitrary + * data with an ActionMode for later reference. + * + * @return Tag associated with this ActionMode + * + * @see #setTag(Object) + */ + public Object getTag() { + return mTag; + } + /** * Set the title of the action mode. This method will have no visible effect if * a custom view has been set. diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java index 4987e2f6b0a4..80244bbe9244 100644 --- a/core/java/android/view/GLES20Canvas.java +++ b/core/java/android/view/GLES20Canvas.java @@ -286,6 +286,38 @@ class GLES20Canvas extends HardwareCanvas { private static native boolean nCallDrawGLFunction(int renderer, int drawGLFunction); + + /////////////////////////////////////////////////////////////////////////// + // Memory + /////////////////////////////////////////////////////////////////////////// + + /** + * @see #flushCaches(int) + */ + public static final int FLUSH_CACHES_MODERATE = 0; + + /** + * @see #flushCaches(int) + */ + public static final int FLUSH_CACHES_FULL = 1; + + /** + * Flush caches to reclaim as much memory as possible. The amount of memory + * to reclaim is indicate by the level parameter. + * + * The level can be one of {@link #FLUSH_CACHES_MODERATE} or + * {@link #FLUSH_CACHES_FULL}. + * + * @param level Hint about the amount of memory to reclaim + * + * @hide + */ + public static void flushCaches(int level) { + nFlushCaches(level); + } + + private static native void nFlushCaches(int level); + /////////////////////////////////////////////////////////////////////////// // Display list /////////////////////////////////////////////////////////////////////////// diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java index 011e44c5157c..9a2564fc052e 100644 --- a/core/java/android/view/HardwareRenderer.java +++ b/core/java/android/view/HardwareRenderer.java @@ -17,6 +17,7 @@ package android.view; +import android.content.ComponentCallbacks; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.SurfaceTexture; @@ -263,6 +264,18 @@ public abstract class HardwareRenderer { } /** + * Invoke this method when the system is running out of memory. This + * method will attempt to recover as much memory as possible, based on + * the specified hint. + * + * @param level Hint about the amount of memory that should be trimmed, + * see {@link android.content.ComponentCallbacks} + */ + static void trimMemory(int level) { + Gl20Renderer.flushCaches(level); + } + + /** * Indicates whether hardware acceleration is currently enabled. * * @return True if hardware acceleration is in use, false otherwise. @@ -858,5 +871,16 @@ public abstract class HardwareRenderer { } return null; } + + static void flushCaches(int level) { + switch (level) { + case ComponentCallbacks.TRIM_MEMORY_MODERATE: + GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_MODERATE); + break; + case ComponentCallbacks.TRIM_MEMORY_COMPLETE: + GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_FULL); + break; + } + } } } diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index 764899fae527..cbdb38e3181a 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -426,7 +426,7 @@ public class SurfaceView extends View { if (!mHaveFrame) { return; } - ViewAncestor viewRoot = (ViewAncestor) getRootView().getParent(); + ViewRootImpl viewRoot = (ViewRootImpl) getRootView().getParent(); if (viewRoot != null) { mTranslator = viewRoot.mTranslator; } diff --git a/core/java/android/view/TextureView.java b/core/java/android/view/TextureView.java index d656f315a4db..96d6f095d300 100644 --- a/core/java/android/view/TextureView.java +++ b/core/java/android/view/TextureView.java @@ -20,6 +20,7 @@ import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Paint; +import android.graphics.Rect; import android.graphics.SurfaceTexture; import android.util.AttributeSet; import android.util.Log; @@ -107,6 +108,14 @@ public class TextureView extends View { private SurfaceTexture.OnFrameAvailableListener mUpdateListener; + private Canvas mCanvas; + private int mSaveCount; + + private final Object[] mNativeWindowLock = new Object[0]; + // Used from native code, do not write! + @SuppressWarnings({"UnusedDeclaration"}) + private int mNativeWindow; + /** * Creates a new TextureView. * @@ -190,7 +199,11 @@ public class TextureView extends View { mListener.onSurfaceTextureDestroyed(mSurface); } - mLayer.destroy(); + synchronized (mNativeWindowLock) { + nDestroyNativeWindow(); + } + + mLayer.destroy(); mSurface = null; mLayer = null; } @@ -274,6 +287,7 @@ public class TextureView extends View { mLayer = mAttachInfo.mHardwareRenderer.createHardwareLayer(mOpaque); mSurface = mAttachInfo.mHardwareRenderer.createSurfaceTexture(mLayer); nSetDefaultBufferSize(mSurface, getWidth(), getHeight()); + nCreateNativeWindow(mSurface); mUpdateListener = new SurfaceTexture.OnFrameAvailableListener() { @Override @@ -431,6 +445,79 @@ public class TextureView extends View { } /** + * <p>Start editing the pixels in the surface. The returned Canvas can be used + * to draw into the surface's bitmap. A null is returned if the surface has + * not been created or otherwise cannot be edited. You will usually need + * to implement + * {@link SurfaceTextureListener#onSurfaceTextureAvailable(android.graphics.SurfaceTexture, int, int)} + * to find out when the Surface is available for use.</p> + * + * <p>The content of the Surface is never preserved between unlockCanvas() + * and lockCanvas(), for this reason, every pixel within the Surface area + * must be written. The only exception to this rule is when a dirty + * rectangle is specified, in which case, non-dirty pixels will be + * preserved.</p> + * + * @return A Canvas used to draw into the surface. + * + * @see #lockCanvas(android.graphics.Rect) + * @see #unlockCanvasAndPost(android.graphics.Canvas) + */ + public Canvas lockCanvas() { + return lockCanvas(null); + } + + /** + * Just like {@link #lockCanvas()} but allows specification of a dirty + * rectangle. Every pixel within that rectangle must be written; however + * pixels outside the dirty rectangle will be preserved by the next call + * to lockCanvas(). + * + * @param dirty Area of the surface that will be modified. + + * @return A Canvas used to draw into the surface. + * + * @see #lockCanvas() + * @see #unlockCanvasAndPost(android.graphics.Canvas) + */ + public Canvas lockCanvas(Rect dirty) { + if (!isAvailable()) return null; + + if (mCanvas == null) { + mCanvas = new Canvas(); + } + + synchronized (mNativeWindowLock) { + nLockCanvas(mNativeWindow, mCanvas, dirty); + } + mSaveCount = mCanvas.save(); + + return mCanvas; + } + + /** + * Finish editing pixels in the surface. After this call, the surface's + * current pixels will be shown on the screen, but its content is lost, + * in particular there is no guarantee that the content of the Surface + * will remain unchanged when lockCanvas() is called again. + * + * @param canvas The Canvas previously returned by lockCanvas() + * + * @see #lockCanvas() + * @see #lockCanvas(android.graphics.Rect) + */ + public void unlockCanvasAndPost(Canvas canvas) { + if (mCanvas != null && canvas == mCanvas) { + canvas.restoreToCount(mSaveCount); + mSaveCount = 0; + + synchronized (mNativeWindowLock) { + nUnlockCanvasAndPost(mNativeWindow, mCanvas); + } + } + } + + /** * Returns the {@link SurfaceTexture} used by this view. This method * may return null if the view is not attached to a window or if the surface * texture has not been initialized yet. @@ -506,6 +593,12 @@ public class TextureView extends View { public void onSurfaceTextureUpdated(SurfaceTexture surface); } + private native void nCreateNativeWindow(SurfaceTexture surface); + private native void nDestroyNativeWindow(); + private static native void nSetDefaultBufferSize(SurfaceTexture surfaceTexture, int width, int height); + + private static native void nLockCanvas(int nativeWindow, Canvas canvas, Rect dirty); + private static native void nUnlockCanvasAndPost(int nativeWindow, Canvas canvas); } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 2a65f0cbeda9..4e9c0b7ef822 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -5043,9 +5043,9 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit } /** Gets the ViewAncestor, or null if not attached. */ - /*package*/ ViewAncestor getViewAncestor() { + /*package*/ ViewRootImpl getViewRootImpl() { View root = getRootView(); - return root != null ? (ViewAncestor)root.getParent() : null; + return root != null ? (ViewRootImpl)root.getParent() : null; } /** @@ -5061,7 +5061,7 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit public final boolean requestFocusFromTouch() { // Leave touch mode if we need to if (isInTouchMode()) { - ViewAncestor viewRoot = getViewAncestor(); + ViewRootImpl viewRoot = getViewRootImpl(); if (viewRoot != null) { viewRoot.ensureTouchMode(false); } @@ -5653,7 +5653,7 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit if (mAttachInfo != null) { return mAttachInfo.mInTouchMode; } else { - return ViewAncestor.isInTouchMode(); + return ViewRootImpl.isInTouchMode(); } } @@ -8254,7 +8254,7 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit handler = attachInfo.mHandler; } else { // Assume that post will succeed later - ViewAncestor.getRunQueue().post(action); + ViewRootImpl.getRunQueue().post(action); return true; } @@ -8284,7 +8284,7 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit handler = attachInfo.mHandler; } else { // Assume that post will succeed later - ViewAncestor.getRunQueue().postDelayed(action, delayMillis); + ViewRootImpl.getRunQueue().postDelayed(action, delayMillis); return true; } @@ -8308,7 +8308,7 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit handler = attachInfo.mHandler; } else { // Assume that post will succeed later - ViewAncestor.getRunQueue().removeCallbacks(action); + ViewRootImpl.getRunQueue().removeCallbacks(action); return true; } @@ -9127,12 +9127,12 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit // Start user padding override Left user padding. Otherwise, if Left user // padding is not defined, use the default left padding. If Left user padding // is defined, just use it. - if (mUserPaddingStart > 0) { + if (mUserPaddingStart >= 0) { mUserPaddingLeft = mUserPaddingStart; } else if (mUserPaddingLeft < 0) { mUserPaddingLeft = mPaddingLeft; } - if (mUserPaddingEnd > 0) { + if (mUserPaddingEnd >= 0) { mUserPaddingRight = mUserPaddingEnd; } else if (mUserPaddingRight < 0) { mUserPaddingRight = mPaddingRight; @@ -11122,6 +11122,10 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit * background */ public void setBackgroundDrawable(Drawable d) { + if (d == mBGDrawable) { + return; + } + boolean requestLayout = false; mBackgroundResource = 0; @@ -11576,9 +11580,9 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit viewParent = view.mParent; } - if (viewParent instanceof ViewAncestor) { + if (viewParent instanceof ViewRootImpl) { // *cough* - final ViewAncestor vr = (ViewAncestor)viewParent; + final ViewRootImpl vr = (ViewRootImpl)viewParent; location[1] -= vr.mCurScrollY; } } @@ -12705,7 +12709,7 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit surface.unlockCanvasAndPost(canvas); } - final ViewAncestor root = getViewAncestor(); + final ViewRootImpl root = getViewRootImpl(); // Cache the local state object for delivery with DragEvents root.setLocalDragState(myLocalState); @@ -13912,7 +13916,7 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit Canvas mCanvas; /** - * A Handler supplied by a view's {@link android.view.ViewAncestor}. This + * A Handler supplied by a view's {@link android.view.ViewRootImpl}. This * handler can be used to pump events in the UI events queue. */ final Handler mHandler; diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java index f7f5a21399dd..b85159b2eaa8 100644 --- a/core/java/android/view/ViewDebug.java +++ b/core/java/android/view/ViewDebug.java @@ -366,7 +366,7 @@ public class ViewDebug { } private static BufferedWriter sHierarchyTraces; - private static ViewAncestor sHierarhcyRoot; + private static ViewRootImpl sHierarhcyRoot; private static String sHierarchyTracePrefix; /** @@ -415,7 +415,7 @@ public class ViewDebug { * @hide */ public static long getViewAncestorInstanceCount() { - return Debug.countInstancesOfClass(ViewAncestor.class); + return Debug.countInstancesOfClass(ViewRootImpl.class); } /** @@ -748,7 +748,7 @@ public class ViewDebug { return; } - sHierarhcyRoot = (ViewAncestor) view.getRootView().getParent(); + sHierarhcyRoot = (ViewRootImpl) view.getRootView().getParent(); } /** @@ -1100,7 +1100,7 @@ public class ViewDebug { private static void outputDisplayList(View root, String parameter) throws IOException { final View view = findView(root, parameter); - view.getViewAncestor().outputDisplayList(view); + view.getViewRootImpl().outputDisplayList(view); } private static void capture(View root, final OutputStream clientStream, String parameter) diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index cb3e9c684cbd..5f7673aacee6 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -954,7 +954,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager final float tx = event.mX; final float ty = event.mY; - ViewAncestor root = getViewAncestor(); + ViewRootImpl root = getViewRootImpl(); // Dispatch down the view hierarchy switch (event.mAction) { @@ -3839,13 +3839,13 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager if (drawAnimation) { if (view != null) { view.mPrivateFlags |= DRAW_ANIMATION; - } else if (parent instanceof ViewAncestor) { - ((ViewAncestor) parent).mIsAnimating = true; + } else if (parent instanceof ViewRootImpl) { + ((ViewRootImpl) parent).mIsAnimating = true; } } - if (parent instanceof ViewAncestor) { - ((ViewAncestor) parent).invalidate(); + if (parent instanceof ViewRootImpl) { + ((ViewRootImpl) parent).invalidate(); parent = null; } else if (view != null) { if ((view.mPrivateFlags & DRAWN) == DRAWN || @@ -3902,8 +3902,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager if (drawAnimation) { if (view != null) { view.mPrivateFlags |= DRAW_ANIMATION; - } else if (parent instanceof ViewAncestor) { - ((ViewAncestor) parent).mIsAnimating = true; + } else if (parent instanceof ViewRootImpl) { + ((ViewRootImpl) parent).mIsAnimating = true; } } @@ -4426,7 +4426,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager // If this group is dirty, check that the parent is dirty as well if ((mPrivateFlags & DIRTY_MASK) != 0) { final ViewParent parent = getParent(); - if (parent != null && !(parent instanceof ViewAncestor)) { + if (parent != null && !(parent instanceof ViewRootImpl)) { if ((((View) parent).mPrivateFlags & DIRTY_MASK) == 0) { result = false; android.util.Log.d(ViewDebug.CONSISTENCY_LOG_TAG, @@ -4995,7 +4995,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * @hide */ public void requestTransitionStart(LayoutTransition transition) { - ViewAncestor viewAncestor = getViewAncestor(); + ViewRootImpl viewAncestor = getViewRootImpl(); viewAncestor.requestTransitionStart(transition); } diff --git a/core/java/android/view/ViewAncestor.java b/core/java/android/view/ViewRootImpl.java index ac73611b950a..470493d221c3 100644 --- a/core/java/android/view/ViewAncestor.java +++ b/core/java/android/view/ViewRootImpl.java @@ -92,7 +92,7 @@ import java.util.List; * {@hide} */ @SuppressWarnings({"EmptyCatchBlock", "PointlessBooleanExpression"}) -public final class ViewAncestor extends Handler implements ViewParent, +public final class ViewRootImpl extends Handler implements ViewParent, View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks { private static final String TAG = "ViewAncestor"; private static final boolean DBG = false; @@ -303,7 +303,7 @@ public final class ViewAncestor extends Handler implements ViewParent, } } - public ViewAncestor(Context context) { + public ViewRootImpl(Context context) { super(); if (MEASURE_LATENCY) { @@ -3807,14 +3807,14 @@ public final class ViewAncestor extends Handler implements ViewParent, } static class InputMethodCallback extends IInputMethodCallback.Stub { - private WeakReference<ViewAncestor> mViewAncestor; + private WeakReference<ViewRootImpl> mViewAncestor; - public InputMethodCallback(ViewAncestor viewAncestor) { - mViewAncestor = new WeakReference<ViewAncestor>(viewAncestor); + public InputMethodCallback(ViewRootImpl viewAncestor) { + mViewAncestor = new WeakReference<ViewRootImpl>(viewAncestor); } public void finishedEvent(int seq, boolean handled) { - final ViewAncestor viewAncestor = mViewAncestor.get(); + final ViewRootImpl viewAncestor = mViewAncestor.get(); if (viewAncestor != null) { viewAncestor.dispatchFinishedEvent(seq, handled); } @@ -3826,15 +3826,15 @@ public final class ViewAncestor extends Handler implements ViewParent, } static class W extends IWindow.Stub { - private final WeakReference<ViewAncestor> mViewAncestor; + private final WeakReference<ViewRootImpl> mViewAncestor; - W(ViewAncestor viewAncestor) { - mViewAncestor = new WeakReference<ViewAncestor>(viewAncestor); + W(ViewRootImpl viewAncestor) { + mViewAncestor = new WeakReference<ViewRootImpl>(viewAncestor); } public void resized(int w, int h, Rect coveredInsets, Rect visibleInsets, boolean reportDraw, Configuration newConfig) { - final ViewAncestor viewAncestor = mViewAncestor.get(); + final ViewRootImpl viewAncestor = mViewAncestor.get(); if (viewAncestor != null) { viewAncestor.dispatchResized(w, h, coveredInsets, visibleInsets, reportDraw, newConfig); @@ -3842,21 +3842,21 @@ public final class ViewAncestor extends Handler implements ViewParent, } public void dispatchAppVisibility(boolean visible) { - final ViewAncestor viewAncestor = mViewAncestor.get(); + final ViewRootImpl viewAncestor = mViewAncestor.get(); if (viewAncestor != null) { viewAncestor.dispatchAppVisibility(visible); } } public void dispatchGetNewSurface() { - final ViewAncestor viewAncestor = mViewAncestor.get(); + final ViewRootImpl viewAncestor = mViewAncestor.get(); if (viewAncestor != null) { viewAncestor.dispatchGetNewSurface(); } } public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) { - final ViewAncestor viewAncestor = mViewAncestor.get(); + final ViewRootImpl viewAncestor = mViewAncestor.get(); if (viewAncestor != null) { viewAncestor.windowFocusChanged(hasFocus, inTouchMode); } @@ -3872,7 +3872,7 @@ public final class ViewAncestor extends Handler implements ViewParent, } public void executeCommand(String command, String parameters, ParcelFileDescriptor out) { - final ViewAncestor viewAncestor = mViewAncestor.get(); + final ViewRootImpl viewAncestor = mViewAncestor.get(); if (viewAncestor != null) { final View view = viewAncestor.mView; if (view != null) { @@ -3903,7 +3903,7 @@ public final class ViewAncestor extends Handler implements ViewParent, } public void closeSystemDialogs(String reason) { - final ViewAncestor viewAncestor = mViewAncestor.get(); + final ViewRootImpl viewAncestor = mViewAncestor.get(); if (viewAncestor != null) { viewAncestor.dispatchCloseSystemDialogs(reason); } @@ -3931,14 +3931,14 @@ public final class ViewAncestor extends Handler implements ViewParent, /* Drag/drop */ public void dispatchDragEvent(DragEvent event) { - final ViewAncestor viewAncestor = mViewAncestor.get(); + final ViewRootImpl viewAncestor = mViewAncestor.get(); if (viewAncestor != null) { viewAncestor.dispatchDragEvent(event); } } public void dispatchSystemUiVisibilityChanged(int visibility) { - final ViewAncestor viewAncestor = mViewAncestor.get(); + final ViewRootImpl viewAncestor = mViewAncestor.get(); if (viewAncestor != null) { viewAncestor.dispatchSystemUiVisibilityChanged(visibility); } @@ -4269,7 +4269,7 @@ public final class ViewAncestor extends Handler implements ViewParent, if (!registered) { mAttachInfo.mAccessibilityWindowId = mAccessibilityManager.addAccessibilityInteractionConnection(mWindow, - new AccessibilityInteractionConnection(ViewAncestor.this)); + new AccessibilityInteractionConnection(ViewRootImpl.this)); } } @@ -4289,10 +4289,10 @@ public final class ViewAncestor extends Handler implements ViewParent, */ final class AccessibilityInteractionConnection extends IAccessibilityInteractionConnection.Stub { - private final WeakReference<ViewAncestor> mViewAncestor; + private final WeakReference<ViewRootImpl> mViewAncestor; - AccessibilityInteractionConnection(ViewAncestor viewAncestor) { - mViewAncestor = new WeakReference<ViewAncestor>(viewAncestor); + AccessibilityInteractionConnection(ViewRootImpl viewAncestor) { + mViewAncestor = new WeakReference<ViewRootImpl>(viewAncestor); } public void findAccessibilityNodeInfoByAccessibilityId(int accessibilityId, @@ -4421,7 +4421,7 @@ public final class ViewAncestor extends Handler implements ViewParent, try { FindByAccessibilitytIdPredicate predicate = mFindByAccessibilityIdPredicate; predicate.init(accessibilityId); - View root = ViewAncestor.this.mView; + View root = ViewRootImpl.this.mView; View target = root.findViewByPredicate(predicate); if (target != null && target.isShown()) { info = target.createAccessibilityNodeInfo(); @@ -4453,7 +4453,7 @@ public final class ViewAncestor extends Handler implements ViewParent, AccessibilityNodeInfo info = null; try { - View root = ViewAncestor.this.mView; + View root = ViewRootImpl.this.mView; View target = root.findViewById(viewId); if (target != null && target.isShown()) { info = target.createAccessibilityNodeInfo(); @@ -4499,7 +4499,7 @@ public final class ViewAncestor extends Handler implements ViewParent, if (accessibilityViewId != View.NO_ID) { root = findViewByAccessibilityId(accessibilityViewId); } else { - root = ViewAncestor.this.mView; + root = ViewRootImpl.this.mView; } if (root == null || !root.isShown()) { @@ -4624,7 +4624,7 @@ public final class ViewAncestor extends Handler implements ViewParent, } private View findViewByAccessibilityId(int accessibilityId) { - View root = ViewAncestor.this.mView; + View root = ViewRootImpl.this.mView; if (root == null) { return null; } diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java index 54e7c04a4636..a451bb52204f 100644 --- a/core/java/android/view/WindowManagerImpl.java +++ b/core/java/android/view/WindowManagerImpl.java @@ -16,18 +16,16 @@ package android.view; -import java.util.HashMap; - import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.graphics.PixelFormat; import android.os.IBinder; import android.util.AndroidRuntimeException; import android.util.Log; -import android.util.Slog; -import android.view.WindowManager; import android.view.inputmethod.InputMethodManager; +import java.util.HashMap; + final class WindowLeaked extends AndroidRuntimeException { public WindowLeaked(String msg) { super(msg); @@ -80,7 +78,7 @@ public class WindowManagerImpl implements WindowManager { public static final int ADD_PERMISSION_DENIED = -8; private View[] mViews; - private ViewAncestor[] mRoots; + private ViewRootImpl[] mRoots; private WindowManager.LayoutParams[] mParams; private final static Object sLock = new Object(); @@ -204,7 +202,7 @@ public class WindowManagerImpl implements WindowManager { final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params; - ViewAncestor root; + ViewRootImpl root; View panelParentView = null; synchronized (this) { @@ -241,7 +239,7 @@ public class WindowManagerImpl implements WindowManager { } } - root = new ViewAncestor(view.getContext()); + root = new ViewRootImpl(view.getContext()); root.mAddNesting = 1; if (cih == null) { root.mCompatibilityInfo = new CompatibilityInfoHolder(); @@ -254,7 +252,7 @@ public class WindowManagerImpl implements WindowManager { if (mViews == null) { index = 1; mViews = new View[1]; - mRoots = new ViewAncestor[1]; + mRoots = new ViewRootImpl[1]; mParams = new WindowManager.LayoutParams[1]; } else { index = mViews.length + 1; @@ -262,7 +260,7 @@ public class WindowManagerImpl implements WindowManager { mViews = new View[index]; System.arraycopy(old, 0, mViews, 0, index-1); old = mRoots; - mRoots = new ViewAncestor[index]; + mRoots = new ViewRootImpl[index]; System.arraycopy(old, 0, mRoots, 0, index-1); old = mParams; mParams = new WindowManager.LayoutParams[index]; @@ -290,7 +288,7 @@ public class WindowManagerImpl implements WindowManager { synchronized (this) { int index = findViewLocked(view, true); - ViewAncestor root = mRoots[index]; + ViewRootImpl root = mRoots[index]; mParams[index] = wparams; root.setLayoutParams(wparams, false); } @@ -312,7 +310,7 @@ public class WindowManagerImpl implements WindowManager { public void removeViewImmediate(View view) { synchronized (this) { int index = findViewLocked(view, true); - ViewAncestor root = mRoots[index]; + ViewRootImpl root = mRoots[index]; View curView = root.getView(); root.mAddNesting = 0; @@ -328,7 +326,7 @@ public class WindowManagerImpl implements WindowManager { } View removeViewLocked(int index) { - ViewAncestor root = mRoots[index]; + ViewRootImpl root = mRoots[index]; View view = root.getView(); // Don't really remove until we have matched all calls to add(). @@ -356,7 +354,7 @@ public class WindowManagerImpl implements WindowManager { removeItem(tmpViews, mViews, index); mViews = tmpViews; - ViewAncestor[] tmpRoots = new ViewAncestor[count-1]; + ViewRootImpl[] tmpRoots = new ViewRootImpl[count-1]; removeItem(tmpRoots, mRoots, index); mRoots = tmpRoots; @@ -383,7 +381,7 @@ public class WindowManagerImpl implements WindowManager { //Log.i("foo", "@ " + i + " token " + mParams[i].token // + " view " + mRoots[i].getView()); if (token == null || mParams[i].token == token) { - ViewAncestor root = mRoots[i]; + ViewRootImpl root = mRoots[i]; root.mAddNesting = 1; //Log.i("foo", "Force closing " + root); @@ -402,7 +400,16 @@ public class WindowManagerImpl implements WindowManager { } } } - + + /** + * @param level See {@link android.content.ComponentCallbacks} + */ + public void trimMemory(int level) { + if (HardwareRenderer.isAvailable()) { + HardwareRenderer.trimMemory(level); + } + } + public void setStoppedState(IBinder token, boolean stopped) { synchronized (this) { if (mViews == null) @@ -410,7 +417,7 @@ public class WindowManagerImpl implements WindowManager { int count = mViews.length; for (int i=0; i<count; i++) { if (token == null || mParams[i].token == token) { - ViewAncestor root = mRoots[i]; + ViewRootImpl root = mRoots[i]; root.setStopped(stopped); } } @@ -422,7 +429,7 @@ public class WindowManagerImpl implements WindowManager { int count = mViews.length; config = new Configuration(config); for (int i=0; i<count; i++) { - ViewAncestor root = mRoots[i]; + ViewRootImpl root = mRoots[i]; root.requestUpdateConfiguration(config); } } @@ -430,13 +437,13 @@ public class WindowManagerImpl implements WindowManager { public WindowManager.LayoutParams getRootViewLayoutParameter(View view) { ViewParent vp = view.getParent(); - while (vp != null && !(vp instanceof ViewAncestor)) { + while (vp != null && !(vp instanceof ViewRootImpl)) { vp = vp.getParent(); } if (vp == null) return null; - ViewAncestor vr = (ViewAncestor)vp; + ViewRootImpl vr = (ViewRootImpl)vp; int N = mRoots.length; for (int i = 0; i < N; ++i) { @@ -456,8 +463,7 @@ public class WindowManagerImpl implements WindowManager { return new Display(Display.DEFAULT_DISPLAY, null); } - private static void removeItem(Object[] dst, Object[] src, int index) - { + private static void removeItem(Object[] dst, Object[] src, int index) { if (dst.length > 0) { if (index > 0) { System.arraycopy(src, 0, dst, 0, index); @@ -468,8 +474,7 @@ public class WindowManagerImpl implements WindowManager { } } - private int findViewLocked(View view, boolean required) - { + private int findViewLocked(View view, boolean required) { synchronized (this) { final int count = mViews != null ? mViews.length : 0; for (int i=0; i<count; i++) { diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java index ac867690724b..9be2a67be334 100644 --- a/core/java/android/view/accessibility/AccessibilityEvent.java +++ b/core/java/android/view/accessibility/AccessibilityEvent.java @@ -552,7 +552,8 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par /** * Returns a cached instance if such is available or a new one is - * initialized with from the given <code>event</code>. + * created. The returned instance is initialized from the given + * <code>event</code>. * * @param event The other event. * @return An instance. diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java index 314b7ca35821..83c73cb85cc8 100644 --- a/core/java/android/view/accessibility/AccessibilityManager.java +++ b/core/java/android/view/accessibility/AccessibilityManager.java @@ -71,7 +71,9 @@ public final class AccessibilityManager { private static AccessibilityManager sInstance; - private static final int DO_SET_ENABLED = 10; + private static final int DO_SET_ACCESSIBILITY_ENABLED = 10; + + private static final int DO_SET_TOUCH_EXPLORATION_ENABLED = 20; final IAccessibilityManager mService; @@ -79,6 +81,8 @@ public final class AccessibilityManager { boolean mIsEnabled; + boolean mIsTouchExplorationEnabled; + final CopyOnWriteArrayList<AccessibilityStateChangeListener> mAccessibilityStateChangeListeners = new CopyOnWriteArrayList<AccessibilityStateChangeListener>(); @@ -97,7 +101,12 @@ public final class AccessibilityManager { final IAccessibilityManagerClient.Stub mClient = new IAccessibilityManagerClient.Stub() { public void setEnabled(boolean enabled) { - mHandler.obtainMessage(DO_SET_ENABLED, enabled ? 1 : 0, 0).sendToTarget(); + mHandler.obtainMessage(DO_SET_ACCESSIBILITY_ENABLED, enabled ? 1 : 0, 0).sendToTarget(); + } + + public void setTouchExplorationEnabled(boolean enabled) { + mHandler.obtainMessage(DO_SET_TOUCH_EXPLORATION_ENABLED, + enabled ? 1 : 0, 0).sendToTarget(); } }; @@ -110,9 +119,14 @@ public final class AccessibilityManager { @Override public void handleMessage(Message message) { switch (message.what) { - case DO_SET_ENABLED : - final boolean isEnabled = (message.arg1 == 1); - setAccessibilityState(isEnabled); + case DO_SET_ACCESSIBILITY_ENABLED : + final boolean isAccessibilityEnabled = (message.arg1 == 1); + setAccessibilityState(isAccessibilityEnabled); + return; + case DO_SET_TOUCH_EXPLORATION_ENABLED : + synchronized (mHandler) { + mIsTouchExplorationEnabled = (message.arg1 == 1); + } return; default : Log.w(LOG_TAG, "Unknown message type: " + message.what); @@ -168,6 +182,17 @@ public final class AccessibilityManager { } /** + * Returns if the touch exploration in the system is enabled. + * + * @return True if touch exploration is enabled, false otherwise. + */ + public boolean isTouchExplorationEnabled() { + synchronized (mHandler) { + return mIsTouchExplorationEnabled; + } + } + + /** * Returns the client interface this instance registers in * the centralized accessibility manager service. * diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java index 031c6aeffd21..0e044712c0b2 100644 --- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java +++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java @@ -120,7 +120,7 @@ public class AccessibilityNodeInfo implements Parcelable { private CharSequence mText; private CharSequence mContentDescription; - private final SparseIntArray mChildAccessibilityIds = new SparseIntArray(); + private SparseIntArray mChildAccessibilityIds = new SparseIntArray(); private int mActions; private IAccessibilityServiceConnection mConnection; @@ -873,6 +873,20 @@ public class AccessibilityNodeInfo implements Parcelable { } /** + * Returns a cached instance if such is available or a new one is + * create. The returned instance is initialized from the given + * <code>info</code>. + * + * @param info The other info. + * @return An instance. + */ + public static AccessibilityNodeInfo obtain(AccessibilityNodeInfo info) { + AccessibilityNodeInfo infoClone = AccessibilityNodeInfo.obtain(); + infoClone.init(info); + return infoClone; + } + + /** * Return an instance back to be reused. * <p> * <strong>Note:</strong> You must not touch the object after calling this function. @@ -945,6 +959,28 @@ public class AccessibilityNodeInfo implements Parcelable { } /** + * Initializes this instance from another one. + * + * @param other The other instance. + */ + private void init(AccessibilityNodeInfo other) { + mSealed = other.mSealed; + mConnection = other.mConnection; + mAccessibilityViewId = other.mAccessibilityViewId; + mParentAccessibilityViewId = other.mParentAccessibilityViewId; + mAccessibilityWindowId = other.mAccessibilityWindowId; + mBoundsInParent.set(other.mBoundsInParent); + mBoundsInScreen.set(other.mBoundsInScreen); + mPackageName = other.mPackageName; + mClassName = other.mClassName; + mText = other.mText; + mContentDescription = other.mContentDescription; + mActions= other.mActions; + mBooleanProperties = other.mBooleanProperties; + mChildAccessibilityIds = other.mChildAccessibilityIds.clone(); + } + + /** * Creates a new instance from a {@link Parcel}. * * @param parcel A parcel containing the state of a {@link AccessibilityNodeInfo}. @@ -994,6 +1030,7 @@ public class AccessibilityNodeInfo implements Parcelable { mConnection = null; mAccessibilityViewId = View.NO_ID; mParentAccessibilityViewId = View.NO_ID; + mAccessibilityWindowId = View.NO_ID; mChildAccessibilityIds.clear(); mBoundsInParent.set(0, 0, 0, 0); mBoundsInScreen.set(0, 0, 0, 0); diff --git a/core/java/android/view/accessibility/IAccessibilityManagerClient.aidl b/core/java/android/view/accessibility/IAccessibilityManagerClient.aidl index 1eb60fc61b48..4e69692494c5 100644 --- a/core/java/android/view/accessibility/IAccessibilityManagerClient.aidl +++ b/core/java/android/view/accessibility/IAccessibilityManagerClient.aidl @@ -26,4 +26,5 @@ oneway interface IAccessibilityManagerClient { void setEnabled(boolean enabled); + void setTouchExplorationEnabled(boolean enabled); } diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java index abe3c2ccd490..5ec1ec3e03f3 100644 --- a/core/java/android/view/inputmethod/BaseInputConnection.java +++ b/core/java/android/view/inputmethod/BaseInputConnection.java @@ -34,7 +34,7 @@ import android.util.LogPrinter; import android.view.KeyCharacterMap; import android.view.KeyEvent; import android.view.View; -import android.view.ViewAncestor; +import android.view.ViewRootImpl; class ComposingText implements NoCopySpan { } @@ -502,7 +502,7 @@ public class BaseInputConnection implements InputConnection { } } if (h != null) { - h.sendMessage(h.obtainMessage(ViewAncestor.DISPATCH_KEY_FROM_IME, + h.sendMessage(h.obtainMessage(ViewRootImpl.DISPATCH_KEY_FROM_IME, event)); } } diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index d07f4fda63da..da5baf86a97c 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -43,7 +43,7 @@ import android.util.Printer; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; -import android.view.ViewAncestor; +import android.view.ViewRootImpl; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -656,7 +656,7 @@ public final class InputMethodManager { if (vh != null) { // This will result in a call to reportFinishInputConnection() // below. - vh.sendMessage(vh.obtainMessage(ViewAncestor.FINISH_INPUT_CONNECTION, + vh.sendMessage(vh.obtainMessage(ViewRootImpl.FINISH_INPUT_CONNECTION, mServedInputConnection)); } } @@ -1113,9 +1113,9 @@ public final class InputMethodManager { void scheduleCheckFocusLocked(View view) { Handler vh = view.getHandler(); - if (vh != null && !vh.hasMessages(ViewAncestor.CHECK_FOCUS)) { + if (vh != null && !vh.hasMessages(ViewRootImpl.CHECK_FOCUS)) { // This will result in a call to checkFocus() below. - vh.sendMessage(vh.obtainMessage(ViewAncestor.CHECK_FOCUS)); + vh.sendMessage(vh.obtainMessage(ViewRootImpl.CHECK_FOCUS)); } } diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java index 5aa60f4f7b39..738bcb921ca4 100644 --- a/core/java/android/webkit/BrowserFrame.java +++ b/core/java/android/webkit/BrowserFrame.java @@ -35,7 +35,7 @@ import android.os.Message; import android.util.Log; import android.util.TypedValue; import android.view.Surface; -import android.view.ViewAncestor; +import android.view.ViewRootImpl; import android.view.WindowManager; import junit.framework.Assert; @@ -228,7 +228,7 @@ class BrowserFrame extends Handler { sConfigCallback = new ConfigCallback( (WindowManager) appContext.getSystemService( Context.WINDOW_SERVICE)); - ViewAncestor.addConfigCallback(sConfigCallback); + ViewRootImpl.addConfigCallback(sConfigCallback); } sConfigCallback.addHandler(this); diff --git a/core/java/android/widget/ActivityChooserModel.java b/core/java/android/widget/ActivityChooserModel.java index 32c44d86aec9..d7429b38d2ad 100644 --- a/core/java/android/widget/ActivityChooserModel.java +++ b/core/java/android/widget/ActivityChooserModel.java @@ -431,7 +431,6 @@ public class ActivityChooserModel extends DataSetObservable { */ public Intent chooseActivity(int index) { ActivityResolveInfo chosenActivity = mActivites.get(index); - ActivityResolveInfo defaultActivity = mActivites.get(0); ComponentName chosenName = new ComponentName( chosenActivity.resolveInfo.activityInfo.packageName, diff --git a/core/java/android/widget/ActivityChooserView.java b/core/java/android/widget/ActivityChooserView.java index f500b3921384..5b69aa8fb1e9 100644 --- a/core/java/android/widget/ActivityChooserView.java +++ b/core/java/android/widget/ActivityChooserView.java @@ -20,6 +20,7 @@ import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; +import android.content.res.Resources; import android.content.res.TypedArray; import android.database.DataSetObserver; import android.graphics.Canvas; @@ -92,6 +93,11 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod private final ImageButton mDefaultActionButton; /** + * The maximal width of the list popup. + */ + private final int mListPopupMaxWidth; + + /** * Observer for the model data. */ private final DataSetObserver mModelDataSetOberver = new DataSetObserver() { @@ -185,7 +191,7 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod mExpandActivityOverflowButton = (ImageButton) findViewById(R.id.expand_activities_button); mExpandActivityOverflowButton.setOnClickListener(mCallbacks); - mExpandActivityOverflowButton.setBackgroundDrawable(expandActivityOverflowButtonDrawable); + mExpandActivityOverflowButton.setImageDrawable(expandActivityOverflowButtonDrawable); mAdapter = new ActivityChooserViewAdapter(); mAdapter.registerDataSetObserver(new DataSetObserver() { @@ -195,6 +201,10 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod updateButtons(); } }); + + Resources resources = context.getResources(); + mListPopupMaxWidth = Math.max(resources.getDisplayMetrics().widthPixels / 2, + resources.getDimensionPixelSize(com.android.internal.R.dimen.config_prefDialogWidth)); } /** @@ -220,7 +230,7 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod * @param drawable The drawable. */ public void setExpandActivityOverflowButtonDrawable(Drawable drawable) { - mExpandActivityOverflowButton.setBackgroundDrawable(drawable); + mExpandActivityOverflowButton.setImageDrawable(drawable); } /** @@ -264,7 +274,8 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod } else { mAdapter.setShowDefaultActivity(false); } - popupWindow.setContentWidth(mAdapter.measureContentWidth()); + final int contentWidth = Math.min(mAdapter.measureContentWidth(), mListPopupMaxWidth); + popupWindow.setContentWidth(contentWidth); popupWindow.show(); } } @@ -390,7 +401,7 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod } ResolveInfo activity = mAdapter.getDefaultActivity(); PackageManager packageManager = mContext.getPackageManager(); - mDefaultActionButton.setBackgroundDrawable(activity.loadIcon(packageManager)); + mDefaultActionButton.setImageDrawable(activity.loadIcon(packageManager)); } else { mDefaultActionButton.setVisibility(View.INVISIBLE); mExpandActivityOverflowButton.setEnabled(false); @@ -574,7 +585,7 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod // Set the icon ImageView iconView = (ImageView) convertView.findViewById(R.id.icon); ResolveInfo activity = (ResolveInfo) getItem(position); - iconView.setBackgroundDrawable(activity.loadIcon(packageManager)); + iconView.setImageDrawable(activity.loadIcon(packageManager)); // Set the title. TextView titleView = (TextView) convertView.findViewById(R.id.title); titleView.setText(activity.loadLabel(packageManager)); diff --git a/core/java/android/widget/GridLayout.java b/core/java/android/widget/GridLayout.java index b9eb5ff01852..f82c61a2bdb2 100644 --- a/core/java/android/widget/GridLayout.java +++ b/core/java/android/widget/GridLayout.java @@ -28,7 +28,7 @@ import android.util.Pair; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; -import com.android.internal.R.styleable; +import com.android.internal.R; import java.lang.reflect.Array; import java.util.ArrayList; @@ -167,7 +167,7 @@ public class GridLayout extends ViewGroup { // Misc constants private static final String TAG = GridLayout.class.getName(); - static final boolean DEBUG = false; + static boolean DEBUG = false; private static final int PRF = 1; // Defaults @@ -178,19 +178,17 @@ public class GridLayout extends ViewGroup { private static final boolean DEFAULT_ORDER_PRESERVED = false; private static final int DEFAULT_ALIGNMENT_MODE = ALIGN_MARGINS; private static final int DEFAULT_CONTAINER_MARGIN = 0; - private static final int DEFAULT_MARGIN = 8; - private static final int DEFAULT_CONTAINER_PADDING = 16; private static final int MAX_SIZE = 100000; // TypedArray indices - private static final int ORIENTATION = styleable.GridLayout_orientation; - private static final int ROW_COUNT = styleable.GridLayout_rowCount; - private static final int COLUMN_COUNT = styleable.GridLayout_columnCount; - private static final int USE_DEFAULT_MARGINS = styleable.GridLayout_useDefaultMargins; - private static final int ALIGNMENT_MODE = styleable.GridLayout_alignmentMode; - private static final int ROW_ORDER_PRESERVED = styleable.GridLayout_rowOrderPreserved; - private static final int COLUMN_ORDER_PRESERVED = styleable.GridLayout_columnOrderPreserved; + private static final int ORIENTATION = R.styleable.GridLayout_orientation; + private static final int ROW_COUNT = R.styleable.GridLayout_rowCount; + private static final int COLUMN_COUNT = R.styleable.GridLayout_columnCount; + private static final int USE_DEFAULT_MARGINS = R.styleable.GridLayout_useDefaultMargins; + private static final int ALIGNMENT_MODE = R.styleable.GridLayout_alignmentMode; + private static final int ROW_ORDER_PRESERVED = R.styleable.GridLayout_rowOrderPreserved; + private static final int COLUMN_ORDER_PRESERVED = R.styleable.GridLayout_columnOrderPreserved; // Instance variables @@ -201,6 +199,7 @@ public class GridLayout extends ViewGroup { private boolean mUseDefaultMargins = DEFAULT_USE_DEFAULT_MARGINS; private int mAlignmentMode = DEFAULT_ALIGNMENT_MODE; private int mDefaultGravity = Gravity.NO_GRAVITY; + private int mDefaultGap; // Constructors @@ -212,7 +211,8 @@ public class GridLayout extends ViewGroup { if (DEBUG) { setWillNotDraw(false); } - TypedArray a = context.obtainStyledAttributes(attrs, styleable.GridLayout); + mDefaultGap = context.getResources().getDimensionPixelOffset(R.dimen.default_gap); + TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.GridLayout); try { setRowCount(a.getInt(ROW_COUNT, DEFAULT_COUNT)); setColumnCount(a.getInt(COLUMN_COUNT, DEFAULT_COUNT)); @@ -382,7 +382,7 @@ public class GridLayout extends ViewGroup { public void setUseDefaultMargins(boolean useDefaultMargins) { mUseDefaultMargins = useDefaultMargins; if (useDefaultMargins) { - int padding = DEFAULT_CONTAINER_PADDING; + int padding = mDefaultGap; setPadding(padding, padding, padding, padding); } requestLayout(); @@ -538,7 +538,7 @@ public class GridLayout extends ViewGroup { } private int getDefaultMargin(View c, boolean horizontal, boolean leading) { - return DEFAULT_MARGIN; + return mDefaultGap / 2; } private int getDefaultMargin(View c, boolean isAtEdge, boolean horizontal, boolean leading) { @@ -787,6 +787,12 @@ public class GridLayout extends ViewGroup { invalidateStructure(); } + @Override + public void removeAllViews() { + super.removeAllViews(); + invalidateStructure(); + } + // Measurement private boolean isGone(View c) { @@ -1596,8 +1602,8 @@ public class GridLayout extends ViewGroup { * each cell group. The fundamental parameters associated with each cell group are * gathered into their vertical and horizontal components and stored * in the {@link #rowSpec} and {@link #columnSpec} layout parameters. - * {@link android.widget.GridLayout.Spec Specs} are immutable structures and may be shared between the layout - * parameters of different children. + * {@link android.widget.GridLayout.Spec Specs} are immutable structures + * and may be shared between the layout parameters of different children. * <p> * The row and column specs contain the leading and trailing indices along each axis * and together specify the four grid indices that delimit the cells of this cell group. @@ -1667,24 +1673,25 @@ public class GridLayout extends ViewGroup { // TypedArray indices - private static final int MARGIN = styleable.ViewGroup_MarginLayout_layout_margin; - private static final int LEFT_MARGIN = styleable.ViewGroup_MarginLayout_layout_marginLeft; - private static final int TOP_MARGIN = styleable.ViewGroup_MarginLayout_layout_marginTop; - private static final int RIGHT_MARGIN = styleable.ViewGroup_MarginLayout_layout_marginRight; + private static final int MARGIN = R.styleable.ViewGroup_MarginLayout_layout_margin; + private static final int LEFT_MARGIN = R.styleable.ViewGroup_MarginLayout_layout_marginLeft; + private static final int TOP_MARGIN = R.styleable.ViewGroup_MarginLayout_layout_marginTop; + private static final int RIGHT_MARGIN = + R.styleable.ViewGroup_MarginLayout_layout_marginRight; private static final int BOTTOM_MARGIN = - styleable.ViewGroup_MarginLayout_layout_marginBottom; + R.styleable.ViewGroup_MarginLayout_layout_marginBottom; - private static final int COLUMN = styleable.GridLayout_Layout_layout_column; - private static final int COLUMN_SPAN = styleable.GridLayout_Layout_layout_columnSpan; + private static final int COLUMN = R.styleable.GridLayout_Layout_layout_column; + private static final int COLUMN_SPAN = R.styleable.GridLayout_Layout_layout_columnSpan; private static final int COLUMN_FLEXIBILITY = - styleable.GridLayout_Layout_layout_columnFlexibility; + R.styleable.GridLayout_Layout_layout_columnFlexibility; - private static final int ROW = styleable.GridLayout_Layout_layout_row; - private static final int ROW_SPAN = styleable.GridLayout_Layout_layout_rowSpan; + private static final int ROW = R.styleable.GridLayout_Layout_layout_row; + private static final int ROW_SPAN = R.styleable.GridLayout_Layout_layout_rowSpan; private static final int ROW_FLEXIBILITY = - styleable.GridLayout_Layout_layout_rowFlexibility; + R.styleable.GridLayout_Layout_layout_rowFlexibility; - private static final int GRAVITY = styleable.GridLayout_Layout_layout_gravity; + private static final int GRAVITY = R.styleable.GridLayout_Layout_layout_gravity; // Instance variables @@ -1804,7 +1811,8 @@ public class GridLayout extends ViewGroup { // This method could be parametrized and moved into MarginLayout. private void reInitSuper(Context context, AttributeSet attrs) { - TypedArray a = context.obtainStyledAttributes(attrs, styleable.ViewGroup_MarginLayout); + TypedArray a = + context.obtainStyledAttributes(attrs, R.styleable.ViewGroup_MarginLayout); try { int margin = a.getDimensionPixelSize(MARGIN, DEFAULT_MARGIN); @@ -1840,7 +1848,7 @@ public class GridLayout extends ViewGroup { } private void init(Context context, AttributeSet attrs, int defaultGravity) { - TypedArray a = context.obtainStyledAttributes(attrs, styleable.GridLayout_Layout); + TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.GridLayout_Layout); try { int gravity = a.getInt(GRAVITY, defaultGravity); @@ -2301,10 +2309,10 @@ public class GridLayout extends ViewGroup { */ @Deprecated public static class Group extends Spec { - /** - * @deprecated Please replace with {@link #spec(int, int, Alignment)} - * @hide - */ + /** + * @deprecated Please replace with {@link #spec(int, int, Alignment)} + * @hide + */ @Deprecated public Group(int start, int size, Alignment alignment) { super(start, size, alignment, UNDEFINED_FLEXIBILITY); diff --git a/core/java/android/widget/SearchView.java b/core/java/android/widget/SearchView.java index f3bda439fbe8..b2d1a1e53573 100644 --- a/core/java/android/widget/SearchView.java +++ b/core/java/android/widget/SearchView.java @@ -18,8 +18,6 @@ package android.widget; import static android.widget.SuggestionsAdapter.getColumnString; -import com.android.internal.R; - import android.app.PendingIntent; import android.app.SearchManager; import android.app.SearchableInfo; @@ -39,10 +37,14 @@ import android.net.Uri; import android.os.Bundle; import android.speech.RecognizerIntent; import android.text.Editable; +import android.text.Spannable; +import android.text.SpannableStringBuilder; import android.text.TextUtils; import android.text.TextWatcher; +import android.text.style.ImageSpan; import android.util.AttributeSet; import android.util.Log; +import android.util.TypedValue; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; @@ -51,6 +53,8 @@ import android.widget.AdapterView.OnItemClickListener; import android.widget.AdapterView.OnItemSelectedListener; import android.widget.TextView.OnEditorActionListener; +import com.android.internal.R; + import java.util.WeakHashMap; /** @@ -87,6 +91,8 @@ public class SearchView extends LinearLayout { private View mSearchEditFrame; private View mVoiceButton; private SearchAutoComplete mQueryTextView; + private View mDropDownAnchor; + private ImageView mSearchHintIcon; private boolean mSubmitButtonEnabled; private CharSequence mQueryHint; private boolean mQueryRefinement; @@ -195,6 +201,7 @@ public class SearchView extends LinearLayout { mSubmitButton = findViewById(R.id.search_go_btn); mCloseButton = (ImageView) findViewById(R.id.search_close_btn); mVoiceButton = findViewById(R.id.search_voice_btn); + mSearchHintIcon = (ImageView) findViewById(R.id.search_mag_icon); mSearchButton.setOnClickListener(mOnClickListener); mCloseButton.setOnClickListener(mOnClickListener); @@ -244,7 +251,20 @@ public class SearchView extends LinearLayout { mVoiceAppSearchIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH); mVoiceAppSearchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + mDropDownAnchor = findViewById(mQueryTextView.getDropDownAnchor()); + if (mDropDownAnchor != null) { + mDropDownAnchor.addOnLayoutChangeListener(new OnLayoutChangeListener() { + @Override + public void onLayoutChange(View v, int left, int top, int right, int bottom, + int oldLeft, int oldTop, int oldRight, int oldBottom) { + adjustDropDownSizeAndPosition(); + } + + }); + } + updateViewsVisibility(mIconifiedByDefault); + updateQueryHint(); } /** @@ -263,7 +283,7 @@ public class SearchView extends LinearLayout { } // Cache the voice search capability mVoiceButtonEnabled = hasVoiceSearch(); - updateViewsVisibility(mIconifiedByDefault); + updateViewsVisibility(isIconified()); } /** @@ -300,7 +320,6 @@ public class SearchView extends LinearLayout { mQueryTextView.clearFocus(); setImeVisibility(false); mClearingFocus = false; - updateViewsVisibility(mIconifiedByDefault); } /** @@ -555,6 +574,7 @@ public class SearchView extends LinearLayout { mSearchButton.setVisibility(visCollapsed); updateSubmitButton(hasText); mSearchEditFrame.setVisibility(collapsed ? GONE : VISIBLE); + mSearchHintIcon.setVisibility(mIconifiedByDefault ? GONE : VISIBLE); updateCloseButton(); updateVoiceButton(!hasText); updateSubmitArea(); @@ -822,9 +842,29 @@ public class SearchView extends LinearLayout { return result; } + private int getSearchIconId() { + TypedValue outValue = new TypedValue(); + getContext().getTheme().resolveAttribute(com.android.internal.R.attr.searchViewSearchIcon, + outValue, true); + return outValue.resourceId; + } + + private CharSequence getDecoratedHint(CharSequence hintText) { + // If the field is always expanded, then don't add the search icon to the hint + if (!mIconifiedByDefault) return hintText; + + SpannableStringBuilder ssb = new SpannableStringBuilder(" "); // for the icon + ssb.append(hintText); + Drawable searchIcon = getContext().getResources().getDrawable(getSearchIconId()); + int textSize = (int) (mQueryTextView.getTextSize() * 1.25); + searchIcon.setBounds(0, 0, textSize, textSize); + ssb.setSpan(new ImageSpan(searchIcon), 1, 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + return ssb; + } + private void updateQueryHint() { if (mQueryHint != null) { - mQueryTextView.setHint(mQueryHint); + mQueryTextView.setHint(getDecoratedHint(mQueryHint)); } else if (mSearchable != null) { CharSequence hint = null; int hintId = mSearchable.getHintId(); @@ -832,8 +872,10 @@ public class SearchView extends LinearLayout { hint = getContext().getString(hintId); } if (hint != null) { - mQueryTextView.setHint(hint); + mQueryTextView.setHint(getDecoratedHint(hint)); } + } else { + mQueryTextView.setHint(getDecoratedHint("")); } } @@ -922,9 +964,13 @@ public class SearchView extends LinearLayout { CharSequence text = mQueryTextView.getText(); if (TextUtils.isEmpty(text)) { if (mIconifiedByDefault) { - // query field already empty, hide the keyboard and remove focus - clearFocus(); - setImeVisibility(false); + // If the app doesn't override the close behavior + if (mOnCloseListener == null || !mOnCloseListener.onClose()) { + // hide the keyboard and remove focus + clearFocus(); + // collapse the search field + updateViewsVisibility(true); + } } } else { mQueryTextView.setText(""); @@ -932,10 +978,6 @@ public class SearchView extends LinearLayout { setImeVisibility(true); } - if (mIconifiedByDefault && (mOnCloseListener == null || !mOnCloseListener.onClose())) { - updateViewsVisibility(mIconifiedByDefault); - setImeVisibility(false); - } } private void onSearchClicked() { @@ -975,6 +1017,28 @@ public class SearchView extends LinearLayout { updateFocusedState(mQueryTextView.hasFocus()); } + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + } + + private void adjustDropDownSizeAndPosition() { + if (mDropDownAnchor.getWidth() > 1) { + Resources res = getContext().getResources(); + int anchorPadding = mSearchPlate.getPaddingLeft(); + Rect dropDownPadding = new Rect(); + int iconOffset = mIconifiedByDefault + ? res.getDimensionPixelSize(R.dimen.dropdownitem_icon_width) + + res.getDimensionPixelSize(R.dimen.dropdownitem_text_padding_left) + : 0; + mQueryTextView.getDropDownBackground().getPadding(dropDownPadding); + mQueryTextView.setDropDownHorizontalOffset(-(dropDownPadding.left + iconOffset) + + anchorPadding); + mQueryTextView.setDropDownWidth(mDropDownAnchor.getWidth() + dropDownPadding.left + + dropDownPadding.right + iconOffset - (anchorPadding)); + } + } + private boolean onItemClicked(int position, int actionKey, String actionMsg) { if (mOnSuggestionListener == null || !mOnSuggestionListener.onSuggestionClick(position)) { @@ -1393,5 +1457,32 @@ public class SearchView extends LinearLayout { public boolean enoughToFilter() { return mThreshold <= 0 || super.enoughToFilter(); } + + @Override + public boolean onKeyPreIme(int keyCode, KeyEvent event) { + if (keyCode == KeyEvent.KEYCODE_BACK) { + // special case for the back key, we do not even try to send it + // to the drop down list but instead, consume it immediately + if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) { + KeyEvent.DispatcherState state = getKeyDispatcherState(); + if (state != null) { + state.startTracking(event, this); + } + return true; + } else if (event.getAction() == KeyEvent.ACTION_UP) { + KeyEvent.DispatcherState state = getKeyDispatcherState(); + if (state != null) { + state.handleUpEvent(event); + } + if (event.isTracking() && !event.isCanceled()) { + mSearchView.clearFocus(); + mSearchView.setImeVisibility(false); + return true; + } + } + } + return super.onKeyPreIme(keyCode, event); + } + } } diff --git a/core/java/android/widget/ShareActionProvider.java b/core/java/android/widget/ShareActionProvider.java index 2e0cc62113d9..665109a0b2e0 100644 --- a/core/java/android/widget/ShareActionProvider.java +++ b/core/java/android/widget/ShareActionProvider.java @@ -162,14 +162,17 @@ public class ShareActionProvider extends ActionProvider { .setOnMenuItemClickListener(mOnMenuItemClickListener); } - // Add a sub-menu for showing all activities as a list item. - SubMenu expandedSubMenu = subMenu.addSubMenu(Menu.NONE, collapsedActivityCount, - collapsedActivityCount, mContext.getString(R.string.activity_chooser_view_see_all)); - for (int i = 0; i < expandedActivityCount; i++) { - ResolveInfo activity = dataModel.getActivity(i); - expandedSubMenu.add(0, i, i, activity.loadLabel(packageManager)) - .setIcon(activity.loadIcon(packageManager)) - .setOnMenuItemClickListener(mOnMenuItemClickListener); + if (collapsedActivityCount < expandedActivityCount) { + // Add a sub-menu for showing all activities as a list item. + SubMenu expandedSubMenu = subMenu.addSubMenu(Menu.NONE, collapsedActivityCount, + collapsedActivityCount, + mContext.getString(R.string.activity_chooser_view_see_all)); + for (int i = 0; i < expandedActivityCount; i++) { + ResolveInfo activity = dataModel.getActivity(i); + expandedSubMenu.add(0, i, i, activity.loadLabel(packageManager)) + .setIcon(activity.loadIcon(packageManager)) + .setOnMenuItemClickListener(mOnMenuItemClickListener); + } } } diff --git a/core/java/android/widget/SuggestionsAdapter.java b/core/java/android/widget/SuggestionsAdapter.java index 2cfc0169a46e..9e32c9ab14bc 100644 --- a/core/java/android/widget/SuggestionsAdapter.java +++ b/core/java/android/widget/SuggestionsAdapter.java @@ -16,8 +16,6 @@ package android.widget; -import com.android.internal.R; - import android.app.SearchDialog; import android.app.SearchManager; import android.app.SearchableInfo; @@ -47,6 +45,8 @@ import android.view.View; import android.view.ViewGroup; import android.view.View.OnClickListener; +import com.android.internal.R; + import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; @@ -88,8 +88,8 @@ class SuggestionsAdapter extends ResourceCursorAdapter implements OnClickListene private int mIconName2Col = INVALID_INDEX; private int mFlagsCol = INVALID_INDEX; - private final Runnable mStartSpinnerRunnable; - private final Runnable mStopSpinnerRunnable; + // private final Runnable mStartSpinnerRunnable; + // private final Runnable mStopSpinnerRunnable; /** * The amount of time we delay in the filter when the user presses the delete key. @@ -113,17 +113,18 @@ class SuggestionsAdapter extends ResourceCursorAdapter implements OnClickListene mOutsideDrawablesCache = outsideDrawablesCache; - mStartSpinnerRunnable = new Runnable() { - public void run() { - // mSearchView.setWorking(true); // TODO: - } - }; - mStopSpinnerRunnable = new Runnable() { - public void run() { - // mSearchView.setWorking(false); // TODO: - } - }; + // mStartSpinnerRunnable = new Runnable() { + // public void run() { + // // mSearchView.setWorking(true); // TODO: + // } + // }; + // + // mStopSpinnerRunnable = new Runnable() { + // public void run() { + // // mSearchView.setWorking(false); // TODO: + // } + // }; // delay 500ms when deleting getFilter().setDelayer(new Filter.Delayer() { @@ -341,10 +342,10 @@ class SuggestionsAdapter extends ResourceCursorAdapter implements OnClickListene } if (views.mIcon1 != null) { - setViewDrawable(views.mIcon1, getIcon1(cursor)); + setViewDrawable(views.mIcon1, getIcon1(cursor), View.INVISIBLE); } if (views.mIcon2 != null) { - setViewDrawable(views.mIcon2, getIcon2(cursor)); + setViewDrawable(views.mIcon2, getIcon2(cursor), View.GONE); } if (mQueryRefinement == REFINE_ALL || (mQueryRefinement == REFINE_BY_ENTRY @@ -414,13 +415,13 @@ class SuggestionsAdapter extends ResourceCursorAdapter implements OnClickListene * Sets the drawable in an image view, makes sure the view is only visible if there * is a drawable. */ - private void setViewDrawable(ImageView v, Drawable drawable) { + private void setViewDrawable(ImageView v, Drawable drawable, int nullVisibility) { // Set the icon even if the drawable is null, since we need to clear any // previous icon. v.setImageDrawable(drawable); if (drawable == null) { - v.setVisibility(View.GONE); + v.setVisibility(nullVisibility); } else { v.setVisibility(View.VISIBLE); diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 769f5e359275..66a07d31477c 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -117,7 +117,7 @@ import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; -import android.view.ViewAncestor; +import android.view.ViewRootImpl; import android.view.ViewConfiguration; import android.view.ViewDebug; import android.view.ViewGroup; @@ -3731,13 +3731,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener Handler h = getHandler(); if (h != null) { long eventTime = SystemClock.uptimeMillis(); - h.sendMessage(h.obtainMessage(ViewAncestor.DISPATCH_KEY_FROM_IME, + h.sendMessage(h.obtainMessage(ViewRootImpl.DISPATCH_KEY_FROM_IME, new KeyEvent(eventTime, eventTime, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE | KeyEvent.FLAG_EDITOR_ACTION))); - h.sendMessage(h.obtainMessage(ViewAncestor.DISPATCH_KEY_FROM_IME, + h.sendMessage(h.obtainMessage(ViewRootImpl.DISPATCH_KEY_FROM_IME, new KeyEvent(SystemClock.uptimeMillis(), eventTime, KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, diff --git a/core/java/android/widget/ZoomButtonsController.java b/core/java/android/widget/ZoomButtonsController.java index 9e37c7bb1873..f3d891d11989 100644 --- a/core/java/android/widget/ZoomButtonsController.java +++ b/core/java/android/widget/ZoomButtonsController.java @@ -33,7 +33,7 @@ import android.view.View; import android.view.ViewConfiguration; import android.view.ViewGroup; import android.view.ViewParent; -import android.view.ViewAncestor; +import android.view.ViewRootImpl; import android.view.WindowManager; import android.view.View.OnClickListener; import android.view.WindowManager.LayoutParams; @@ -501,7 +501,7 @@ public class ZoomButtonsController implements View.OnTouchListener { } else { - ViewAncestor viewRoot = getOwnerViewAncestor(); + ViewRootImpl viewRoot = getOwnerViewRootImpl(); if (viewRoot != null) { viewRoot.dispatchKey(event); } @@ -526,15 +526,15 @@ public class ZoomButtonsController implements View.OnTouchListener { } } - private ViewAncestor getOwnerViewAncestor() { + private ViewRootImpl getOwnerViewRootImpl() { View rootViewOfOwner = mOwnerView.getRootView(); if (rootViewOfOwner == null) { return null; } ViewParent parentOfRootView = rootViewOfOwner.getParent(); - if (parentOfRootView instanceof ViewAncestor) { - return (ViewAncestor) parentOfRootView; + if (parentOfRootView instanceof ViewRootImpl) { + return (ViewRootImpl) parentOfRootView; } else { return null; } diff --git a/core/java/com/android/internal/app/NetInitiatedActivity.java b/core/java/com/android/internal/app/NetInitiatedActivity.java index 6039cc252631..e1166f120197 100755 --- a/core/java/com/android/internal/app/NetInitiatedActivity.java +++ b/core/java/com/android/internal/app/NetInitiatedActivity.java @@ -23,6 +23,8 @@ import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; import android.os.Bundle; +import android.os.Handler; +import android.os.Message; import android.widget.Toast; import android.util.Log; import android.location.LocationManager; @@ -44,8 +46,12 @@ public class NetInitiatedActivity extends AlertActivity implements DialogInterfa private static final int POSITIVE_BUTTON = AlertDialog.BUTTON_POSITIVE; private static final int NEGATIVE_BUTTON = AlertDialog.BUTTON_NEGATIVE; + private static final int GPS_NO_RESPONSE_TIME_OUT = 1; // Received ID from intent, -1 when no notification is in progress private int notificationId = -1; + private int timeout = -1; + private int default_response = -1; + private int default_response_timeout = 6; /** Used to detect when NI request is received */ private BroadcastReceiver mNetInitiatedReceiver = new BroadcastReceiver() { @@ -58,6 +64,21 @@ public class NetInitiatedActivity extends AlertActivity implements DialogInterfa } }; + private final Handler mHandler = new Handler() { + public void handleMessage(Message msg) { + switch (msg.what) { + case GPS_NO_RESPONSE_TIME_OUT: { + if (notificationId != -1) { + sendUserResponse(default_response); + } + finish(); + } + break; + default: + } + } + }; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -75,8 +96,11 @@ public class NetInitiatedActivity extends AlertActivity implements DialogInterfa p.mNegativeButtonListener = this; notificationId = intent.getIntExtra(GpsNetInitiatedHandler.NI_INTENT_KEY_NOTIF_ID, -1); - if (DEBUG) Log.d(TAG, "onCreate, notifId: " + notificationId); + timeout = intent.getIntExtra(GpsNetInitiatedHandler.NI_INTENT_KEY_TIMEOUT, default_response_timeout); + default_response = intent.getIntExtra(GpsNetInitiatedHandler.NI_INTENT_KEY_DEFAULT_RESPONSE, GpsNetInitiatedHandler.GPS_NI_RESPONSE_ACCEPT); + if (DEBUG) Log.d(TAG, "onCreate() : notificationId: " + notificationId + " timeout: " + timeout + " default_response:" + default_response); + mHandler.sendMessageDelayed(mHandler.obtainMessage(GPS_NO_RESPONSE_TIME_OUT), (timeout * 1000)); setupAlert(); } diff --git a/core/java/com/android/internal/widget/PasswordEntryKeyboardHelper.java b/core/java/com/android/internal/widget/PasswordEntryKeyboardHelper.java index 3070e3ebf194..fb33748ae2c8 100644 --- a/core/java/com/android/internal/widget/PasswordEntryKeyboardHelper.java +++ b/core/java/com/android/internal/widget/PasswordEntryKeyboardHelper.java @@ -29,7 +29,7 @@ import android.util.Log; import android.view.KeyCharacterMap; import android.view.KeyEvent; import android.view.View; -import android.view.ViewAncestor; +import android.view.ViewRootImpl; import com.android.internal.R; public class PasswordEntryKeyboardHelper implements OnKeyboardActionListener { @@ -150,7 +150,7 @@ public class PasswordEntryKeyboardHelper implements OnKeyboardActionListener { KeyEvent event = events[i]; event = KeyEvent.changeFlags(event, event.getFlags() | KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE); - handler.sendMessage(handler.obtainMessage(ViewAncestor.DISPATCH_KEY, event)); + handler.sendMessage(handler.obtainMessage(ViewRootImpl.DISPATCH_KEY, event)); } } } @@ -158,11 +158,11 @@ public class PasswordEntryKeyboardHelper implements OnKeyboardActionListener { public void sendDownUpKeyEvents(int keyEventCode) { long eventTime = SystemClock.uptimeMillis(); Handler handler = mTargetView.getHandler(); - handler.sendMessage(handler.obtainMessage(ViewAncestor.DISPATCH_KEY_FROM_IME, + handler.sendMessage(handler.obtainMessage(ViewRootImpl.DISPATCH_KEY_FROM_IME, new KeyEvent(eventTime, eventTime, KeyEvent.ACTION_DOWN, keyEventCode, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE))); - handler.sendMessage(handler.obtainMessage(ViewAncestor.DISPATCH_KEY_FROM_IME, + handler.sendMessage(handler.obtainMessage(ViewRootImpl.DISPATCH_KEY_FROM_IME, new KeyEvent(eventTime, eventTime, KeyEvent.ACTION_UP, keyEventCode, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE))); diff --git a/core/jni/Android.mk b/core/jni/Android.mk index 06dc083695a1..514e59d8f29f 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -139,6 +139,7 @@ LOCAL_SRC_FILES:= \ android_bluetooth_common.cpp \ android_bluetooth_BluetoothAudioGateway.cpp \ android_bluetooth_BluetoothSocket.cpp \ + android_bluetooth_c.c \ android_server_BluetoothService.cpp \ android_server_BluetoothEventLoop.cpp \ android_server_BluetoothA2dpService.cpp \ diff --git a/core/jni/android/graphics/Canvas.cpp b/core/jni/android/graphics/Canvas.cpp index 942aa8a1d192..682c3c4a25fb 100644 --- a/core/jni/android/graphics/Canvas.cpp +++ b/core/jni/android/graphics/Canvas.cpp @@ -799,24 +799,6 @@ public: x, y, flags, paint); } - static void drawTextWithGlyphs___CIIFFIPaint(JNIEnv* env, jobject, SkCanvas* canvas, - jcharArray text, int index, int count, - jfloat x, jfloat y, int flags, SkPaint* paint) { - jchar* textArray = env->GetCharArrayElements(text, NULL); - drawTextWithGlyphs(canvas, textArray + index, 0, count, x, y, flags, paint); - env->ReleaseCharArrayElements(text, textArray, JNI_ABORT); - } - - static void drawTextWithGlyphs__StringIIFFIPaint(JNIEnv* env, jobject, - SkCanvas* canvas, jstring text, - int start, int end, - jfloat x, jfloat y, int flags, SkPaint* paint) { - - const jchar* textArray = env->GetStringChars(text, NULL); - drawTextWithGlyphs(canvas, textArray, start, end, x, y, flags, paint); - env->ReleaseStringChars(text, textArray); - } - static void doDrawGlyphs(SkCanvas* canvas, const jchar* glyphArray, int index, int count, jfloat x, jfloat y, int flags, SkPaint* paint) { // TODO: need to suppress this code after the GL renderer is modified for not @@ -833,16 +815,6 @@ public: paint->setTextEncoding(oldEncoding); } - static void drawGlyphs___CIIFFIPaint(JNIEnv* env, jobject, SkCanvas* canvas, - jcharArray glyphs, int index, int count, - jfloat x, jfloat y, int flags, SkPaint* paint) { - jchar* glyphArray = env->GetCharArrayElements(glyphs, NULL); - - doDrawGlyphs(canvas, glyphArray, index, count, x, y, flags, paint); - - env->ReleaseCharArrayElements(glyphs, glyphArray, JNI_ABORT); - } - static void drawTextRun___CIIIIFFIPaint( JNIEnv* env, jobject, SkCanvas* canvas, jcharArray text, int index, int count, int contextIndex, int contextCount, @@ -1044,12 +1016,6 @@ static JNINativeMethod gCanvasMethods[] = { (void*) SkCanvasGlue::drawText___CIIFFIPaint}, {"native_drawText","(ILjava/lang/String;IIFFII)V", (void*) SkCanvasGlue::drawText__StringIIFFIPaint}, - {"native_drawTextWithGlyphs","(I[CIIFFII)V", - (void*) SkCanvasGlue::drawTextWithGlyphs___CIIFFIPaint}, - {"native_drawTextWithGlyphs","(ILjava/lang/String;IIFFII)V", - (void*) SkCanvasGlue::drawTextWithGlyphs__StringIIFFIPaint}, - {"native_drawGlyphs","(I[CIIFFII)V", - (void*) SkCanvasGlue::drawGlyphs___CIIFFIPaint}, {"native_drawTextRun","(I[CIIIIFFII)V", (void*) SkCanvasGlue::drawTextRun___CIIIIFFIPaint}, {"native_drawTextRun","(ILjava/lang/String;IIIIFFII)V", diff --git a/core/jni/android/graphics/TextLayoutCache.cpp b/core/jni/android/graphics/TextLayoutCache.cpp index 6a13876031a9..30fe298207a2 100644 --- a/core/jni/android/graphics/TextLayoutCache.cpp +++ b/core/jni/android/graphics/TextLayoutCache.cpp @@ -323,9 +323,7 @@ size_t TextLayoutCacheValue::getSize() { void TextLayoutCacheValue::setupShaperItem(HB_ShaperItem* shaperItem, HB_FontRec* font, FontData* fontData, SkPaint* paint, const UChar* chars, size_t start, size_t count, - size_t contextCount, int dirFlags) { - bool isRTL = dirFlags & 0x1; - + size_t contextCount, bool isRTL) { font->klass = &harfbuzzSkiaClass; font->userData = 0; // The values which harfbuzzSkiaClass returns are already scaled to @@ -374,10 +372,10 @@ void TextLayoutCacheValue::setupShaperItem(HB_ShaperItem* shaperItem, HB_FontRec void TextLayoutCacheValue::shapeWithHarfbuzz(HB_ShaperItem* shaperItem, HB_FontRec* font, FontData* fontData, SkPaint* paint, const UChar* chars, size_t start, size_t count, - size_t contextCount, int dirFlags) { + size_t contextCount, bool isRTL) { // Setup Harfbuzz Shaper setupShaperItem(shaperItem, font, fontData, paint, chars, start, count, - contextCount, dirFlags); + contextCount, isRTL); // Shape resetGlyphArrays(shaperItem); @@ -430,7 +428,7 @@ void TextLayoutCacheValue::computeValuesWithHarfbuzz(SkPaint* paint, const UChar LOGD("computeValuesWithHarfbuzz -- forcing run with LTR=%d RTL=%d", forceLTR, forceRTL); #endif - computeRunValuesWithHarfbuzz(paint, chars, start, count, contextCount, dirFlags, + computeRunValuesWithHarfbuzz(paint, chars, start, count, contextCount, forceRTL, outAdvances, outTotalAdvance, outGlyphs, outGlyphsCount); if (forceRTL && *outGlyphsCount > 1) { @@ -451,10 +449,15 @@ void TextLayoutCacheValue::computeValuesWithHarfbuzz(SkPaint* paint, const UChar LOGD("computeValuesWithHarfbuzz -- dirFlags=%d run-count=%d paraDir=%d", dirFlags, rc, paraDir); #endif if (rc == 1 || !U_SUCCESS(status)) { + bool isRTL = (paraDir == 1); +#if DEBUG_GLYPHS + LOGD("computeValuesWithHarfbuzz -- processing SINGLE run " + "-- run-start=%d run-len=%d isRTL=%d", start, count, isRTL); +#endif computeRunValuesWithHarfbuzz(paint, chars, start, count, contextCount, - dirFlags, outAdvances, outTotalAdvance, outGlyphs, outGlyphsCount); + isRTL, outAdvances, outTotalAdvance, outGlyphs, outGlyphsCount); - if (dirFlags == 1 && *outGlyphsCount > 1) { + if (isRTL && *outGlyphsCount > 1) { reverseGlyphArray(*outGlyphs, *outGlyphsCount); } } else { @@ -485,14 +488,14 @@ void TextLayoutCacheValue::computeValuesWithHarfbuzz(SkPaint* paint, const UChar lengthRun = endRun - startRun; - int newFlags = (runDir == UBIDI_RTL) ? kDirection_RTL : kDirection_LTR; + bool isRTL = (runDir == UBIDI_RTL); jfloat runTotalAdvance = 0; #if DEBUG_GLYPHS - LOGD("computeValuesWithHarfbuzz -- run-start=%d run-len=%d newFlags=%d", - startRun, lengthRun, newFlags); + LOGD("computeValuesWithHarfbuzz -- run-start=%d run-len=%d isRTL=%d", + startRun, lengthRun, isRTL); #endif computeRunValuesWithHarfbuzz(paint, chars, startRun, - lengthRun, contextCount, newFlags, + lengthRun, contextCount, isRTL, outAdvances, &runTotalAdvance, &runGlyphs, &runGlyphsCount); @@ -506,7 +509,7 @@ void TextLayoutCacheValue::computeValuesWithHarfbuzz(SkPaint* paint, const UChar LOGD(" -- glyphs[%d]=%d", j, runGlyphs[j]); } #endif - glyphRuns.push(GlyphRun(runGlyphs, runGlyphsCount, newFlags)); + glyphRuns.push(GlyphRun(runGlyphs, runGlyphsCount, isRTL)); } *outGlyphs = new jchar[*outGlyphsCount]; @@ -528,13 +531,15 @@ void TextLayoutCacheValue::computeValuesWithHarfbuzz(SkPaint* paint, const UChar ubidi_close(bidi); } else { // Cannot run BiDi, just consider one Run + bool isRTL = (bidiReq = 1) || (bidiReq = UBIDI_DEFAULT_RTL); #if DEBUG_GLYPHS - LOGD("computeValuesWithHarfbuzz -- cannot run BiDi, considering only one Run"); + LOGD("computeValuesWithHarfbuzz -- cannot run BiDi, considering a SINGLE Run " + "-- run-start=%d run-len=%d isRTL=%d", start, count, isRTL); #endif - computeRunValuesWithHarfbuzz(paint, chars, start, count, contextCount, dirFlags, + computeRunValuesWithHarfbuzz(paint, chars, start, count, contextCount, isRTL, outAdvances, outTotalAdvance, outGlyphs, outGlyphsCount); - if (dirFlags == 1 && *outGlyphsCount > 1) { + if (isRTL && *outGlyphsCount > 1) { reverseGlyphArray(*outGlyphs, *outGlyphsCount); } } @@ -545,17 +550,15 @@ void TextLayoutCacheValue::computeValuesWithHarfbuzz(SkPaint* paint, const UChar } void TextLayoutCacheValue::computeRunValuesWithHarfbuzz(SkPaint* paint, const UChar* chars, - size_t start, size_t count, size_t contextCount, int dirFlags, + size_t start, size_t count, size_t contextCount, bool isRTL, jfloat* outAdvances, jfloat* outTotalAdvance, jchar** outGlyphs, size_t* outGlyphsCount) { - bool isRTL = dirFlags & 0x1; - HB_ShaperItem shaperItem; HB_FontRec font; FontData fontData; shapeWithHarfbuzz(&shaperItem, &font, &fontData, paint, chars, start, count, - contextCount, dirFlags); + contextCount, isRTL); #if DEBUG_GLYPHS LOGD("HARFBUZZ -- num_glypth=%d - kerning_applied=%d", shaperItem.num_glyphs, diff --git a/core/jni/android/graphics/TextLayoutCache.h b/core/jni/android/graphics/TextLayoutCache.h index 690caacd6c65..10dee87e09d8 100644 --- a/core/jni/android/graphics/TextLayoutCache.h +++ b/core/jni/android/graphics/TextLayoutCache.h @@ -128,11 +128,11 @@ public: static void setupShaperItem(HB_ShaperItem* shaperItem, HB_FontRec* font, FontData* fontData, SkPaint* paint, const UChar* chars, size_t start, size_t count, size_t contextCount, - int dirFlags); + bool isRTL); static void shapeWithHarfbuzz(HB_ShaperItem* shaperItem, HB_FontRec* font, FontData* fontData, SkPaint* paint, const UChar* chars, size_t start, size_t count, size_t contextCount, - int dirFlags); + bool isRTL); static void computeValuesWithHarfbuzz(SkPaint* paint, const UChar* chars, size_t start, size_t count, size_t contextCount, int dirFlags, @@ -179,7 +179,7 @@ private: static void resetGlyphArrays(HB_ShaperItem* shaperItem); static void computeRunValuesWithHarfbuzz(SkPaint* paint, const UChar* chars, size_t start, - size_t count, size_t contextCount, int dirFlags, + size_t count, size_t contextCount, bool isRTL, jfloat* outAdvances, jfloat* outTotalAdvance, jchar** outGlyphs, size_t* outGlyphsCount); }; // TextLayoutCacheValue diff --git a/core/jni/android_bluetooth_BluetoothAudioGateway.cpp b/core/jni/android_bluetooth_BluetoothAudioGateway.cpp index cb742a3d5b1c..29c9c2db9734 100755 --- a/core/jni/android_bluetooth_BluetoothAudioGateway.cpp +++ b/core/jni/android_bluetooth_BluetoothAudioGateway.cpp @@ -17,6 +17,7 @@ #define LOG_TAG "BluetoothAudioGateway.cpp" #include "android_bluetooth_common.h" +#include "android_bluetooth_c.h" #include "android_runtime/AndroidRuntime.h" #include "JNIHelp.h" #include "jni.h" @@ -491,7 +492,8 @@ static int setup_listening_socket(int dev, int channel) { } laddr.rc_family = AF_BLUETOOTH; - memcpy(&laddr.rc_bdaddr, BDADDR_ANY, sizeof(bdaddr_t)); + bdaddr_t any = android_bluetooth_bdaddr_any(); + memcpy(&laddr.rc_bdaddr, &any, sizeof(bdaddr_t)); laddr.rc_channel = channel; if (bind(sk, (struct sockaddr *)&laddr, sizeof(laddr)) < 0) { diff --git a/core/jni/android_bluetooth_BluetoothSocket.cpp b/core/jni/android_bluetooth_BluetoothSocket.cpp index d09c4e9f6132..4c84324af266 100644 --- a/core/jni/android_bluetooth_BluetoothSocket.cpp +++ b/core/jni/android_bluetooth_BluetoothSocket.cpp @@ -17,6 +17,7 @@ #define LOG_TAG "BluetoothSocket.cpp" #include "android_bluetooth_common.h" +#include "android_bluetooth_c.h" #include "android_runtime/AndroidRuntime.h" #include "JNIHelp.h" #include "utils/Log.h" @@ -245,7 +246,7 @@ static int bindListenNative(JNIEnv *env, jobject obj) { jint type; socklen_t addr_sz; struct sockaddr *addr; - bdaddr_t bdaddr = *BDADDR_ANY; + bdaddr_t bdaddr = android_bluetooth_bdaddr_any(); struct asocket *s = get_socketData(env, obj); if (!s) diff --git a/core/jni/android_bluetooth_c.c b/core/jni/android_bluetooth_c.c new file mode 100755 index 000000000000..b4c672711594 --- /dev/null +++ b/core/jni/android_bluetooth_c.c @@ -0,0 +1,31 @@ +/* +** Copyright 2011, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifdef HAVE_BLUETOOTH + +#include "android_bluetooth_c.h" + +/* + * A C helper for creating a bdaddr_t object with the value BDADDR_ANY. + * We have to do this in C because the macro BDADDR_ANY in bluetooth.h + * is not valid C++ code. + */ +bdaddr_t android_bluetooth_bdaddr_any(void) +{ + bdaddr_t any = *BDADDR_ANY; + return any; +} +#endif diff --git a/core/jni/android_bluetooth_c.h b/core/jni/android_bluetooth_c.h new file mode 100644 index 000000000000..e89024469475 --- /dev/null +++ b/core/jni/android_bluetooth_c.h @@ -0,0 +1,39 @@ +/* +** 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. +*/ + +#ifndef ANDROID_BLUETOOTH_C_H +#define ANDROID_BLUETOOTH_C_H +#ifdef HAVE_BLUETOOTH + +#include <bluetooth/bluetooth.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * A C helper for creating a bdaddr_t object with the value BDADDR_ANY. + * We have to do this in C because the macro BDADDR_ANY in bluetooth.h + * is not valid C++ code. + */ +bdaddr_t android_bluetooth_bdaddr_any(void); + +#ifdef __cplusplus +} +#endif + +#endif /*HAVE_BLUETOOTH*/ +#endif /*ANDROID_BLUETOOTH_C_H*/ diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp index 681f43f32b41..b0c2f2c2da07 100644 --- a/core/jni/android_view_GLES20Canvas.cpp +++ b/core/jni/android_view_GLES20Canvas.cpp @@ -127,6 +127,13 @@ static void android_view_GLES20Canvas_disableVsync(JNIEnv* env, jobject clazz) { } } +static void android_view_GLES20Canvas_flushCaches(JNIEnv* env, jobject clazz, + Caches::FlushMode mode) { + if (Caches::hasInstance()) { + Caches::getInstance().flush(mode); + } +} + // ---------------------------------------------------------------------------- // Constructors // ---------------------------------------------------------------------------- @@ -735,6 +742,7 @@ static JNINativeMethod gMethods[] = { { "nIsBackBufferPreserved", "()Z", (void*) android_view_GLES20Canvas_isBackBufferPreserved }, { "nPreserveBackBuffer", "()Z", (void*) android_view_GLES20Canvas_preserveBackBuffer }, { "nDisableVsync", "()V", (void*) android_view_GLES20Canvas_disableVsync }, + { "nFlushCaches", "(I)V", (void*) android_view_GLES20Canvas_flushCaches }, { "nCreateRenderer", "()I", (void*) android_view_GLES20Canvas_createRenderer }, { "nDestroyRenderer", "(I)V", (void*) android_view_GLES20Canvas_destroyRenderer }, @@ -859,10 +867,8 @@ int register_android_view_GLES20Canvas(JNIEnv* env) { const char* const kActivityThreadPathName = "android/app/ActivityThread"; -int register_android_app_ActivityThread(JNIEnv* env) -{ - return AndroidRuntime::registerNativeMethods( - env, kActivityThreadPathName, +int register_android_app_ActivityThread(JNIEnv* env) { + return AndroidRuntime::registerNativeMethods(env, kActivityThreadPathName, gActivityThreadMethods, NELEM(gActivityThreadMethods)); } diff --git a/core/jni/android_view_TextureView.cpp b/core/jni/android_view_TextureView.cpp index b046b2360d55..9484c6b5798d 100644 --- a/core/jni/android_view_TextureView.cpp +++ b/core/jni/android_view_TextureView.cpp @@ -19,11 +19,48 @@ #include <android_runtime/AndroidRuntime.h> #include <android_runtime/android_graphics_SurfaceTexture.h> +#include <ui/Region.h> +#include <ui/Rect.h> + #include <gui/SurfaceTexture.h> +#include <gui/SurfaceTextureClient.h> + +#include <SkBitmap.h> +#include <SkCanvas.h> namespace android { // ---------------------------------------------------------------------------- +// JNI Glue +// ---------------------------------------------------------------------------- + +static struct { + jmethodID set; + jfieldID left; + jfieldID top; + jfieldID right; + jfieldID bottom; +} gRectClassInfo; + +static struct { + jfieldID nativeCanvas; + jfieldID surfaceFormat; +} gCanvasClassInfo; + +static struct { + jfieldID nativeWindow; +} gTextureViewClassInfo; + +#define GET_INT(object, field) \ + env->GetIntField(object, field) + +#define SET_INT(object, field, value) \ + env->SetIntField(object, field, value) + +#define INVOKEV(object, method, ...) \ + env->CallVoidMethod(object, method, __VA_ARGS__) + +// ---------------------------------------------------------------------------- // Native layer // ---------------------------------------------------------------------------- @@ -34,6 +71,118 @@ static void android_view_TextureView_setDefaultBufferSize(JNIEnv* env, jobject, surfaceTexture->setDefaultBufferSize(width, height); } +static inline SkBitmap::Config convertPixelFormat(int32_t format) { + switch (format) { + case WINDOW_FORMAT_RGBA_8888: + return SkBitmap::kARGB_8888_Config; + case WINDOW_FORMAT_RGBX_8888: + return SkBitmap::kARGB_8888_Config; + case WINDOW_FORMAT_RGB_565: + return SkBitmap::kRGB_565_Config; + default: + return SkBitmap::kNo_Config; + } +} + +/** + * This is a private API, and this implementation is also provided in the NDK. + * However, the NDK links against android_runtime, which means that using the + * NDK implementation would create a circular dependency between the libraries. + */ +static int32_t native_window_lock(ANativeWindow* window, ANativeWindow_Buffer* outBuffer, + Rect* inOutDirtyBounds) { + return window->perform(window, NATIVE_WINDOW_LOCK, outBuffer, inOutDirtyBounds); +} + +static int32_t native_window_unlockAndPost(ANativeWindow* window) { + return window->perform(window, NATIVE_WINDOW_UNLOCK_AND_POST); +} + +static void android_view_TextureView_createNativeWindow(JNIEnv* env, jobject textureView, + jobject surface) { + + sp<SurfaceTexture> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, surface)); + sp<ANativeWindow> window = new SurfaceTextureClient(surfaceTexture); + + window->incStrong(0); + SET_INT(textureView, gTextureViewClassInfo.nativeWindow, jint(window.get())); +} + +static void android_view_TextureView_destroyNativeWindow(JNIEnv* env, jobject textureView) { + + ANativeWindow* nativeWindow = (ANativeWindow*) + GET_INT(textureView, gTextureViewClassInfo.nativeWindow); + + if (nativeWindow) { + sp<ANativeWindow> window(nativeWindow); + window->decStrong(0); + SET_INT(textureView, gTextureViewClassInfo.nativeWindow, 0); + } +} + +static void android_view_TextureView_lockCanvas(JNIEnv* env, jobject, + jint nativeWindow, jobject canvas, jobject dirtyRect) { + + if (!nativeWindow) { + return; + } + + ANativeWindow_Buffer buffer; + + Rect rect; + if (dirtyRect) { + rect.left = GET_INT(dirtyRect, gRectClassInfo.left); + rect.top = GET_INT(dirtyRect, gRectClassInfo.top); + rect.right = GET_INT(dirtyRect, gRectClassInfo.right); + rect.bottom = GET_INT(dirtyRect, gRectClassInfo.bottom); + } else { + rect.set(Rect(0x3FFF, 0x3FFF)); + } + + sp<ANativeWindow> window((ANativeWindow*) nativeWindow); + native_window_lock(window.get(), &buffer, &rect); + + ssize_t bytesCount = buffer.stride * bytesPerPixel(buffer.format); + + SkBitmap bitmap; + bitmap.setConfig(convertPixelFormat(buffer.format), buffer.width, buffer.height, bytesCount); + + if (buffer.format == WINDOW_FORMAT_RGBX_8888) { + bitmap.setIsOpaque(true); + } + + if (buffer.width > 0 && buffer.height > 0) { + bitmap.setPixels(buffer.bits); + } else { + bitmap.setPixels(NULL); + } + + SET_INT(canvas, gCanvasClassInfo.surfaceFormat, buffer.format); + SkCanvas* nativeCanvas = (SkCanvas*) GET_INT(canvas, gCanvasClassInfo.nativeCanvas); + nativeCanvas->setBitmapDevice(bitmap); + + SkRect clipRect; + clipRect.set(rect.left, rect.top, rect.right, rect.bottom); + nativeCanvas->clipRect(clipRect); + + if (dirtyRect) { + INVOKEV(dirtyRect, gRectClassInfo.set, + int(rect.left), int(rect.top), int(rect.right), int(rect.bottom)); + } +} + +static void android_view_TextureView_unlockCanvasAndPost(JNIEnv* env, jobject, + jint nativeWindow, jobject canvas) { + + SkCanvas* nativeCanvas = (SkCanvas*) GET_INT(canvas, gCanvasClassInfo.nativeCanvas); + nativeCanvas->setBitmapDevice(SkBitmap()); + + if (nativeWindow) { + sp<ANativeWindow> window((ANativeWindow*) nativeWindow); + native_window_unlockAndPost(window.get()); + } +} + // ---------------------------------------------------------------------------- // JNI Glue // ---------------------------------------------------------------------------- @@ -42,10 +191,47 @@ const char* const kClassPathName = "android/view/TextureView"; static JNINativeMethod gMethods[] = { { "nSetDefaultBufferSize", "(Landroid/graphics/SurfaceTexture;II)V", - (void*) android_view_TextureView_setDefaultBufferSize } + (void*) android_view_TextureView_setDefaultBufferSize }, + + { "nCreateNativeWindow", "(Landroid/graphics/SurfaceTexture;)V", + (void*) android_view_TextureView_createNativeWindow }, + { "nDestroyNativeWindow", "()V", + (void*) android_view_TextureView_destroyNativeWindow }, + + { "nLockCanvas", "(ILandroid/graphics/Canvas;Landroid/graphics/Rect;)V", + (void*) android_view_TextureView_lockCanvas }, + { "nUnlockCanvasAndPost", "(ILandroid/graphics/Canvas;)V", + (void*) android_view_TextureView_unlockCanvasAndPost }, }; +#define FIND_CLASS(var, className) \ + var = env->FindClass(className); \ + LOG_FATAL_IF(!var, "Unable to find class " className); + +#define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \ + var = env->GetMethodID(clazz, methodName, methodDescriptor); \ + LOG_FATAL_IF(!var, "Unable to find method " methodName); + +#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \ + var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \ + LOG_FATAL_IF(!var, "Unable to find field" fieldName); + int register_android_view_TextureView(JNIEnv* env) { + jclass clazz; + FIND_CLASS(clazz, "android/graphics/Rect"); + GET_METHOD_ID(gRectClassInfo.set, clazz, "set", "(IIII)V"); + GET_FIELD_ID(gRectClassInfo.left, clazz, "left", "I"); + GET_FIELD_ID(gRectClassInfo.top, clazz, "top", "I"); + GET_FIELD_ID(gRectClassInfo.right, clazz, "right", "I"); + GET_FIELD_ID(gRectClassInfo.bottom, clazz, "bottom", "I"); + + FIND_CLASS(clazz, "android/graphics/Canvas"); + GET_FIELD_ID(gCanvasClassInfo.nativeCanvas, clazz, "mNativeCanvas", "I"); + GET_FIELD_ID(gCanvasClassInfo.surfaceFormat, clazz, "mSurfaceFormat", "I"); + + FIND_CLASS(clazz, "android/view/TextureView"); + GET_FIELD_ID(gTextureViewClassInfo.nativeWindow, clazz, "mNativeWindow", "I"); + return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods)); } diff --git a/core/res/res/layout/activity_chooser_view.xml b/core/res/res/layout/activity_chooser_view.xml index 902b3c0612cb..50b18784b138 100644 --- a/core/res/res/layout/activity_chooser_view.xml +++ b/core/res/res/layout/activity_chooser_view.xml @@ -23,13 +23,9 @@ style="?android:attr/actionButtonStyle"> <ImageButton android:id="@+id/default_activity_button" - android:layout_width="32dip" - android:layout_height="32dip" - android:layout_marginRight="8dip" /> + style="@style/Widget.ActivityChooserViewButton" /> <ImageButton android:id="@+id/expand_activities_button" - android:layout_width="32dip" - android:layout_height="32dip" - android:layout_marginLeft="8dip" /> + style="@style/Widget.ActivityChooserViewButton" /> </LinearLayout> diff --git a/core/res/res/layout/activity_chooser_view_list_item.xml b/core/res/res/layout/activity_chooser_view_list_item.xml index f90044e4494e..88498d992c35 100644 --- a/core/res/res/layout/activity_chooser_view_list_item.xml +++ b/core/res/res/layout/activity_chooser_view_list_item.xml @@ -20,6 +20,7 @@ android:layout_height="?android:attr/dropdownListPreferredItemHeight" android:paddingLeft="16dip" android:paddingRight="16dip" + android:minWidth="196dip" android:background="?android:attr/activatedBackgroundIndicator" android:orientation="vertical" > diff --git a/core/res/res/layout/search_bar.xml b/core/res/res/layout/search_bar.xml index 790ac6b09601..f6b5b5372fe1 100644 --- a/core/res/res/layout/search_bar.xml +++ b/core/res/res/layout/search_bar.xml @@ -66,7 +66,6 @@ android:layout_height="wrap_content" android:layout_weight="1" android:maxWidth="600dip" - android:iconifiedByDefault="false" android:layout_gravity="center_vertical" /> diff --git a/core/res/res/layout/search_dropdown_item_icons_2line.xml b/core/res/res/layout/search_dropdown_item_icons_2line.xml index 53906f94e025..acef2cc71ef0 100644 --- a/core/res/res/layout/search_dropdown_item_icons_2line.xml +++ b/core/res/res/layout/search_dropdown_item_icons_2line.xml @@ -19,21 +19,21 @@ --> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:paddingLeft="4dip" - android:paddingRight="2dip" + android:paddingLeft="@dimen/dropdownitem_text_padding_left" + android:paddingRight="4dip" android:layout_width="match_parent" android:layout_height="?android:attr/searchResultListItemHeight" > <!-- Icons come first in the layout, since their placement doesn't depend on the placement of the text views. --> <ImageView android:id="@android:id/icon1" - android:layout_width="48dip" + android:layout_width="@dimen/dropdownitem_icon_width" android:layout_height="48dip" android:scaleType="centerInside" android:layout_alignParentLeft="true" android:layout_alignParentTop="true" android:layout_alignParentBottom="true" - android:visibility="gone" /> + android:visibility="invisible" /> <ImageView android:id="@+id/edit_query" android:layout_width="48dip" diff --git a/core/res/res/layout/search_view.xml b/core/res/res/layout/search_view.xml index fee27ebe67c3..6b70d8dc42f7 100644 --- a/core/res/res/layout/search_view.xml +++ b/core/res/res/layout/search_view.xml @@ -22,6 +22,8 @@ android:id="@+id/search_bar" android:layout_width="match_parent" android:layout_height="match_parent" + android:paddingLeft="8dip" + android:paddingRight="8dip" android:orientation="horizontal" > @@ -30,7 +32,7 @@ android:id="@+id/search_badge" android:layout_width="wrap_content" android:layout_height="match_parent" - android:layout_gravity="center_vertical" + android:gravity="center_vertical" android:layout_marginBottom="2dip" android:drawablePadding="0dip" android:textAppearance="?android:attr/textAppearanceMedium" @@ -54,12 +56,21 @@ android:layout_height="wrap_content" android:layout_weight="1" android:layout_gravity="center_vertical" - android:layout_marginLeft="8dip" - android:layout_marginRight="8dip" android:layout_marginTop="4dip" android:layout_marginBottom="4dip" android:orientation="horizontal"> + <ImageView + android:id="@+id/search_mag_icon" + android:layout_width="@dimen/dropdownitem_icon_width" + android:layout_height="wrap_content" + android:scaleType="centerInside" + android:layout_marginLeft="@dimen/dropdownitem_text_padding_left" + android:layout_gravity="center_vertical" + android:src="?android:attr/searchViewSearchIcon" + android:visibility="gone" + /> + <!-- Inner layout contains the app icon, button(s) and EditText --> <LinearLayout android:id="@+id/search_plate" @@ -70,14 +81,6 @@ android:orientation="horizontal" android:background="?android:attr/searchViewTextField"> - <ImageView - android:id="@+id/search_app_icon" - android:layout_width="wrap_content" - android:layout_height="match_parent" - android:layout_gravity="center_vertical" - android:src="?android:attr/searchViewSearchIcon" - /> - <view class="android.widget.SearchView$SearchAutoComplete" android:id="@+id/search_src_text" android:layout_height="36dip" @@ -85,8 +88,8 @@ android:layout_weight="1" android:minWidth="@dimen/search_view_text_min_width" android:layout_gravity="bottom" - android:paddingLeft="8dip" - android:paddingRight="6dip" + android:paddingLeft="@dimen/dropdownitem_text_padding_left" + android:paddingRight="@dimen/dropdownitem_text_padding_right" android:singleLine="true" android:ellipsize="end" android:background="@null" @@ -100,7 +103,7 @@ <ImageView android:id="@+id/search_close_btn" - android:layout_width="wrap_content" + android:layout_width="@dimen/dropdownitem_icon_width" android:layout_height="match_parent" android:paddingLeft="8dip" android:paddingRight="8dip" @@ -131,7 +134,7 @@ android:visibility="gone" android:focusable="true" /> - + <ImageView android:id="@+id/search_voice_btn" android:layout_width="wrap_content" diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 96137121a193..d0361ca447e7 100755 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -2302,6 +2302,8 @@ <!-- Flag whether the accessibility service wants to be able to retrieve the active window content. This setting cannot be changed at runtime. --> <attr name="canRetrieveWindowContent" format="boolean" /> + <!-- Short description of the accessibility serivce purpose or behavior.--> + <attr name="description" /> </declare-styleable> <!-- =============================== --> diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index abc56eccbfb9..2ba4e668fe22 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -138,4 +138,16 @@ <!-- Minimum popup width for selecting an activity in ActivityChooserDialog/ActivityChooserView. --> <dimen name="activity_chooser_popup_min_width">200dip</dimen> + <!-- The default gap between components in a layout. --> + <dimen name="default_gap">16dip</dimen> + + <!-- Text padding for dropdown items --> + <dimen name="dropdownitem_text_padding_left">6dip</dimen> + + <!-- Text padding for dropdown items --> + <dimen name="dropdownitem_text_padding_right">6dip</dimen> + + <!-- Width of the icon in a dropdown list --> + <dimen name="dropdownitem_icon_width">48dip</dimen> + </resources> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 6dedc83eb087..f46462399452 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -1787,6 +1787,11 @@ <public type="attr" name="colorActivatedHighlight" /> <public type="attr" name="colorMultiSelectHighlight" /> + <public type="attr" name="drawableStart" /> + <public type="attr" name="drawableEnd" /> + + <public type="attr" name="actionModeStyle" /> + <public type="style" name="TextAppearance.SuggestionHighlight" /> <public type="style" name="Theme.Holo.SplitActionBarWhenNarrow" /> <public type="style" name="Theme.Holo.Light.SplitActionBarWhenNarrow" /> @@ -1826,7 +1831,4 @@ <public type="color" name="holo_purple" /> <public type="color" name="holo_blue_bright" /> - <public type="attr" name="drawableStart" /> - <public type="attr" name="drawableEnd" /> - </resources> diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml index 6b751469610f..d647467a59fc 100644 --- a/core/res/res/values/styles.xml +++ b/core/res/res/values/styles.xml @@ -563,8 +563,8 @@ <style name="Widget.DropDownItem"> <item name="android:textAppearance">@style/TextAppearance.Widget.DropDownItem</item> - <item name="android:paddingLeft">6dip</item> - <item name="android:paddingRight">6dip</item> + <item name="android:paddingLeft">@dimen/dropdownitem_text_padding_left</item> + <item name="android:paddingRight">@dimen/dropdownitem_text_padding_right</item> <item name="android:gravity">center_vertical</item> </style> @@ -713,6 +713,16 @@ <item name="android:quickContactWindowSize">modeLarge</item> </style> + <style name="Widget.ActivityChooserViewButton"> + <item name="android:layout_width">wrap_content</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:layout_gravity">center</item> + <item name="android:background">?android:attr/selectableItemBackground</item> + <item name="android:adjustViewBounds">true</item> + <item name="android:scaleType">fitCenter</item> + <item name="android:padding">@android:dimen/action_bar_icon_vertical_padding</item> + </style> + <!-- Text Appearances --> <eat-comment /> diff --git a/core/tests/coretests/src/android/content/ObserverNodeTest.java b/core/tests/coretests/src/android/content/ObserverNodeTest.java index 736c75926db4..95b8465e2d48 100644 --- a/core/tests/coretests/src/android/content/ObserverNodeTest.java +++ b/core/tests/coretests/src/android/content/ObserverNodeTest.java @@ -48,9 +48,9 @@ public class ObserverNodeTest extends AndroidTestCase { int[] nums = new int[] {4, 7, 1, 4, 2, 2, 3, 3}; // special case - root.addObserverLocked(uris[0], new TestObserver().getContentObserver(), false, root); + root.addObserverLocked(uris[0], new TestObserver().getContentObserver(), false, root, 0, 0); for(int i = 1; i < uris.length; i++) { - root.addObserverLocked(uris[i], new TestObserver().getContentObserver(), true, root); + root.addObserverLocked(uris[i], new TestObserver().getContentObserver(), true, root, 0, 0); } ArrayList<ObserverCall> calls = new ArrayList<ObserverCall>(); @@ -77,7 +77,7 @@ public class ObserverNodeTest extends AndroidTestCase { int[] nums = new int[] {7, 1, 3, 3, 1, 1, 1, 1}; for(int i = 0; i < uris.length; i++) { - root.addObserverLocked(uris[i], new TestObserver().getContentObserver(), false, root); + root.addObserverLocked(uris[i], new TestObserver().getContentObserver(), false, root, 0, 0); } ArrayList<ObserverCall> calls = new ArrayList<ObserverCall>(); diff --git a/core/tests/coretests/src/android/util/JsonReaderTest.java b/core/tests/coretests/src/android/util/JsonReaderTest.java index b5c2c2743f33..440aeb5129ed 100644 --- a/core/tests/coretests/src/android/util/JsonReaderTest.java +++ b/core/tests/coretests/src/android/util/JsonReaderTest.java @@ -856,4 +856,33 @@ public final class JsonReaderTest extends TestCase { } catch (IOException expected) { } } + + public void testFailWithPosition() throws IOException { + testFailWithPosition("Expected literal value at line 6 column 3", + "[\n\n\n\n\n0,}]"); + } + + public void testFailWithPositionGreaterThanBufferSize() throws IOException { + String spaces = repeat(' ', 8192); + testFailWithPosition("Expected literal value at line 6 column 3", + "[\n\n" + spaces + "\n\n\n0,}]"); + } + + private void testFailWithPosition(String message, String json) throws IOException { + JsonReader reader = new JsonReader(new StringReader(json)); + reader.beginArray(); + reader.nextInt(); + try { + reader.peek(); + fail(); + } catch (IOException expected) { + assertEquals(message, expected.getMessage()); + } + } + + private String repeat(char c, int count) { + char[] array = new char[count]; + Arrays.fill(array, c); + return new String(array); + } } diff --git a/core/tests/coretests/src/android/util/JsonWriterTest.java b/core/tests/coretests/src/android/util/JsonWriterTest.java index b29e2fdde08c..1239a3c398e8 100644 --- a/core/tests/coretests/src/android/util/JsonWriterTest.java +++ b/core/tests/coretests/src/android/util/JsonWriterTest.java @@ -289,6 +289,15 @@ public final class JsonWriterTest extends TestCase { + "\"\\u0019\"]", stringWriter.toString()); } + public void testUnicodeLineBreaksEscaped() throws IOException { + StringWriter stringWriter = new StringWriter(); + JsonWriter jsonWriter = new JsonWriter(stringWriter); + jsonWriter.beginArray(); + jsonWriter.value("\u2028 \u2029"); + jsonWriter.endArray(); + assertEquals("[\"\\u2028 \\u2029\"]", stringWriter.toString()); + } + public void testEmptyArray() throws IOException { StringWriter stringWriter = new StringWriter(); JsonWriter jsonWriter = new JsonWriter(stringWriter); diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java index 5b50f8fc00ba..35ed4d2087cc 100644 --- a/graphics/java/android/graphics/Canvas.java +++ b/graphics/java/android/graphics/Canvas.java @@ -1414,77 +1414,13 @@ public class Canvas { } else { char[] buf = TemporaryBuffer.obtain(end - start); TextUtils.getChars(text, start, end, buf, 0); - native_drawText(mNativeCanvas, buf, 0, end - start, x, y, + native_drawText(mNativeCanvas, buf, 0, end - start, x, y, paint.mBidiFlags, paint.mNativePaint); TemporaryBuffer.recycle(buf); } } /** - * Draw the text, with origin at (x,y), using the specified paint. The - * origin is interpreted based on the Align setting in the paint. - * - * @param text The text to be drawn - * @param x The x-coordinate of the origin of the text being drawn - * @param y The y-coordinate of the origin of the text being drawn - * @param paint The paint used for the text (e.g. color, size, style) - * - * @hide - * - * Used only for BiDi / RTL Tests - */ - public void drawTextWithGlyphs(char[] text, int index, int count, float x, float y, - Paint paint) { - if ((index | count | (index + count) | - (text.length - index - count)) < 0) { - throw new IndexOutOfBoundsException(); - } - native_drawTextWithGlyphs(mNativeCanvas, text, index, count, x, y, paint.mBidiFlags, - paint.mNativePaint); - } - - /** - * Draw the text, with origin at (x,y), using the specified paint. The - * origin is interpreted based on the Align setting in the paint. - * - * @param text The text to be drawn - * @param x The x-coordinate of the origin of the text being drawn - * @param y The y-coordinate of the origin of the text being drawn - * @param paint The paint used for the text (e.g. color, size, style) - * - * @hide - * - * Used only for BiDi / RTL Tests - */ - public void drawTextWithGlyphs(String text, float x, float y, Paint paint) { - native_drawTextWithGlyphs(mNativeCanvas, text, 0, text.length(), x, y, paint.mBidiFlags, - paint.mNativePaint); - } - - /** - * Draw the glyphs, with origin at (x,y), using the specified paint. The - * origin is interpreted based on the Align setting in the paint. - * - * @param glyphs The glyphs to be drawn - * @param x The x-coordinate of the origin of the text being drawn - * @param y The y-coordinate of the origin of the text being drawn - * @param paint The paint used for the text (e.g. color, size, style) - * - * @hide - * - * Used only for BiDi / RTL Tests - */ - public void drawGlyphs(char[] glyphs, int index, int count, float x, float y, - Paint paint) { - if ((index | count | (index + count) | - (glyphs.length - index - count)) < 0) { - throw new IndexOutOfBoundsException(); - } - native_drawGlyphs(mNativeCanvas, glyphs, index, count, x, y, paint.mBidiFlags, - paint.mNativePaint); - } - - /** * Render a run of all LTR or all RTL text, with shaping. This does not run * bidi on the provided text, but renders it as a uniform right-to-left or * left-to-right run, as indicated by dir. Alignment of the text is as @@ -1813,16 +1749,6 @@ public class Canvas { int start, int end, float x, float y, int flags, int paint); - private static native void native_drawTextWithGlyphs(int nativeCanvas, char[] text, - int index, int count, float x, - float y, int flags, int paint); - private static native void native_drawTextWithGlyphs(int nativeCanvas, String text, - int start, int end, float x, - float y, int flags, int paint); - private static native void native_drawGlyphs(int nativeCanvas, char[] glyphs, - int index, int count, float x, - float y, int flags, int paint); - private static native void native_drawTextRun(int nativeCanvas, String text, int start, int end, int contextStart, int contextEnd, float x, float y, int flags, int paint); diff --git a/include/gui/SurfaceTextureClient.h b/include/gui/SurfaceTextureClient.h index 5ec469e9b841..cfe2aa13b68b 100644 --- a/include/gui/SurfaceTextureClient.h +++ b/include/gui/SurfaceTextureClient.h @@ -21,6 +21,7 @@ #include <gui/SurfaceTexture.h> #include <ui/egl/android_natives.h> +#include <ui/Region.h> #include <utils/RefBase.h> #include <utils/threads.h> @@ -37,29 +38,24 @@ public: sp<ISurfaceTexture> getISurfaceTexture() const; -private: - friend class Surface; +protected: + SurfaceTextureClient(); + void setISurfaceTexture(const sp<ISurfaceTexture>& surfaceTexture); +private: // can't be copied SurfaceTextureClient& operator = (const SurfaceTextureClient& rhs); SurfaceTextureClient(const SurfaceTextureClient& rhs); + void init(); // ANativeWindow hooks - static int cancelBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer); - static int dequeueBuffer(ANativeWindow* window, ANativeWindowBuffer** buffer); - static int lockBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer); - static int perform(ANativeWindow* window, int operation, ...); - static int query(const ANativeWindow* window, int what, int* value); - static int queueBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer); - static int setSwapInterval(ANativeWindow* window, int interval); - - int cancelBuffer(ANativeWindowBuffer* buffer); - int dequeueBuffer(ANativeWindowBuffer** buffer); - int lockBuffer(ANativeWindowBuffer* buffer); - int perform(int operation, va_list args); - int query(int what, int* value) const; - int queueBuffer(ANativeWindowBuffer* buffer); - int setSwapInterval(int interval); + static int hook_cancelBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer); + static int hook_dequeueBuffer(ANativeWindow* window, ANativeWindowBuffer** buffer); + static int hook_lockBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer); + static int hook_perform(ANativeWindow* window, int operation, ...); + static int hook_query(const ANativeWindow* window, int what, int* value); + static int hook_queueBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer); + static int hook_setSwapInterval(ANativeWindow* window, int interval); int dispatchConnect(va_list args); int dispatchDisconnect(va_list args); @@ -71,26 +67,38 @@ private: int dispatchSetBuffersTimestamp(va_list args); int dispatchSetCrop(va_list args); int dispatchSetUsage(va_list args); - - int connect(int api); - int disconnect(int api); - int setBufferCount(int bufferCount); - int setBuffersDimensions(int w, int h); - int setBuffersFormat(int format); - int setBuffersTransform(int transform); - int setBuffersTimestamp(int64_t timestamp); - int setCrop(Rect const* rect); - int setUsage(uint32_t reqUsage); - - void freeAllBuffers(); - int getSlotFromBufferLocked(android_native_buffer_t* buffer) const; - - int getConnectedApi() const; + int dispatchLock(va_list args); + int dispatchUnlockAndPost(va_list args); + +protected: + virtual int cancelBuffer(ANativeWindowBuffer* buffer); + virtual int dequeueBuffer(ANativeWindowBuffer** buffer); + virtual int lockBuffer(ANativeWindowBuffer* buffer); + virtual int perform(int operation, va_list args); + virtual int query(int what, int* value) const; + virtual int queueBuffer(ANativeWindowBuffer* buffer); + virtual int setSwapInterval(int interval); + + virtual int connect(int api); + virtual int disconnect(int api); + virtual int setBufferCount(int bufferCount); + virtual int setBuffersDimensions(int w, int h); + virtual int setBuffersFormat(int format); + virtual int setBuffersTransform(int transform); + virtual int setBuffersTimestamp(int64_t timestamp); + virtual int setCrop(Rect const* rect); + virtual int setUsage(uint32_t reqUsage); + virtual int lock(ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds); + virtual int unlockAndPost(); enum { MIN_UNDEQUEUED_BUFFERS = SurfaceTexture::MIN_UNDEQUEUED_BUFFERS }; enum { NUM_BUFFER_SLOTS = SurfaceTexture::NUM_BUFFER_SLOTS }; enum { DEFAULT_FORMAT = PIXEL_FORMAT_RGBA_8888 }; +private: + void freeAllBuffers(); + int getSlotFromBufferLocked(android_native_buffer_t* buffer) const; + // mSurfaceTexture is the interface to the surface texture server. All // operations on the surface texture client ultimately translate into // interactions with the server using this interface. @@ -145,6 +153,12 @@ private: // variables of SurfaceTexture objects. It must be locked whenever the // member variables are accessed. mutable Mutex mMutex; + + // must be used from the lock/unlock thread + sp<GraphicBuffer> mLockedBuffer; + sp<GraphicBuffer> mPostedBuffer; + mutable Region mOldDirtyRegion; + bool mConnectedToCpu; }; }; // namespace android diff --git a/include/media/mediaplayer.h b/include/media/mediaplayer.h index ea5a9d3c9939..1136f6c0a917 100644 --- a/include/media/mediaplayer.h +++ b/include/media/mediaplayer.h @@ -25,6 +25,8 @@ #include <utils/KeyedVector.h> #include <utils/String8.h> +class ANativeWindow; + namespace android { class Surface; @@ -196,6 +198,8 @@ private: status_t prepareAsync_l(); status_t getDuration_l(int *msec); status_t setDataSource(const sp<IMediaPlayer>& player); + void disconnectNativeWindow(); + status_t reset_l(); sp<IMediaPlayer> mPlayer; thread_id_t mLockThreadId; @@ -218,6 +222,8 @@ private: int mVideoHeight; int mAudioSessionId; float mSendLevel; + sp<ANativeWindow> mConnectedWindow; + sp<IBinder> mConnectedWindowBinder; }; }; // namespace android diff --git a/include/media/stagefright/OMXCodec.h b/include/media/stagefright/OMXCodec.h index a042ddbbf2b2..20fcde52d828 100644 --- a/include/media/stagefright/OMXCodec.h +++ b/include/media/stagefright/OMXCodec.h @@ -355,7 +355,7 @@ struct CodecCapabilities { status_t QueryCodecs( const sp<IOMX> &omx, - const char *mimeType, bool queryDecoders, + const char *mimeType, bool queryDecoders, bool hwCodecOnly, Vector<CodecCapabilities> *results); } // namespace android diff --git a/include/surfaceflinger/Surface.h b/include/surfaceflinger/Surface.h index dc2a84562082..c2a494de41b6 100644 --- a/include/surfaceflinger/Surface.h +++ b/include/surfaceflinger/Surface.h @@ -28,6 +28,8 @@ #include <ui/Region.h> #include <ui/egl/android_natives.h> +#include <gui/SurfaceTextureClient.h> + #include <surfaceflinger/ISurface.h> #include <surfaceflinger/ISurfaceComposerClient.h> @@ -37,14 +39,9 @@ namespace android { // --------------------------------------------------------------------------- -class GraphicBuffer; -class GraphicBufferMapper; -class IOMX; class ISurfaceTexture; -class Rect; class Surface; class SurfaceComposerClient; -class SurfaceTextureClient; // --------------------------------------------------------------------------- @@ -129,8 +126,7 @@ private: // --------------------------------------------------------------------------- -class Surface - : public EGLNativeBase<ANativeWindow, Surface, RefBase> +class Surface : public SurfaceTextureClient { public: struct SurfaceInfo { @@ -158,32 +154,14 @@ public: sp<ISurfaceTexture> getSurfaceTexture(); // the lock/unlock APIs must be used from the same thread - status_t lock(SurfaceInfo* info, bool blocking = true); - status_t lock(SurfaceInfo* info, Region* dirty, bool blocking = true); + status_t lock(SurfaceInfo* info, Region* dirty = NULL); status_t unlockAndPost(); sp<IBinder> asBinder() const; private: - /* - * Android frameworks friends - * (eventually this should go away and be replaced by proper APIs) - */ - // camera and camcorder need access to the ISurface binder interface for preview - friend class CameraService; - friend class MediaRecorder; - // MediaPlayer needs access to ISurface for display - friend class MediaPlayer; - friend class IOMX; - friend class SoftwareRenderer; // this is just to be able to write some unit tests friend class Test; - // videoEditor preview classes - friend class VideoEditorPreviewController; - friend class PreviewRenderer; - -private: - friend class SurfaceComposerClient; friend class SurfaceControl; // can't be copied @@ -194,62 +172,27 @@ private: Surface(const Parcel& data, const sp<IBinder>& ref); ~Surface(); - - /* - * ANativeWindow hooks - */ - static int setSwapInterval(ANativeWindow* window, int interval); - static int dequeueBuffer(ANativeWindow* window, ANativeWindowBuffer** buffer); - static int cancelBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer); - static int lockBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer); - static int queueBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer); - static int query(const ANativeWindow* window, int what, int* value); - static int perform(ANativeWindow* window, int operation, ...); - - int setSwapInterval(int interval); - int dequeueBuffer(ANativeWindowBuffer** buffer); - int lockBuffer(ANativeWindowBuffer* buffer); - int queueBuffer(ANativeWindowBuffer* buffer); - int cancelBuffer(ANativeWindowBuffer* buffer); - int query(int what, int* value) const; - int perform(int operation, va_list args); - /* * private stuff... */ void init(); status_t validate(bool inCancelBuffer = false) const; - int getConnectedApi() const; - static void cleanCachedSurfacesLocked(); + virtual int query(int what, int* value) const; + // constants status_t mInitCheck; sp<ISurface> mSurface; - sp<SurfaceTextureClient> mSurfaceTextureClient; uint32_t mIdentity; PixelFormat mFormat; uint32_t mFlags; - - // protected by mSurfaceLock. These are also used from lock/unlock - // but in that case, they must be called form the same thread. - mutable Region mDirtyRegion; - - // must be used from the lock/unlock thread - sp<GraphicBuffer> mLockedBuffer; - sp<GraphicBuffer> mPostedBuffer; - mutable Region mOldDirtyRegion; - bool mReserved; // query() must be called from dequeueBuffer() thread uint32_t mWidth; uint32_t mHeight; - // Inherently thread-safe - mutable Mutex mSurfaceLock; - mutable Mutex mApiLock; - // A cache of Surface objects that have been deserialized into this process. static Mutex sCachedSurfacesLock; static DefaultKeyedVector<wp<IBinder>, wp<Surface> > sCachedSurfaces; diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp index 9185e1e9930c..dabe643f270c 100644 --- a/libs/gui/Surface.cpp +++ b/libs/gui/Surface.cpp @@ -46,59 +46,6 @@ namespace android { -// ---------------------------------------------------------------------- - -static status_t copyBlt( - const sp<GraphicBuffer>& dst, - const sp<GraphicBuffer>& src, - const Region& reg) -{ - // src and dst with, height and format must be identical. no verification - // is done here. - status_t err; - uint8_t const * src_bits = NULL; - err = src->lock(GRALLOC_USAGE_SW_READ_OFTEN, reg.bounds(), (void**)&src_bits); - LOGE_IF(err, "error locking src buffer %s", strerror(-err)); - - uint8_t* dst_bits = NULL; - err = dst->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, reg.bounds(), (void**)&dst_bits); - LOGE_IF(err, "error locking dst buffer %s", strerror(-err)); - - Region::const_iterator head(reg.begin()); - Region::const_iterator tail(reg.end()); - if (head != tail && src_bits && dst_bits) { - const size_t bpp = bytesPerPixel(src->format); - const size_t dbpr = dst->stride * bpp; - const size_t sbpr = src->stride * bpp; - - while (head != tail) { - const Rect& r(*head++); - ssize_t h = r.height(); - if (h <= 0) continue; - size_t size = r.width() * bpp; - uint8_t const * s = src_bits + (r.left + src->stride * r.top) * bpp; - uint8_t * d = dst_bits + (r.left + dst->stride * r.top) * bpp; - if (dbpr==sbpr && size==sbpr) { - size *= h; - h = 1; - } - do { - memcpy(d, s, size); - d += dbpr; - s += sbpr; - } while (--h > 0); - } - } - - if (src_bits) - src->unlock(); - - if (dst_bits) - dst->unlock(); - - return err; -} - // ============================================================================ // SurfaceControl // ============================================================================ @@ -277,7 +224,8 @@ sp<Surface> SurfaceControl::getSurface() const // --------------------------------------------------------------------------- Surface::Surface(const sp<SurfaceControl>& surface) - : mInitCheck(NO_INIT), + : SurfaceTextureClient(), + mInitCheck(NO_INIT), mSurface(surface->mSurface), mIdentity(surface->mIdentity), mFormat(surface->mFormat), mFlags(surface->mFlags), @@ -287,7 +235,8 @@ Surface::Surface(const sp<SurfaceControl>& surface) } Surface::Surface(const Parcel& parcel, const sp<IBinder>& ref) - : mInitCheck(NO_INIT) + : SurfaceTextureClient(), + mInitCheck(NO_INIT) { mSurface = interface_cast<ISurface>(ref); mIdentity = parcel.readInt32(); @@ -363,36 +312,21 @@ void Surface::cleanCachedSurfacesLocked() { void Surface::init() { - ANativeWindow::setSwapInterval = setSwapInterval; - ANativeWindow::dequeueBuffer = dequeueBuffer; - ANativeWindow::cancelBuffer = cancelBuffer; - ANativeWindow::lockBuffer = lockBuffer; - ANativeWindow::queueBuffer = queueBuffer; - ANativeWindow::query = query; - ANativeWindow::perform = perform; - if (mSurface != NULL) { sp<ISurfaceTexture> surfaceTexture(mSurface->getSurfaceTexture()); LOGE_IF(surfaceTexture==0, "got a NULL ISurfaceTexture from ISurface"); if (surfaceTexture != NULL) { - mSurfaceTextureClient = new SurfaceTextureClient(surfaceTexture); - mSurfaceTextureClient->setUsage(GraphicBuffer::USAGE_HW_RENDER); + setISurfaceTexture(surfaceTexture); + setUsage(GraphicBuffer::USAGE_HW_RENDER); } DisplayInfo dinfo; SurfaceComposerClient::getDisplayInfo(0, &dinfo); const_cast<float&>(ANativeWindow::xdpi) = dinfo.xdpi; const_cast<float&>(ANativeWindow::ydpi) = dinfo.ydpi; - - const_cast<int&>(ANativeWindow::minSwapInterval) = - mSurfaceTextureClient->minSwapInterval; - - const_cast<int&>(ANativeWindow::maxSwapInterval) = - mSurfaceTextureClient->maxSwapInterval; - const_cast<uint32_t&>(ANativeWindow::flags) = 0; - if (mSurfaceTextureClient != 0) { + if (surfaceTexture != NULL) { mInitCheck = NO_ERROR; } } @@ -402,7 +336,6 @@ Surface::~Surface() { // clear all references and trigger an IPC now, to make sure things // happen without delay, since these resources are quite heavy. - mSurfaceTextureClient.clear(); mSurface.clear(); IPCThreadState::self()->flushCommands(); } @@ -431,77 +364,6 @@ sp<IBinder> Surface::asBinder() const { // ---------------------------------------------------------------------------- -int Surface::setSwapInterval(ANativeWindow* window, int interval) { - Surface* self = getSelf(window); - return self->setSwapInterval(interval); -} - -int Surface::dequeueBuffer(ANativeWindow* window, - ANativeWindowBuffer** buffer) { - Surface* self = getSelf(window); - return self->dequeueBuffer(buffer); -} - -int Surface::cancelBuffer(ANativeWindow* window, - ANativeWindowBuffer* buffer) { - Surface* self = getSelf(window); - return self->cancelBuffer(buffer); -} - -int Surface::lockBuffer(ANativeWindow* window, - ANativeWindowBuffer* buffer) { - Surface* self = getSelf(window); - return self->lockBuffer(buffer); -} - -int Surface::queueBuffer(ANativeWindow* window, - ANativeWindowBuffer* buffer) { - Surface* self = getSelf(window); - return self->queueBuffer(buffer); -} - -int Surface::query(const ANativeWindow* window, - int what, int* value) { - const Surface* self = getSelf(window); - return self->query(what, value); -} - -int Surface::perform(ANativeWindow* window, - int operation, ...) { - va_list args; - va_start(args, operation); - Surface* self = getSelf(window); - int res = self->perform(operation, args); - va_end(args); - return res; -} - -// ---------------------------------------------------------------------------- - -int Surface::setSwapInterval(int interval) { - return mSurfaceTextureClient->setSwapInterval(interval); -} - -int Surface::dequeueBuffer(ANativeWindowBuffer** buffer) { - status_t err = mSurfaceTextureClient->dequeueBuffer(buffer); - if (err == NO_ERROR) { - mDirtyRegion.set(buffer[0]->width, buffer[0]->height); - } - return err; -} - -int Surface::cancelBuffer(ANativeWindowBuffer* buffer) { - return mSurfaceTextureClient->cancelBuffer(buffer); -} - -int Surface::lockBuffer(ANativeWindowBuffer* buffer) { - return mSurfaceTextureClient->lockBuffer(buffer); -} - -int Surface::queueBuffer(ANativeWindowBuffer* buffer) { - return mSurfaceTextureClient->queueBuffer(buffer); -} - int Surface::query(int what, int* value) const { switch (what) { case NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER: @@ -509,141 +371,39 @@ int Surface::query(int what, int* value) const { *value = 1; return NO_ERROR; case NATIVE_WINDOW_CONCRETE_TYPE: - // TODO: this is not needed anymore *value = NATIVE_WINDOW_SURFACE; return NO_ERROR; } - return mSurfaceTextureClient->query(what, value); -} - -int Surface::perform(int operation, va_list args) { - return mSurfaceTextureClient->perform(operation, args); + return SurfaceTextureClient::query(what, value); } // ---------------------------------------------------------------------------- -int Surface::getConnectedApi() const { - return mSurfaceTextureClient->getConnectedApi(); -} +status_t Surface::lock(SurfaceInfo* other, Region* dirtyIn) { + ANativeWindow_Buffer outBuffer; -// ---------------------------------------------------------------------------- - -status_t Surface::lock(SurfaceInfo* info, bool blocking) { - return Surface::lock(info, NULL, blocking); -} - -status_t Surface::lock(SurfaceInfo* other, Region* dirtyIn, bool blocking) -{ - if (getConnectedApi()) { - LOGE("Surface::lock(%p) failed. Already connected to another API", - (ANativeWindow*)this); - CallStack stack; - stack.update(); - stack.dump(""); - return INVALID_OPERATION; - } - - if (mApiLock.tryLock() != NO_ERROR) { - LOGE("calling Surface::lock from different threads!"); - CallStack stack; - stack.update(); - stack.dump(""); - return WOULD_BLOCK; + ARect temp; + ARect* inOutDirtyBounds = NULL; + if (dirtyIn) { + temp = dirtyIn->getBounds(); + inOutDirtyBounds = &temp; } - /* Here we're holding mApiLock */ - - if (mLockedBuffer != 0) { - LOGE("Surface::lock failed, already locked"); - mApiLock.unlock(); - return INVALID_OPERATION; - } - - // we're intending to do software rendering from this point - mSurfaceTextureClient->setUsage( - GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN); + status_t err = SurfaceTextureClient::lock(&outBuffer, inOutDirtyBounds); - ANativeWindowBuffer* out; - status_t err = mSurfaceTextureClient->dequeueBuffer(&out); - LOGE_IF(err, "dequeueBuffer failed (%s)", strerror(-err)); if (err == NO_ERROR) { - sp<GraphicBuffer> backBuffer(GraphicBuffer::getSelf(out)); - err = mSurfaceTextureClient->lockBuffer(backBuffer.get()); - LOGE_IF(err, "lockBuffer (handle=%p) failed (%s)", - backBuffer->handle, strerror(-err)); - if (err == NO_ERROR) { - const Rect bounds(backBuffer->width, backBuffer->height); - const Region boundsRegion(bounds); - Region scratch(boundsRegion); - Region& newDirtyRegion(dirtyIn ? *dirtyIn : scratch); - newDirtyRegion &= boundsRegion; - - // figure out if we can copy the frontbuffer back - const sp<GraphicBuffer>& frontBuffer(mPostedBuffer); - const bool canCopyBack = (frontBuffer != 0 && - backBuffer->width == frontBuffer->width && - backBuffer->height == frontBuffer->height && - backBuffer->format == frontBuffer->format && - !(mFlags & ISurfaceComposer::eDestroyBackbuffer)); - - // the dirty region we report to surfaceflinger is the one - // given by the user (as opposed to the one *we* return to the - // user). - mDirtyRegion = newDirtyRegion; - - if (canCopyBack) { - // copy the area that is invalid and not repainted this round - const Region copyback(mOldDirtyRegion.subtract(newDirtyRegion)); - if (!copyback.isEmpty()) - copyBlt(backBuffer, frontBuffer, copyback); - } else { - // if we can't copy-back anything, modify the user's dirty - // region to make sure they redraw the whole buffer - newDirtyRegion = boundsRegion; - } - - // keep track of the are of the buffer that is "clean" - // (ie: that will be redrawn) - mOldDirtyRegion = newDirtyRegion; - - void* vaddr; - status_t res = backBuffer->lock( - GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN, - newDirtyRegion.bounds(), &vaddr); - - LOGW_IF(res, "failed locking buffer (handle = %p)", - backBuffer->handle); - - mLockedBuffer = backBuffer; - other->w = backBuffer->width; - other->h = backBuffer->height; - other->s = backBuffer->stride; - other->usage = backBuffer->usage; - other->format = backBuffer->format; - other->bits = vaddr; - } + other->w = uint32_t(outBuffer.width); + other->h = uint32_t(outBuffer.height); + other->s = uint32_t(outBuffer.stride); + other->usage = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN; + other->format = uint32_t(outBuffer.format); + other->bits = outBuffer.bits; } - mApiLock.unlock(); return err; } - -status_t Surface::unlockAndPost() -{ - if (mLockedBuffer == 0) { - LOGE("Surface::unlockAndPost failed, no locked buffer"); - return INVALID_OPERATION; - } - - status_t err = mLockedBuffer->unlock(); - LOGE_IF(err, "failed unlocking buffer (%p)", mLockedBuffer->handle); - - err = mSurfaceTextureClient->queueBuffer(mLockedBuffer.get()); - LOGE_IF(err, "queueBuffer (handle=%p) failed (%s)", - mLockedBuffer->handle, strerror(-err)); - mPostedBuffer = mLockedBuffer; - mLockedBuffer = 0; - return err; +status_t Surface::unlockAndPost() { + return SurfaceTextureClient::unlockAndPost(); } // ---------------------------------------------------------------------------- diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp index 14104810f8bc..a12d40adcabb 100644 --- a/libs/gui/SurfaceTexture.cpp +++ b/libs/gui/SurfaceTexture.cpp @@ -495,7 +495,7 @@ status_t SurfaceTexture::setTransform(uint32_t transform) { } status_t SurfaceTexture::connect(int api) { - LOGV("SurfaceTexture::connect"); + LOGV("SurfaceTexture::connect(this=%p, %d)", this, api); Mutex::Autolock lock(mMutex); int err = NO_ERROR; switch (api) { @@ -504,6 +504,8 @@ status_t SurfaceTexture::connect(int api) { case NATIVE_WINDOW_API_MEDIA: case NATIVE_WINDOW_API_CAMERA: if (mConnectedApi != NO_CONNECTED_API) { + LOGE("connect: already connected (cur=%d, req=%d)", + mConnectedApi, api); err = -EINVAL; } else { mConnectedApi = api; @@ -517,7 +519,7 @@ status_t SurfaceTexture::connect(int api) { } status_t SurfaceTexture::disconnect(int api) { - LOGV("SurfaceTexture::disconnect"); + LOGV("SurfaceTexture::disconnect(this=%p, %d)", this, api); Mutex::Autolock lock(mMutex); int err = NO_ERROR; switch (api) { @@ -528,6 +530,8 @@ status_t SurfaceTexture::disconnect(int api) { if (mConnectedApi == api) { mConnectedApi = NO_CONNECTED_API; } else { + LOGE("disconnect: connected to another api (cur=%d, req=%d)", + mConnectedApi, api); err = -EINVAL; } break; diff --git a/libs/gui/SurfaceTextureClient.cpp b/libs/gui/SurfaceTextureClient.cpp index f39cabf09c15..d5b7c89f8f95 100644 --- a/libs/gui/SurfaceTextureClient.cpp +++ b/libs/gui/SurfaceTextureClient.cpp @@ -24,24 +24,45 @@ namespace android { SurfaceTextureClient::SurfaceTextureClient( - const sp<ISurfaceTexture>& surfaceTexture): - mSurfaceTexture(surfaceTexture), mAllocator(0), mReqWidth(0), - mReqHeight(0), mReqFormat(0), mReqUsage(0), - mTimestamp(NATIVE_WINDOW_TIMESTAMP_AUTO), - mQueryWidth(0), mQueryHeight(0), mQueryFormat(0), - mMutex() { + const sp<ISurfaceTexture>& surfaceTexture) +{ + SurfaceTextureClient::init(); + SurfaceTextureClient::setISurfaceTexture(surfaceTexture); +} + +SurfaceTextureClient::SurfaceTextureClient() { + SurfaceTextureClient::init(); +} + +void SurfaceTextureClient::init() { // Initialize the ANativeWindow function pointers. - ANativeWindow::setSwapInterval = setSwapInterval; - ANativeWindow::dequeueBuffer = dequeueBuffer; - ANativeWindow::cancelBuffer = cancelBuffer; - ANativeWindow::lockBuffer = lockBuffer; - ANativeWindow::queueBuffer = queueBuffer; - ANativeWindow::query = query; - ANativeWindow::perform = perform; + ANativeWindow::setSwapInterval = hook_setSwapInterval; + ANativeWindow::dequeueBuffer = hook_dequeueBuffer; + ANativeWindow::cancelBuffer = hook_cancelBuffer; + ANativeWindow::lockBuffer = hook_lockBuffer; + ANativeWindow::queueBuffer = hook_queueBuffer; + ANativeWindow::query = hook_query; + ANativeWindow::perform = hook_perform; const_cast<int&>(ANativeWindow::minSwapInterval) = 0; const_cast<int&>(ANativeWindow::maxSwapInterval) = 1; + mReqWidth = 0; + mReqHeight = 0; + mReqFormat = 0; + mReqUsage = 0; + mTimestamp = NATIVE_WINDOW_TIMESTAMP_AUTO; + mQueryWidth = 0; + mQueryHeight = 0; + mQueryFormat = 0; + mConnectedToCpu = false; +} + +void SurfaceTextureClient::setISurfaceTexture( + const sp<ISurfaceTexture>& surfaceTexture) +{ + mSurfaceTexture = surfaceTexture; + // Get a reference to the allocator. mAllocator = mSurfaceTexture->getAllocator(); } @@ -50,42 +71,42 @@ sp<ISurfaceTexture> SurfaceTextureClient::getISurfaceTexture() const { return mSurfaceTexture; } -int SurfaceTextureClient::setSwapInterval(ANativeWindow* window, int interval) { +int SurfaceTextureClient::hook_setSwapInterval(ANativeWindow* window, int interval) { SurfaceTextureClient* c = getSelf(window); return c->setSwapInterval(interval); } -int SurfaceTextureClient::dequeueBuffer(ANativeWindow* window, +int SurfaceTextureClient::hook_dequeueBuffer(ANativeWindow* window, ANativeWindowBuffer** buffer) { SurfaceTextureClient* c = getSelf(window); return c->dequeueBuffer(buffer); } -int SurfaceTextureClient::cancelBuffer(ANativeWindow* window, +int SurfaceTextureClient::hook_cancelBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer) { SurfaceTextureClient* c = getSelf(window); return c->cancelBuffer(buffer); } -int SurfaceTextureClient::lockBuffer(ANativeWindow* window, +int SurfaceTextureClient::hook_lockBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer) { SurfaceTextureClient* c = getSelf(window); return c->lockBuffer(buffer); } -int SurfaceTextureClient::queueBuffer(ANativeWindow* window, +int SurfaceTextureClient::hook_queueBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer) { SurfaceTextureClient* c = getSelf(window); return c->queueBuffer(buffer); } -int SurfaceTextureClient::query(const ANativeWindow* window, +int SurfaceTextureClient::hook_query(const ANativeWindow* window, int what, int* value) { const SurfaceTextureClient* c = getSelf(window); return c->query(what, value); } -int SurfaceTextureClient::perform(ANativeWindow* window, int operation, ...) { +int SurfaceTextureClient::hook_perform(ANativeWindow* window, int operation, ...) { va_list args; va_start(args, operation); SurfaceTextureClient* c = getSelf(window); @@ -219,7 +240,6 @@ int SurfaceTextureClient::query(int what, int* value) const { *value = 0; return NO_ERROR; case NATIVE_WINDOW_CONCRETE_TYPE: - // TODO: this is not needed anymore *value = NATIVE_WINDOW_SURFACE_TEXTURE_CLIENT; return NO_ERROR; } @@ -260,6 +280,12 @@ int SurfaceTextureClient::perform(int operation, va_list args) case NATIVE_WINDOW_SET_BUFFERS_FORMAT: res = dispatchSetBuffersFormat(args); break; + case NATIVE_WINDOW_LOCK: + res = dispatchLock(args); + break; + case NATIVE_WINDOW_UNLOCK_AND_POST: + res = dispatchUnlockAndPost(args); + break; default: res = NAME_NOT_FOUND; break; @@ -324,28 +350,37 @@ int SurfaceTextureClient::dispatchSetBuffersTimestamp(va_list args) { return setBuffersTimestamp(timestamp); } +int SurfaceTextureClient::dispatchLock(va_list args) { + ANativeWindow_Buffer* outBuffer = va_arg(args, ANativeWindow_Buffer*); + ARect* inOutDirtyBounds = va_arg(args, ARect*); + return lock(outBuffer, inOutDirtyBounds); +} + +int SurfaceTextureClient::dispatchUnlockAndPost(va_list args) { + return unlockAndPost(); +} + + int SurfaceTextureClient::connect(int api) { LOGV("SurfaceTextureClient::connect"); Mutex::Autolock lock(mMutex); - return mSurfaceTexture->connect(api); + int err = mSurfaceTexture->connect(api); + if (!err && api == NATIVE_WINDOW_API_CPU) { + mConnectedToCpu = true; + } + return err; } int SurfaceTextureClient::disconnect(int api) { LOGV("SurfaceTextureClient::disconnect"); Mutex::Autolock lock(mMutex); - return mSurfaceTexture->disconnect(api); -} - -int SurfaceTextureClient::getConnectedApi() const -{ - // XXX: This method will be going away shortly, and is currently bogus. It - // always returns "nothing is connected". It will go away once Surface gets - // updated to actually connect as the 'CPU' API when locking a buffer. - Mutex::Autolock lock(mMutex); - return 0; + int err = mSurfaceTexture->disconnect(api); + if (!err && api == NATIVE_WINDOW_API_CPU) { + mConnectedToCpu = false; + } + return err; } - int SurfaceTextureClient::setUsage(uint32_t reqUsage) { LOGV("SurfaceTextureClient::setUsage"); @@ -443,4 +478,160 @@ void SurfaceTextureClient::freeAllBuffers() { } } +// ---------------------------------------------------------------------- +// the lock/unlock APIs must be used from the same thread + +static status_t copyBlt( + const sp<GraphicBuffer>& dst, + const sp<GraphicBuffer>& src, + const Region& reg) +{ + // src and dst with, height and format must be identical. no verification + // is done here. + status_t err; + uint8_t const * src_bits = NULL; + err = src->lock(GRALLOC_USAGE_SW_READ_OFTEN, reg.bounds(), (void**)&src_bits); + LOGE_IF(err, "error locking src buffer %s", strerror(-err)); + + uint8_t* dst_bits = NULL; + err = dst->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, reg.bounds(), (void**)&dst_bits); + LOGE_IF(err, "error locking dst buffer %s", strerror(-err)); + + Region::const_iterator head(reg.begin()); + Region::const_iterator tail(reg.end()); + if (head != tail && src_bits && dst_bits) { + const size_t bpp = bytesPerPixel(src->format); + const size_t dbpr = dst->stride * bpp; + const size_t sbpr = src->stride * bpp; + + while (head != tail) { + const Rect& r(*head++); + ssize_t h = r.height(); + if (h <= 0) continue; + size_t size = r.width() * bpp; + uint8_t const * s = src_bits + (r.left + src->stride * r.top) * bpp; + uint8_t * d = dst_bits + (r.left + dst->stride * r.top) * bpp; + if (dbpr==sbpr && size==sbpr) { + size *= h; + h = 1; + } + do { + memcpy(d, s, size); + d += dbpr; + s += sbpr; + } while (--h > 0); + } + } + + if (src_bits) + src->unlock(); + + if (dst_bits) + dst->unlock(); + + return err; +} + +// ---------------------------------------------------------------------------- + +status_t SurfaceTextureClient::lock( + ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds) +{ + if (mLockedBuffer != 0) { + LOGE("Surface::lock failed, already locked"); + return INVALID_OPERATION; + } + + if (!mConnectedToCpu) { + int err = SurfaceTextureClient::connect(NATIVE_WINDOW_API_CPU); + if (err) { + return err; + } + // we're intending to do software rendering from this point + setUsage(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN); + } + + ANativeWindowBuffer* out; + status_t err = dequeueBuffer(&out); + LOGE_IF(err, "dequeueBuffer failed (%s)", strerror(-err)); + if (err == NO_ERROR) { + sp<GraphicBuffer> backBuffer(GraphicBuffer::getSelf(out)); + err = lockBuffer(backBuffer.get()); + LOGE_IF(err, "lockBuffer (handle=%p) failed (%s)", + backBuffer->handle, strerror(-err)); + if (err == NO_ERROR) { + const Rect bounds(backBuffer->width, backBuffer->height); + + Region newDirtyRegion; + if (inOutDirtyBounds) { + newDirtyRegion.set(static_cast<Rect const&>(*inOutDirtyBounds)); + newDirtyRegion.andSelf(bounds); + } else { + newDirtyRegion.set(bounds); + } + + // figure out if we can copy the frontbuffer back + const sp<GraphicBuffer>& frontBuffer(mPostedBuffer); + const bool canCopyBack = (frontBuffer != 0 && + backBuffer->width == frontBuffer->width && + backBuffer->height == frontBuffer->height && + backBuffer->format == frontBuffer->format); + + if (canCopyBack) { + // copy the area that is invalid and not repainted this round + const Region copyback(mOldDirtyRegion.subtract(newDirtyRegion)); + if (!copyback.isEmpty()) + copyBlt(backBuffer, frontBuffer, copyback); + } else { + // if we can't copy-back anything, modify the user's dirty + // region to make sure they redraw the whole buffer + newDirtyRegion.set(bounds); + } + + // keep track of the are of the buffer that is "clean" + // (ie: that will be redrawn) + mOldDirtyRegion = newDirtyRegion; + + if (inOutDirtyBounds) { + *inOutDirtyBounds = newDirtyRegion.getBounds(); + } + + void* vaddr; + status_t res = backBuffer->lock( + GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN, + newDirtyRegion.bounds(), &vaddr); + + LOGW_IF(res, "failed locking buffer (handle = %p)", + backBuffer->handle); + + mLockedBuffer = backBuffer; + outBuffer->width = backBuffer->width; + outBuffer->height = backBuffer->height; + outBuffer->stride = backBuffer->stride; + outBuffer->format = backBuffer->format; + outBuffer->bits = vaddr; + } + } + return err; +} + +status_t SurfaceTextureClient::unlockAndPost() +{ + if (mLockedBuffer == 0) { + LOGE("Surface::unlockAndPost failed, no locked buffer"); + return INVALID_OPERATION; + } + + status_t err = mLockedBuffer->unlock(); + LOGE_IF(err, "failed unlocking buffer (%p)", mLockedBuffer->handle); + + err = queueBuffer(mLockedBuffer.get()); + LOGE_IF(err, "queueBuffer (handle=%p) failed (%s)", + mLockedBuffer->handle, strerror(-err)); + + mPostedBuffer = mLockedBuffer; + mLockedBuffer = 0; + return err; +} + }; // namespace android diff --git a/libs/gui/tests/SurfaceTextureClient_test.cpp b/libs/gui/tests/SurfaceTextureClient_test.cpp index 519b40e44a57..2b8f204563cd 100644 --- a/libs/gui/tests/SurfaceTextureClient_test.cpp +++ b/libs/gui/tests/SurfaceTextureClient_test.cpp @@ -613,4 +613,90 @@ TEST_F(SurfaceTextureClientTest, QueryFormatAfterSettingWorks) { } } +class MultiSurfaceTextureClientTest : public ::testing::Test { + +public: + MultiSurfaceTextureClientTest() : + mEglDisplay(EGL_NO_DISPLAY), + mEglContext(EGL_NO_CONTEXT) { + for (int i = 0; i < NUM_SURFACE_TEXTURES; i++) { + mEglSurfaces[i] = EGL_NO_CONTEXT; + } + } + +protected: + + enum { NUM_SURFACE_TEXTURES = 32 }; + + virtual void SetUp() { + mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + ASSERT_NE(EGL_NO_DISPLAY, mEglDisplay); + + EGLint majorVersion, minorVersion; + EXPECT_TRUE(eglInitialize(mEglDisplay, &majorVersion, &minorVersion)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + + EGLConfig myConfig; + EGLint numConfigs = 0; + EGLint configAttribs[] = { + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_NONE + }; + EXPECT_TRUE(eglChooseConfig(mEglDisplay, configAttribs, &myConfig, 1, + &numConfigs)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + + mEglContext = eglCreateContext(mEglDisplay, myConfig, EGL_NO_CONTEXT, + 0); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + ASSERT_NE(EGL_NO_CONTEXT, mEglContext); + + for (int i = 0; i < NUM_SURFACE_TEXTURES; i++) { + sp<SurfaceTexture> st(new SurfaceTexture(i)); + sp<SurfaceTextureClient> stc(new SurfaceTextureClient(st)); + mEglSurfaces[i] = eglCreateWindowSurface(mEglDisplay, myConfig, + static_cast<ANativeWindow*>(stc.get()), NULL); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + ASSERT_NE(EGL_NO_SURFACE, mEglSurfaces[i]); + } + } + + virtual void TearDown() { + eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, + EGL_NO_CONTEXT); + + for (int i = 0; i < NUM_SURFACE_TEXTURES; i++) { + if (mEglSurfaces[i] != EGL_NO_SURFACE) { + eglDestroySurface(mEglDisplay, mEglSurfaces[i]); + } + } + + if (mEglContext != EGL_NO_CONTEXT) { + eglDestroyContext(mEglDisplay, mEglContext); + } + + if (mEglDisplay != EGL_NO_DISPLAY) { + eglTerminate(mEglDisplay); + } + } + + EGLDisplay mEglDisplay; + EGLSurface mEglSurfaces[NUM_SURFACE_TEXTURES]; + EGLContext mEglContext; +}; + +// XXX: This test is disabled because it causes a hang on some devices. See bug +// 5015672. +TEST_F(MultiSurfaceTextureClientTest, DISABLED_MakeCurrentBetweenSurfacesWorks) { + for (int iter = 0; iter < 8; iter++) { + for (int i = 0; i < NUM_SURFACE_TEXTURES; i++) { + eglMakeCurrent(mEglDisplay, mEglSurfaces[i], mEglSurfaces[i], + mEglContext); + glClear(GL_COLOR_BUFFER_BIT); + eglSwapBuffers(mEglDisplay, mEglSurfaces[i]); + } + } +} + } // namespace android diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp index e232dddd3951..7114b6adc30e 100644 --- a/libs/hwui/Caches.cpp +++ b/libs/hwui/Caches.cpp @@ -33,6 +33,16 @@ ANDROID_SINGLETON_STATIC_INSTANCE(Caches); namespace uirenderer { /////////////////////////////////////////////////////////////////////////////// +// Macros +/////////////////////////////////////////////////////////////////////////////// + +#if DEBUG_CACHE_FLUSH + #define FLUSH_LOGD(...) LOGD(__VA_ARGS__) +#else + #define FLUSH_LOGD(...) +#endif + +/////////////////////////////////////////////////////////////////////////////// // Constructors/destructor /////////////////////////////////////////////////////////////////////////////// @@ -150,6 +160,30 @@ void Caches::deleteLayerDeferred(Layer* layer) { mLayerGarbage.push(layer); } +void Caches::flush(FlushMode mode) { + FLUSH_LOGD("Flushing caches (mode %d)", mode); + + clearGarbage(); + + switch (mode) { + case kFlushMode_Full: + textureCache.clear(); + patchCache.clear(); + dropShadowCache.clear(); + gradientCache.clear(); + // fall through + case kFlushMode_Moderate: + layerCache.clear(); + pathCache.clear(); + roundRectShapeCache.clear(); + circleShapeCache.clear(); + ovalShapeCache.clear(); + rectShapeCache.clear(); + arcShapeCache.clear(); + break; + } +} + /////////////////////////////////////////////////////////////////////////////// // VBO /////////////////////////////////////////////////////////////////////////////// diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h index e64d8ac48d56..76dff4b4078b 100644 --- a/libs/hwui/Caches.h +++ b/libs/hwui/Caches.h @@ -100,6 +100,18 @@ class Caches: public Singleton<Caches> { Vector<Layer*> mLayerGarbage; public: + enum FlushMode { + kFlushMode_Moderate = 0, + kFlushMode_Full + }; + + /** + * Flush the cache. + * + * @param mode Indicates how much of the cache should be flushed + */ + void flush(FlushMode mode); + /** * Indicates whether the renderer is in debug mode. * This debug mode provides limited information to app developers. diff --git a/libs/hwui/Debug.h b/libs/hwui/Debug.h index 2cdc8c39e9f9..5db73db9fc35 100644 --- a/libs/hwui/Debug.h +++ b/libs/hwui/Debug.h @@ -26,6 +26,9 @@ // Turn on to enable memory usage summary on each frame #define DEBUG_MEMORY_USAGE 0 +// Turn on to enable debugging of cache flushes +#define DEBUG_CACHE_FLUSH 1 + // Turn on to enable layers debugging when rendered as regions #define DEBUG_LAYERS_AS_REGIONS 0 diff --git a/libs/ui/FramebufferNativeWindow.cpp b/libs/ui/FramebufferNativeWindow.cpp index 9c10c754ba0a..794747d654f2 100644 --- a/libs/ui/FramebufferNativeWindow.cpp +++ b/libs/ui/FramebufferNativeWindow.cpp @@ -303,6 +303,10 @@ int FramebufferNativeWindow::perform(ANativeWindow* window, case NATIVE_WINDOW_CONNECT: case NATIVE_WINDOW_DISCONNECT: break; + case NATIVE_WINDOW_LOCK: + return INVALID_OPERATION; + case NATIVE_WINDOW_UNLOCK_AND_POST: + return INVALID_OPERATION; default: return NAME_NOT_FOUND; } diff --git a/media/libeffects/preprocessing/Android.mk b/media/libeffects/preprocessing/Android.mk new file mode 100755 index 000000000000..77d40b6ca591 --- /dev/null +++ b/media/libeffects/preprocessing/Android.mk @@ -0,0 +1,32 @@ +LOCAL_PATH:= $(call my-dir) + +# audio preprocessing wrapper +include $(CLEAR_VARS) + +LOCAL_MODULE:= libaudiopreprocessing +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/soundfx + +LOCAL_SRC_FILES:= \ + PreProcessing.cpp + +LOCAL_C_INCLUDES += \ + external/webrtc/src \ + external/webrtc/src/modules/interface \ + external/webrtc/src/modules/audio_processing/main/interface \ + system/media/audio_effects/include + +LOCAL_C_INCLUDES += $(call include-path-for, speex) + +LOCAL_SHARED_LIBRARIES := \ + libwebrtc_audio_preprocessing \ + libspeexresampler \ + libutils + +ifeq ($(TARGET_SIMULATOR),true) +LOCAL_LDLIBS += -ldl +else +LOCAL_SHARED_LIBRARIES += libdl +endif + +include $(BUILD_SHARED_LIBRARY) diff --git a/media/libeffects/preprocessing/PreProcessing.cpp b/media/libeffects/preprocessing/PreProcessing.cpp new file mode 100755 index 000000000000..ba286a15eeeb --- /dev/null +++ b/media/libeffects/preprocessing/PreProcessing.cpp @@ -0,0 +1,1609 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdlib.h> +#include <string.h> +#define LOG_TAG "PreProcessing" +//#define LOG_NDEBUG 0 +#include <utils/Log.h> +#include <utils/Timers.h> +#include <hardware/audio_effect.h> +#include <audio_effects/effect_aec.h> +#include <audio_effects/effect_agc.h> +#include <audio_effects/effect_ns.h> +#include "modules/interface/module_common_types.h" +#include "modules/audio_processing/main/interface/audio_processing.h" +#include "speex/speex_resampler.h" + + +//------------------------------------------------------------------------------ +// local definitions +//------------------------------------------------------------------------------ + +// maximum number of sessions +#define PREPROC_NUM_SESSIONS 8 + +// types of pre processing modules +enum preproc_id +{ + PREPROC_AGC, // Automatic Gain Control + PREPROC_AEC, // Acoustic Echo Canceler + PREPROC_NS, // Noise Suppressor + PREPROC_NUM_EFFECTS +}; + +// Session state +enum preproc_session_state { + PREPROC_SESSION_STATE_INIT, // initialized + PREPROC_SESSION_STATE_CONFIG // configuration received +}; + +// Effect/Preprocessor state +enum preproc_effect_state { + PREPROC_EFFECT_STATE_INIT, // initialized + PREPROC_EFFECT_STATE_CREATED, // webRTC engine created + PREPROC_EFFECT_STATE_CONFIG, // configuration received/disabled + PREPROC_EFFECT_STATE_ACTIVE // active/enabled +}; + +// handle on webRTC engine +typedef void* preproc_fx_handle_t; + +typedef struct preproc_session_s preproc_session_t; +typedef struct preproc_effect_s preproc_effect_t; +typedef struct preproc_ops_s preproc_ops_t; + +// Effect operation table. Functions for all pre processors are declared in sPreProcOps[] table. +// Function pointer can be null if no action required. +struct preproc_ops_s { + int (* create)(preproc_effect_t *fx); + int (* init)(preproc_effect_t *fx); + int (* reset)(preproc_effect_t *fx); + void (* enable)(preproc_effect_t *fx); + void (* disable)(preproc_effect_t *fx); + int (* set_parameter)(preproc_effect_t *fx, void *param, void *value); + int (* get_parameter)(preproc_effect_t *fx, void *param, size_t *size, void *value); + int (* set_device)(preproc_effect_t *fx, uint32_t device); +}; + +// Effect context +struct preproc_effect_s { + const struct effect_interface_s *itfe; + uint32_t procId; // type of pre processor (enum preproc_id) + uint32_t state; // current state (enum preproc_effect_state) + preproc_session_t *session; // session the effect is on + const preproc_ops_t *ops; // effect ops table + preproc_fx_handle_t engine; // handle on webRTC engine +}; + +// Session context +struct preproc_session_s { + struct preproc_effect_s effects[PREPROC_NUM_EFFECTS]; // effects in this session + uint32_t state; // current state (enum preproc_session_state) + int id; // audio session ID + int io; // handle of input stream this session is on + webrtc::AudioProcessing* apm; // handle on webRTC audio processing module (APM) + size_t apmFrameCount; // buffer size for webRTC process (10 ms) + uint32_t apmSamplingRate; // webRTC APM sampling rate (8/16 or 32 kHz) + size_t frameCount; // buffer size before input resampler ( <=> apmFrameCount) + uint32_t samplingRate; // sampling rate at effect process interface + uint32_t inChannelCount; // input channel count + uint32_t outChannelCount; // output channel count + uint32_t createdMsk; // bit field containing IDs of crested pre processors + uint32_t enabledMsk; // bit field containing IDs of enabled pre processors + uint32_t processedMsk; // bit field containing IDs of pre processors already + // processed in current round + webrtc::AudioFrame *procFrame; // audio frame passed to webRTC AMP ProcessStream() + int16_t *inBuf; // input buffer used when resampling + size_t inBufSize; // input buffer size in frames + size_t framesIn; // number of frames in input buffer + SpeexResamplerState *inResampler; // handle on input speex resampler + int16_t *outBuf; // output buffer used when resampling + size_t outBufSize; // output buffer size in frames + size_t framesOut; // number of frames in output buffer + SpeexResamplerState *outResampler; // handle on output speex resampler + uint32_t revChannelCount; // number of channels on reverse stream + uint32_t revEnabledMsk; // bit field containing IDs of enabled pre processors + // with reverse channel + uint32_t revProcessedMsk; // bit field containing IDs of pre processors with reverse + // channel already processed in current round + webrtc::AudioFrame *revFrame; // audio frame passed to webRTC AMP AnalyzeReverseStream() + int16_t *revBuf; // reverse channel input buffer + size_t revBufSize; // reverse channel input buffer size + size_t framesRev; // number of frames in reverse channel input buffer + SpeexResamplerState *revResampler; // handle on reverse channel input speex resampler +}; + +//------------------------------------------------------------------------------ +// Effect descriptors +//------------------------------------------------------------------------------ + +// UUIDs for effect types have been generated from http://www.itu.int/ITU-T/asn1/uuid.html +// as the pre processing effects are not defined by OpenSL ES + +// Automatic Gain Control +static const effect_descriptor_t sAgcDescriptor = { + { 0x0a8abfe0, 0x654c, 0x11e0, 0xba26, { 0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b } }, // type + { 0xaa8130e0, 0x66fc, 0x11e0, 0xbad0, { 0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b } }, // uuid + EFFECT_CONTROL_API_VERSION, + (EFFECT_FLAG_TYPE_PRE_PROC|EFFECT_FLAG_DEVICE_IND), + 0, //FIXME indicate CPU load + 0, //FIXME indicate memory usage + "Automatic Gain Control", + "The Android Open Source Project" +}; + +// Acoustic Echo Cancellation +static const effect_descriptor_t sAecDescriptor = { + { 0x7b491460, 0x8d4d, 0x11e0, 0xbd61, { 0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b } }, // type + { 0xbb392ec0, 0x8d4d, 0x11e0, 0xa896, { 0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b } }, // uuid + EFFECT_CONTROL_API_VERSION, + (EFFECT_FLAG_TYPE_PRE_PROC|EFFECT_FLAG_DEVICE_IND), + 0, //FIXME indicate CPU load + 0, //FIXME indicate memory usage + "Acoustic Echo Canceler", + "The Android Open Source Project" +}; + +// Noise suppression +static const effect_descriptor_t sNsDescriptor = { + { 0x58b4b260, 0x8e06, 0x11e0, 0xaa8e, { 0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b } }, // type + { 0xc06c8400, 0x8e06, 0x11e0, 0x9cb6, { 0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b } }, // uuid + EFFECT_CONTROL_API_VERSION, + (EFFECT_FLAG_TYPE_PRE_PROC|EFFECT_FLAG_DEVICE_IND), + 0, //FIXME indicate CPU load + 0, //FIXME indicate memory usage + "Noise Suppression", + "The Android Open Source Project" +}; + + +static const effect_descriptor_t *sDescriptors[PREPROC_NUM_EFFECTS] = { + &sAgcDescriptor, + &sAecDescriptor, + &sNsDescriptor +}; + +//------------------------------------------------------------------------------ +// Helper functions +//------------------------------------------------------------------------------ + +const effect_uuid_t * const sUuidToPreProcTable[PREPROC_NUM_EFFECTS] = { + FX_IID_AGC, + FX_IID_AEC, + FX_IID_NS +}; + + +const effect_uuid_t * ProcIdToUuid(int procId) +{ + if (procId >= PREPROC_NUM_EFFECTS) { + return EFFECT_UUID_NULL; + } + return sUuidToPreProcTable[procId]; +} + +uint32_t UuidToProcId(const effect_uuid_t * uuid) +{ + size_t i; + for (i = 0; i < PREPROC_NUM_EFFECTS; i++) { + if (memcmp(uuid, sUuidToPreProcTable[i], sizeof(*uuid)) == 0) { + break; + } + } + return i; +} + +bool HasReverseStream(uint32_t procId) +{ + if (procId == PREPROC_AEC) { + return true; + } + return false; +} + + +//------------------------------------------------------------------------------ +// Automatic Gain Control (AGC) +//------------------------------------------------------------------------------ + +static const int kAgcDefaultTargetLevel = 0; +static const int kAgcDefaultCompGain = 90; +static const bool kAgcDefaultLimiter = true; + +int AgcInit (preproc_effect_t *effect) +{ + LOGV("AgcInit"); + webrtc::GainControl *agc = static_cast<webrtc::GainControl *>(effect->engine); + agc->set_mode(webrtc::GainControl::kFixedDigital); + agc->set_target_level_dbfs(kAgcDefaultTargetLevel); + agc->set_compression_gain_db(kAgcDefaultCompGain); + agc->enable_limiter(kAgcDefaultLimiter); + return 0; +} + +int AgcCreate(preproc_effect_t *effect) +{ + webrtc::GainControl *agc = effect->session->apm->gain_control(); + LOGV("AgcCreate got agc %p", agc); + if (agc == NULL) { + LOGW("AgcCreate Error"); + return -ENOMEM; + } + effect->engine = static_cast<preproc_fx_handle_t>(agc); + AgcInit(effect); + return 0; +} + +int AgcGetParameter(preproc_effect_t *effect, + void *pParam, + size_t *pValueSize, + void *pValue) +{ + int status = 0; + uint32_t param = *(uint32_t *)pParam; + t_agc_settings *pProperties = (t_agc_settings *)pValue; + webrtc::GainControl *agc = static_cast<webrtc::GainControl *>(effect->engine); + + switch (param) { + case AGC_PARAM_TARGET_LEVEL: + case AGC_PARAM_COMP_GAIN: + if (*pValueSize < sizeof(int16_t)) { + *pValueSize = 0; + return -EINVAL; + } + break; + case AGC_PARAM_LIMITER_ENA: + if (*pValueSize < sizeof(bool)) { + *pValueSize = 0; + return -EINVAL; + } + break; + case AGC_PARAM_PROPERTIES: + if (*pValueSize < sizeof(t_agc_settings)) { + *pValueSize = 0; + return -EINVAL; + } + break; + + default: + LOGW("AgcGetParameter() unknown param %08x", param); + status = -EINVAL; + break; + } + + switch (param) { + case AGC_PARAM_TARGET_LEVEL: + *(int16_t *) pValue = (int16_t)(agc->target_level_dbfs() * -100); + LOGV("AgcGetParameter() target level %d milliBels", *(int16_t *) pValue); + break; + case AGC_PARAM_COMP_GAIN: + *(int16_t *) pValue = (int16_t)(agc->compression_gain_db() * 100); + LOGV("AgcGetParameter() comp gain %d milliBels", *(int16_t *) pValue); + break; + case AGC_PARAM_LIMITER_ENA: + *(bool *) pValue = (bool)agc->is_limiter_enabled(); + LOGV("AgcGetParameter() limiter enabled %s", + (*(int16_t *) pValue != 0) ? "true" : "false"); + break; + case AGC_PARAM_PROPERTIES: + pProperties->targetLevel = (int16_t)(agc->target_level_dbfs() * -100); + pProperties->compGain = (int16_t)(agc->compression_gain_db() * 100); + pProperties->limiterEnabled = (bool)agc->is_limiter_enabled(); + break; + default: + LOGW("AgcGetParameter() unknown param %d", param); + status = -EINVAL; + break; + } + return status; +} + +int AgcSetParameter (preproc_effect_t *effect, void *pParam, void *pValue) +{ + int status = 0; + uint32_t param = *(uint32_t *)pParam; + t_agc_settings *pProperties = (t_agc_settings *)pValue; + webrtc::GainControl *agc = static_cast<webrtc::GainControl *>(effect->engine); + + switch (param) { + case AGC_PARAM_TARGET_LEVEL: + LOGV("AgcSetParameter() target level %d milliBels", *(int16_t *)pValue); + status = agc->set_target_level_dbfs(-(*(int16_t *)pValue / 100)); + break; + case AGC_PARAM_COMP_GAIN: + LOGV("AgcSetParameter() comp gain %d milliBels", *(int16_t *)pValue); + status = agc->set_compression_gain_db(*(int16_t *)pValue / 100); + break; + case AGC_PARAM_LIMITER_ENA: + LOGV("AgcSetParameter() limiter enabled %s", *(bool *)pValue ? "true" : "false"); + status = agc->enable_limiter(*(bool *)pValue); + break; + case AGC_PARAM_PROPERTIES: + LOGV("AgcSetParameter() properties level %d, gain %d limiter %d", + pProperties->targetLevel, + pProperties->compGain, + pProperties->limiterEnabled); + status = agc->set_target_level_dbfs(-(pProperties->targetLevel / 100)); + if (status != 0) break; + status = agc->set_compression_gain_db(pProperties->compGain / 100); + if (status != 0) break; + status = agc->enable_limiter(pProperties->limiterEnabled); + break; + default: + LOGW("AgcSetParameter() unknown param %08x value %08x", param, *(uint32_t *)pValue); + status = -EINVAL; + break; + } + + LOGV("AgcSetParameter() done status %d", status); + + return status; +} + +void AgcEnable(preproc_effect_t *effect) +{ + webrtc::GainControl *agc = static_cast<webrtc::GainControl *>(effect->engine); + LOGV("AgcEnable agc %p", agc); + agc->Enable(true); +} + +void AgcDisable(preproc_effect_t *effect) +{ + LOGV("AgcDisable"); + webrtc::GainControl *agc = static_cast<webrtc::GainControl *>(effect->engine); + agc->Enable(false); +} + + +static const preproc_ops_t sAgcOps = { + AgcCreate, + AgcInit, + NULL, + AgcEnable, + AgcDisable, + AgcSetParameter, + AgcGetParameter, + NULL +}; + + +//------------------------------------------------------------------------------ +// Acoustic Echo Canceler (AEC) +//------------------------------------------------------------------------------ + +static const webrtc::EchoControlMobile::RoutingMode kAecDefaultMode = + webrtc::EchoControlMobile::kEarpiece; +static const bool kAecDefaultComfortNoise = true; + +int AecInit (preproc_effect_t *effect) +{ + LOGV("AecInit"); + webrtc::EchoControlMobile *aec = static_cast<webrtc::EchoControlMobile *>(effect->engine); + aec->set_routing_mode(kAecDefaultMode); + aec->enable_comfort_noise(kAecDefaultComfortNoise); + return 0; +} + +int AecCreate(preproc_effect_t *effect) +{ + webrtc::EchoControlMobile *aec = effect->session->apm->echo_control_mobile(); + LOGV("AecCreate got aec %p", aec); + if (aec == NULL) { + LOGW("AgcCreate Error"); + return -ENOMEM; + } + effect->engine = static_cast<preproc_fx_handle_t>(aec); + AecInit (effect); + return 0; +} + +int AecGetParameter(preproc_effect_t *effect, + void *pParam, + size_t *pValueSize, + void *pValue) +{ + int status = 0; + uint32_t param = *(uint32_t *)pParam; + + if (*pValueSize < sizeof(uint32_t)) { + return -EINVAL; + } + switch (param) { + case AEC_PARAM_ECHO_DELAY: + case AEC_PARAM_PROPERTIES: + *(uint32_t *)pValue = 1000 * effect->session->apm->stream_delay_ms(); + LOGV("AecGetParameter() echo delay %d us", *(uint32_t *)pValue); + break; + default: + LOGW("AecGetParameter() unknown param %08x value %08x", param, *(uint32_t *)pValue); + status = -EINVAL; + break; + } + return status; +} + +int AecSetParameter (preproc_effect_t *effect, void *pParam, void *pValue) +{ + int status = 0; + uint32_t param = *(uint32_t *)pParam; + uint32_t value = *(uint32_t *)pValue; + + switch (param) { + case AEC_PARAM_ECHO_DELAY: + case AEC_PARAM_PROPERTIES: + status = effect->session->apm->set_stream_delay_ms(value/1000); + LOGV("AecSetParameter() echo delay %d us, status %d", value, status); + break; + default: + LOGW("AecSetParameter() unknown param %08x value %08x", param, *(uint32_t *)pValue); + status = -EINVAL; + break; + } + return status; +} + +void AecEnable(preproc_effect_t *effect) +{ + webrtc::EchoControlMobile *aec = static_cast<webrtc::EchoControlMobile *>(effect->engine); + LOGV("AecEnable aec %p", aec); + aec->Enable(true); +} + +void AecDisable(preproc_effect_t *effect) +{ + LOGV("AecDisable"); + webrtc::EchoControlMobile *aec = static_cast<webrtc::EchoControlMobile *>(effect->engine); + aec->Enable(false); +} + +int AecSetDevice(preproc_effect_t *effect, uint32_t device) +{ + LOGV("AecSetDevice %08x", device); + webrtc::EchoControlMobile *aec = static_cast<webrtc::EchoControlMobile *>(effect->engine); + webrtc::EchoControlMobile::RoutingMode mode = webrtc::EchoControlMobile::kQuietEarpieceOrHeadset; + + switch(device) { + case AUDIO_DEVICE_OUT_EARPIECE: + mode = webrtc::EchoControlMobile::kEarpiece; + break; + case AUDIO_DEVICE_OUT_SPEAKER: + mode = webrtc::EchoControlMobile::kSpeakerphone; + break; + case AUDIO_DEVICE_OUT_WIRED_HEADSET: + case AUDIO_DEVICE_OUT_WIRED_HEADPHONE: + default: + break; + } + aec->set_routing_mode(mode); + return 0; +} + +static const preproc_ops_t sAecOps = { + AecCreate, + AecInit, + NULL, + AecEnable, + AecDisable, + AecSetParameter, + AecGetParameter, + AecSetDevice +}; + +//------------------------------------------------------------------------------ +// Noise Suppression (NS) +//------------------------------------------------------------------------------ + +static const webrtc::NoiseSuppression::Level kNsDefaultLevel = webrtc::NoiseSuppression::kModerate; + +int NsInit (preproc_effect_t *effect) +{ + LOGV("NsInit"); + webrtc::NoiseSuppression *ns = static_cast<webrtc::NoiseSuppression *>(effect->engine); + ns->set_level(kNsDefaultLevel); + return 0; +} + +int NsCreate(preproc_effect_t *effect) +{ + webrtc::NoiseSuppression *ns = effect->session->apm->noise_suppression(); + LOGV("NsCreate got ns %p", ns); + if (ns == NULL) { + LOGW("AgcCreate Error"); + return -ENOMEM; + } + effect->engine = static_cast<preproc_fx_handle_t>(ns); + NsInit (effect); + return 0; +} + +int NsGetParameter(preproc_effect_t *effect, + void *pParam, + size_t *pValueSize, + void *pValue) +{ + int status = 0; + return status; +} + +int NsSetParameter (preproc_effect_t *effect, void *pParam, void *pValue) +{ + int status = 0; + return status; +} + +void NsEnable(preproc_effect_t *effect) +{ + webrtc::NoiseSuppression *ns = static_cast<webrtc::NoiseSuppression *>(effect->engine); + LOGV("NsEnable ns %p", ns); + ns->Enable(true); +} + +void NsDisable(preproc_effect_t *effect) +{ + LOGV("NsDisable"); + webrtc::NoiseSuppression *ns = static_cast<webrtc::NoiseSuppression *>(effect->engine); + ns->Enable(false); +} + +static const preproc_ops_t sNsOps = { + NsCreate, + NsInit, + NULL, + NsEnable, + NsDisable, + NsSetParameter, + NsGetParameter, + NULL +}; + + +static const preproc_ops_t *sPreProcOps[PREPROC_NUM_EFFECTS] = { + &sAgcOps, + &sAecOps, + &sNsOps +}; + + +//------------------------------------------------------------------------------ +// Effect functions +//------------------------------------------------------------------------------ + +void Session_SetProcEnabled(preproc_session_t *session, uint32_t procId, bool enabled); + +extern "C" const struct effect_interface_s sEffectInterface; +extern "C" const struct effect_interface_s sEffectInterfaceReverse; + +#define BAD_STATE_ABORT(from, to) \ + LOG_ALWAYS_FATAL("Bad state transition from %d to %d", from, to); + +int Effect_SetState(preproc_effect_t *effect, uint32_t state) +{ + int status = 0; + LOGV("Effect_SetState proc %d, new %d old %d", effect->procId, state, effect->state); + switch(state) { + case PREPROC_EFFECT_STATE_INIT: + switch(effect->state) { + case PREPROC_EFFECT_STATE_ACTIVE: + effect->ops->disable(effect); + Session_SetProcEnabled(effect->session, effect->procId, false); + case PREPROC_EFFECT_STATE_CONFIG: + case PREPROC_EFFECT_STATE_CREATED: + case PREPROC_EFFECT_STATE_INIT: + break; + default: + BAD_STATE_ABORT(effect->state, state); + } + break; + case PREPROC_EFFECT_STATE_CREATED: + switch(effect->state) { + case PREPROC_EFFECT_STATE_INIT: + status = effect->ops->create(effect); + break; + case PREPROC_EFFECT_STATE_CREATED: + case PREPROC_EFFECT_STATE_ACTIVE: + case PREPROC_EFFECT_STATE_CONFIG: + LOGE("Effect_SetState invalid transition"); + status = -ENOSYS; + break; + default: + BAD_STATE_ABORT(effect->state, state); + } + break; + case PREPROC_EFFECT_STATE_CONFIG: + switch(effect->state) { + case PREPROC_EFFECT_STATE_INIT: + LOGE("Effect_SetState invalid transition"); + status = -ENOSYS; + break; + case PREPROC_EFFECT_STATE_ACTIVE: + effect->ops->disable(effect); + Session_SetProcEnabled(effect->session, effect->procId, false); + break; + case PREPROC_EFFECT_STATE_CREATED: + case PREPROC_EFFECT_STATE_CONFIG: + break; + default: + BAD_STATE_ABORT(effect->state, state); + } + break; + case PREPROC_EFFECT_STATE_ACTIVE: + switch(effect->state) { + case PREPROC_EFFECT_STATE_INIT: + case PREPROC_EFFECT_STATE_CREATED: + case PREPROC_EFFECT_STATE_ACTIVE: + LOGE("Effect_SetState invalid transition"); + status = -ENOSYS; + break; + case PREPROC_EFFECT_STATE_CONFIG: + effect->ops->enable(effect); + Session_SetProcEnabled(effect->session, effect->procId, true); + break; + default: + BAD_STATE_ABORT(effect->state, state); + } + break; + default: + BAD_STATE_ABORT(effect->state, state); + } + if (status == 0) { + effect->state = state; + } + return status; +} + +int Effect_Init(preproc_effect_t *effect, uint32_t procId) +{ + if (HasReverseStream(procId)) { + effect->itfe = &sEffectInterfaceReverse; + } else { + effect->itfe = &sEffectInterface; + } + effect->ops = sPreProcOps[procId]; + effect->procId = procId; + effect->state = PREPROC_EFFECT_STATE_INIT; + return 0; +} + +int Effect_Create(preproc_effect_t *effect, + preproc_session_t *session, + effect_handle_t *interface) +{ + effect->session = session; + *interface = (effect_handle_t)&effect->itfe; + return Effect_SetState(effect, PREPROC_EFFECT_STATE_CREATED); +} + +int Effect_Release(preproc_effect_t *effect) +{ + return Effect_SetState(effect, PREPROC_EFFECT_STATE_INIT); +} + + +//------------------------------------------------------------------------------ +// Session functions +//------------------------------------------------------------------------------ + +#define RESAMPLER_QUALITY SPEEX_RESAMPLER_QUALITY_VOIP + +static const int kPreprocDefaultSr = 16000; +static const int kPreProcDefaultCnl = 1; + +int Session_Init(preproc_session_t *session) +{ + size_t i; + int status = 0; + + session->state = PREPROC_SESSION_STATE_INIT; + session->id = 0; + session->io = 0; + session->createdMsk = 0; + session->apm = NULL; + for (i = 0; i < PREPROC_NUM_EFFECTS && status == 0; i++) { + status = Effect_Init(&session->effects[i], i); + } + return status; +} + + +extern "C" int Session_CreateEffect(preproc_session_t *session, + int32_t procId, + effect_handle_t *interface) +{ + int status = -ENOMEM; + + LOGV("Session_CreateEffect procId %d, createdMsk %08x", procId, session->createdMsk); + + if (session->createdMsk == 0) { + session->apm = webrtc::AudioProcessing::Create(session->io); + if (session->apm == NULL) { + LOGW("Session_CreateEffect could not get apm engine"); + goto error; + } + session->apm->set_sample_rate_hz(kPreprocDefaultSr); + session->apm->set_num_channels(kPreProcDefaultCnl, kPreProcDefaultCnl); + session->apm->set_num_reverse_channels(kPreProcDefaultCnl); + session->procFrame = new webrtc::AudioFrame(); + if (session->procFrame == NULL) { + LOGW("Session_CreateEffect could not allocate audio frame"); + goto error; + } + session->revFrame = new webrtc::AudioFrame(); + if (session->revFrame == NULL) { + LOGW("Session_CreateEffect could not allocate reverse audio frame"); + goto error; + } + session->apmSamplingRate = kPreprocDefaultSr; + session->apmFrameCount = (kPreprocDefaultSr) / 100; + session->frameCount = session->apmFrameCount; + session->samplingRate = kPreprocDefaultSr; + session->inChannelCount = kPreProcDefaultCnl; + session->outChannelCount = kPreProcDefaultCnl; + session->procFrame->_frequencyInHz = kPreprocDefaultSr; + session->procFrame->_audioChannel = kPreProcDefaultCnl; + session->revChannelCount = kPreProcDefaultCnl; + session->revFrame->_frequencyInHz = kPreprocDefaultSr; + session->revFrame->_audioChannel = kPreProcDefaultCnl; + session->enabledMsk = 0; + session->processedMsk = 0; + session->revEnabledMsk = 0; + session->revProcessedMsk = 0; + session->inResampler = NULL; + session->inBuf = NULL; + session->inBufSize = 0; + session->outResampler = NULL; + session->outBuf = NULL; + session->outBufSize = 0; + session->revResampler = NULL; + session->revBuf = NULL; + session->revBufSize = 0; + } + status = Effect_Create(&session->effects[procId], session, interface); + if (status < 0) { + goto error; + } + LOGV("Session_CreateEffect OK"); + session->createdMsk |= (1<<procId); + return status; + +error: + if (session->createdMsk == 0) { + delete session->revFrame; + session->revFrame = NULL; + delete session->procFrame; + session->procFrame = NULL; + webrtc::AudioProcessing::Destroy(session->apm); + session->apm = NULL; + } + return status; +} + +int Session_ReleaseEffect(preproc_session_t *session, + preproc_effect_t *fx) +{ + LOGW_IF(Effect_Release(fx) != 0, " Effect_Release() failed for proc ID %d", fx->procId); + session->createdMsk &= ~(1<<fx->procId); + if (session->createdMsk == 0) { + webrtc::AudioProcessing::Destroy(session->apm); + session->apm = NULL; + delete session->procFrame; + session->procFrame = NULL; + delete session->revFrame; + session->revFrame = NULL; + if (session->inResampler != NULL) { + speex_resampler_destroy(session->inResampler); + session->inResampler = NULL; + } + if (session->outResampler != NULL) { + speex_resampler_destroy(session->outResampler); + session->outResampler = NULL; + } + if (session->revResampler != NULL) { + speex_resampler_destroy(session->revResampler); + session->revResampler = NULL; + } + delete session->inBuf; + session->inBuf = NULL; + delete session->outBuf; + session->outBuf = NULL; + delete session->revBuf; + session->revBuf = NULL; + + session->io = 0; + } + + return 0; +} + + +int Session_SetConfig(preproc_session_t *session, effect_config_t *config) +{ + uint32_t sr; + uint32_t inCnl = popcount(config->inputCfg.channels); + uint32_t outCnl = popcount(config->outputCfg.channels); + + if (config->inputCfg.samplingRate != config->outputCfg.samplingRate || + config->inputCfg.format != config->outputCfg.format || + config->inputCfg.format != AUDIO_FORMAT_PCM_16_BIT) { + return -EINVAL; + } + + LOGV("Session_SetConfig sr %d cnl %08x", + config->inputCfg.samplingRate, config->inputCfg.channels); + int status; + + // AEC implementation is limited to 16kHz + if (config->inputCfg.samplingRate >= 32000 && !(session->createdMsk & (1 << PREPROC_AEC))) { + session->apmSamplingRate = 32000; + } else + if (config->inputCfg.samplingRate >= 16000) { + session->apmSamplingRate = 16000; + } else if (config->inputCfg.samplingRate >= 8000) { + session->apmSamplingRate = 8000; + } + status = session->apm->set_sample_rate_hz(session->apmSamplingRate); + if (status < 0) { + return -EINVAL; + } + status = session->apm->set_num_channels(inCnl, outCnl); + if (status < 0) { + return -EINVAL; + } + status = session->apm->set_num_reverse_channels(inCnl); + if (status < 0) { + return -EINVAL; + } + + session->samplingRate = config->inputCfg.samplingRate; + session->apmFrameCount = session->apmSamplingRate / 100; + if (session->samplingRate == session->apmSamplingRate) { + session->frameCount = session->apmFrameCount; + } else { + session->frameCount = (session->apmFrameCount * session->samplingRate) / + session->apmSamplingRate + 1; + } + session->inChannelCount = inCnl; + session->outChannelCount = outCnl; + session->procFrame->_audioChannel = inCnl; + session->procFrame->_frequencyInHz = session->apmSamplingRate; + + session->revChannelCount = inCnl; + session->revFrame->_audioChannel = inCnl; + session->revFrame->_frequencyInHz = session->apmSamplingRate; + + if (session->inResampler != NULL) { + speex_resampler_destroy(session->inResampler); + session->inResampler = NULL; + } + if (session->outResampler != NULL) { + speex_resampler_destroy(session->outResampler); + session->outResampler = NULL; + } + if (session->revResampler != NULL) { + speex_resampler_destroy(session->revResampler); + session->revResampler = NULL; + } + if (session->samplingRate != session->apmSamplingRate) { + int error; + session->inResampler = speex_resampler_init(session->inChannelCount, + session->samplingRate, + session->apmSamplingRate, + RESAMPLER_QUALITY, + &error); + if (session->inResampler == NULL) { + LOGW("Session_SetConfig Cannot create speex resampler: %s", + speex_resampler_strerror(error)); + return -EINVAL; + } + session->outResampler = speex_resampler_init(session->outChannelCount, + session->apmSamplingRate, + session->samplingRate, + RESAMPLER_QUALITY, + &error); + if (session->outResampler == NULL) { + LOGW("Session_SetConfig Cannot create speex resampler: %s", + speex_resampler_strerror(error)); + speex_resampler_destroy(session->inResampler); + session->inResampler = NULL; + return -EINVAL; + } + session->revResampler = speex_resampler_init(session->inChannelCount, + session->samplingRate, + session->apmSamplingRate, + RESAMPLER_QUALITY, + &error); + if (session->revResampler == NULL) { + LOGW("Session_SetConfig Cannot create speex resampler: %s", + speex_resampler_strerror(error)); + speex_resampler_destroy(session->inResampler); + session->inResampler = NULL; + speex_resampler_destroy(session->outResampler); + session->outResampler = NULL; + return -EINVAL; + } + } + + session->state = PREPROC_SESSION_STATE_CONFIG; + return 0; +} + +int Session_SetReverseConfig(preproc_session_t *session, effect_config_t *config) +{ + if (config->inputCfg.samplingRate != config->outputCfg.samplingRate || + config->inputCfg.format != config->outputCfg.format || + config->inputCfg.format != AUDIO_FORMAT_PCM_16_BIT) { + return -EINVAL; + } + + LOGV("Session_SetReverseConfig sr %d cnl %08x", + config->inputCfg.samplingRate, config->inputCfg.channels); + + if (session->state < PREPROC_SESSION_STATE_CONFIG) { + return -ENOSYS; + } + if (config->inputCfg.samplingRate != session->samplingRate || + config->inputCfg.format != AUDIO_FORMAT_PCM_16_BIT) { + return -EINVAL; + } + uint32_t inCnl = popcount(config->inputCfg.channels); + int status = session->apm->set_num_reverse_channels(inCnl); + if (status < 0) { + return -EINVAL; + } + session->revChannelCount = inCnl; + session->revFrame->_audioChannel = inCnl; + session->revFrame->_frequencyInHz = session->apmSamplingRate; + return 0; +} + +void Session_SetProcEnabled(preproc_session_t *session, uint32_t procId, bool enabled) +{ + if (enabled) { + if(session->enabledMsk == 0) { + session->framesIn = 0; + if (session->inResampler != NULL) { + speex_resampler_reset_mem(session->inResampler); + } + session->framesOut = 0; + if (session->outResampler != NULL) { + speex_resampler_reset_mem(session->outResampler); + } + } + session->enabledMsk |= (1 << procId); + if (HasReverseStream(procId)) { + session->framesRev = 0; + if (session->revResampler != NULL) { + speex_resampler_reset_mem(session->revResampler); + } + session->revEnabledMsk |= (1 << procId); + } + } else { + session->enabledMsk &= ~(1 << procId); + if (HasReverseStream(procId)) { + session->revEnabledMsk &= ~(1 << procId); + } + } + LOGV("Session_SetProcEnabled proc %d, enabled %d enabledMsk %08x revEnabledMsk %08x", + procId, enabled, session->enabledMsk, session->revEnabledMsk); + session->processedMsk = 0; + if (HasReverseStream(procId)) { + session->revProcessedMsk = 0; + } +} + +//------------------------------------------------------------------------------ +// Bundle functions +//------------------------------------------------------------------------------ + +static int sInitStatus = 1; +static preproc_session_t sSessions[PREPROC_NUM_SESSIONS]; + +preproc_session_t *PreProc_GetSession(int32_t procId, int32_t sessionId, int32_t ioId) +{ + size_t i; + int free = -1; + for (i = 0; i < PREPROC_NUM_SESSIONS; i++) { + if (sSessions[i].io == ioId) { + if (sSessions[i].createdMsk & (1 << procId)) { + return NULL; + } + return &sSessions[i]; + } + } + for (i = 0; i < PREPROC_NUM_SESSIONS; i++) { + if (sSessions[i].io == 0) { + sSessions[i].id = sessionId; + sSessions[i].io = ioId; + return &sSessions[i]; + } + } + return NULL; +} + + +int PreProc_Init() { + size_t i; + int status = 0; + + if (sInitStatus <= 0) { + return sInitStatus; + } + for (i = 0; i < PREPROC_NUM_SESSIONS && status == 0; i++) { + status = Session_Init(&sSessions[i]); + } + sInitStatus = status; + return sInitStatus; +} + +const effect_descriptor_t *PreProc_GetDescriptor(effect_uuid_t *uuid) +{ + size_t i; + for (i = 0; i < PREPROC_NUM_EFFECTS; i++) { + if (memcmp(&sDescriptors[i]->uuid, uuid, sizeof(effect_uuid_t)) == 0) { + return sDescriptors[i]; + } + } + return NULL; +} + + +extern "C" { + +//------------------------------------------------------------------------------ +// Effect Control Interface Implementation +//------------------------------------------------------------------------------ + +int PreProcessingFx_Process(effect_handle_t self, + audio_buffer_t *inBuffer, + audio_buffer_t *outBuffer) +{ + preproc_effect_t * effect = (preproc_effect_t *)self; + int status = 0; + + if (effect == NULL){ + LOGV("PreProcessingFx_Process() ERROR effect == NULL"); + return -EINVAL; + } + preproc_session_t * session = (preproc_session_t *)effect->session; + + if (inBuffer == NULL || inBuffer->raw == NULL || + outBuffer == NULL || outBuffer->raw == NULL){ + LOGW("PreProcessingFx_Process() ERROR bad pointer"); + return -EINVAL; + } + + session->processedMsk |= (1<<effect->procId); + +// LOGV("PreProcessingFx_Process In %d frames enabledMsk %08x processedMsk %08x", +// inBuffer->frameCount, session->enabledMsk, session->processedMsk); + + if ((session->processedMsk & session->enabledMsk) == session->enabledMsk) { + effect->session->processedMsk = 0; + size_t framesRq = outBuffer->frameCount; + size_t framesWr = 0; + if (session->framesOut) { + size_t fr = session->framesOut; + if (outBuffer->frameCount < fr) { + fr = outBuffer->frameCount; + } + memcpy(outBuffer->s16, + session->outBuf, + fr * session->outChannelCount * sizeof(int16_t)); + memcpy(session->outBuf, + session->outBuf + fr * session->outChannelCount, + (session->framesOut - fr) * session->outChannelCount * sizeof(int16_t)); + session->framesOut -= fr; + framesWr += fr; + } + outBuffer->frameCount = framesWr; + if (framesWr == framesRq) { + inBuffer->frameCount = 0; + return 0; + } + + if (session->inResampler != NULL) { + size_t fr = session->frameCount - session->framesIn; + if (inBuffer->frameCount < fr) { + fr = inBuffer->frameCount; + } + if (session->inBufSize < session->framesIn + fr) { + session->inBufSize = session->framesIn + fr; + session->inBuf = (int16_t *)realloc(session->inBuf, + session->inBufSize * session->inChannelCount * sizeof(int16_t)); + } + memcpy(session->inBuf + session->framesIn * session->inChannelCount, + inBuffer->s16, + fr * session->inChannelCount * sizeof(int16_t)); + + session->framesIn += fr; + inBuffer->frameCount = fr; + if (session->framesIn < session->frameCount) { + return 0; + } + size_t frIn = session->framesIn; + size_t frOut = session->apmFrameCount; + if (session->inChannelCount == 1) { + speex_resampler_process_int(session->inResampler, + 0, + session->inBuf, + &frIn, + session->procFrame->_payloadData, + &frOut); + } else { + speex_resampler_process_interleaved_int(session->inResampler, + session->inBuf, + &frIn, + session->procFrame->_payloadData, + &frOut); + } + memcpy(session->inBuf, + session->inBuf + frIn * session->inChannelCount, + (session->framesIn - frIn) * session->inChannelCount * sizeof(int16_t)); + session->framesIn -= frIn; + } else { + size_t fr = session->frameCount - session->framesIn; + if (inBuffer->frameCount < fr) { + fr = inBuffer->frameCount; + } + memcpy(session->procFrame->_payloadData + session->framesIn * session->inChannelCount, + inBuffer->s16, + fr * session->inChannelCount * sizeof(int16_t)); + session->framesIn += fr; + inBuffer->frameCount = fr; + if (session->framesIn < session->frameCount) { + return 0; + } + session->framesIn = 0; + } + session->procFrame->_payloadDataLengthInSamples = + session->apmFrameCount * session->inChannelCount; + + effect->session->apm->ProcessStream(session->procFrame); + + if (session->outBufSize < session->framesOut + session->frameCount) { + session->outBufSize = session->framesOut + session->frameCount; + session->outBuf = (int16_t *)realloc(session->outBuf, + session->outBufSize * session->outChannelCount * sizeof(int16_t)); + } + + if (session->outResampler != NULL) { + size_t frIn = session->apmFrameCount; + size_t frOut = session->frameCount; + if (session->inChannelCount == 1) { + speex_resampler_process_int(session->outResampler, + 0, + session->procFrame->_payloadData, + &frIn, + session->outBuf + session->framesOut * session->outChannelCount, + &frOut); + } else { + speex_resampler_process_interleaved_int(session->outResampler, + session->procFrame->_payloadData, + &frIn, + session->outBuf + session->framesOut * session->outChannelCount, + &frOut); + } + session->framesOut += frOut; + } else { + memcpy(session->outBuf + session->framesOut * session->outChannelCount, + session->procFrame->_payloadData, + session->frameCount * session->outChannelCount * sizeof(int16_t)); + session->framesOut += session->frameCount; + } + size_t fr = session->framesOut; + if (framesRq - framesWr < fr) { + fr = framesRq - framesWr; + } + memcpy(outBuffer->s16 + framesWr * session->outChannelCount, + session->outBuf, + fr * session->outChannelCount * sizeof(int16_t)); + memcpy(session->outBuf, + session->outBuf + fr * session->outChannelCount, + (session->framesOut - fr) * session->outChannelCount * sizeof(int16_t)); + session->framesOut -= fr; + outBuffer->frameCount += fr; + + return 0; + } else { + return -ENODATA; + } +} + +int PreProcessingFx_Command(effect_handle_t self, + uint32_t cmdCode, + uint32_t cmdSize, + void *pCmdData, + uint32_t *replySize, + void *pReplyData) +{ + preproc_effect_t * effect = (preproc_effect_t *) self; + int retsize; + int status; + + if (effect == NULL){ + return -EINVAL; + } + + //LOGV("PreProcessingFx_Command: command %d cmdSize %d",cmdCode, cmdSize); + + switch (cmdCode){ + case EFFECT_CMD_INIT: + if (pReplyData == NULL || *replySize != sizeof(int)){ + return -EINVAL; + } + if (effect->ops->init) { + effect->ops->init(effect); + } + *(int *)pReplyData = 0; + break; + + case EFFECT_CMD_CONFIGURE: + if (pCmdData == NULL|| + cmdSize != sizeof(effect_config_t)|| + pReplyData == NULL|| + *replySize != sizeof(int)){ + LOGV("PreProcessingFx_Command cmdCode Case: " + "EFFECT_CMD_CONFIGURE: ERROR"); + return -EINVAL; + } + *(int *)pReplyData = Session_SetConfig(effect->session, (effect_config_t *)pCmdData); + if (*(int *)pReplyData != 0) { + break; + } + *(int *)pReplyData = Effect_SetState(effect, PREPROC_EFFECT_STATE_CONFIG); + break; + + case EFFECT_CMD_CONFIGURE_REVERSE: + if (pCmdData == NULL|| + cmdSize != sizeof(effect_config_t)|| + pReplyData == NULL|| + *replySize != sizeof(int)){ + LOGV("PreProcessingFx_Command cmdCode Case: " + "EFFECT_CMD_CONFIGURE_REVERSE: ERROR"); + return -EINVAL; + } + *(int *)pReplyData = Session_SetReverseConfig(effect->session, + (effect_config_t *)pCmdData); + if (*(int *)pReplyData != 0) { + break; + } + break; + + case EFFECT_CMD_RESET: + if (effect->ops->reset) { + effect->ops->reset(effect); + } + break; + + case EFFECT_CMD_GET_PARAM:{ + if (pCmdData == NULL || + cmdSize < (int)sizeof(effect_param_t) || + pReplyData == NULL || + *replySize < (int)sizeof(effect_param_t)){ + LOGV("PreProcessingFx_Command cmdCode Case: " + "EFFECT_CMD_GET_PARAM: ERROR"); + return -EINVAL; + } + effect_param_t *p = (effect_param_t *)pCmdData; + + memcpy(pReplyData, pCmdData, sizeof(effect_param_t) + p->psize); + + p = (effect_param_t *)pReplyData; + + int voffset = ((p->psize - 1) / sizeof(int32_t) + 1) * sizeof(int32_t); + + if (effect->ops->get_parameter) { + p->status = effect->ops->get_parameter(effect, p->data, + (size_t *)&p->vsize, + p->data + voffset); + *replySize = sizeof(effect_param_t) + voffset + p->vsize; + } + } break; + + case EFFECT_CMD_SET_PARAM:{ + if (pCmdData == NULL|| + cmdSize < (int)sizeof(effect_param_t) || + pReplyData == NULL || + *replySize != sizeof(int32_t)){ + LOGV("PreProcessingFx_Command cmdCode Case: " + "EFFECT_CMD_SET_PARAM: ERROR"); + return -EINVAL; + } + effect_param_t *p = (effect_param_t *) pCmdData; + + if (p->psize != sizeof(int32_t)){ + LOGV("PreProcessingFx_Command cmdCode Case: " + "EFFECT_CMD_SET_PARAM: ERROR, psize is not sizeof(int32_t)"); + return -EINVAL; + } + if (effect->ops->set_parameter) { + *(int *)pReplyData = effect->ops->set_parameter(effect, + (void *)p->data, + p->data + p->psize); + } + } break; + + case EFFECT_CMD_ENABLE: + if (pReplyData == NULL || *replySize != sizeof(int)){ + LOGV("PreProcessingFx_Command cmdCode Case: EFFECT_CMD_ENABLE: ERROR"); + return -EINVAL; + } + *(int *)pReplyData = Effect_SetState(effect, PREPROC_EFFECT_STATE_ACTIVE); + break; + + case EFFECT_CMD_DISABLE: + if (pReplyData == NULL || *replySize != sizeof(int)){ + LOGV("PreProcessingFx_Command cmdCode Case: EFFECT_CMD_DISABLE: ERROR"); + return -EINVAL; + } + *(int *)pReplyData = Effect_SetState(effect, PREPROC_EFFECT_STATE_CONFIG); + break; + + case EFFECT_CMD_SET_DEVICE: + case EFFECT_CMD_SET_INPUT_DEVICE: + if (pCmdData == NULL || + cmdSize != sizeof(uint32_t)) { + LOGV("PreProcessingFx_Command cmdCode Case: EFFECT_CMD_SET_DEVICE: ERROR"); + return -EINVAL; + } + + if (effect->ops->set_device) { + effect->ops->set_device(effect, *(uint32_t *)pCmdData); + } + break; + + case EFFECT_CMD_SET_VOLUME: + case EFFECT_CMD_SET_AUDIO_MODE: + break; + + default: + return -EINVAL; + } + return 0; +} + + +int PreProcessingFx_GetDescriptor(effect_handle_t self, + effect_descriptor_t *pDescriptor) +{ + preproc_effect_t * effect = (preproc_effect_t *) self; + + if (effect == NULL || pDescriptor == NULL) { + return -EINVAL; + } + + memcpy(pDescriptor, sDescriptors[effect->procId], sizeof(effect_descriptor_t)); + + return 0; +} + +int PreProcessingFx_ProcessReverse(effect_handle_t self, + audio_buffer_t *inBuffer, + audio_buffer_t *outBuffer) +{ + preproc_effect_t * effect = (preproc_effect_t *)self; + int status = 0; + + if (effect == NULL){ + LOGW("PreProcessingFx_ProcessReverse() ERROR effect == NULL"); + return -EINVAL; + } + preproc_session_t * session = (preproc_session_t *)effect->session; + + if (inBuffer == NULL || inBuffer->raw == NULL){ + LOGW("PreProcessingFx_ProcessReverse() ERROR bad pointer"); + return -EINVAL; + } + + session->revProcessedMsk |= (1<<effect->procId); + +// LOGV("PreProcessingFx_ProcessReverse In %d frames revEnabledMsk %08x revProcessedMsk %08x", +// inBuffer->frameCount, session->revEnabledMsk, session->revProcessedMsk); + + + if ((session->revProcessedMsk & session->revEnabledMsk) == session->revEnabledMsk) { + effect->session->revProcessedMsk = 0; + if (session->revResampler != NULL) { + size_t fr = session->frameCount - session->framesRev; + if (inBuffer->frameCount < fr) { + fr = inBuffer->frameCount; + } + if (session->revBufSize < session->framesRev + fr) { + session->revBufSize = session->framesRev + fr; + session->revBuf = (int16_t *)realloc(session->revBuf, + session->revBufSize * session->inChannelCount * sizeof(int16_t)); + } + memcpy(session->revBuf + session->framesRev * session->inChannelCount, + inBuffer->s16, + fr * session->inChannelCount * sizeof(int16_t)); + + session->framesRev += fr; + inBuffer->frameCount = fr; + if (session->framesRev < session->frameCount) { + return 0; + } + size_t frIn = session->framesRev; + size_t frOut = session->apmFrameCount; + if (session->inChannelCount == 1) { + speex_resampler_process_int(session->revResampler, + 0, + session->revBuf, + &frIn, + session->revFrame->_payloadData, + &frOut); + } else { + speex_resampler_process_interleaved_int(session->revResampler, + session->revBuf, + &frIn, + session->revFrame->_payloadData, + &frOut); + } + memcpy(session->revBuf, + session->revBuf + frIn * session->inChannelCount, + (session->framesRev - frIn) * session->inChannelCount * sizeof(int16_t)); + session->framesRev -= frIn; + } else { + size_t fr = session->frameCount - session->framesRev; + if (inBuffer->frameCount < fr) { + fr = inBuffer->frameCount; + } + memcpy(session->revFrame->_payloadData + session->framesRev * session->inChannelCount, + inBuffer->s16, + fr * session->inChannelCount * sizeof(int16_t)); + session->framesRev += fr; + inBuffer->frameCount = fr; + if (session->framesRev < session->frameCount) { + return 0; + } + session->framesRev = 0; + } + session->revFrame->_payloadDataLengthInSamples = + session->apmFrameCount * session->inChannelCount; + effect->session->apm->AnalyzeReverseStream(session->revFrame); + return 0; + } else { + return -ENODATA; + } +} + + +// effect_handle_t interface implementation for effect +const struct effect_interface_s sEffectInterface = { + PreProcessingFx_Process, + PreProcessingFx_Command, + PreProcessingFx_GetDescriptor, + NULL +}; + +const struct effect_interface_s sEffectInterfaceReverse = { + PreProcessingFx_Process, + PreProcessingFx_Command, + PreProcessingFx_GetDescriptor, + PreProcessingFx_ProcessReverse +}; + +//------------------------------------------------------------------------------ +// Effect Library Interface Implementation +//------------------------------------------------------------------------------ + +int PreProcessingLib_QueryNumberEffects(uint32_t *pNumEffects) +{ + if (PreProc_Init() != 0) { + return sInitStatus; + } + if (pNumEffects == NULL) { + return -EINVAL; + } + *pNumEffects = PREPROC_NUM_EFFECTS; + return sInitStatus; +} + +int PreProcessingLib_QueryEffect(uint32_t index, effect_descriptor_t *pDescriptor) +{ + if (PreProc_Init() != 0) { + return sInitStatus; + } + if (index >= PREPROC_NUM_EFFECTS) { + return -EINVAL; + } + memcpy(pDescriptor, sDescriptors[index], sizeof(effect_descriptor_t)); + return 0; +} + +int PreProcessingLib_Create(effect_uuid_t *uuid, + int32_t sessionId, + int32_t ioId, + effect_handle_t *pInterface) +{ + LOGV("EffectCreate: uuid: %08x session %d IO: %d", uuid->timeLow, sessionId, ioId); + + int status; + const effect_descriptor_t *desc; + preproc_session_t *session; + uint32_t procId; + + if (PreProc_Init() != 0) { + return sInitStatus; + } + desc = PreProc_GetDescriptor(uuid); + if (desc == NULL) { + LOGW("EffectCreate: fx not found uuid: %08x", uuid->timeLow); + return -EINVAL; + } + procId = UuidToProcId(&desc->type); + + session = PreProc_GetSession(procId, sessionId, ioId); + if (session == NULL) { + LOGW("EffectCreate: no more session available"); + return -EINVAL; + } + + status = Session_CreateEffect(session, procId, pInterface); + + if (status < 0 && session->createdMsk == 0) { + session->io = 0; + } + return status; +} + +int PreProcessingLib_Release(effect_handle_t interface) +{ + int status; + LOGV("EffectRelease start %p", interface); + if (PreProc_Init() != 0) { + return sInitStatus; + } + + preproc_effect_t *fx = (preproc_effect_t *)interface; + + if (fx->session->io == 0) { + return -EINVAL; + } + return Session_ReleaseEffect(fx->session, fx); +} + +int PreProcessingLib_GetDescriptor(effect_uuid_t *uuid, + effect_descriptor_t *pDescriptor) { + + if (pDescriptor == NULL || uuid == NULL){ + return -EINVAL; + } + + const effect_descriptor_t *desc = PreProc_GetDescriptor(uuid); + if (desc == NULL) { + LOGV("PreProcessingLib_GetDescriptor() not found"); + return -EINVAL; + } + + LOGV("PreProcessingLib_GetDescriptor() got fx %s", desc->name); + + memcpy(pDescriptor, desc, sizeof(effect_descriptor_t)); + return 0; +} + +audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = { + tag : AUDIO_EFFECT_LIBRARY_TAG, + version : EFFECT_LIBRARY_API_VERSION, + name : "Audio Preprocessing Library", + implementor : "The Android Open Source Project", + query_num_effects : PreProcessingLib_QueryNumberEffects, + query_effect : PreProcessingLib_QueryEffect, + create_effect : PreProcessingLib_Create, + release_effect : PreProcessingLib_Release, + get_descriptor : PreProcessingLib_GetDescriptor +}; + +}; // extern "C" diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp index 7b7ba74f9a84..178039c6e03b 100644 --- a/media/libmedia/mediaplayer.cpp +++ b/media/libmedia/mediaplayer.cpp @@ -27,6 +27,8 @@ #include <binder/IServiceManager.h> #include <binder/IPCThreadState.h> +#include <gui/SurfaceTextureClient.h> + #include <media/mediaplayer.h> #include <media/AudioTrack.h> @@ -38,6 +40,7 @@ #include <utils/String8.h> #include <system/audio.h> +#include <system/window.h> namespace android { @@ -194,13 +197,62 @@ status_t MediaPlayer::getMetadata(bool update_only, bool apply_filter, Parcel *m return mPlayer->getMetadata(update_only, apply_filter, metadata); } +void MediaPlayer::disconnectNativeWindow() { + if (mConnectedWindow != NULL) { + status_t err = native_window_disconnect(mConnectedWindow.get(), + NATIVE_WINDOW_API_MEDIA); + + if (err != OK) { + LOGW("native_window_disconnect returned an error: %s (%d)", + strerror(-err), err); + } + } + mConnectedWindow.clear(); +} + status_t MediaPlayer::setVideoSurface(const sp<Surface>& surface) { LOGV("setVideoSurface"); Mutex::Autolock _l(mLock); if (mPlayer == 0) return NO_INIT; - return mPlayer->setVideoSurface(surface); + sp<IBinder> binder(surface == NULL ? NULL : surface->asBinder()); + if (mConnectedWindowBinder == binder) { + return OK; + } + + if (surface != NULL) { + status_t err = native_window_connect(surface.get(), + NATIVE_WINDOW_API_MEDIA); + + if (err != OK) { + // Note that we must do the reset before disconnecting from the ANW. + // Otherwise queue/dequeue calls could be made on the disconnected + // ANW, which may result in errors. + reset_l(); + + disconnectNativeWindow(); + + return err; + } + } + + // Note that we must set the player's new surface before disconnecting the + // old one. Otherwise queue/dequeue calls could be made on the disconnected + // ANW, which may result in errors. + status_t err = mPlayer->setVideoSurface(surface); + + disconnectNativeWindow(); + + mConnectedWindow = surface; + + if (err == OK) { + mConnectedWindowBinder = binder; + } else { + disconnectNativeWindow(); + } + + return err; } status_t MediaPlayer::setVideoSurfaceTexture( @@ -210,7 +262,46 @@ status_t MediaPlayer::setVideoSurfaceTexture( Mutex::Autolock _l(mLock); if (mPlayer == 0) return NO_INIT; - return mPlayer->setVideoSurfaceTexture(surfaceTexture); + sp<IBinder> binder(surfaceTexture == NULL ? NULL : + surfaceTexture->asBinder()); + if (mConnectedWindowBinder == binder) { + return OK; + } + + sp<ANativeWindow> anw; + if (surfaceTexture != NULL) { + anw = new SurfaceTextureClient(surfaceTexture); + status_t err = native_window_connect(anw.get(), + NATIVE_WINDOW_API_MEDIA); + + if (err != OK) { + // Note that we must do the reset before disconnecting from the ANW. + // Otherwise queue/dequeue calls could be made on the disconnected + // ANW, which may result in errors. + reset_l(); + + disconnectNativeWindow(); + + return err; + } + } + + // Note that we must set the player's new SurfaceTexture before + // disconnecting the old one. Otherwise queue/dequeue calls could be made + // on the disconnected ANW, which may result in errors. + status_t err = mPlayer->setVideoSurfaceTexture(surfaceTexture); + + disconnectNativeWindow(); + + mConnectedWindow = anw; + + if (err == OK) { + mConnectedWindowBinder = binder; + } else { + disconnectNativeWindow(); + } + + return err; } // must call with lock held @@ -434,10 +525,8 @@ status_t MediaPlayer::seekTo(int msec) return result; } -status_t MediaPlayer::reset() +status_t MediaPlayer::reset_l() { - LOGV("reset"); - Mutex::Autolock _l(mLock); mLoop = false; if (mCurrentState == MEDIA_PLAYER_IDLE) return NO_ERROR; mPrepareSync = false; @@ -458,6 +547,13 @@ status_t MediaPlayer::reset() return NO_ERROR; } +status_t MediaPlayer::reset() +{ + LOGV("reset"); + Mutex::Autolock _l(mLock); + return reset_l(); +} + status_t MediaPlayer::setAudioStreamType(int type) { LOGV("MediaPlayer::setAudioStreamType"); diff --git a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp index 5a5330d69feb..0251baf5f6c1 100644 --- a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp +++ b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp @@ -58,8 +58,10 @@ NuPlayer::HTTPLiveSource::HTTPLiveSource( } NuPlayer::HTTPLiveSource::~HTTPLiveSource() { - mLiveSession->disconnect(); - mLiveLooper->stop(); + if (mLiveSession != NULL) { + mLiveSession->disconnect(); + mLiveLooper->stop(); + } } void NuPlayer::HTTPLiveSource::start() { diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp index 7cd8b6cc7f55..c6fca2c0aff0 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp @@ -118,9 +118,15 @@ status_t NuPlayerDriver::start() { mPlayer->start(); if (mStartupSeekTimeUs >= 0) { - mPlayer->seekToAsync(mStartupSeekTimeUs); + if (mStartupSeekTimeUs == 0) { + notifySeekComplete(); + } else { + mPlayer->seekToAsync(mStartupSeekTimeUs); + } + mStartupSeekTimeUs = -1; } + break; } case PLAYING: diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp index 5cab60e5fb9e..4f8336ed9cbe 100755 --- a/media/libstagefright/OMXCodec.cpp +++ b/media/libstagefright/OMXCodec.cpp @@ -4330,26 +4330,19 @@ status_t OMXCodec::pause() { status_t QueryCodecs( const sp<IOMX> &omx, - const char *mime, bool queryDecoders, + const char *mime, bool queryDecoders, bool hwCodecOnly, Vector<CodecCapabilities> *results) { + Vector<String8> matchingCodecs; results->clear(); - for (int index = 0;; ++index) { - const char *componentName; + OMXCodec::findMatchingCodecs(mime, + !queryDecoders /*createEncoder*/, + NULL /*matchComponentName*/, + hwCodecOnly ? OMXCodec::kHardwareCodecsOnly : 0 /*flags*/, + &matchingCodecs); - if (!queryDecoders) { - componentName = GetCodec( - kEncoderInfo, sizeof(kEncoderInfo) / sizeof(kEncoderInfo[0]), - mime, index); - } else { - componentName = GetCodec( - kDecoderInfo, sizeof(kDecoderInfo) / sizeof(kDecoderInfo[0]), - mime, index); - } - - if (!componentName) { - return OK; - } + for (size_t c = 0; c < matchingCodecs.size(); c++) { + const char *componentName = matchingCodecs.itemAt(c).string(); if (strncmp(componentName, "OMX.", 4)) { // Not an OpenMax component but a software codec. @@ -4411,6 +4404,8 @@ status_t QueryCodecs( CHECK_EQ(omx->freeNode(node), (status_t)OK); } + + return OK; } void OMXCodec::restorePatchedDataPointer(BufferInfo *info) { diff --git a/media/libstagefright/codecs/aacenc/src/bit_cnt.c b/media/libstagefright/codecs/aacenc/src/bit_cnt.c index dd0b9b452602..8853efc035e9 100644 --- a/media/libstagefright/codecs/aacenc/src/bit_cnt.c +++ b/media/libstagefright/codecs/aacenc/src/bit_cnt.c @@ -496,7 +496,7 @@ Word16 codeValues(Word16 *values, Word16 width, Word16 codeBook, HANDLE_BIT_BUF { Word32 i, t0, t1, t2, t3, t00, t01; - Word16 codeWord, codeLength; + UWord16 codeWord, codeLength; Word16 sign, signLength; diff --git a/media/libstagefright/codecs/aacenc/src/memalign.c b/media/libstagefright/codecs/aacenc/src/memalign.c index 7d2035274cf8..44dd4bab415d 100644 --- a/media/libstagefright/codecs/aacenc/src/memalign.c +++ b/media/libstagefright/codecs/aacenc/src/memalign.c @@ -23,6 +23,11 @@ #include "memalign.h" +#ifdef _MSC_VER +#include <stddef.h> +#else +#include <stdint.h> +#endif /***************************************************************************** * @@ -66,8 +71,8 @@ mem_malloc(VO_MEM_OPERATOR *pMemop, unsigned int size, unsigned char alignment, pMemop->Set(CodecID, tmp, 0, size + alignment); mem_ptr = - (unsigned char *) ((unsigned int) (tmp + alignment - 1) & - (~((unsigned int) (alignment - 1)))); + (unsigned char *) ((intptr_t) (tmp + alignment - 1) & + (~((intptr_t) (alignment - 1)))); if (mem_ptr == tmp) mem_ptr += alignment; diff --git a/media/libstagefright/codecs/amrwbenc/src/cmnMemory.c b/media/libstagefright/codecs/amrwbenc/src/cmnMemory.c deleted file mode 100644 index dd7c26d55a5a..000000000000 --- a/media/libstagefright/codecs/amrwbenc/src/cmnMemory.c +++ /dev/null @@ -1,73 +0,0 @@ -/* - ** Copyright 2003-2010, VisualOn, Inc. - ** - ** 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. - */ -/******************************************************************************* - File: cmnMemory.c - - Content: sample code for memory operator implementation - -*******************************************************************************/ -#include "cmnMemory.h" - -#include <malloc.h> -#if defined LINUX -#include <string.h> -#endif - -//VO_MEM_OPERATOR g_memOP; - -VO_U32 cmnMemAlloc (VO_S32 uID, VO_MEM_INFO * pMemInfo) -{ - if (!pMemInfo) - return VO_ERR_INVALID_ARG; - - pMemInfo->VBuffer = malloc (pMemInfo->Size); - return 0; -} - -VO_U32 cmnMemFree (VO_S32 uID, VO_PTR pMem) -{ - free (pMem); - return 0; -} - -VO_U32 cmnMemSet (VO_S32 uID, VO_PTR pBuff, VO_U8 uValue, VO_U32 uSize) -{ - memset (pBuff, uValue, uSize); - return 0; -} - -VO_U32 cmnMemCopy (VO_S32 uID, VO_PTR pDest, VO_PTR pSource, VO_U32 uSize) -{ - memcpy (pDest, pSource, uSize); - return 0; -} - -VO_U32 cmnMemCheck (VO_S32 uID, VO_PTR pBuffer, VO_U32 uSize) -{ - return 0; -} - -VO_S32 cmnMemCompare (VO_S32 uID, VO_PTR pBuffer1, VO_PTR pBuffer2, VO_U32 uSize) -{ - return memcmp(pBuffer1, pBuffer2, uSize); -} - -VO_U32 cmnMemMove (VO_S32 uID, VO_PTR pDest, VO_PTR pSource, VO_U32 uSize) -{ - memmove (pDest, pSource, uSize); - return 0; -} - diff --git a/media/libstagefright/codecs/common/cmnMemory.c b/media/libstagefright/codecs/common/cmnMemory.c index dd7c26d55a5a..aa52bd98f070 100644 --- a/media/libstagefright/codecs/common/cmnMemory.c +++ b/media/libstagefright/codecs/common/cmnMemory.c @@ -21,10 +21,8 @@ *******************************************************************************/ #include "cmnMemory.h" -#include <malloc.h> -#if defined LINUX +#include <stdlib.h> #include <string.h> -#endif //VO_MEM_OPERATOR g_memOP; diff --git a/media/libstagefright/codecs/common/include/voType.h b/media/libstagefright/codecs/common/include/voType.h index 70b2e83e1b2e..5f659ab05110 100644 --- a/media/libstagefright/codecs/common/include/voType.h +++ b/media/libstagefright/codecs/common/include/voType.h @@ -101,7 +101,7 @@ typedef signed long VO_S32; since the compiler does not support the way the component was written. */ #ifndef VO_SKIP64BIT -#ifdef _WIN32 +#ifdef _MSC_VER /** VO_U64 is a 64 bit unsigned quantity that is 64 bit word aligned */ typedef unsigned __int64 VO_U64; /** VO_S64 is a 64 bit signed quantity that is 64 bit word aligned */ diff --git a/media/libstagefright/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp index ca61b3d2cc5f..73b3d5b9a905 100644 --- a/media/libstagefright/httplive/LiveSession.cpp +++ b/media/libstagefright/httplive/LiveSession.cpp @@ -494,6 +494,12 @@ rinse_repeat: bool firstTime = (mPlaylist == NULL); + if ((ssize_t)bandwidthIndex != mPrevBandwidthIndex) { + // If we switch bandwidths, do not pay any heed to whether + // playlists changed since the last time... + mPlaylist.clear(); + } + bool unchanged; sp<M3UParser> playlist = fetchPlaylist(url.c_str(), &unchanged); if (playlist == NULL) { diff --git a/media/mtp/MtpDataPacket.cpp b/media/mtp/MtpDataPacket.cpp index 817eac055f72..20225ba2cce5 100644 --- a/media/mtp/MtpDataPacket.cpp +++ b/media/mtp/MtpDataPacket.cpp @@ -345,56 +345,28 @@ void MtpDataPacket::putString(const uint16_t* string) { #ifdef MTP_DEVICE int MtpDataPacket::read(int fd) { - // first read the header - int ret = ::read(fd, mBuffer, MTP_CONTAINER_HEADER_SIZE); - if (ret != MTP_CONTAINER_HEADER_SIZE) - return -1; - // then the following data - int total = MtpPacket::getUInt32(MTP_CONTAINER_LENGTH_OFFSET); - allocate(total); - int remaining = total - MTP_CONTAINER_HEADER_SIZE; - ret = ::read(fd, &mBuffer[0] + MTP_CONTAINER_HEADER_SIZE, remaining); - if (ret != remaining) + int ret = ::read(fd, mBuffer, mBufferSize); + if (ret < MTP_CONTAINER_HEADER_SIZE) return -1; - - mPacketSize = total; + mPacketSize = ret; mOffset = MTP_CONTAINER_HEADER_SIZE; - return total; -} - -int MtpDataPacket::readDataHeader(int fd) { - int ret = ::read(fd, mBuffer, MTP_CONTAINER_HEADER_SIZE); - if (ret > 0) - mPacketSize = ret; - else - mPacketSize = 0; return ret; } int MtpDataPacket::write(int fd) { MtpPacket::putUInt32(MTP_CONTAINER_LENGTH_OFFSET, mPacketSize); MtpPacket::putUInt16(MTP_CONTAINER_TYPE_OFFSET, MTP_CONTAINER_TYPE_DATA); - // send header separately from data - int ret = ::write(fd, mBuffer, MTP_CONTAINER_HEADER_SIZE); - if (ret == MTP_CONTAINER_HEADER_SIZE) - ret = ::write(fd, mBuffer + MTP_CONTAINER_HEADER_SIZE, - mPacketSize - MTP_CONTAINER_HEADER_SIZE); - return (ret < 0 ? ret : 0); -} - -int MtpDataPacket::writeDataHeader(int fd, uint32_t length) { - MtpPacket::putUInt32(MTP_CONTAINER_LENGTH_OFFSET, length); - MtpPacket::putUInt16(MTP_CONTAINER_TYPE_OFFSET, MTP_CONTAINER_TYPE_DATA); - int ret = ::write(fd, mBuffer, MTP_CONTAINER_HEADER_SIZE); + int ret = ::write(fd, mBuffer, mPacketSize); return (ret < 0 ? ret : 0); } int MtpDataPacket::writeData(int fd, void* data, uint32_t length) { - MtpPacket::putUInt32(MTP_CONTAINER_LENGTH_OFFSET, length + MTP_CONTAINER_HEADER_SIZE); + allocate(length); + memcpy(mBuffer + MTP_CONTAINER_HEADER_SIZE, data, length); + length += MTP_CONTAINER_HEADER_SIZE; + MtpPacket::putUInt32(MTP_CONTAINER_LENGTH_OFFSET, length); MtpPacket::putUInt16(MTP_CONTAINER_TYPE_OFFSET, MTP_CONTAINER_TYPE_DATA); - int ret = ::write(fd, mBuffer, MTP_CONTAINER_HEADER_SIZE); - if (ret == MTP_CONTAINER_HEADER_SIZE) - ret = ::write(fd, data, length); + int ret = ::write(fd, mBuffer, length); return (ret < 0 ? ret : 0); } diff --git a/media/mtp/MtpDataPacket.h b/media/mtp/MtpDataPacket.h index 8a08948b43e8..2b810633a27a 100644 --- a/media/mtp/MtpDataPacket.h +++ b/media/mtp/MtpDataPacket.h @@ -41,6 +41,7 @@ public: void setOperationCode(MtpOperationCode code); void setTransactionID(MtpTransactionID id); + inline const uint8_t* getData() const { return mBuffer + MTP_CONTAINER_HEADER_SIZE; } inline uint8_t getUInt8() { return (uint8_t)mBuffer[mOffset++]; } inline int8_t getInt8() { return (int8_t)mBuffer[mOffset++]; } uint16_t getUInt16(); @@ -95,11 +96,9 @@ public: #ifdef MTP_DEVICE // fill our buffer with data from the given file descriptor int read(int fd); - int readDataHeader(int fd); // write our data to the given file descriptor int write(int fd); - int writeDataHeader(int fd, uint32_t length); int writeData(int fd, void* data, uint32_t length); #endif diff --git a/media/mtp/MtpServer.cpp b/media/mtp/MtpServer.cpp index 4047e2e8777e..a9b539b022a6 100644 --- a/media/mtp/MtpServer.cpp +++ b/media/mtp/MtpServer.cpp @@ -731,14 +731,12 @@ MtpResponseCode MtpServer::doGetObject() { } mfr.offset = 0; mfr.length = fileLength; - - // send data header - mData.setOperationCode(mRequest.getOperationCode()); - mData.setTransactionID(mRequest.getTransactionID()); - mData.writeDataHeader(mFD, fileLength + MTP_CONTAINER_HEADER_SIZE); + mfr.command = mRequest.getOperationCode(); + mfr.transaction_id = mRequest.getTransactionID(); // then transfer the file - int ret = ioctl(mFD, MTP_SEND_FILE, (unsigned long)&mfr); + int ret = ioctl(mFD, MTP_SEND_FILE_WITH_HEADER, (unsigned long)&mfr); + LOGV("MTP_SEND_FILE_WITH_HEADER returned %d\n", ret); close(mfr.fd); if (ret < 0) { if (errno == ECANCELED) @@ -798,15 +796,13 @@ MtpResponseCode MtpServer::doGetPartialObject(MtpOperationCode operation) { } mfr.offset = offset; mfr.length = length; + mfr.command = mRequest.getOperationCode(); + mfr.transaction_id = mRequest.getTransactionID(); mResponse.setParameter(1, length); - // send data header - mData.setOperationCode(mRequest.getOperationCode()); - mData.setTransactionID(mRequest.getTransactionID()); - mData.writeDataHeader(mFD, length + MTP_CONTAINER_HEADER_SIZE); - - // then transfer the file - int ret = ioctl(mFD, MTP_SEND_FILE, (unsigned long)&mfr); + // transfer the file + int ret = ioctl(mFD, MTP_SEND_FILE_WITH_HEADER, (unsigned long)&mfr); + LOGV("MTP_SEND_FILE_WITH_HEADER returned %d\n", ret); close(mfr.fd); if (ret < 0) { if (errno == ECANCELED) @@ -918,7 +914,7 @@ MtpResponseCode MtpServer::doSendObject() { return MTP_RESPONSE_GENERAL_ERROR; MtpResponseCode result = MTP_RESPONSE_OK; mode_t mask; - int ret; + int ret, initialData; if (mSendObjectHandle == kInvalidObjectHandle) { LOGE("Expected SendObjectInfo before SendObject"); @@ -926,12 +922,13 @@ MtpResponseCode MtpServer::doSendObject() { goto done; } - // read the header - ret = mData.readDataHeader(mFD); - // FIXME - check for errors here. - - // reset so we don't attempt to send this back - mData.reset(); + // read the header, and possibly some data + ret = mData.read(mFD); + if (ret < MTP_CONTAINER_HEADER_SIZE) { + result = MTP_RESPONSE_GENERAL_ERROR; + goto done; + } + initialData = ret - MTP_CONTAINER_HEADER_SIZE; mtp_file_range mfr; mfr.fd = open(mSendObjectFilePath, O_RDWR | O_CREAT | O_TRUNC); @@ -945,15 +942,19 @@ MtpResponseCode MtpServer::doSendObject() { fchmod(mfr.fd, mFilePermission); umask(mask); - mfr.offset = 0; - mfr.length = mSendObjectFileSize; + if (initialData > 0) + ret = write(mfr.fd, mData.getData(), initialData); - LOGV("receiving %s\n", (const char *)mSendObjectFilePath); - // transfer the file - ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr); - close(mfr.fd); + if (mSendObjectFileSize - initialData > 0) { + mfr.offset = initialData; + mfr.length = mSendObjectFileSize - initialData; - LOGV("MTP_RECEIVE_FILE returned %d", ret); + LOGV("receiving %s\n", (const char *)mSendObjectFilePath); + // transfer the file + ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr); + LOGV("MTP_RECEIVE_FILE returned %d\n", ret); + } + close(mfr.fd); if (ret < 0) { unlink(mSendObjectFilePath); @@ -964,6 +965,9 @@ MtpResponseCode MtpServer::doSendObject() { } done: + // reset so we don't attempt to send the data back + mData.reset(); + mDatabase->endSendObject(mSendObjectFilePath, mSendObjectHandle, mSendObjectFormat, result == MTP_RESPONSE_OK); mSendObjectHandle = kInvalidObjectHandle; @@ -1096,23 +1100,31 @@ MtpResponseCode MtpServer::doSendPartialObject() { return MTP_RESPONSE_GENERAL_ERROR; } - // read the header - int ret = mData.readDataHeader(mFD); - // FIXME - check for errors here. + const char* filePath = (const char *)edit->mPath; + LOGV("receiving partial %s %lld %lld\n", filePath, offset, length); - // reset so we don't attempt to send this back - mData.reset(); + // read the header, and possibly some data + int ret = mData.read(mFD); + if (ret < MTP_CONTAINER_HEADER_SIZE) + return MTP_RESPONSE_GENERAL_ERROR; + int initialData = ret - MTP_CONTAINER_HEADER_SIZE; - const char* filePath = (const char *)edit->mPath; - LOGV("receiving partial %s %lld %ld\n", filePath, offset, length); - mtp_file_range mfr; - mfr.fd = edit->mFD; - mfr.offset = offset; - mfr.length = length; + if (initialData > 0) { + ret = write(edit->mFD, mData.getData(), initialData); + offset += initialData; + length -= initialData; + } - // transfer the file - ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr); - LOGV("MTP_RECEIVE_FILE returned %d", ret); + if (length > 0) { + mtp_file_range mfr; + mfr.fd = edit->mFD; + mfr.offset = offset; + mfr.length = length; + + // transfer the file + ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr); + LOGV("MTP_RECEIVE_FILE returned %d", ret); + } if (ret < 0) { mResponse.setParameter(1, 0); if (errno == ECANCELED) @@ -1120,6 +1132,9 @@ MtpResponseCode MtpServer::doSendPartialObject() { else return MTP_RESPONSE_GENERAL_ERROR; } + + // reset so we don't attempt to send this back + mData.reset(); mResponse.setParameter(1, length); uint64_t end = offset + length; if (end > edit->mSize) { diff --git a/native/android/native_window.cpp b/native/android/native_window.cpp index 2c0e88eef996..5c016c478df7 100644 --- a/native/android/native_window.cpp +++ b/native/android/native_window.cpp @@ -81,39 +81,9 @@ int32_t ANativeWindow_setBuffersGeometry(ANativeWindow* window, int32_t width, int32_t ANativeWindow_lock(ANativeWindow* window, ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds) { - int type = -1; - if (window->query(window, NATIVE_WINDOW_CONCRETE_TYPE, &type) != 0 || - type != NATIVE_WINDOW_SURFACE) { - return BAD_VALUE; - } - - Region dirtyRegion; - Region* dirtyParam = NULL; - if (inOutDirtyBounds != NULL) { - dirtyRegion.set(*(Rect*)inOutDirtyBounds); - dirtyParam = &dirtyRegion; - } - - Surface::SurfaceInfo info; - status_t res = static_cast<Surface*>(window)->lock(&info, dirtyParam); - if (res != OK) { - return -1; - } - - outBuffer->width = (int32_t)info.w; - outBuffer->height = (int32_t)info.h; - outBuffer->stride = (int32_t)info.s; - outBuffer->format = (int32_t)info.format; - outBuffer->bits = info.bits; - - if (inOutDirtyBounds != NULL) { - *inOutDirtyBounds = dirtyRegion.getBounds(); - } - - return 0; + return window->perform(window, NATIVE_WINDOW_LOCK, outBuffer, inOutDirtyBounds); } int32_t ANativeWindow_unlockAndPost(ANativeWindow* window) { - status_t res = static_cast<Surface*>(window)->unlockAndPost(); - return res == android::OK ? 0 : -1; + return window->perform(window, NATIVE_WINDOW_UNLOCK_AND_POST); } diff --git a/native/include/android/native_window.h b/native/include/android/native_window.h index 337fa9611b70..2f4f2d33bec9 100644 --- a/native/include/android/native_window.h +++ b/native/include/android/native_window.h @@ -99,10 +99,16 @@ int32_t ANativeWindow_getFormat(ANativeWindow* window); * width and height must be either both zero or both non-zero. * */ -int32_t ANativeWindow_setBuffersGeometry(ANativeWindow* window, int32_t width, int32_t height, int32_t format); +int32_t ANativeWindow_setBuffersGeometry(ANativeWindow* window, + int32_t width, int32_t height, int32_t format); /** * Lock the window's next drawing surface for writing. + * inOutDirtyBounds is used as an in/out parameter, upon entering the + * function, it contains the dirty region, that is, the region the caller + * intends to redraw. When the function returns, inOutDirtyBounds is updated + * with the actual area the caller needs to redraw -- this region is often + * extended by ANativeWindow_lock. */ int32_t ANativeWindow_lock(ANativeWindow* window, ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds); diff --git a/opengl/include/EGL/eglext.h b/opengl/include/EGL/eglext.h index 1123e1673b64..6a199db155a1 100644 --- a/opengl/include/EGL/eglext.h +++ b/opengl/include/EGL/eglext.h @@ -226,7 +226,7 @@ typedef EGLBoolean (EGLAPIENTRYP PFNEGLGETSYNCATTRIBNVPROC) (EGLSyncNV sync, EGL #ifndef EGL_ANDROID_image_native_buffer #define EGL_ANDROID_image_native_buffer 1 struct ANativeWindowBuffer; -#define EGL_NATIVE_BUFFER_ANDROID 0x3140 /* eglCreateImageKHR target */ +#define EGL_NATIVE_BUFFER_ANDROID 0x3140 /* eglCreateImageKHR target */ #endif #ifndef EGL_ANDROID_swap_rectangle @@ -237,6 +237,11 @@ EGLAPI EGLBoolean EGLAPIENTRY eglSetSwapRectangleANDROID (EGLDisplay dpy, EGLSur typedef EGLBoolean (EGLAPIENTRYP PFNEGLSETSWAPRECTANGLEANDROIDPROC) (EGLDisplay dpy, EGLSurface draw, EGLint left, EGLint top, EGLint width, EGLint height); #endif +#ifndef EGL_ANDROID_recordable +#define EGL_ANDROID_recordable 1 +#define EGL_RECORDABLE_ANDROID 0x3142 /* EGLConfig attribute */ +#endif + #ifdef __cplusplus } #endif diff --git a/opengl/specs/EGL_ANDROID_recordable.txt b/opengl/specs/EGL_ANDROID_recordable.txt index cf44465198d7..8dbd26fdffeb 100644 --- a/opengl/specs/EGL_ANDROID_recordable.txt +++ b/opengl/specs/EGL_ANDROID_recordable.txt @@ -55,7 +55,7 @@ New Tokens Accepted by the <attribute> parameter of eglGetConfigAttrib and the <attrib_list> parameter of eglChooseConfig: - EGL_RECORDABLE_ANDROID 0xXXXX + EGL_RECORDABLE_ANDROID 0x3142 Changes to Chapter 3 of the EGL 1.4 Specification (EGL Functions and Errors) @@ -103,11 +103,38 @@ Issues RESOLVED: It should not affect sorting. Some implementations may not have any drawback associated with using a recordable EGLConfig. Such - implementations should not have to double-up some of their configs to one sort earlier than . - Implementations that do have drawbacks can use the existing caveat - mechanism to report this drawback to the client. + implementations should not have to double-up some of their configs to one + sort earlier than . Implementations that do have drawbacks can use the + existing caveat mechanism to report this drawback to the client. + + 3. How is this extension expected to be implemented? + + RESPONSE: There are two basic approaches to implementing this extension + that were considered during its design. In both cases it is assumed that a + color space conversion must be performed at some point because most video + encoding formats use a YUV color space. The two approaches are + distinguished by the point at which this color space conversion is + performed. + + One approach involves performing the color space conversion as part of the + eglSwapBuffers call before queuing the rendered image to the ANativeWindow. + In this case, the VisualID of the EGLConfig would correspond to a YUV + Android HAL pixel format from which the video encoder can read. The + EGLConfig would likely have the EGL_SLOW_CONFIG caveat because using that + config to render normal window contents would result in an RGB -> YUV color + space conversion when rendering the frame as well as a YUV -> RGB + conversion when compositing the window. + + The other approach involves performing the color space conversion in the + video encoder. In this case, the VisualID of the EGLConfig would + correspond to an RGB HAL pixel format from which the video encoder can + read. The EGLConfig would likely not need to have any caveat set, as using + this config for normal window rendering would not have any added cost. Revision History +#2 (Jamie Gennis, July 15, 2011) + - Added issue 3. + #1 (Jamie Gennis, July 8, 2011) - Initial draft. diff --git a/packages/SystemUI/res/drawable/recents_thumbnail_bg_selector.xml b/packages/SystemUI/res/drawable/recents_thumbnail_bg_selector.xml deleted file mode 100644 index 0e58e12ba425..000000000000 --- a/packages/SystemUI/res/drawable/recents_thumbnail_bg_selector.xml +++ /dev/null @@ -1,27 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2011 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<selector xmlns:android="http://schemas.android.com/apk/res/android" - android:exitFadeDuration="@android:integer/config_mediumAnimTime"> - - <item android:state_window_focused="false" android:drawable="@android:color/transparent" /> - - <!-- Even though these two point to the same resource, have two states so the drawable will invalidate itself when coming out of pressed state. --> - <item android:state_focused="true" android:state_pressed="true" android:drawable="@drawable/recents_thumbnail_bg_holo" /> - <item android:state_focused="false" android:state_pressed="true" android:drawable="@drawable/recents_thumbnail_bg_holo" /> - <item android:state_focused="true" android:drawable="@drawable/recents_thumbnail_bg_holo" /> -</selector> - diff --git a/packages/SystemUI/res/drawable/recents_thumbnail_layers.xml b/packages/SystemUI/res/drawable/recents_thumbnail_layers.xml new file mode 100644 index 000000000000..6cae2c496367 --- /dev/null +++ b/packages/SystemUI/res/drawable/recents_thumbnail_layers.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2011 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<layer-list xmlns:android="http://schemas.android.com/apk/res/android" > + <item android:drawable="@drawable/recents_thumbnail_bg" android:id="@+id/base_layer"/> + <item android:drawable="@drawable/recents_thumbnail_overlay" android:id="@+id/overlay_layer"/> +</layer-list>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/recents_thumbnail_overlay.xml b/packages/SystemUI/res/drawable/recents_thumbnail_overlay.xml new file mode 100644 index 000000000000..200bac40b947 --- /dev/null +++ b/packages/SystemUI/res/drawable/recents_thumbnail_overlay.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2011 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:drawable="@drawable/recents_thumbnail_bg_press" android:state_pressed="true" /> + <item android:drawable="@*android:color/transparent"/> +</selector> diff --git a/packages/SystemUI/res/layout-land/status_bar_recent_item.xml b/packages/SystemUI/res/layout-land/status_bar_recent_item.xml index be4f1d775440..8c290425e31c 100644 --- a/packages/SystemUI/res/layout-land/status_bar_recent_item.xml +++ b/packages/SystemUI/res/layout-land/status_bar_recent_item.xml @@ -24,14 +24,15 @@ android:layout_height="wrap_content" android:layout_width="@dimen/status_bar_recents_thumbnail_view_width"> - <ImageView android:id="@+id/app_thumbnail" + <FrameLayout android:id="@+id/app_thumbnail" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_alignParentTop="true" android:layout_marginLeft="@dimen/status_bar_recents_thumbnail_left_margin" android:scaleType="center" - android:background="@drawable/recents_thumbnail_bg_selector" + android:clickable="true" + android:background="@drawable/recents_thumbnail_layers" /> <ImageView android:id="@+id/app_icon" diff --git a/packages/SystemUI/res/layout-land/status_bar_recent_panel.xml b/packages/SystemUI/res/layout-land/status_bar_recent_panel.xml index efdd9ac1a65e..20ef7cf26b37 100644 --- a/packages/SystemUI/res/layout-land/status_bar_recent_panel.xml +++ b/packages/SystemUI/res/layout-land/status_bar_recent_panel.xml @@ -51,7 +51,6 @@ android:fadingEdge="horizontal" android:scrollbars="none" android:fadingEdgeLength="@dimen/status_bar_recents_fading_edge_length" - android:listSelector="@drawable/recents_thumbnail_bg_selector" android:layout_gravity="bottom|left" android:orientation="horizontal" android:clipToPadding="false" diff --git a/packages/SystemUI/res/layout-port/status_bar_recent_item.xml b/packages/SystemUI/res/layout-port/status_bar_recent_item.xml index 76965c98d633..c705a699a5d0 100644 --- a/packages/SystemUI/res/layout-port/status_bar_recent_item.xml +++ b/packages/SystemUI/res/layout-port/status_bar_recent_item.xml @@ -24,13 +24,15 @@ android:layout_height="wrap_content" android:layout_width="@dimen/status_bar_recents_thumbnail_view_width"> - <ImageView android:id="@+id/app_thumbnail" + <FrameLayout android:id="@+id/app_thumbnail" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_alignParentTop="true" + android:clickable="true" android:layout_marginLeft="@dimen/status_bar_recents_thumbnail_left_margin" android:scaleType="center" + android:background="@drawable/recents_thumbnail_layers" /> <ImageView android:id="@+id/app_icon" diff --git a/packages/SystemUI/res/layout-port/status_bar_recent_panel.xml b/packages/SystemUI/res/layout-port/status_bar_recent_panel.xml index 28ef239fd1ac..c680b8eeeceb 100644 --- a/packages/SystemUI/res/layout-port/status_bar_recent_panel.xml +++ b/packages/SystemUI/res/layout-port/status_bar_recent_panel.xml @@ -48,7 +48,6 @@ android:fadingEdge="vertical" android:scrollbars="none" android:fadingEdgeLength="@*android:dimen/status_bar_height" - android:listSelector="@drawable/recents_thumbnail_bg_selector" android:layout_gravity="bottom|left" android:clipToPadding="false" android:clipChildren="false"> diff --git a/packages/SystemUI/res/layout-sw600dp/status_bar_recent_item.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_recent_item.xml index 9687866600b7..386ce30a01dc 100644 --- a/packages/SystemUI/res/layout-sw600dp/status_bar_recent_item.xml +++ b/packages/SystemUI/res/layout-sw600dp/status_bar_recent_item.xml @@ -24,13 +24,15 @@ android:layout_height="wrap_content" android:layout_width="@dimen/status_bar_recents_thumbnail_view_width"> - <ImageView android:id="@+id/app_thumbnail" + <FrameLayout android:id="@+id/app_thumbnail" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_alignParentTop="true" android:layout_marginLeft="@dimen/status_bar_recents_thumbnail_left_margin" android:scaleType="center" + android:clickable="true" + android:background="@drawable/recents_thumbnail_layers" /> <ImageView android:id="@+id/app_icon" diff --git a/packages/SystemUI/res/layout-sw600dp/status_bar_recent_panel.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_recent_panel.xml index 75fdc67cf534..2c9a15202b31 100644 --- a/packages/SystemUI/res/layout-sw600dp/status_bar_recent_panel.xml +++ b/packages/SystemUI/res/layout-sw600dp/status_bar_recent_panel.xml @@ -56,7 +56,6 @@ android:scrollbars="none" android:fadingEdgeLength="20dip" android:layout_gravity="bottom|left" - android:listSelector="@drawable/recents_thumbnail_bg_selector" android:clipToPadding="false" android:clipChildren="false"> diff --git a/packages/SystemUI/res/drawable/recents_thumbnail_bg_holo.xml b/packages/SystemUI/res/menu/recent_popup_menu.xml index f9bba2aa1b25..eecfb9ab7fbe 100644 --- a/packages/SystemUI/res/drawable/recents_thumbnail_bg_holo.xml +++ b/packages/SystemUI/res/menu/recent_popup_menu.xml @@ -17,7 +17,7 @@ ** limitations under the License. */ --> -<transition xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:drawable="@drawable/recents_thumbnail_bg_press"/> - <item android:drawable="@drawable/recents_thumbnail_bg_press"/> -</transition> +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:id="@+id/recent_remove_item" android:title="@string/status_bar_recent_remove_item_title" /> + <item android:id="@+id/recent_inspect_item" android:title="@string/status_bar_recent_inspect_item_title" /> +</menu> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 882455e423a5..01cf2dcf4a6f 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -35,6 +35,14 @@ shown again. [CHAR LIMIT=25] --> <string name="status_bar_please_disturb_button">Show notifications</string> + <!-- Title shown in recents popup for removing an application from the list --> + <string name="status_bar_recent_remove_item_title">Remove</string> + + <!-- Title shown in recents popup for inspecting an application's properties --> + <string name="status_bar_recent_inspect_item_title">Inspect</string> + + + <!-- The label in the bar at the top of the status bar when there are no notifications showing. [CHAR LIMIT=40]--> diff --git a/packages/SystemUI/src/com/android/systemui/recent/Choreographer.java b/packages/SystemUI/src/com/android/systemui/recent/Choreographer.java index 37a9913e273a..2d327c41c6eb 100644 --- a/packages/SystemUI/src/com/android/systemui/recent/Choreographer.java +++ b/packages/SystemUI/src/com/android/systemui/recent/Choreographer.java @@ -53,8 +53,6 @@ import android.view.View; void createAnimation(boolean appearing) { float start, end; - if (RecentsPanelView.DEBUG) Log.e(TAG, "createAnimation()", new Exception()); - // 0: on-screen // height: off-screen float y = mContentView.getTranslationY(); diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsCallback.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsCallback.java index 5d29e2aedf22..797f94cceb1d 100644 --- a/packages/SystemUI/src/com/android/systemui/recent/RecentsCallback.java +++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsCallback.java @@ -26,5 +26,5 @@ public interface RecentsCallback { void handleOnClick(View selectedView); void handleSwipe(View selectedView, int direction); - void handleLongPress(View selectedView); + void handleLongPress(View selectedView, View anchorView); } diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java index f984aac55b25..2a5d1dd7ba70 100644 --- a/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java +++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java @@ -20,6 +20,7 @@ import com.android.systemui.recent.RecentsPanelView.ActivityDescriptionAdapter; import android.animation.Animator; import android.animation.Animator.AnimatorListener; +import android.animation.AnimatorListenerAdapter; import android.animation.LayoutTransition; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; @@ -30,7 +31,6 @@ import android.database.DataSetObserver; import android.graphics.RectF; import android.util.AttributeSet; import android.util.Log; -import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.View; @@ -42,10 +42,9 @@ import android.widget.LinearLayout; import com.android.systemui.R; -public class RecentsHorizontalScrollView extends HorizontalScrollView - implements View.OnClickListener, View.OnTouchListener { - private static final boolean DEBUG_INVALIDATE = false; +public class RecentsHorizontalScrollView extends HorizontalScrollView { private static final String TAG = RecentsPanelView.TAG; + private static final boolean DEBUG_INVALIDATE = false; private static final boolean DEBUG = RecentsPanelView.DEBUG; private LinearLayout mLinearLayout; private ActivityDescriptionAdapter mAdapter; @@ -57,6 +56,15 @@ public class RecentsHorizontalScrollView extends HorizontalScrollView private VelocityTracker mVelocityTracker; private float mDensityScale; private float mPagingTouchSlop; + private OnLongClickListener mOnLongClick = new OnLongClickListener() { + public boolean onLongClick(View v) { + final View anchorView = v.findViewById(R.id.app_description); + mCurrentView = v; + mCallback.handleLongPress(v, anchorView); + mCurrentView = null; // make sure we don't accept the return click from this + return true; + } + }; public RecentsHorizontalScrollView(Context context) { this(context, null); @@ -72,13 +80,12 @@ public class RecentsHorizontalScrollView extends HorizontalScrollView return mLinearLayout.getWidth() - getWidth(); } - public void update() { + private void update() { mLinearLayout.removeAllViews(); for (int i = 0; i < mAdapter.getCount(); i++) { - View view = mAdapter.getView(i, null, mLinearLayout); + final View view = mAdapter.getView(i, null, mLinearLayout); view.setClickable(true); - view.setOnClickListener(this); - view.setOnTouchListener(this); + view.setOnLongClickListener(mOnLongClick); mLinearLayout.addView(view); } // Scroll to end after layout. @@ -91,7 +98,20 @@ public class RecentsHorizontalScrollView extends HorizontalScrollView } @Override + public void removeViewInLayout(final View view) { + ObjectAnimator anim = animateClosed(view, Constants.MAX_ESCAPE_ANIMATION_DURATION, + "y", view.getY(), view.getY() + view.getHeight()); + anim.addListener(new AnimatorListenerAdapter() { + public void onAnimationEnd(Animator animation) { + RecentsHorizontalScrollView.super.removeView(view); + } + }); + anim.start(); + } + + @Override public boolean onInterceptTouchEvent(MotionEvent ev) { + if (DEBUG) Log.v(TAG, "onInterceptTouchEvent()"); if (mVelocityTracker == null) { mVelocityTracker = VelocityTracker.obtain(); } @@ -100,6 +120,18 @@ public class RecentsHorizontalScrollView extends HorizontalScrollView case MotionEvent.ACTION_DOWN: mDragging = false; mLastY = ev.getY(); + final float x = ev.getX() + getScrollX(); + final float y = ev.getY() + getScrollY(); + mCurrentView = null; + for (int i = 0; i < mLinearLayout.getChildCount(); i++) { + View item = mLinearLayout.getChildAt(i); + if (x >= item.getLeft() && x < item.getRight() + && y >= item.getTop() && y < item.getBottom()) { + mCurrentView = item; + if (DEBUG) Log.v(TAG, "Hit item " + item); + break; + } + } break; case MotionEvent.ACTION_MOVE: @@ -111,6 +143,9 @@ public class RecentsHorizontalScrollView extends HorizontalScrollView break; case MotionEvent.ACTION_UP: + if (mCurrentView != null) { + mCallback.handleOnClick(mCurrentView); + } mDragging = false; break; } @@ -125,7 +160,6 @@ public class RecentsHorizontalScrollView extends HorizontalScrollView } else if (view.getY() < thumbHeight * (1.0f - Constants.ALPHA_FADE_START)) { result = 1.0f + (thumbHeight * Constants.ALPHA_FADE_START + view.getY()) / fadeHeight; } - if (DEBUG) Log.v(TAG, "FADE AMOUNT: " + result); return result; } @@ -138,12 +172,12 @@ public class RecentsHorizontalScrollView extends HorizontalScrollView mVelocityTracker.addMovement(ev); final View animView = mCurrentView; - // TODO: Cache thumbnail - final View thumb = animView.findViewById(R.id.app_thumbnail); + switch (ev.getAction()) { case MotionEvent.ACTION_MOVE: if (animView != null) { final float delta = ev.getY() - mLastY; + final View thumb = animView.findViewById(R.id.app_thumbnail); animView.setY(animView.getY() + delta); animView.setAlpha(getAlphaForOffset(animView, thumb.getHeight())); invalidateGlobalRegion(animView); @@ -167,35 +201,18 @@ public class RecentsHorizontalScrollView extends HorizontalScrollView long duration = (long) (Math.abs(newY - curY) * 1000.0f / Math.abs(velocityY)); duration = Math.min(duration, Constants.MAX_ESCAPE_ANIMATION_DURATION); - anim = ObjectAnimator.ofFloat(animView, "y", curY, newY); - anim.setInterpolator(new LinearInterpolator()); - final int swipeDirection = animView.getY() >= 0.0f ? - RecentsCallback.SWIPE_RIGHT : RecentsCallback.SWIPE_LEFT; - anim.addListener(new AnimatorListener() { - public void onAnimationStart(Animator animation) { - } - public void onAnimationRepeat(Animator animation) { - } - public void onAnimationEnd(Animator animation) { - mLinearLayout.removeView(mCurrentView); - mCallback.handleSwipe(animView, swipeDirection); - } - public void onAnimationCancel(Animator animation) { - mLinearLayout.removeView(mCurrentView); - mCallback.handleSwipe(animView, swipeDirection); - } - }); - anim.setDuration(duration); + anim = animateClosed(animView, duration, "y", curY, newY); } else { // Animate back to position long duration = Math.abs(velocityY) > 0.0f ? (long) (Math.abs(newY - curY) * 1000.0f / Math.abs(velocityY)) : Constants.SNAP_BACK_DURATION; duration = Math.min(duration, Constants.SNAP_BACK_DURATION); anim = ObjectAnimator.ofFloat(animView, "y", animView.getY(), 0.0f); - anim.setInterpolator(new DecelerateInterpolator(2.0f)); + anim.setInterpolator(new DecelerateInterpolator(4.0f)); anim.setDuration(duration); } + final View thumb = animView.findViewById(R.id.app_thumbnail); anim.addUpdateListener(new AnimatorUpdateListener() { public void onAnimationUpdate(ValueAnimator animation) { animView.setAlpha(getAlphaForOffset(animView, thumb.getHeight())); @@ -212,6 +229,26 @@ public class RecentsHorizontalScrollView extends HorizontalScrollView return true; } + private ObjectAnimator animateClosed(final View animView, long duration, + String attr, float from, float to) { + ObjectAnimator anim = ObjectAnimator.ofFloat(animView, attr, from, to); + anim.setInterpolator(new LinearInterpolator()); + final int swipeDirection = animView.getX() >= 0.0f ? + RecentsCallback.SWIPE_RIGHT : RecentsCallback.SWIPE_LEFT; + anim.addListener(new AnimatorListenerAdapter() { + public void onAnimationEnd(Animator animation) { + mLinearLayout.removeView(animView); + mCallback.handleSwipe(animView, swipeDirection); + } + public void onAnimationCancel(Animator animation) { + mLinearLayout.removeView(animView); + mCallback.handleSwipe(animView, swipeDirection); + } + }); + anim.setDuration(duration); + return anim; + } + void invalidateGlobalRegion(View view) { RectF childBounds = new RectF(view.getLeft(), view.getTop(), view.getRight(), view.getBottom()); @@ -236,13 +273,8 @@ public class RecentsHorizontalScrollView extends HorizontalScrollView @Override protected void onFinishInflate() { super.onFinishInflate(); - LayoutInflater inflater = (LayoutInflater) - mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - setScrollbarFadingEnabled(true); - mLinearLayout = (LinearLayout) findViewById(R.id.recents_linear_layout); - final int leftPadding = mContext.getResources() .getDimensionPixelOffset(R.dimen.status_bar_recents_thumbnail_left_margin); setOverScrollEffectPadding(leftPadding, 0); @@ -306,16 +338,7 @@ public class RecentsHorizontalScrollView extends HorizontalScrollView mLinearLayout.setLayoutTransition(transition); } - public void onClick(View view) { - mCallback.handleOnClick(view); - } - public void setCallback(RecentsCallback callback) { mCallback = callback; } - - public boolean onTouch(View v, MotionEvent event) { - mCurrentView = v; - return false; - } } diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java index a55fe9c0017c..bc0a508c06a7 100644 --- a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java @@ -22,6 +22,7 @@ import java.util.List; import android.animation.Animator; import android.animation.LayoutTransition; import android.app.ActivityManager; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; @@ -39,16 +40,22 @@ import android.graphics.RectF; import android.graphics.Shader.TileMode; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; +import android.graphics.drawable.LayerDrawable; +import android.net.Uri; +import android.provider.Settings; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.util.Log; import android.view.LayoutInflater; +import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.BaseAdapter; +import android.widget.Button; import android.widget.ImageView; +import android.widget.PopupMenu; import android.widget.RelativeLayout; import android.widget.TextView; @@ -69,7 +76,7 @@ public class RecentsPanelView extends RelativeLayout private int mIconDpi; private View mRecentsScrim; private View mRecentsGlowView; - private View mRecentsContainer; + private ViewGroup mRecentsContainer; private Bitmap mGlowBitmap; // TODO: add these widgets attributes to the layout file private int mGlowBitmapPaddingLeftPx; @@ -107,8 +114,18 @@ public class RecentsPanelView extends RelativeLayout } }; + private final class OnLongClickDelegate implements View.OnLongClickListener { + View mOtherView; + OnLongClickDelegate(View other) { + mOtherView = other; + } + public boolean onLongClick(View v) { + return mOtherView.performLongClick(); + } + } + /* package */ final static class ViewHolder { - ImageView thumbnailView; + View thumbnailView; ImageView iconView; TextView labelView; TextView descriptionView; @@ -139,7 +156,7 @@ public class RecentsPanelView extends RelativeLayout if (convertView == null) { convertView = mInflater.inflate(R.layout.status_bar_recent_item, null); holder = new ViewHolder(); - holder.thumbnailView = (ImageView) convertView.findViewById(R.id.app_thumbnail); + holder.thumbnailView = convertView.findViewById(R.id.app_thumbnail); holder.iconView = (ImageView) convertView.findViewById(R.id.app_icon); holder.labelView = (TextView) convertView.findViewById(R.id.app_label); holder.descriptionView = (TextView) convertView.findViewById(R.id.app_description); @@ -153,11 +170,12 @@ public class RecentsPanelView extends RelativeLayout final ActivityDescription activityDescription = mActivityDescriptions.get(activityId); final Bitmap thumb = activityDescription.thumbnail; - holder.thumbnailView.setImageBitmap(compositeBitmap(mGlowBitmap, thumb)); + updateDrawable(holder.thumbnailView, compositeBitmap(mGlowBitmap, thumb)); holder.iconView.setImageDrawable(activityDescription.icon); holder.labelView.setText(activityDescription.label); holder.descriptionView.setText(activityDescription.description); holder.thumbnailView.setTag(activityDescription); + holder.thumbnailView.setOnLongClickListener(new OnLongClickDelegate(convertView)); holder.activityDescription = activityDescription; return convertView; @@ -174,6 +192,20 @@ public class RecentsPanelView extends RelativeLayout return x >= l && x < r && y >= t && y < b; } + private void updateDrawable(View thumbnailView, Bitmap bitmap) { + Drawable d = thumbnailView.getBackground(); + if (d instanceof LayerDrawable) { + LayerDrawable layerD = (LayerDrawable) d; + Drawable thumb = layerD.findDrawableByLayerId(R.id.base_layer); + if (thumb != null) { + layerD.setDrawableByLayerId(R.id.base_layer, + new BitmapDrawable(getResources(), bitmap)); + return; + } + } + Log.w(TAG, "Failed to update drawable"); + } + public void show(boolean show, boolean animate) { if (animate) { if (mShowing != show) { @@ -260,7 +292,7 @@ public class RecentsPanelView extends RelativeLayout protected void onFinishInflate() { super.onFinishInflate(); mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - mRecentsContainer = findViewById(R.id.recents_container); + mRecentsContainer = (ViewGroup) findViewById(R.id.recents_container); mListAdapter = new ActivityDescriptionAdapter(mContext); if (mRecentsContainer instanceof RecentsListView) { RecentsListView listView = (RecentsListView) mRecentsContainer; @@ -503,7 +535,35 @@ public class RecentsPanelView extends RelativeLayout am.removeTask(ad.taskId, ActivityManager.REMOVE_TASK_KILL_PROCESS); } - public void handleLongPress(View selectedView) { - // TODO show context menu : "Remove from list", "Show properties" + private void startApplicationDetailsActivity(String packageName) { + Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, + Uri.fromParts("package", packageName, null)); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + getContext().startActivity(intent); + } + + public void handleLongPress(final View selectedView, final View anchorView) { + PopupMenu popup = new PopupMenu(mContext, anchorView == null ? selectedView : anchorView); + popup.getMenuInflater().inflate(R.menu.recent_popup_menu, popup.getMenu()); + popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { + public boolean onMenuItemClick(MenuItem item) { + if (item.getItemId() == R.id.recent_remove_item) { + mRecentsContainer.removeViewInLayout(selectedView); + } else if (item.getItemId() == R.id.recent_inspect_item) { + ViewHolder viewHolder = (ViewHolder) selectedView.getTag(); + if (viewHolder != null) { + final ActivityDescription ad = viewHolder.activityDescription; + startApplicationDetailsActivity(ad.packageName); + mBar.animateCollapse(); + } else { + throw new IllegalStateException("Oops, no tag on view " + selectedView); + } + } else { + return false; + } + return true; + } + }); + popup.show(); } } diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java index 27bb0b57461c..47ee4aa3f8f0 100644 --- a/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java +++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java @@ -20,6 +20,7 @@ import com.android.systemui.recent.RecentsPanelView.ActivityDescriptionAdapter; import android.animation.Animator; import android.animation.Animator.AnimatorListener; +import android.animation.AnimatorListenerAdapter; import android.animation.LayoutTransition; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; @@ -42,8 +43,7 @@ import android.widget.ScrollView; import com.android.systemui.R; -public class RecentsVerticalScrollView extends ScrollView - implements View.OnClickListener, View.OnTouchListener { +public class RecentsVerticalScrollView extends ScrollView { private static final String TAG = RecentsPanelView.TAG; private static final boolean DEBUG_INVALIDATE = false; private static final boolean DEBUG = RecentsPanelView.DEBUG; @@ -57,6 +57,15 @@ public class RecentsVerticalScrollView extends ScrollView private VelocityTracker mVelocityTracker; private float mDensityScale; private float mPagingTouchSlop; + private OnLongClickListener mOnLongClick = new OnLongClickListener() { + public boolean onLongClick(View v) { + final View anchorView = v.findViewById(R.id.app_description); + mCurrentView = v; + mCallback.handleLongPress(v, anchorView); + mCurrentView = null; // make sure we don't accept the return click from this + return true; + } + }; public RecentsVerticalScrollView(Context context) { this(context, null); @@ -72,13 +81,12 @@ public class RecentsVerticalScrollView extends ScrollView return mLinearLayout.getHeight() - getHeight(); } - public void update() { + private void update() { mLinearLayout.removeAllViews(); for (int i = 0; i < mAdapter.getCount(); i++) { - View view = mAdapter.getView(i, null, mLinearLayout); + final View view = mAdapter.getView(i, null, mLinearLayout); view.setClickable(true); - view.setOnClickListener(this); - view.setOnTouchListener(this); + view.setOnLongClickListener(mOnLongClick); mLinearLayout.addView(view); } // Scroll to end after layout. @@ -91,7 +99,20 @@ public class RecentsVerticalScrollView extends ScrollView } @Override + public void removeViewInLayout(final View view) { + ObjectAnimator anim = animateClosed(view, Constants.MAX_ESCAPE_ANIMATION_DURATION, + "x", view.getX(), view.getX() + view.getWidth()); + anim.addListener(new AnimatorListenerAdapter() { + public void onAnimationEnd(Animator animation) { + RecentsVerticalScrollView.super.removeView(view); + } + }); + anim.start(); + } + + @Override public boolean onInterceptTouchEvent(MotionEvent ev) { + if (DEBUG) Log.v(TAG, "onInterceptTouchEvent()"); if (mVelocityTracker == null) { mVelocityTracker = VelocityTracker.obtain(); } @@ -100,6 +121,18 @@ public class RecentsVerticalScrollView extends ScrollView case MotionEvent.ACTION_DOWN: mDragging = false; mLastX = ev.getX(); + final float x = ev.getX() + getScrollX(); + final float y = ev.getY() + getScrollY(); + mCurrentView = null; + for (int i = 0; i < mLinearLayout.getChildCount(); i++) { + View item = mLinearLayout.getChildAt(i); + if (x >= item.getLeft() && x < item.getRight() + && y >= item.getTop() && y < item.getBottom()) { + mCurrentView = item; + Log.v(TAG, "Hit item " + item); + break; + } + } break; case MotionEvent.ACTION_MOVE: @@ -111,6 +144,9 @@ public class RecentsVerticalScrollView extends ScrollView break; case MotionEvent.ACTION_UP: + if (mCurrentView != null) { + mCallback.handleOnClick(mCurrentView); + } mDragging = false; break; } @@ -125,7 +161,6 @@ public class RecentsVerticalScrollView extends ScrollView } else if (view.getX() < thumbWidth* (1.0f - Constants.ALPHA_FADE_START)) { result = 1.0f + (thumbWidth*Constants.ALPHA_FADE_START + view.getX()) / fadeWidth; } - if (DEBUG) Log.v(TAG, "FADE AMOUNT: " + result); return result; } @@ -138,12 +173,12 @@ public class RecentsVerticalScrollView extends ScrollView mVelocityTracker.addMovement(ev); final View animView = mCurrentView; - // TODO: Cache thumbnail - final View thumb = animView.findViewById(R.id.app_thumbnail); + switch (ev.getAction()) { case MotionEvent.ACTION_MOVE: if (animView != null) { final float delta = ev.getX() - mLastX; + final View thumb = animView.findViewById(R.id.app_thumbnail); animView.setX(animView.getX() + delta); animView.setAlpha(getAlphaForOffset(animView, thumb.getWidth())); invalidateGlobalRegion(animView); @@ -163,29 +198,11 @@ public class RecentsVerticalScrollView extends ScrollView final float maxVelocity = Constants.ESCAPE_VELOCITY * mDensityScale; if (Math.abs(velocityX) > Math.abs(velocityY) && Math.abs(velocityX) > maxVelocity - && (velocityX > 0.0f) == (animView.getX() >= 0)) { + && (velocityX >= 0.0f) == (animView.getX() >= 0)) { long duration = (long) (Math.abs(newX-curX) * 1000.0f / Math.abs(velocityX)); duration = Math.min(duration, Constants.MAX_ESCAPE_ANIMATION_DURATION); - anim = ObjectAnimator.ofFloat(animView, "x", curX, newX); - anim.setInterpolator(new LinearInterpolator()); - final int swipeDirection = animView.getX() >= 0.0f ? - RecentsCallback.SWIPE_RIGHT : RecentsCallback.SWIPE_LEFT; - anim.addListener(new AnimatorListener() { - public void onAnimationStart(Animator animation) { - } - public void onAnimationRepeat(Animator animation) { - } - public void onAnimationEnd(Animator animation) { - mLinearLayout.removeView(mCurrentView); - mCallback.handleSwipe(animView, swipeDirection); - } - public void onAnimationCancel(Animator animation) { - mLinearLayout.removeView(mCurrentView); - mCallback.handleSwipe(animView, swipeDirection); - } - }); - anim.setDuration(duration); + anim = animateClosed(animView, duration, "x", curX, newX); } else { // Animate back to position long duration = Math.abs(velocityX) > 0.0f ? (long) (Math.abs(newX-curX) * 1000.0f / Math.abs(velocityX)) @@ -196,6 +213,7 @@ public class RecentsVerticalScrollView extends ScrollView anim.setDuration(duration); } + final View thumb = animView.findViewById(R.id.app_thumbnail); anim.addUpdateListener(new AnimatorUpdateListener() { public void onAnimationUpdate(ValueAnimator animation) { animView.setAlpha(getAlphaForOffset(animView, thumb.getWidth())); @@ -212,6 +230,26 @@ public class RecentsVerticalScrollView extends ScrollView return true; } + private ObjectAnimator animateClosed(final View animView, long duration, + String attr, float from, float to) { + ObjectAnimator anim = ObjectAnimator.ofFloat(animView, attr, from, to); + anim.setInterpolator(new LinearInterpolator()); + final int swipeDirection = animView.getX() >= 0.0f ? + RecentsCallback.SWIPE_RIGHT : RecentsCallback.SWIPE_LEFT; + anim.addListener(new AnimatorListenerAdapter() { + public void onAnimationEnd(Animator animation) { + mLinearLayout.removeView(animView); + mCallback.handleSwipe(animView, swipeDirection); + } + public void onAnimationCancel(Animator animation) { + mLinearLayout.removeView(animView); + mCallback.handleSwipe(animView, swipeDirection); + } + }); + anim.setDuration(duration); + return anim; + } + void invalidateGlobalRegion(View view) { RectF childBounds = new RectF(view.getLeft(), view.getTop(), view.getRight(), view.getBottom()); @@ -236,13 +274,8 @@ public class RecentsVerticalScrollView extends ScrollView @Override protected void onFinishInflate() { super.onFinishInflate(); - LayoutInflater inflater = (LayoutInflater) - mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - setScrollbarFadingEnabled(true); - mLinearLayout = (LinearLayout) findViewById(R.id.recents_linear_layout); - final int leftPadding = mContext.getResources() .getDimensionPixelOffset(R.dimen.status_bar_recents_thumbnail_left_margin); setOverScrollEffectPadding(leftPadding, 0); @@ -306,16 +339,7 @@ public class RecentsVerticalScrollView extends ScrollView mLinearLayout.setLayoutTransition(transition); } - public void onClick(View view) { - mCallback.handleOnClick(view); - } - public void setCallback(RecentsCallback callback) { mCallback = callback; } - - public boolean onTouch(View v, MotionEvent event) { - mCurrentView = v; - return false; - } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java index dedbe5d70f8f..af5c72d92cdc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java @@ -218,9 +218,165 @@ public class PhoneStatusBarPolicy { R.drawable.stat_sys_roaming_cdma_0, R.drawable.stat_sys_roaming_cdma_0, R.drawable.stat_sys_roaming_cdma_0, - R.drawable.stat_sys_roaming_cdma_0 //83 + R.drawable.stat_sys_roaming_cdma_0, //83 + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0, + R.drawable.stat_sys_roaming_cdma_0 //239 - // 128-255 Reserved + // 240-255 Reserved }; //***** Data connection icons diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp index 9b09983e3a82..0eff776af84f 100644 --- a/services/camera/libcameraservice/CameraService.cpp +++ b/services/camera/libcameraservice/CameraService.cpp @@ -502,77 +502,82 @@ void CameraService::Client::disconnect() { // ---------------------------------------------------------------------------- -// set the Surface that the preview will use -status_t CameraService::Client::setPreviewDisplay(const sp<Surface>& surface) { - LOG1("setPreviewDisplay(%p) (pid %d)", surface.get(), getCallingPid()); +static void disconnectWindow(const sp<ANativeWindow>& window) { + if (window != 0) { + status_t result = native_window_disconnect(window.get(), + NATIVE_WINDOW_API_CAMERA); + if (result != NO_ERROR) { + LOGW("native_window_disconnect failed: %s (%d)", strerror(-result), + result); + } + } +} + +status_t CameraService::Client::setPreviewWindow(const sp<IBinder>& binder, + const sp<ANativeWindow>& window) { Mutex::Autolock lock(mLock); status_t result = checkPidAndHardware(); if (result != NO_ERROR) return result; - result = NO_ERROR; - // return if no change in surface. - sp<IBinder> binder(surface != 0 ? surface->asBinder() : 0); if (binder == mSurface) { - return result; + return NO_ERROR; } - if (mSurface != 0) { - LOG1("clearing old preview surface %p", mSurface.get()); + if (window != 0) { + result = native_window_connect(window.get(), NATIVE_WINDOW_API_CAMERA); + if (result != NO_ERROR) { + LOGE("native_window_connect failed: %s (%d)", strerror(-result), + result); + return result; + } } - mSurface = binder; - mPreviewWindow = surface; - // If preview has been already started, register preview - // buffers now. + // If preview has been already started, register preview buffers now. if (mHardware->previewEnabled()) { - if (mPreviewWindow != 0) { - native_window_set_buffers_transform(mPreviewWindow.get(), - mOrientation); - result = mHardware->setPreviewWindow(mPreviewWindow); + if (window != 0) { + native_window_set_buffers_transform(window.get(), mOrientation); + result = mHardware->setPreviewWindow(window); } } + if (result == NO_ERROR) { + // Everything has succeeded. Disconnect the old window and remember the + // new window. + disconnectWindow(mPreviewWindow); + mSurface = binder; + mPreviewWindow = window; + } else { + // Something went wrong after we connected to the new window, so + // disconnect here. + disconnectWindow(window); + } + return result; } +// set the Surface that the preview will use +status_t CameraService::Client::setPreviewDisplay(const sp<Surface>& surface) { + LOG1("setPreviewDisplay(%p) (pid %d)", surface.get(), getCallingPid()); + + sp<IBinder> binder(surface != 0 ? surface->asBinder() : 0); + sp<ANativeWindow> window(surface); + return setPreviewWindow(binder, window); +} + // set the SurfaceTexture that the preview will use status_t CameraService::Client::setPreviewTexture( const sp<ISurfaceTexture>& surfaceTexture) { LOG1("setPreviewTexture(%p) (pid %d)", surfaceTexture.get(), getCallingPid()); - Mutex::Autolock lock(mLock); - status_t result = checkPidAndHardware(); - if (result != NO_ERROR) return result; - - // return if no change in surface. - // asBinder() is safe on NULL (returns NULL) - if (surfaceTexture->asBinder() == mSurface) { - return result; - } - if (mSurface != 0) { - LOG1("clearing old preview surface %p", mSurface.get()); - } - mSurface = surfaceTexture->asBinder(); + sp<IBinder> binder; + sp<ANativeWindow> window; if (surfaceTexture != 0) { - mPreviewWindow = new SurfaceTextureClient(surfaceTexture); - } else { - mPreviewWindow = 0; - } - - // If preview has been already started, set overlay or register preview - // buffers now. - if (mHardware->previewEnabled()) { - // XXX: What if the new preview window is 0? - if (mPreviewWindow != 0) { - native_window_set_buffers_transform(mPreviewWindow.get(), - mOrientation); - result = mHardware->setPreviewWindow(mPreviewWindow); - } + binder = surfaceTexture->asBinder(); + window = new SurfaceTextureClient(surfaceTexture); } - - return result; + return setPreviewWindow(binder, window); } // set the preview callback flag to affect how the received frames from diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h index 5e2d57151271..c5fefb86b8c6 100644 --- a/services/camera/libcameraservice/CameraService.h +++ b/services/camera/libcameraservice/CameraService.h @@ -161,6 +161,10 @@ private: int getOrientation(int orientation, bool mirror); + status_t setPreviewWindow( + const sp<IBinder>& binder, + const sp<ANativeWindow>& window); + // these are initialized in the constructor. sp<CameraService> mCameraService; // immutable after constructor sp<ICameraClient> mCameraClient; diff --git a/services/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/java/com/android/server/accessibility/AccessibilityManagerService.java index a8dc885a6613..bb9d15b3ca05 100644 --- a/services/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -127,13 +127,15 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub private int mHandledFeedbackTypes = 0; - private boolean mIsEnabled; + private boolean mIsAccessibilityEnabled; + + private boolean mIsTouchExplorationRequested; private AccessibilityInputFilter mInputFilter; private final List<AccessibilityServiceInfo> mEnabledServicesForFeedbackTempList = new ArrayList<AccessibilityServiceInfo>(); - private boolean mHasInputFilter; + private boolean mIsTouchExplorationEnabled; private final WindowManagerService mWindowManagerService; @@ -230,16 +232,21 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub if (intent.getAction() == Intent.ACTION_BOOT_COMPLETED) { synchronized (mLock) { populateAccessibilityServiceListLocked(); - // get the accessibility enabled setting on boot - mIsEnabled = Settings.Secure.getInt(mContext.getContentResolver(), + // get accessibility enabled setting on boot + mIsAccessibilityEnabled = Settings.Secure.getInt( + mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_ENABLED, 0) == 1; - // if accessibility is enabled inform our clients we are on - if (mIsEnabled) { - updateClientsLocked(); + if (mIsAccessibilityEnabled) { + sendAccessibilityEnabledToClientsLocked(); } - manageServicesLocked(); + + // get touch exploration enabled setting on boot + mIsTouchExplorationRequested = Settings.Secure.getInt( + mContext.getContentResolver(), + Settings.Secure.TOUCH_EXPLORATION_REQUESTED, 0) == 1; + updateTouchExplorationEnabledLocked(); } return; @@ -264,29 +271,48 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub private void registerSettingsContentObservers() { ContentResolver contentResolver = mContext.getContentResolver(); - Uri enabledUri = Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_ENABLED); - contentResolver.registerContentObserver(enabledUri, false, + Uri accessibilityEnabledUri = Settings.Secure.getUriFor( + Settings.Secure.ACCESSIBILITY_ENABLED); + contentResolver.registerContentObserver(accessibilityEnabledUri, false, new ContentObserver(new Handler()) { @Override public void onChange(boolean selfChange) { super.onChange(selfChange); synchronized (mLock) { - mIsEnabled = Settings.Secure.getInt(mContext.getContentResolver(), + mIsAccessibilityEnabled = Settings.Secure.getInt( + mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_ENABLED, 0) == 1; - if (mIsEnabled) { + if (mIsAccessibilityEnabled) { manageServicesLocked(); } else { unbindAllServicesLocked(); } - updateClientsLocked(); + sendAccessibilityEnabledToClientsLocked(); } } }); - Uri providersUri = + Uri touchExplorationRequestedUri = Settings.Secure.getUriFor( + Settings.Secure.TOUCH_EXPLORATION_REQUESTED); + contentResolver.registerContentObserver(touchExplorationRequestedUri, false, + new ContentObserver(new Handler()) { + @Override + public void onChange(boolean selfChange) { + super.onChange(selfChange); + + synchronized (mLock) { + mIsTouchExplorationRequested = Settings.Secure.getInt( + mContext.getContentResolver(), + Settings.Secure.TOUCH_EXPLORATION_REQUESTED, 0) == 1; + updateTouchExplorationEnabledLocked(); + } + } + }); + + Uri accessibilityServicesUri = Settings.Secure.getUriFor(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES); - contentResolver.registerContentObserver(providersUri, false, + contentResolver.registerContentObserver(accessibilityServicesUri, false, new ContentObserver(new Handler()) { @Override public void onChange(boolean selfChange) { @@ -312,7 +338,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } } }, 0); - return mIsEnabled; + return mIsAccessibilityEnabled; } } @@ -602,7 +628,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub service.linkToOwnDeath(); mServices.add(service); mComponentNameToServiceMap.put(service.mComponentName, service); - updateInputFilterLocked(); + updateTouchExplorationEnabledLocked(); } catch (RemoteException e) { /* do nothing */ } @@ -622,7 +648,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub mComponentNameToServiceMap.remove(service.mComponentName); mHandler.removeMessages(service.mId); service.unlinkToOwnDeath(); - updateInputFilterLocked(); + updateTouchExplorationEnabledLocked(); return removed; } @@ -727,7 +753,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub Set<ComponentName> enabledServices) { Map<ComponentName, Service> componentNameToServiceMap = mComponentNameToServiceMap; - boolean isEnabled = mIsEnabled; + boolean isEnabled = mIsAccessibilityEnabled; for (int i = 0, count = installedServices.size(); i < count; i++) { AccessibilityServiceInfo installedService = installedServices.get(i); @@ -741,7 +767,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub service = new Service(componentName, installedService, false); } service.bind(); - } else if (!enabledServices.contains(componentName)) { + } else { if (service != null) { service.unbind(); } @@ -757,10 +783,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub /** * Updates the state of {@link android.view.accessibility.AccessibilityManager} clients. */ - private void updateClientsLocked() { + private void sendAccessibilityEnabledToClientsLocked() { for (int i = 0, count = mClients.size(); i < count; i++) { try { - mClients.get(i).setEnabled(mIsEnabled); + mClients.get(i).setEnabled(mIsAccessibilityEnabled); } catch (RemoteException re) { mClients.remove(i); count--; @@ -770,29 +796,48 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } /** - * Updates the input filter state. The filter is enabled if accessibility - * is enabled and there is at least one accessibility service providing - * spoken feedback. + * Sends the touch exploration state to clients. */ - private void updateInputFilterLocked() { - if (mIsEnabled) { - final boolean hasSpokenFeedbackServices = !getEnabledAccessibilityServiceList( - AccessibilityServiceInfo.FEEDBACK_SPOKEN).isEmpty(); - if (hasSpokenFeedbackServices) { - if (mHasInputFilter) { - return; + private void sendTouchExplorationEnabledToClientsLocked() { + for (int i = 0, count = mClients.size(); i < count; i++) { + try { + mClients.get(i).setTouchExplorationEnabled(mIsTouchExplorationEnabled); + } catch (RemoteException re) { + mClients.remove(i); + count--; + i--; + } + } + } + + /** + * Updates the touch exploration state. Touch exploration is enabled if it + * is requested, accessibility is on and there is at least one enabled + * accessibility service providing spoken feedback. + */ + private void updateTouchExplorationEnabledLocked() { + if (mIsAccessibilityEnabled && mIsTouchExplorationRequested) { + final boolean hasSpeakingServicesEnabled = !getEnabledAccessibilityServiceList( + AccessibilityServiceInfo.FEEDBACK_SPOKEN).isEmpty(); + if (!mIsTouchExplorationEnabled) { + if (!hasSpeakingServicesEnabled) { + return; } if (mInputFilter == null) { mInputFilter = new AccessibilityInputFilter(mContext); } mWindowManagerService.setInputFilter(mInputFilter); - mHasInputFilter = true; + mIsTouchExplorationEnabled = true; + sendTouchExplorationEnabledToClientsLocked(); + return; + } else if (hasSpeakingServicesEnabled) { return; } } - if (mHasInputFilter) { + if (mIsTouchExplorationEnabled) { mWindowManagerService.setInputFilter(null); - mHasInputFilter = false; + mIsTouchExplorationEnabled = false; + sendTouchExplorationEnabledToClientsLocked(); } } diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index a8fb1ed65640..94af46d94fe2 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -3610,8 +3610,10 @@ public final class ActivityManagerService extends ActivityManagerNative String processName = app.processName; try { - thread.asBinder().linkToDeath(new AppDeathRecipient( - app, pid, thread), 0); + AppDeathRecipient adr = new AppDeathRecipient( + app, pid, thread); + thread.asBinder().linkToDeath(adr, 0); + app.deathRecipient = adr; } catch (RemoteException e) { app.resetPackageList(); startProcessLocked(app, "link fail", processName); @@ -3687,6 +3689,7 @@ public final class ActivityManagerService extends ActivityManagerNative Slog.w(TAG, "Exception thrown during bind!", e); app.resetPackageList(); + app.unlinkDeathRecipient(); startProcessLocked(app, "bind fail", processName); return false; } @@ -9210,6 +9213,7 @@ public final class ActivityManagerService extends ActivityManagerNative app.notResponding = false; app.resetPackageList(); + app.unlinkDeathRecipient(); app.thread = null; app.forcingToForeground = null; app.foregroundServices = false; @@ -9327,7 +9331,6 @@ public final class ActivityManagerService extends ActivityManagerNative // This app is persistent, so we need to keep its record around. // If it is not already on the pending app list, add it there // and start a new process for it. - app.thread = null; app.forcingToForeground = null; app.foregroundServices = false; if (mPersistentStartingProcesses.indexOf(app) < 0) { diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java index 99830f93136c..9e597aa93d18 100644 --- a/services/java/com/android/server/am/ProcessRecord.java +++ b/services/java/com/android/server/am/ProcessRecord.java @@ -73,6 +73,7 @@ class ProcessRecord { int adjSeq; // Sequence id for identifying oom_adj assignment cycles int lruSeq; // Sequence id for identifying LRU update cycles CompatibilityInfo compat; // last used compatibility mode + IBinder.DeathRecipient deathRecipient; // Who is watching for the death. ComponentName instrumentationClass;// class installed to instrument app ApplicationInfo instrumentationInfo; // the application being instrumented String instrumentationProfileFile; // where to save profiling @@ -297,6 +298,13 @@ class ProcessRecord { } } + public void unlinkDeathRecipient() { + if (deathRecipient != null && thread != null) { + thread.asBinder().unlinkToDeath(deathRecipient, 0); + } + deathRecipient = null; + } + public String toShortString() { if (shortStringName != null) { return shortStringName; diff --git a/services/java/com/android/server/usb/UsbDeviceManager.java b/services/java/com/android/server/usb/UsbDeviceManager.java index 313979808ced..c80cd0a87115 100644 --- a/services/java/com/android/server/usb/UsbDeviceManager.java +++ b/services/java/com/android/server/usb/UsbDeviceManager.java @@ -473,10 +473,7 @@ public class UsbDeviceManager { case MSG_SET_CURRENT_FUNCTION: String function = (String)msg.obj; boolean makeDefault = (msg.arg1 == 1); - if (makeDefault) { - if (function == null) { - throw new NullPointerException(); - } + if (function != null && makeDefault) { if (mAdbEnabled) { function = addFunction(function, UsbManager.USB_FUNCTION_ADB); } diff --git a/services/java/com/android/server/wm/DragState.java b/services/java/com/android/server/wm/DragState.java index 118cd55fd975..8146fca74303 100644 --- a/services/java/com/android/server/wm/DragState.java +++ b/services/java/com/android/server/wm/DragState.java @@ -51,6 +51,8 @@ class DragState { float mCurrentX, mCurrentY; float mThumbOffsetX, mThumbOffsetY; InputChannel mServerChannel, mClientChannel; + InputApplicationHandle mDragApplicationHandle; + InputWindowHandle mDragWindowHandle; WindowState mTargetWindow; ArrayList<WindowState> mNotifiedWindows; boolean mDragInProgress; @@ -91,6 +93,38 @@ class DragState { mService.mInputManager.registerInputChannel(mServerChannel, null); InputQueue.registerInputChannel(mClientChannel, mService.mDragInputHandler, mService.mH.getLooper().getQueue()); + + mDragApplicationHandle = new InputApplicationHandle(null); + mDragApplicationHandle.name = "drag"; + mDragApplicationHandle.dispatchingTimeoutNanos = + WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS; + + mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle, null); + mDragWindowHandle.name = "drag"; + mDragWindowHandle.inputChannel = mServerChannel; + mDragWindowHandle.layer = getDragLayerLw(); + mDragWindowHandle.layoutParamsFlags = 0; + mDragWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG; + mDragWindowHandle.dispatchingTimeoutNanos = + WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS; + mDragWindowHandle.visible = true; + mDragWindowHandle.canReceiveKeys = false; + mDragWindowHandle.hasFocus = true; + mDragWindowHandle.hasWallpaper = false; + mDragWindowHandle.paused = false; + mDragWindowHandle.ownerPid = Process.myPid(); + mDragWindowHandle.ownerUid = Process.myUid(); + mDragWindowHandle.inputFeatures = 0; + mDragWindowHandle.scaleFactor = 1.0f; + + // The drag window cannot receive new touches. + mDragWindowHandle.touchableRegion.setEmpty(); + + // The drag window covers the entire display + mDragWindowHandle.frameLeft = 0; + mDragWindowHandle.frameTop = 0; + mDragWindowHandle.frameRight = mService.mDisplay.getRealWidth(); + mDragWindowHandle.frameBottom = mService.mDisplay.getRealHeight(); } } diff --git a/services/java/com/android/server/wm/InputMonitor.java b/services/java/com/android/server/wm/InputMonitor.java index 08a356033815..12ef23827342 100644 --- a/services/java/com/android/server/wm/InputMonitor.java +++ b/services/java/com/android/server/wm/InputMonitor.java @@ -42,10 +42,6 @@ final class InputMonitor { // When true, need to call updateInputWindowsLw(). private boolean mUpdateInputWindowsNeeded = true; - // Fake handles for the drag surface, lazily initialized. - private InputApplicationHandle mDragApplicationHandle; - private InputWindowHandle mDragWindowHandle; - // Array of window handles to provide to the input dispatcher. private InputWindowHandle[] mInputWindowHandles; private int mInputWindowHandleCount; @@ -121,44 +117,6 @@ final class InputMonitor { return 0; // abort dispatching } - private void addDragInputWindowLw() { - if (mDragWindowHandle == null) { - mDragApplicationHandle = new InputApplicationHandle(null); - mDragApplicationHandle.name = "drag"; - mDragApplicationHandle.dispatchingTimeoutNanos = - WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS; - - mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle, null); - mDragWindowHandle.name = "drag"; - mDragWindowHandle.layoutParamsFlags = 0; - mDragWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG; - mDragWindowHandle.dispatchingTimeoutNanos = - WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS; - mDragWindowHandle.visible = true; - mDragWindowHandle.canReceiveKeys = false; - mDragWindowHandle.hasFocus = true; - mDragWindowHandle.hasWallpaper = false; - mDragWindowHandle.paused = false; - mDragWindowHandle.ownerPid = Process.myPid(); - mDragWindowHandle.ownerUid = Process.myUid(); - mDragWindowHandle.inputFeatures = 0; - mDragWindowHandle.scaleFactor = 1.0f; - - // The drag window cannot receive new touches. - mDragWindowHandle.touchableRegion.setEmpty(); - } - - mDragWindowHandle.layer = mService.mDragState.getDragLayerLw(); - - // The drag window covers the entire display - mDragWindowHandle.frameLeft = 0; - mDragWindowHandle.frameTop = 0; - mDragWindowHandle.frameRight = mService.mDisplay.getRealWidth(); - mDragWindowHandle.frameBottom = mService.mDisplay.getRealHeight(); - - addInputWindowHandleLw(mDragWindowHandle); - } - private void addInputWindowHandleLw(InputWindowHandle windowHandle) { if (mInputWindowHandles == null) { mInputWindowHandles = new InputWindowHandle[16]; @@ -202,7 +160,7 @@ final class InputMonitor { if (WindowManagerService.DEBUG_DRAG) { Log.d(WindowManagerService.TAG, "Inserting drag window"); } - addDragInputWindowLw(); + addInputWindowHandleLw(mService.mDragState.mDragWindowHandle); } final int N = windows.size(); @@ -429,4 +387,4 @@ final class InputMonitor { private void updateInputDispatchModeLw() { mService.mInputManager.setInputDispatchMode(mInputDispatchEnabled, mInputDispatchFrozen); } -}
\ No newline at end of file +} diff --git a/services/tests/servicestests/src/com/android/server/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/AccessibilityManagerServiceTest.java index 302a2d60bb07..1234bfd8220e 100644 --- a/services/tests/servicestests/src/com/android/server/AccessibilityManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/AccessibilityManagerServiceTest.java @@ -542,6 +542,9 @@ public class AccessibilityManagerServiceTest extends AndroidTestCase { public void setEnabled(boolean enabled) { mIsEnabled = enabled; } + + public void setTouchExplorationEnabled(boolean enabled) { + } } /** diff --git a/telephony/java/com/android/internal/telephony/BaseCommands.java b/telephony/java/com/android/internal/telephony/BaseCommands.java index 8427d1439895..f0d2fbaf018f 100644 --- a/telephony/java/com/android/internal/telephony/BaseCommands.java +++ b/telephony/java/com/android/internal/telephony/BaseCommands.java @@ -857,22 +857,28 @@ public abstract class BaseCommands implements CommandsInterface { */ public static int getLteOnCdmaModeStatic() { int retVal; - String productType; - - Matcher matcher = sProductTypePattern.matcher(sKernelCmdLine); - if (matcher.find()) { - productType = matcher.group(1); - if (sLteOnCdmaProductType.equals(productType)) { - retVal = Phone.LTE_ON_CDMA_TRUE; + int curVal; + String productType = ""; + + curVal = SystemProperties.getInt(TelephonyProperties.PROPERTY_LTE_ON_CDMA_DEVICE, + Phone.LTE_ON_CDMA_UNKNOWN); + retVal = curVal; + if (retVal == Phone.LTE_ON_CDMA_UNKNOWN) { + Matcher matcher = sProductTypePattern.matcher(sKernelCmdLine); + if (matcher.find()) { + productType = matcher.group(1); + if (sLteOnCdmaProductType.equals(productType)) { + retVal = Phone.LTE_ON_CDMA_TRUE; + } else { + retVal = Phone.LTE_ON_CDMA_FALSE; + } } else { retVal = Phone.LTE_ON_CDMA_FALSE; } - } else { - retVal = Phone.LTE_ON_CDMA_FALSE; - productType = ""; } - Log.d(LOG_TAG, "getLteOnCdmaMode=" + retVal + " product_type='" + productType + + Log.d(LOG_TAG, "getLteOnCdmaMode=" + retVal + " curVal=" + curVal + + " product_type='" + productType + "' lteOnCdmaProductType='" + sLteOnCdmaProductType + "'"); return retVal; } diff --git a/telephony/java/com/android/internal/telephony/TelephonyProperties.java b/telephony/java/com/android/internal/telephony/TelephonyProperties.java index 60cf9b7f362b..abb45234dc8e 100644 --- a/telephony/java/com/android/internal/telephony/TelephonyProperties.java +++ b/telephony/java/com/android/internal/telephony/TelephonyProperties.java @@ -79,6 +79,15 @@ public interface TelephonyProperties */ static final String PROPERTY_LTE_ON_CDMA_PRODUCT_TYPE = "telephony.lteOnCdmaProductType"; + /** + * The contents of this property is the one of {@link Phone#LTE_ON_CDMA_TRUE} or + * {@link Phone#LTE_ON_CDMA_FALSE}. If absent the value will assumed to be false + * and the {@see #PROPERTY_LTE_ON_CDMA_PRODUCT_TYPE} will be used to determine its + * final value which could also be {@link Phone#LTE_ON_CDMA_FALSE}. + * {@see BaseCommands#getLteOnCdmaMode()} + */ + static final String PROPERTY_LTE_ON_CDMA_DEVICE = "telephony.lteOnCdmaDevice"; + static final String CURRENT_ACTIVE_PHONE = "gsm.current.phone-type"; //****** SIM Card diff --git a/tests/BiDiTests/res/layout/canvas2.xml b/tests/BiDiTests/res/layout/canvas2.xml new file mode 100644 index 000000000000..b3e038fba07d --- /dev/null +++ b/tests/BiDiTests/res/layout/canvas2.xml @@ -0,0 +1,66 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2011 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/canvas2" + android:layout_width="fill_parent" + android:layout_height="fill_parent"> + + <LinearLayout + xmlns:local="http://schemas.android.com/apk/res/com.android.bidi" + android:orientation="vertical" + android:layout_width="fill_parent" + android:layout_height="fill_parent"> + + <TextView + android:text="@string/ltr" + android:textSize="40dip" + android:gravity="center" + android:layout_width="fill_parent" + android:layout_height="wrap_content" /> + + <com.android.bidi.BiDiTestViewDrawText + local:text="@string/ltr" + android:layout_width="fill_parent" + android:layout_height="64dp" /> + + <TextView + android:text="@string/rtl" + android:textSize="40dip" + android:gravity="center" + android:layout_width="fill_parent" + android:layout_height="wrap_content"/> + + <com.android.bidi.BiDiTestViewDrawText + local:text="@string/rtl" + android:layout_width="fill_parent" + android:layout_height="64dp" /> + + <TextView + android:text="@string/composing" + android:textSize="40dip" + android:gravity="center" + android:layout_width="fill_parent" + android:layout_height="wrap_content"/> + + <com.android.bidi.BiDiTestViewDrawText + local:text="@string/composing" + android:layout_width="fill_parent" + android:layout_height="64dp" /> + + </LinearLayout> + +</FrameLayout>
\ No newline at end of file diff --git a/tests/BiDiTests/res/values/attrs.xml b/tests/BiDiTests/res/values/attrs.xml new file mode 100644 index 000000000000..7f8a1d8f1e90 --- /dev/null +++ b/tests/BiDiTests/res/values/attrs.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <declare-styleable name="DrawTextTestView"> + <attr name="size" format="dimension" /> + <attr name="color" format="color" /> + <attr name="text" format="string" /> + </declare-styleable> +</resources>
\ No newline at end of file diff --git a/tests/BiDiTests/res/values/strings.xml b/tests/BiDiTests/res/values/strings.xml index bc99e79bb253..1f6be7fd7ef6 100644 --- a/tests/BiDiTests/res/values/strings.xml +++ b/tests/BiDiTests/res/values/strings.xml @@ -42,5 +42,8 @@ <string name="textview_hebrew_text">םמab?!</string> <string name="textview_latin_text">abםמ?!</string> <string name="textview_multiline_text">םמ?!\nab?!\n?!</string> + <string name="ltr">Left to right text"</string> + <string name="rtl">"والحق أن تترك ونص"</string> + <string name="composing">"\u0644\u0627"</string> </resources> diff --git a/tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java b/tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java index 7002c4148786..6b38cc145c26 100644 --- a/tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java +++ b/tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java @@ -102,6 +102,7 @@ public class BiDiTestActivity extends Activity { addItem(result, "Basic", BiDiTestBasic.class, R.id.basic); addItem(result, "Canvas", BiDiTestCanvas.class, R.id.canvas); + addItem(result, "Canvas2", BiDiTestCanvas2.class, R.id.canvas2); addItem(result, "Linear LTR", BiDiTestLinearLayoutLtr.class, R.id.linear_layout_ltr); addItem(result, "Linear RTL", BiDiTestLinearLayoutRtl.class, R.id.linear_layout_rtl); diff --git a/tests/BiDiTests/src/com/android/bidi/BiDiTestCanvas2.java b/tests/BiDiTests/src/com/android/bidi/BiDiTestCanvas2.java new file mode 100644 index 000000000000..b801f0eefc60 --- /dev/null +++ b/tests/BiDiTests/src/com/android/bidi/BiDiTestCanvas2.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.bidi; + +import android.app.Fragment; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.SeekBar; + +import static com.android.bidi.BiDiTestConstants.FONT_MAX_SIZE; +import static com.android.bidi.BiDiTestConstants.FONT_MIN_SIZE; + +public class BiDiTestCanvas2 extends Fragment { + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + return inflater.inflate(R.layout.canvas2, container, false); + } +} diff --git a/tests/BiDiTests/src/com/android/bidi/BiDiTestView.java b/tests/BiDiTests/src/com/android/bidi/BiDiTestView.java index 4f17e529eaae..0126deafe89d 100644 --- a/tests/BiDiTests/src/com/android/bidi/BiDiTestView.java +++ b/tests/BiDiTests/src/com/android/bidi/BiDiTestView.java @@ -21,7 +21,7 @@ import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Rect; -import android.graphics.Typeface; +import android.text.TextPaint; import android.util.AttributeSet; import android.util.Log; import android.view.View; @@ -37,7 +37,6 @@ public class BiDiTestView extends View { private static final float DEFAULT_ITALIC_SKEW_X = -0.25f; - private Paint paint = new Paint(); private Rect rect = new Rect(); private String NORMAL_TEXT; @@ -51,8 +50,7 @@ public class BiDiTestView extends View { private String CHINESE_TEXT; private String MIXED_TEXT_1; private String HEBREW_TEXT; - - private Typeface typeface; + private String RTL_TEXT; private int currentTextSize; @@ -83,9 +81,7 @@ public class BiDiTestView extends View { CHINESE_TEXT = context.getString(R.string.chinese_text); MIXED_TEXT_1 = context.getString(R.string.mixed_text_1); HEBREW_TEXT = context.getString(R.string.hebrew_text); - - typeface = paint.getTypeface(); - paint.setAntiAlias(true); + RTL_TEXT = context.getString(R.string.rtl); } public void setCurrentTextSize(int size) { @@ -95,54 +91,56 @@ public class BiDiTestView extends View { @Override public void onDraw(Canvas canvas) { - drawInsideRect(canvas, Color.BLACK); + drawInsideRect(canvas, new Paint(), Color.BLACK); int deltaX = 0; deltaX = testString(canvas, NORMAL_TEXT, ORIGIN, ORIGIN, - paint, typeface, false, false, Paint.DIRECTION_LTR, currentTextSize); + false, false, Paint.DIRECTION_LTR, currentTextSize); deltaX += testString(canvas, ITALIC_TEXT, ORIGIN + deltaX, ORIGIN, - paint, typeface, true, false, Paint.DIRECTION_LTR, currentTextSize); + true, false, Paint.DIRECTION_LTR, currentTextSize); deltaX += testString(canvas, BOLD_TEXT, ORIGIN + deltaX, ORIGIN, - paint, typeface, false, true, Paint.DIRECTION_LTR, currentTextSize); + false, true, Paint.DIRECTION_LTR, currentTextSize); deltaX += testString(canvas, BOLD_ITALIC_TEXT, ORIGIN + deltaX, ORIGIN, - paint, typeface, true, true, Paint.DIRECTION_LTR, currentTextSize); + true, true, Paint.DIRECTION_LTR, currentTextSize); // Test with a long string deltaX = testString(canvas, NORMAL_LONG_TEXT, ORIGIN, ORIGIN + 2 * currentTextSize, - paint, typeface, false, false, Paint.DIRECTION_LTR, currentTextSize); + false, false, Paint.DIRECTION_LTR, currentTextSize); // Test with a long string deltaX = testString(canvas, NORMAL_LONG_TEXT_2, ORIGIN, ORIGIN + 4 * currentTextSize, - paint, typeface, false, false, Paint.DIRECTION_LTR, currentTextSize); + false, false, Paint.DIRECTION_LTR, currentTextSize); // Test with a long string deltaX = testString(canvas, NORMAL_LONG_TEXT_3, ORIGIN, ORIGIN + 6 * currentTextSize, - paint, typeface, false, false, Paint.DIRECTION_LTR, currentTextSize); + false, false, Paint.DIRECTION_LTR, currentTextSize); // Test Arabic ligature deltaX = testString(canvas, ARABIC_TEXT, ORIGIN, ORIGIN + 8 * currentTextSize, - paint, typeface, false, false, Paint.DIRECTION_RTL, currentTextSize); + false, false, Paint.DIRECTION_RTL, currentTextSize); // Test Chinese deltaX = testString(canvas, CHINESE_TEXT, ORIGIN, ORIGIN + 10 * currentTextSize, - paint, typeface, false, false, Paint.DIRECTION_LTR, currentTextSize); + false, false, Paint.DIRECTION_LTR, currentTextSize); // Test Mixed (English and Arabic) deltaX = testString(canvas, MIXED_TEXT_1, ORIGIN, ORIGIN + 12 * currentTextSize, - paint, typeface, false, false, Paint.DIRECTION_LTR, currentTextSize); + false, false, Paint.DIRECTION_LTR, currentTextSize); // Test Hebrew - deltaX = testString(canvas, HEBREW_TEXT, ORIGIN, ORIGIN + 14 * currentTextSize, - paint, typeface, false, false, Paint.DIRECTION_RTL, currentTextSize); + deltaX = testString(canvas, RTL_TEXT, ORIGIN, ORIGIN + 14 * currentTextSize, + false, false, Paint.DIRECTION_RTL, currentTextSize); } - private int testString(Canvas canvas, String text, int x, int y, Paint paint, Typeface typeface, + private int testString(Canvas canvas, String text, int x, int y, boolean isItalic, boolean isBold, int dir, int textSize) { - paint.setTypeface(typeface); + + TextPaint paint = new TextPaint(); + paint.setAntiAlias(true); // Set paint properties boolean oldFakeBold = paint.isFakeBoldText(); @@ -153,9 +151,9 @@ public class BiDiTestView extends View { paint.setTextSkewX(DEFAULT_ITALIC_SKEW_X); } - Log.v(TAG, "START -- drawTextWithCanvasDrawText"); - drawTextWithCanvasDrawText(text, canvas, x, y, textSize, Color.WHITE, dir); - Log.v(TAG, "END -- drawTextWithCanvasDrawText"); + paint.setTextSize(textSize); + paint.setColor(Color.WHITE); + canvas.drawText(text, x, y, paint); int length = text.length(); float[] advances = new float[length]; @@ -167,17 +165,6 @@ public class BiDiTestView extends View { logAdvances(text, textWidthHB, textWidthICU, advances); drawMetricsAroundText(canvas, x, y, textWidthHB, textWidthICU, textSize, Color.RED, Color.GREEN); - paint.setColor(Color.WHITE); -// char[] glyphs = new char[2*length]; -// int count = getGlyphs(text, glyphs, dir); -// -// logGlypths(glyphs, count); -// drawTextWithDrawGlyph(canvas, glyphs, count, x, y + currentTextSize); - - Log.v(TAG, "START -- drawTextWithGlyphs"); - drawTextWithGlyphs(canvas, text, x, y + currentTextSize, dir); - Log.v(TAG, "END -- drawTextWithGlyphs"); - // Restore old paint properties paint.setFakeBoldText(oldFakeBold); paint.setTextSkewX(oldTextSkewX); @@ -190,27 +177,7 @@ public class BiDiTestView extends View { paint.setBidiFlags(dir); } - private void drawTextWithDrawGlyph(Canvas canvas, char[] glyphs, int count, int x, int y) { - canvas.drawGlyphs(glyphs, 0, count, x, y, paint); - } - - private void drawTextWithGlyphs(Canvas canvas, String text, int x, int y, int dir) { - setPaintDir(paint, dir); - canvas.drawTextWithGlyphs(text, x, y, paint); - } - - private void logGlypths(char[] glyphs, int count) { - Log.v(TAG, "GlyphIds - count=" + count); - for (int n = 0; n < count; n++) { - Log.v(TAG, "GlyphIds - Id[" + n + "]="+ (int)glyphs[n]); - } - } - - private int getGlyphs(String text, char[] glyphs, int dir) { - return paint.getTextGlypths(text, 0, text.length(), 0, text.length(), dir, glyphs); - } - - private void drawInsideRect(Canvas canvas, int color) { + private void drawInsideRect(Canvas canvas, Paint paint, int color) { paint.setColor(color); int width = getWidth(); int height = getHeight(); @@ -218,16 +185,9 @@ public class BiDiTestView extends View { canvas.drawRect(rect, paint); } - private void drawTextWithCanvasDrawText(String text, Canvas canvas, - float x, float y, float textSize, int color, int dir) { - setPaintDir(paint, dir); - paint.setColor(color); - paint.setTextSize(textSize); - canvas.drawText(text, x, y, paint); - } - private void drawMetricsAroundText(Canvas canvas, int x, int y, float textWidthHB, float textWidthICU, int textSize, int color, int colorICU) { + Paint paint = new Paint(); paint.setColor(color); canvas.drawLine(x, y - textSize, x, y + 8, paint); canvas.drawLine(x, y + 8, x + textWidthHB, y + 8, paint); diff --git a/tests/BiDiTests/src/com/android/bidi/BiDiTestViewDrawText.java b/tests/BiDiTests/src/com/android/bidi/BiDiTestViewDrawText.java new file mode 100644 index 000000000000..dfdb8072836a --- /dev/null +++ b/tests/BiDiTests/src/com/android/bidi/BiDiTestViewDrawText.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.bidi; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint.Align; +import android.text.TextPaint; +import android.util.AttributeSet; +import android.view.View; + +public class BiDiTestViewDrawText extends View { + private float mSize; + private int mColor; + private String mText; + + public BiDiTestViewDrawText(Context context) { + this(context, null); + } + + public BiDiTestViewDrawText(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public BiDiTestViewDrawText(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + + final TypedArray a = context.obtainStyledAttributes(attrs, + R.styleable.DrawTextTestView, defStyle, 0); + mSize = a.getDimension(R.styleable.DrawTextTestView_size, 40.0f); + mColor = a.getColor(R.styleable.DrawTextTestView_color, Color.YELLOW); + final CharSequence text = a.getText(R.styleable.DrawTextTestView_text); + mText = (text != null) ? text.toString() : "(empty)"; + a.recycle(); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + final int width = getWidth(); + final int height = getHeight(); + + final TextPaint paint = new TextPaint(); + paint.setTextSize(mSize); + paint.setColor(mColor); + paint.setTextAlign(Align.CENTER); + + canvas.drawText(mText, width / 2, height * 2 / 3, paint); + } +}
\ No newline at end of file diff --git a/tests/GridLayoutTest/src/com/android/test/layout/Activity2.java b/tests/GridLayoutTest/src/com/android/test/layout/Activity2.java index af5006f28f15..38a85a3a2c98 100644 --- a/tests/GridLayoutTest/src/com/android/test/layout/Activity2.java +++ b/tests/GridLayoutTest/src/com/android/test/layout/Activity2.java @@ -95,9 +95,7 @@ public class Activity2 extends Activity { } { Space v = new Space(context); - { - vg.addView(v, new LayoutParams(row5, col3)); - } + vg.addView(v, new LayoutParams(row5, col3)); } { Button v = new Button(context); diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml index 32a6a659172b..9fcd05aa3555 100644 --- a/tests/HwAccelerationTest/AndroidManifest.xml +++ b/tests/HwAccelerationTest/AndroidManifest.xml @@ -94,6 +94,15 @@ </activity> <activity + android:name="CanvasTextureViewActivity" + android:label="_CanvasTextureView"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + + <activity android:name="GLTextureViewActivity" android:label="_TextureViewGL"> <intent-filter> diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/CanvasTextureViewActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/CanvasTextureViewActivity.java new file mode 100644 index 000000000000..81c22b88900b --- /dev/null +++ b/tests/HwAccelerationTest/src/com/android/test/hwui/CanvasTextureViewActivity.java @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.test.hwui; + +import android.app.Activity; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.PorterDuff; +import android.graphics.SurfaceTexture; +import android.os.Bundle; +import android.view.Gravity; +import android.view.TextureView; +import android.widget.FrameLayout; + +@SuppressWarnings({"UnusedDeclaration"}) +public class CanvasTextureViewActivity extends Activity + implements TextureView.SurfaceTextureListener { + private TextureView mTextureView; + private CanvasTextureViewActivity.RenderingThread mThread; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + FrameLayout content = new FrameLayout(this); + + mTextureView = new TextureView(this); + mTextureView.setSurfaceTextureListener(this); + mTextureView.setOpaque(false); + + content.addView(mTextureView, new FrameLayout.LayoutParams(500, 500, Gravity.CENTER)); + setContentView(content); + } + + @Override + public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { + mThread = new RenderingThread(mTextureView); + mThread.start(); + } + + @Override + public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { + // Ignored + } + + @Override + public void onSurfaceTextureDestroyed(SurfaceTexture surface) { + if (mThread != null) mThread.stopRendering(); + } + + @Override + public void onSurfaceTextureUpdated(SurfaceTexture surface) { + // Ignored + } + + private static class RenderingThread extends Thread { + private final TextureView mSurface; + private volatile boolean mRunning = true; + + public RenderingThread(TextureView surface) { + mSurface = surface; + } + + @Override + public void run() { + float x = 0.0f; + float y = 0.0f; + float speedX = 5.0f; + float speedY = 3.0f; + + Paint paint = new Paint(); + paint.setColor(0xff00ff00); + + while (mRunning && !Thread.interrupted()) { + final Canvas canvas = mSurface.lockCanvas(null); + try { + canvas.drawColor(0x00000000, PorterDuff.Mode.CLEAR); + canvas.drawRect(x, y, x + 20.0f, y + 20.0f, paint); + } finally { + mSurface.unlockCanvasAndPost(canvas); + } + + if (x + 20.0f + speedX >= mSurface.getWidth() || x + speedX <= 0.0f) { + speedX = -speedX; + } + if (y + 20.0f + speedY >= mSurface.getHeight() || y + speedY <= 0.0f) { + speedY = -speedY; + } + + x += speedX; + y += speedY; + + try { + Thread.sleep(15); + } catch (InterruptedException e) { + // Interrupted + } + } + } + + void stopRendering() { + interrupt(); + mRunning = false; + } + } +} |