diff options
author | Jim Miller <jaggies@google.com> | 2011-02-28 13:12:56 -0800 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2011-02-28 13:12:56 -0800 |
commit | 7eae7a97bc78b1a03b3df2444c6cdff9808d536a (patch) | |
tree | 84f013e0b2613431747d01387cf5fd5a22af6f96 | |
parent | 7c4beadf57c893f17efc0550b13498a06958f90c (diff) | |
parent | 5fda69cf1e7cad4d40f80203e5f790595f66e840 (diff) | |
download | frameworks_base-7eae7a97bc78b1a03b3df2444c6cdff9808d536a.zip frameworks_base-7eae7a97bc78b1a03b3df2444c6cdff9808d536a.tar.gz frameworks_base-7eae7a97bc78b1a03b3df2444c6cdff9808d536a.tar.bz2 |
Merge "Fix 3386135: Make recent apps scrollable with more entries"
-rw-r--r-- | packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_default.png | bin | 1914 -> 3746 bytes | |||
-rw-r--r-- | packages/SystemUI/res/drawable-xlarge-mdpi/recents_thumbnail_bg.png | bin | 1564 -> 3981 bytes | |||
-rw-r--r-- | packages/SystemUI/res/drawable-xlarge-mdpi/recents_thumbnail_bg_press.png | bin | 0 -> 2327 bytes | |||
-rw-r--r-- | packages/SystemUI/res/layout-xlarge/status_bar_recent_item.xml | 8 | ||||
-rw-r--r-- | packages/SystemUI/res/layout-xlarge/status_bar_recent_panel.xml | 33 | ||||
-rw-r--r-- | packages/SystemUI/res/layout-xlarge/status_bar_recent_panel_footer.xml | 25 | ||||
-rw-r--r-- | packages/SystemUI/res/values/dimens.xml | 29 | ||||
-rw-r--r-- | packages/SystemUI/src/com/android/systemui/statusbar/tablet/RecentAppsPanel.java | 186 | ||||
-rw-r--r-- | packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java | 11 |
9 files changed, 202 insertions, 90 deletions
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_default.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_default.png Binary files differindex e4d5a32..7b54daf 100644 --- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_default.png +++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_default.png diff --git a/packages/SystemUI/res/drawable-xlarge-mdpi/recents_thumbnail_bg.png b/packages/SystemUI/res/drawable-xlarge-mdpi/recents_thumbnail_bg.png Binary files differindex 9f72549..87a67c9 100644 --- a/packages/SystemUI/res/drawable-xlarge-mdpi/recents_thumbnail_bg.png +++ b/packages/SystemUI/res/drawable-xlarge-mdpi/recents_thumbnail_bg.png diff --git a/packages/SystemUI/res/drawable-xlarge-mdpi/recents_thumbnail_bg_press.png b/packages/SystemUI/res/drawable-xlarge-mdpi/recents_thumbnail_bg_press.png Binary files differnew file mode 100644 index 0000000..7f86fb3 --- /dev/null +++ b/packages/SystemUI/res/drawable-xlarge-mdpi/recents_thumbnail_bg_press.png diff --git a/packages/SystemUI/res/layout-xlarge/status_bar_recent_item.xml b/packages/SystemUI/res/layout-xlarge/status_bar_recent_item.xml index d7e1633..bfa6c36 100644 --- a/packages/SystemUI/res/layout-xlarge/status_bar_recent_item.xml +++ b/packages/SystemUI/res/layout-xlarge/status_bar_recent_item.xml @@ -22,14 +22,14 @@ <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_height="wrap_content" - android:layout_width="156dip"> + android:layout_width="@dimen/status_bar_recents_thumbnail_view_width"> <ImageView 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="105dip" + android:layout_marginLeft="@dimen/status_bar_recents_thumbnail_left_margin" android:scaleType="center" /> @@ -40,8 +40,8 @@ android:layout_alignParentTop="true" android:layout_marginLeft="123dip" android:layout_marginTop="16dip" - android:maxWidth="64dip" - android:maxHeight="64dip" + android:maxWidth="@dimen/status_bar_recents_thumbnail_max_width" + android:maxHeight="@dimen/status_bar_recents_thumbnail_max_height" android:adjustViewBounds="true" /> diff --git a/packages/SystemUI/res/layout-xlarge/status_bar_recent_panel.xml b/packages/SystemUI/res/layout-xlarge/status_bar_recent_panel.xml index ecd2b6f..4be57a2 100644 --- a/packages/SystemUI/res/layout-xlarge/status_bar_recent_panel.xml +++ b/packages/SystemUI/res/layout-xlarge/status_bar_recent_panel.xml @@ -36,36 +36,35 @@ <LinearLayout android:id="@+id/recents_glow" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:orientation="horizontal" - android:layout_marginBottom="-28dip" + android:layout_marginBottom="-52dip" android:layout_gravity="bottom" - android:background="@drawable/recents_blue_glow"> + android:background="@drawable/recents_blue_glow" + android:orientation="horizontal" + > - <LinearLayout android:id="@+id/recents_container" - android:layout_width="356dip" + <ListView android:id="@+id/recents_container" + android:layout_width="@dimen/status_bar_recents_width" android:layout_height="wrap_content" - android:orientation="vertical" android:layout_marginRight="100dip" + android:divider="@null" + android:scrollingCache="true" + android:stackFromBottom="true" + android:fadingEdge="vertical" + android:scrollbars="none" + android:fadingEdgeLength="30dip" + android:listSelector="@drawable/recents_thumbnail_bg_press" /> </LinearLayout> </FrameLayout> - <!-- The outer FrameLayout is just used as an opaque background for the dismiss icon --> - <FrameLayout + <View android:id="@+id/recents_dismiss_button" android:layout_width="80px" android:layout_height="@*android:dimen/status_bar_height" android:layout_alignParentBottom="true" android:layout_alignParentLeft="true" - android:background="#ff000000"> - - <View android:id="@+id/recents_dismiss_button" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:background="@drawable/ic_sysbar_back_ime" - /> - - </FrameLayout> + android:background="@drawable/ic_sysbar_back_ime" + /> </com.android.systemui.statusbar.tablet.RecentAppsPanel> diff --git a/packages/SystemUI/res/layout-xlarge/status_bar_recent_panel_footer.xml b/packages/SystemUI/res/layout-xlarge/status_bar_recent_panel_footer.xml new file mode 100644 index 0000000..4d14d1f --- /dev/null +++ b/packages/SystemUI/res/layout-xlarge/status_bar_recent_panel_footer.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* apps/common/assets/default/default/skins/StatusBar.xml +** +** 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. +*/ +--> + +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/listview_footer_padding" + android:layout_height="24dip" + android:layout_width="match_parent"> +</FrameLayout> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 93cf377..88cd43c 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -2,21 +2,34 @@ <!-- * 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 + * 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 + * 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 + * 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. */ --> <resources> <!-- Margin at the edge of the screen to ignore touch events for in the windowshade. --> <dimen name="status_bar_edge_ignore">5dp</dimen> + + <!-- Recent Applications parameters --> + <!-- Width of a recent app view, including all content --> + <dimen name="status_bar_recents_thumbnail_view_width">156dp</dimen> + <!-- How far the thumbnail for a recent app appears from left edge --> + <dimen name="status_bar_recents_thumbnail_left_margin">110dp</dimen> + <!-- Upper width limit for application icon --> + <dimen name="status_bar_recents_thumbnail_max_width">64dp</dimen> + <!-- Upper height limit for application icon --> + <dimen name="status_bar_recents_thumbnail_max_height">64dp</dimen> + <!-- Width of scrollable area in recents --> + <dimen name="status_bar_recents_width">356dp</dimen> + </resources> diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/RecentAppsPanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/RecentAppsPanel.java index e0d558f..ebe1a7c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/RecentAppsPanel.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/RecentAppsPanel.java @@ -40,37 +40,43 @@ import android.graphics.RectF; import android.graphics.Shader.TileMode; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; +import android.os.Parcelable; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.util.Log; import android.util.Slog; +import android.view.LayoutInflater; import android.view.View; -import android.view.View.OnClickListener; -import android.view.animation.DecelerateInterpolator; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemClickListener; +import android.widget.BaseAdapter; import android.widget.ImageView; -import android.widget.LinearLayout; +import android.widget.ListView; import android.widget.RelativeLayout; import android.widget.TextView; import com.android.systemui.R; -public class RecentAppsPanel extends RelativeLayout implements StatusBarPanel, OnClickListener { +public class RecentAppsPanel extends RelativeLayout implements StatusBarPanel, OnItemClickListener { private static final int GLOW_PADDING = 15; private static final String TAG = "RecentAppsPanel"; private static final boolean DEBUG = TabletStatusBar.DEBUG; - private static final int DISPLAY_TASKS_PORTRAIT = 7; // Limited by max binder transaction size - private static final int DISPLAY_TASKS_LANDSCAPE = 5; // number of recent tasks to display - private static final int MAX_TASKS = DISPLAY_TASKS_PORTRAIT + 1; // allow extra for non-apps + private static final int DISPLAY_TASKS = 20; + private static final int MAX_TASKS = DISPLAY_TASKS + 1; // allow extra for non-apps + private static final int BOTTOM_OFFSET = 28; // TODO: Get from dimens.xml private TabletStatusBar mBar; private ArrayList<ActivityDescription> mActivityDescriptions; private int mIconDpi; private View mRecentsScrim; private View mRecentsGlowView; - private LinearLayout mRecentsContainer; + private ListView mRecentsContainer; private Bitmap mGlowBitmap; private boolean mShowing; private Choreographer mChoreo; private View mRecentsDismissButton; + private ActvityDescriptionAdapter mListAdapter; + protected int mLastVisibleItem; static class ActivityDescription { int id; @@ -98,6 +104,63 @@ public class RecentAppsPanel extends RelativeLayout implements StatusBarPanel, O } }; + private static class ViewHolder { + private ImageView thumbnailView; + private ImageView iconView; + private TextView labelView; + private TextView descriptionView; + private ActivityDescription activityDescription; + } + + private class ActvityDescriptionAdapter extends BaseAdapter { + private LayoutInflater mInflater; + + public ActvityDescriptionAdapter(Context context) { + mInflater = LayoutInflater.from(context); + } + + public int getCount() { + return mActivityDescriptions != null ? mActivityDescriptions.size() : 0; + } + + public Object getItem(int position) { + return position; // we only need the index + } + + public long getItemId(int position) { + return position; // we just need something unique for this position + } + + public View getView(int position, View convertView, ViewGroup parent) { + ViewHolder holder; + 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.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); + convertView.setTag(holder); + } else { + holder = (ViewHolder) convertView.getTag(); + } + + // activityId is reverse since most recent appears at the bottom... + final int activityId = mActivityDescriptions.size() - position - 1; + + final ActivityDescription activityDescription = mActivityDescriptions.get(activityId); + final Bitmap thumb = activityDescription.thumbnail; + holder.thumbnailView.setImageBitmap(compositeBitmap(mGlowBitmap, thumb)); + holder.iconView.setImageDrawable(activityDescription.icon); + holder.labelView.setText(activityDescription.label); + holder.descriptionView.setText(activityDescription.description); + holder.thumbnailView.setTag(activityDescription); + holder.activityDescription = activityDescription; + + return convertView; + } + } + public boolean isInContentArea(int x, int y) { // use mRecentsContainer's exact bounds to determine horizontal position final int l = mRecentsContainer.getLeft(); @@ -267,9 +330,41 @@ public class RecentAppsPanel extends RelativeLayout implements StatusBarPanel, O } @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + super.onSizeChanged(w, h, oldw, oldh); + // Keep track of the last visible item in the list so we can restore it + // to the bottom when the orientation changes. + int childCount = mRecentsContainer.getChildCount(); + if (childCount > 0) { + mLastVisibleItem = mRecentsContainer.getFirstVisiblePosition() + childCount - 1; + View view = mRecentsContainer.getChildAt(childCount - 1); + final int distanceFromBottom = mRecentsContainer.getHeight() - view.getTop(); + //final int distanceFromBottom = view.getHeight() + BOTTOM_OFFSET; + + // This has to happen post-layout, so run it "in the future" + post(new Runnable() { + public void run() { + mRecentsContainer.setSelectionFromTop(mLastVisibleItem, + mRecentsContainer.getHeight() - distanceFromBottom); + } + }); + } + } + + @Override protected void onFinishInflate() { super.onFinishInflate(); - mRecentsContainer = (LinearLayout) findViewById(R.id.recents_container); + LayoutInflater inflater = (LayoutInflater) + mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + + mRecentsContainer = (ListView) findViewById(R.id.recents_container); + View footer = inflater.inflate(R.layout.status_bar_recent_panel_footer, + mRecentsContainer, false); + mRecentsContainer.setScrollbarFadingEnabled(true); + mRecentsContainer.addFooterView(footer); + mRecentsContainer.setAdapter(mListAdapter = new ActvityDescriptionAdapter(mContext)); + mRecentsContainer.setOnItemClickListener(this); + mRecentsGlowView = findViewById(R.id.recents_glow); mRecentsScrim = (View) findViewById(R.id.recents_bg_protect); mChoreo = new Choreographer(this, mRecentsScrim, mRecentsGlowView); @@ -287,20 +382,16 @@ public class RecentAppsPanel extends RelativeLayout implements StatusBarPanel, O } @Override - protected void onConfigurationChanged(Configuration newConfig) { - super.onConfigurationChanged(newConfig); - // we show more in portrait mode, so update UI if orientation changes - updateUiElements(newConfig, false); - } - - @Override protected void onVisibilityChanged(View changedView, int visibility) { super.onVisibilityChanged(changedView, visibility); if (DEBUG) Log.v(TAG, "onVisibilityChanged(" + changedView + ", " + visibility + ")"); if (visibility == View.VISIBLE && changedView == this) { refreshApplicationList(); - mRecentsContainer.setScrollbarFadingEnabled(true); - mRecentsContainer.scrollTo(0, 0); + post(new Runnable() { + public void run() { + mRecentsContainer.setSelection(mActivityDescriptions.size() - 1); + } + }); } } @@ -402,11 +493,12 @@ public class RecentAppsPanel extends RelativeLayout implements StatusBarPanel, O private void refreshApplicationList() { mActivityDescriptions = getRecentTasks(); + mListAdapter.notifyDataSetInvalidated(); if (mActivityDescriptions.size() > 0) { - updateUiElements(getResources().getConfiguration(), true); + mLastVisibleItem = mActivityDescriptions.size() - 1; // scroll to bottom after reloading + updateUiElements(getResources().getConfiguration()); } else { // Immediately hide this panel - mShowing = false; hide(false); } } @@ -426,44 +518,29 @@ public class RecentAppsPanel extends RelativeLayout implements StatusBarPanel, O canvas.drawBitmap(thumbnail, new Rect(0, 0, srcWidth-1, srcHeight-1), new RectF(GLOW_PADDING, - GLOW_PADDING - 4.0f, - outBitmap.getWidth() - GLOW_PADDING + 2.0f, - outBitmap.getHeight() - GLOW_PADDING + 3.0f), paint); + GLOW_PADDING - 7.0f, + outBitmap.getWidth() - GLOW_PADDING + 3.0f, + outBitmap.getHeight() - GLOW_PADDING + 7.0f), paint); } return outBitmap; } - private void updateUiElements(Configuration config, boolean animate) { - mRecentsContainer.removeAllViews(); - - final int first = 0; - final boolean isPortrait = config.orientation == Configuration.ORIENTATION_PORTRAIT; - final int taskCount = isPortrait ? DISPLAY_TASKS_PORTRAIT : DISPLAY_TASKS_LANDSCAPE; - final int last = Math.min(mActivityDescriptions.size(), taskCount) - 1; - for (int i = last; i >= first; i--) { - ActivityDescription activityDescription = mActivityDescriptions.get(i); - View view = View.inflate(mContext, R.layout.status_bar_recent_item, null); - ImageView appThumbnail = (ImageView) view.findViewById(R.id.app_thumbnail); - ImageView appIcon = (ImageView) view.findViewById(R.id.app_icon); - TextView appLabel = (TextView) view.findViewById(R.id.app_label); - TextView appDesc = (TextView) view.findViewById(R.id.app_description); - final Bitmap thumb = activityDescription.thumbnail; - appThumbnail.setImageBitmap(compositeBitmap(mGlowBitmap, thumb)); - appIcon.setImageDrawable(activityDescription.icon); - appLabel.setText(activityDescription.label); - appDesc.setText(activityDescription.description); - appThumbnail.setOnClickListener(this); - appThumbnail.setTag(activityDescription); - mRecentsContainer.addView(view); - } + private void updateUiElements(Configuration config) { + final int items = mActivityDescriptions.size(); + + mRecentsContainer.setVisibility(items > 0 ? View.VISIBLE : View.GONE); + mRecentsGlowView.setVisibility(items > 0 ? View.VISIBLE : View.GONE); + } - int views = mRecentsContainer.getChildCount(); - mRecentsContainer.setVisibility(views > 0 ? View.VISIBLE : View.GONE); - mRecentsGlowView.setVisibility(views > 0 ? View.VISIBLE : View.GONE); + private void hide(boolean animate) { + if (!animate) { + setVisibility(View.GONE); + } + mBar.animateCollapse(); } - public void onClick(View v) { - ActivityDescription ad = (ActivityDescription)v.getTag(); + public void onItemClick(AdapterView<?> parent, View view, int position, long id) { + ActivityDescription ad = ((ViewHolder) view.getTag()).activityDescription; final ActivityManager am = (ActivityManager) getContext().getSystemService(Context.ACTIVITY_SERVICE); if (ad.id >= 0) { @@ -478,11 +555,4 @@ public class RecentAppsPanel extends RelativeLayout implements StatusBarPanel, O } hide(true); } - - private void hide(boolean animate) { - setVisibility(View.GONE); - if (animate) { - mBar.animateCollapse(); - } - } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java index 233d601..f0408a2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java @@ -267,6 +267,8 @@ public class TabletStatusBar extends StatusBar implements lp.gravity = Gravity.BOTTOM | Gravity.LEFT; lp.setTitle("RecentsPanel"); lp.windowAnimations = R.style.Animation_RecentPanel; + lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED + | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING; WindowManagerImpl.getDefault().addView(mRecentsPanel, lp); mRecentsPanel.setBar(this); @@ -509,7 +511,7 @@ public class TabletStatusBar extends StatusBar implements final int peekIndex = m.arg1; if (peekIndex < N) { //Slog.d(TAG, "loading peek: " + peekIndex); - NotificationData.Entry entry = + NotificationData.Entry entry = mNotificationDNDMode ? mNotificationDNDDummyEntry : mNotificationData.get(N-1-peekIndex); @@ -555,7 +557,7 @@ public class TabletStatusBar extends StatusBar implements final int N = mNotificationData.size(); if (mNotificationPeekIndex >= 0 && mNotificationPeekIndex < N) { - NotificationData.Entry entry = + NotificationData.Entry entry = mNotificationDNDMode ? mNotificationDNDDummyEntry : mNotificationData.get(N-1-mNotificationPeekIndex); @@ -584,6 +586,8 @@ public class TabletStatusBar extends StatusBar implements case MSG_OPEN_RECENTS_PANEL: if (DEBUG) Slog.d(TAG, "opening recents panel"); if (mRecentsPanel != null) { + disable(StatusBarManager.DISABLE_NAVIGATION + | StatusBarManager.DISABLE_BACK); mRecentsPanel.setVisibility(View.VISIBLE); mRecentsPanel.show(true, true); } @@ -591,6 +595,7 @@ public class TabletStatusBar extends StatusBar implements case MSG_CLOSE_RECENTS_PANEL: if (DEBUG) Slog.d(TAG, "closing recents panel"); if (mRecentsPanel != null && mRecentsPanel.isShowing()) { + disable(StatusBarManager.DISABLE_NONE); mRecentsPanel.show(false, true); } break; @@ -701,7 +706,7 @@ public class TabletStatusBar extends StatusBar implements && oldContentView.getLayoutId() == contentView.getLayoutId(); ViewGroup rowParent = (ViewGroup) oldEntry.row.getParent(); boolean orderUnchanged = notification.notification.when==oldNotification.notification.when - && notification.priority == oldNotification.priority; + && notification.priority == oldNotification.priority; // priority now encompasses isOngoing() boolean isLastAnyway = rowParent.indexOfChild(oldEntry.row) == rowParent.getChildCount()-1; if (contentsUnchanged && (orderUnchanged || isLastAnyway)) { |