Loading core/java/com/android/internal/app/ChooserActivity.java +238 −35 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package com.android.internal.app; import android.animation.ObjectAnimator; import android.annotation.NonNull; import android.app.Activity; import android.content.ComponentName; import android.content.Context; Loading @@ -29,6 +31,7 @@ import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; import android.database.DataSetObserver; import android.graphics.Color; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; import android.os.Bundle; Loading @@ -46,13 +49,18 @@ import android.service.chooser.ChooserTargetService; import android.service.chooser.IChooserTargetResult; import android.service.chooser.IChooserTargetService; import android.text.TextUtils; import android.util.FloatProperty; import android.util.Log; import android.util.Slog; import android.view.LayoutInflater; import android.view.View; import android.view.View.MeasureSpec; import android.view.View.OnClickListener; import android.view.View.OnLongClickListener; import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; import android.widget.AbsListView; import android.widget.BaseAdapter; import android.widget.ListView; Loading Loading @@ -80,6 +88,7 @@ public class ChooserActivity extends ResolverActivity { private Intent mReferrerFillInIntent; private ChooserListAdapter mChooserListAdapter; private ChooserRowAdapter mChooserRowAdapter; private final List<ChooserTargetServiceConnection> mServiceConnections = new ArrayList<>(); Loading Loading @@ -253,7 +262,9 @@ public class ChooserActivity extends ResolverActivity { boolean alwaysUseOption) { final ListView listView = adapterView instanceof ListView ? (ListView) adapterView : null; mChooserListAdapter = (ChooserListAdapter) adapter; adapterView.setAdapter(new ChooserRowAdapter(mChooserListAdapter)); mChooserRowAdapter = new ChooserRowAdapter(mChooserListAdapter); mChooserRowAdapter.registerDataSetObserver(new OffsetDataSetObserver(adapterView)); adapterView.setAdapter(mChooserRowAdapter); if (listView != null) { listView.setItemsCanFocus(true); } Loading Loading @@ -913,19 +924,103 @@ public class ChooserActivity extends ResolverActivity { } } static class RowScale { private static final int DURATION = 400; float mScale; ChooserRowAdapter mAdapter; private final ObjectAnimator mAnimator; public static final FloatProperty<RowScale> PROPERTY = new FloatProperty<RowScale>("scale") { @Override public void setValue(RowScale object, float value) { object.mScale = value; object.mAdapter.notifyDataSetChanged(); } @Override public Float get(RowScale object) { return object.mScale; } }; public RowScale(@NonNull ChooserRowAdapter adapter, float from, float to) { mAdapter = adapter; mScale = from; if (from == to) { mAnimator = null; return; } mAnimator = ObjectAnimator.ofFloat(this, PROPERTY, from, to).setDuration(DURATION); } public RowScale setInterpolator(Interpolator interpolator) { if (mAnimator != null) { mAnimator.setInterpolator(interpolator); } return this; } public float get() { return mScale; } public void startAnimation() { if (mAnimator != null) { mAnimator.start(); } } public void cancelAnimation() { if (mAnimator != null) { mAnimator.cancel(); } } } class ChooserRowAdapter extends BaseAdapter { private ChooserListAdapter mChooserListAdapter; private final LayoutInflater mLayoutInflater; private final int mColumnCount = 4; private RowScale[] mServiceTargetScale; private final Interpolator mInterpolator; public ChooserRowAdapter(ChooserListAdapter wrappedAdapter) { mChooserListAdapter = wrappedAdapter; mLayoutInflater = LayoutInflater.from(ChooserActivity.this); mInterpolator = AnimationUtils.loadInterpolator(ChooserActivity.this, android.R.interpolator.decelerate_quint); wrappedAdapter.registerDataSetObserver(new DataSetObserver() { @Override public void onChanged() { super.onChanged(); final int rcount = getServiceTargetRowCount(); if (mServiceTargetScale == null || mServiceTargetScale.length != rcount) { RowScale[] old = mServiceTargetScale; int oldRCount = old != null ? old.length : 0; mServiceTargetScale = new RowScale[rcount]; if (old != null && rcount > 0) { System.arraycopy(old, 0, mServiceTargetScale, 0, Math.min(old.length, rcount)); } for (int i = rcount; i < oldRCount; i++) { old[i].cancelAnimation(); } for (int i = oldRCount; i < rcount; i++) { final RowScale rs = new RowScale(ChooserRowAdapter.this, 0.f, 1.f) .setInterpolator(mInterpolator); mServiceTargetScale[i] = rs; rs.startAnimation(); } } notifyDataSetChanged(); } Loading @@ -933,19 +1028,43 @@ public class ChooserActivity extends ResolverActivity { public void onInvalidated() { super.onInvalidated(); notifyDataSetInvalidated(); if (mServiceTargetScale != null) { for (RowScale rs : mServiceTargetScale) { rs.cancelAnimation(); } } } }); } private float getRowScale(int rowPosition) { final int start = getCallerTargetRowCount(); final int end = start + getServiceTargetRowCount(); if (rowPosition >= start && rowPosition < end) { return mServiceTargetScale[rowPosition - start].get(); } return 1.f; } @Override public int getCount() { return (int) ( Math.ceil((float) mChooserListAdapter.getCallerTargetCount() / mColumnCount) + Math.ceil((float) mChooserListAdapter.getServiceTargetCount() / mColumnCount) getCallerTargetRowCount() + getServiceTargetRowCount() + Math.ceil((float) mChooserListAdapter.getStandardTargetCount() / mColumnCount) ); } public int getCallerTargetRowCount() { return (int) Math.ceil( (float) mChooserListAdapter.getCallerTargetCount() / mColumnCount); } public int getServiceTargetRowCount() { return (int) Math.ceil( (float) mChooserListAdapter.getServiceTargetCount() / mColumnCount); } @Override public Object getItem(int position) { // We have nothing useful to return here. Loading @@ -959,33 +1078,67 @@ public class ChooserActivity extends ResolverActivity { @Override public View getView(int position, View convertView, ViewGroup parent) { final View[] holder; final RowViewHolder holder; if (convertView == null) { holder = createViewHolder(parent); } else { holder = (View[]) convertView.getTag(); holder = (RowViewHolder) convertView.getTag(); } bindViewHolder(position, holder); // We keep the actual list item view as the last item in the holder array return holder[mColumnCount]; return holder.row; } View[] createViewHolder(ViewGroup parent) { final View[] holder = new View[mColumnCount + 1]; RowViewHolder createViewHolder(ViewGroup parent) { final ViewGroup row = (ViewGroup) mLayoutInflater.inflate(R.layout.chooser_row, parent, false); final RowViewHolder holder = new RowViewHolder(row, mColumnCount); final int spec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); for (int i = 0; i < mColumnCount; i++) { holder[i] = mChooserListAdapter.createView(row); row.addView(holder[i]); final View v = mChooserListAdapter.createView(row); v.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { startSelected(holder.itemIndex, false, true); } }); v.setOnLongClickListener(new OnLongClickListener() { @Override public boolean onLongClick(View v) { showAppDetails( mChooserListAdapter.resolveInfoForPosition(holder.itemIndex, true)); return true; } }); row.addView(v); holder.cells[i] = v; // Force height to be a given so we don't have visual disruption during scaling. LayoutParams lp = v.getLayoutParams(); v.measure(spec, spec); if (lp == null) { lp = new LayoutParams(LayoutParams.MATCH_PARENT, v.getMeasuredHeight()); row.setLayoutParams(lp); } else { lp.height = v.getMeasuredHeight(); } } // Pre-measure so we can scale later. holder.measure(); LayoutParams lp = row.getLayoutParams(); if (lp == null) { lp = new LayoutParams(LayoutParams.MATCH_PARENT, holder.measuredRowHeight); row.setLayoutParams(lp); } else { lp.height = holder.measuredRowHeight; } row.setTag(holder); holder[mColumnCount] = row; return holder; } void bindViewHolder(int rowPosition, View[] holder) { void bindViewHolder(int rowPosition, RowViewHolder holder) { final int start = getFirstRowPosition(rowPosition); final int startType = mChooserListAdapter.getPositionTargetType(start); Loading @@ -994,34 +1147,26 @@ public class ChooserActivity extends ResolverActivity { end--; } final ViewGroup row = (ViewGroup) holder[mColumnCount]; if (startType == ChooserListAdapter.TARGET_SERVICE) { row.setBackgroundColor(getColor(R.color.chooser_service_row_background_color)); holder.row.setBackgroundColor( getColor(R.color.chooser_service_row_background_color)); } else { row.setBackground(null); holder.row.setBackgroundColor(Color.TRANSPARENT); } final int oldHeight = holder.row.getLayoutParams().height; holder.row.getLayoutParams().height = Math.max(1, (int) (holder.measuredRowHeight * getRowScale(rowPosition))); if (holder.row.getLayoutParams().height != oldHeight) { holder.row.requestLayout(); } for (int i = 0; i < mColumnCount; i++) { final View v = holder[i]; final View v = holder.cells[i]; if (start + i <= end) { v.setVisibility(View.VISIBLE); final int itemIndex = start + i; mChooserListAdapter.bindView(itemIndex, v); v.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { startSelected(itemIndex, false, true); } }); v.setOnLongClickListener(new OnLongClickListener() { @Override public boolean onLongClick(View v) { showAppDetails( mChooserListAdapter.resolveInfoForPosition(itemIndex, true)); return true; } }); holder.itemIndex = start + i; mChooserListAdapter.bindView(holder.itemIndex, v); } else { v.setVisibility(View.GONE); } Loading @@ -1048,6 +1193,24 @@ public class ChooserActivity extends ResolverActivity { } } static class RowViewHolder { final View[] cells; final ViewGroup row; int measuredRowHeight; int itemIndex; public RowViewHolder(ViewGroup row, int cellCount) { this.row = row; this.cells = new View[cellCount]; } public void measure() { final int spec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); row.measure(spec, spec); measuredRowHeight = row.getMeasuredHeight(); } } static class ChooserTargetServiceConnection implements ServiceConnection { private final DisplayResolveInfo mOriginalTarget; private ComponentName mConnectedComponent; Loading Loading @@ -1199,4 +1362,44 @@ public class ChooserActivity extends ResolverActivity { mSelectedTarget = null; } } class OffsetDataSetObserver extends DataSetObserver { private final AbsListView mListView; private int mCachedViewType = -1; private View mCachedView; public OffsetDataSetObserver(AbsListView listView) { mListView = listView; } @Override public void onChanged() { if (mResolverDrawerLayout == null) { return; } final int chooserTargetRows = mChooserRowAdapter.getServiceTargetRowCount(); int offset = 0; for (int i = 0; i < chooserTargetRows; i++) { final int pos = mChooserRowAdapter.getCallerTargetRowCount() + i; final int vt = mChooserRowAdapter.getItemViewType(pos); if (vt != mCachedViewType) { mCachedView = null; } final View v = mChooserRowAdapter.getView(pos, mCachedView, mListView); int height = ((RowViewHolder) (v.getTag())).measuredRowHeight; offset += (int) (height * mChooserRowAdapter.getRowScale(pos) * chooserTargetRows); if (vt >= 0) { mCachedViewType = vt; mCachedView = v; } else { mCachedViewType = -1; } } mResolverDrawerLayout.setCollapsibleHeightReserved(offset); } } } core/java/com/android/internal/app/ResolverActivity.java +7 −1 Original line number Diff line number Diff line Loading @@ -103,6 +103,8 @@ public class ResolverActivity extends Activity { private ResolverComparator mResolverComparator; private PickTargetOptionRequest mPickOptionRequest; protected ResolverDrawerLayout mResolverDrawerLayout; private boolean mRegistered; private final PackageMonitor mPackageMonitor = new PackageMonitor() { @Override public void onSomePackagesChanged() { Loading Loading @@ -253,6 +255,7 @@ public class ResolverActivity extends Activity { if (isVoiceInteraction()) { rdl.setCollapsed(false); } mResolverDrawerLayout = rdl; } if (title == null) { Loading Loading @@ -1570,7 +1573,10 @@ public class ResolverActivity extends Activity { private void onBindView(View view, TargetInfo info) { final ViewHolder holder = (ViewHolder) view.getTag(); final CharSequence label = info.getDisplayLabel(); if (!TextUtils.equals(holder.text.getText(), label)) { holder.text.setText(info.getDisplayLabel()); } if (showsExtendedInfo(info)) { holder.text2.setVisibility(View.VISIBLE); holder.text2.setText(info.getExtendedInfo()); Loading core/java/com/android/internal/widget/ResolverDrawerLayout.java +90 −21 Original line number Diff line number Diff line Loading @@ -69,6 +69,12 @@ public class ResolverDrawerLayout extends ViewGroup { private int mCollapsibleHeight; private int mUncollapsibleHeight; /** * The height in pixels of reserved space added to the top of the collapsed UI; * e.g. chooser targets */ private int mCollapsibleHeightReserved; private int mTopOffset; private boolean mIsDragging; Loading Loading @@ -153,12 +159,62 @@ public class ResolverDrawerLayout extends ViewGroup { } } public void setCollapsibleHeightReserved(int heightPixels) { final int oldReserved = mCollapsibleHeightReserved; mCollapsibleHeightReserved = heightPixels; final int dReserved = mCollapsibleHeightReserved - oldReserved; if (dReserved != 0 && mIsDragging) { mLastTouchY -= dReserved; } final int oldCollapsibleHeight = mCollapsibleHeight; mCollapsibleHeight = Math.max(mCollapsibleHeight, getMaxCollapsedHeight()); if (updateCollapseOffset(oldCollapsibleHeight, !isDragging())) { return; } invalidate(); } private boolean isMoving() { return mIsDragging || !mScroller.isFinished(); } private boolean isDragging() { return mIsDragging || getNestedScrollAxes() == SCROLL_AXIS_VERTICAL; } private boolean updateCollapseOffset(int oldCollapsibleHeight, boolean remainClosed) { if (oldCollapsibleHeight == mCollapsibleHeight) { return false; } if (isLaidOut()) { final boolean isCollapsedOld = mCollapseOffset != 0; if (remainClosed && (oldCollapsibleHeight < mCollapsibleHeight && mCollapseOffset == oldCollapsibleHeight)) { // Stay closed even at the new height. mCollapseOffset = mCollapsibleHeight; } else { mCollapseOffset = Math.min(mCollapseOffset, mCollapsibleHeight); } final boolean isCollapsedNew = mCollapseOffset != 0; if (isCollapsedOld != isCollapsedNew) { notifyViewAccessibilityStateChangedIfNeeded( AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED); } } else { // Start out collapsed at first unless we restored state for otherwise mCollapseOffset = mOpenOnLayout ? 0 : mCollapsibleHeight; } return true; } private int getMaxCollapsedHeight() { return isSmallCollapsed() ? mMaxCollapsedHeightSmall : mMaxCollapsedHeight; return (isSmallCollapsed() ? mMaxCollapsedHeightSmall : mMaxCollapsedHeight) + mCollapsibleHeightReserved; } public void setOnDismissedListener(OnDismissedListener listener) { Loading Loading @@ -676,7 +732,7 @@ public class ResolverDrawerLayout extends ViewGroup { final LayoutParams lp = (LayoutParams) child.getLayoutParams(); if (lp.alwaysShow && child.getVisibility() != GONE) { measureChildWithMargins(child, widthSpec, widthPadding, heightSpec, heightUsed); heightUsed += lp.topMargin + child.getMeasuredHeight() + lp.bottomMargin; heightUsed += getHeightUsed(child); } } Loading @@ -688,7 +744,7 @@ public class ResolverDrawerLayout extends ViewGroup { final LayoutParams lp = (LayoutParams) child.getLayoutParams(); if (!lp.alwaysShow && child.getVisibility() != GONE) { measureChildWithMargins(child, widthSpec, widthPadding, heightSpec, heightUsed); heightUsed += lp.topMargin + child.getMeasuredHeight() + lp.bottomMargin; heightUsed += getHeightUsed(child); } } Loading @@ -697,28 +753,41 @@ public class ResolverDrawerLayout extends ViewGroup { heightUsed - alwaysShowHeight - getMaxCollapsedHeight()); mUncollapsibleHeight = heightUsed - mCollapsibleHeight; if (isLaidOut()) { final boolean isCollapsedOld = mCollapseOffset != 0; if (oldCollapsibleHeight < mCollapsibleHeight && mCollapseOffset == oldCollapsibleHeight) { // Stay closed even at the new height. mCollapseOffset = mCollapsibleHeight; } else { mCollapseOffset = Math.min(mCollapseOffset, mCollapsibleHeight); updateCollapseOffset(oldCollapsibleHeight, !isDragging()); mTopOffset = Math.max(0, heightSize - heightUsed) + (int) mCollapseOffset; setMeasuredDimension(sourceWidth, heightSize); } final boolean isCollapsedNew = mCollapseOffset != 0; if (isCollapsedOld != isCollapsedNew) { notifyViewAccessibilityStateChangedIfNeeded( AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED); private int getHeightUsed(View child) { // This method exists because we're taking a fast path at measuring ListViews that // lets us get away with not doing the more expensive wrap_content measurement which // imposes double child view measurement costs. If we're looking at a ListView, we can // check against the lowest child view plus padding and margin instead of the actual // measured height of the ListView. This lets the ListView hang off the edge when // all of the content would fit on-screen. int heightUsed = child.getMeasuredHeight(); if (child instanceof AbsListView) { final AbsListView lv = (AbsListView) child; final int lvPaddingBottom = lv.getPaddingBottom(); int lowest = 0; for (int i = 0, N = lv.getChildCount(); i < N; i++) { final int bottom = lv.getChildAt(i).getBottom() + lvPaddingBottom; if (bottom > lowest) { lowest = bottom; } } else { // Start out collapsed at first unless we restored state for otherwise mCollapseOffset = mOpenOnLayout ? 0 : mCollapsibleHeight; } mTopOffset = Math.max(0, heightSize - heightUsed) + (int) mCollapseOffset; if (lowest < heightUsed) { heightUsed = lowest; } } setMeasuredDimension(sourceWidth, heightSize); final LayoutParams lp = (LayoutParams) child.getLayoutParams(); return lp.topMargin + heightUsed + lp.bottomMargin; } @Override Loading core/res/res/layout/chooser_grid.xml +1 −1 Original line number Diff line number Diff line Loading @@ -85,7 +85,7 @@ <ListView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_height="match_parent" android:id="@+id/resolver_list" android:clipToPadding="false" android:scrollbarStyle="outsideOverlay" Loading core/res/res/layout/resolve_grid_item.xml +2 −1 Original line number Diff line number Diff line Loading @@ -70,6 +70,7 @@ android:minLines="2" android:maxLines="2" android:gravity="top|center_horizontal" android:ellipsize="marquee" /> android:ellipsize="marquee" android:visibility="gone" /> </LinearLayout> Loading
core/java/com/android/internal/app/ChooserActivity.java +238 −35 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package com.android.internal.app; import android.animation.ObjectAnimator; import android.annotation.NonNull; import android.app.Activity; import android.content.ComponentName; import android.content.Context; Loading @@ -29,6 +31,7 @@ import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; import android.database.DataSetObserver; import android.graphics.Color; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; import android.os.Bundle; Loading @@ -46,13 +49,18 @@ import android.service.chooser.ChooserTargetService; import android.service.chooser.IChooserTargetResult; import android.service.chooser.IChooserTargetService; import android.text.TextUtils; import android.util.FloatProperty; import android.util.Log; import android.util.Slog; import android.view.LayoutInflater; import android.view.View; import android.view.View.MeasureSpec; import android.view.View.OnClickListener; import android.view.View.OnLongClickListener; import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; import android.widget.AbsListView; import android.widget.BaseAdapter; import android.widget.ListView; Loading Loading @@ -80,6 +88,7 @@ public class ChooserActivity extends ResolverActivity { private Intent mReferrerFillInIntent; private ChooserListAdapter mChooserListAdapter; private ChooserRowAdapter mChooserRowAdapter; private final List<ChooserTargetServiceConnection> mServiceConnections = new ArrayList<>(); Loading Loading @@ -253,7 +262,9 @@ public class ChooserActivity extends ResolverActivity { boolean alwaysUseOption) { final ListView listView = adapterView instanceof ListView ? (ListView) adapterView : null; mChooserListAdapter = (ChooserListAdapter) adapter; adapterView.setAdapter(new ChooserRowAdapter(mChooserListAdapter)); mChooserRowAdapter = new ChooserRowAdapter(mChooserListAdapter); mChooserRowAdapter.registerDataSetObserver(new OffsetDataSetObserver(adapterView)); adapterView.setAdapter(mChooserRowAdapter); if (listView != null) { listView.setItemsCanFocus(true); } Loading Loading @@ -913,19 +924,103 @@ public class ChooserActivity extends ResolverActivity { } } static class RowScale { private static final int DURATION = 400; float mScale; ChooserRowAdapter mAdapter; private final ObjectAnimator mAnimator; public static final FloatProperty<RowScale> PROPERTY = new FloatProperty<RowScale>("scale") { @Override public void setValue(RowScale object, float value) { object.mScale = value; object.mAdapter.notifyDataSetChanged(); } @Override public Float get(RowScale object) { return object.mScale; } }; public RowScale(@NonNull ChooserRowAdapter adapter, float from, float to) { mAdapter = adapter; mScale = from; if (from == to) { mAnimator = null; return; } mAnimator = ObjectAnimator.ofFloat(this, PROPERTY, from, to).setDuration(DURATION); } public RowScale setInterpolator(Interpolator interpolator) { if (mAnimator != null) { mAnimator.setInterpolator(interpolator); } return this; } public float get() { return mScale; } public void startAnimation() { if (mAnimator != null) { mAnimator.start(); } } public void cancelAnimation() { if (mAnimator != null) { mAnimator.cancel(); } } } class ChooserRowAdapter extends BaseAdapter { private ChooserListAdapter mChooserListAdapter; private final LayoutInflater mLayoutInflater; private final int mColumnCount = 4; private RowScale[] mServiceTargetScale; private final Interpolator mInterpolator; public ChooserRowAdapter(ChooserListAdapter wrappedAdapter) { mChooserListAdapter = wrappedAdapter; mLayoutInflater = LayoutInflater.from(ChooserActivity.this); mInterpolator = AnimationUtils.loadInterpolator(ChooserActivity.this, android.R.interpolator.decelerate_quint); wrappedAdapter.registerDataSetObserver(new DataSetObserver() { @Override public void onChanged() { super.onChanged(); final int rcount = getServiceTargetRowCount(); if (mServiceTargetScale == null || mServiceTargetScale.length != rcount) { RowScale[] old = mServiceTargetScale; int oldRCount = old != null ? old.length : 0; mServiceTargetScale = new RowScale[rcount]; if (old != null && rcount > 0) { System.arraycopy(old, 0, mServiceTargetScale, 0, Math.min(old.length, rcount)); } for (int i = rcount; i < oldRCount; i++) { old[i].cancelAnimation(); } for (int i = oldRCount; i < rcount; i++) { final RowScale rs = new RowScale(ChooserRowAdapter.this, 0.f, 1.f) .setInterpolator(mInterpolator); mServiceTargetScale[i] = rs; rs.startAnimation(); } } notifyDataSetChanged(); } Loading @@ -933,19 +1028,43 @@ public class ChooserActivity extends ResolverActivity { public void onInvalidated() { super.onInvalidated(); notifyDataSetInvalidated(); if (mServiceTargetScale != null) { for (RowScale rs : mServiceTargetScale) { rs.cancelAnimation(); } } } }); } private float getRowScale(int rowPosition) { final int start = getCallerTargetRowCount(); final int end = start + getServiceTargetRowCount(); if (rowPosition >= start && rowPosition < end) { return mServiceTargetScale[rowPosition - start].get(); } return 1.f; } @Override public int getCount() { return (int) ( Math.ceil((float) mChooserListAdapter.getCallerTargetCount() / mColumnCount) + Math.ceil((float) mChooserListAdapter.getServiceTargetCount() / mColumnCount) getCallerTargetRowCount() + getServiceTargetRowCount() + Math.ceil((float) mChooserListAdapter.getStandardTargetCount() / mColumnCount) ); } public int getCallerTargetRowCount() { return (int) Math.ceil( (float) mChooserListAdapter.getCallerTargetCount() / mColumnCount); } public int getServiceTargetRowCount() { return (int) Math.ceil( (float) mChooserListAdapter.getServiceTargetCount() / mColumnCount); } @Override public Object getItem(int position) { // We have nothing useful to return here. Loading @@ -959,33 +1078,67 @@ public class ChooserActivity extends ResolverActivity { @Override public View getView(int position, View convertView, ViewGroup parent) { final View[] holder; final RowViewHolder holder; if (convertView == null) { holder = createViewHolder(parent); } else { holder = (View[]) convertView.getTag(); holder = (RowViewHolder) convertView.getTag(); } bindViewHolder(position, holder); // We keep the actual list item view as the last item in the holder array return holder[mColumnCount]; return holder.row; } View[] createViewHolder(ViewGroup parent) { final View[] holder = new View[mColumnCount + 1]; RowViewHolder createViewHolder(ViewGroup parent) { final ViewGroup row = (ViewGroup) mLayoutInflater.inflate(R.layout.chooser_row, parent, false); final RowViewHolder holder = new RowViewHolder(row, mColumnCount); final int spec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); for (int i = 0; i < mColumnCount; i++) { holder[i] = mChooserListAdapter.createView(row); row.addView(holder[i]); final View v = mChooserListAdapter.createView(row); v.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { startSelected(holder.itemIndex, false, true); } }); v.setOnLongClickListener(new OnLongClickListener() { @Override public boolean onLongClick(View v) { showAppDetails( mChooserListAdapter.resolveInfoForPosition(holder.itemIndex, true)); return true; } }); row.addView(v); holder.cells[i] = v; // Force height to be a given so we don't have visual disruption during scaling. LayoutParams lp = v.getLayoutParams(); v.measure(spec, spec); if (lp == null) { lp = new LayoutParams(LayoutParams.MATCH_PARENT, v.getMeasuredHeight()); row.setLayoutParams(lp); } else { lp.height = v.getMeasuredHeight(); } } // Pre-measure so we can scale later. holder.measure(); LayoutParams lp = row.getLayoutParams(); if (lp == null) { lp = new LayoutParams(LayoutParams.MATCH_PARENT, holder.measuredRowHeight); row.setLayoutParams(lp); } else { lp.height = holder.measuredRowHeight; } row.setTag(holder); holder[mColumnCount] = row; return holder; } void bindViewHolder(int rowPosition, View[] holder) { void bindViewHolder(int rowPosition, RowViewHolder holder) { final int start = getFirstRowPosition(rowPosition); final int startType = mChooserListAdapter.getPositionTargetType(start); Loading @@ -994,34 +1147,26 @@ public class ChooserActivity extends ResolverActivity { end--; } final ViewGroup row = (ViewGroup) holder[mColumnCount]; if (startType == ChooserListAdapter.TARGET_SERVICE) { row.setBackgroundColor(getColor(R.color.chooser_service_row_background_color)); holder.row.setBackgroundColor( getColor(R.color.chooser_service_row_background_color)); } else { row.setBackground(null); holder.row.setBackgroundColor(Color.TRANSPARENT); } final int oldHeight = holder.row.getLayoutParams().height; holder.row.getLayoutParams().height = Math.max(1, (int) (holder.measuredRowHeight * getRowScale(rowPosition))); if (holder.row.getLayoutParams().height != oldHeight) { holder.row.requestLayout(); } for (int i = 0; i < mColumnCount; i++) { final View v = holder[i]; final View v = holder.cells[i]; if (start + i <= end) { v.setVisibility(View.VISIBLE); final int itemIndex = start + i; mChooserListAdapter.bindView(itemIndex, v); v.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { startSelected(itemIndex, false, true); } }); v.setOnLongClickListener(new OnLongClickListener() { @Override public boolean onLongClick(View v) { showAppDetails( mChooserListAdapter.resolveInfoForPosition(itemIndex, true)); return true; } }); holder.itemIndex = start + i; mChooserListAdapter.bindView(holder.itemIndex, v); } else { v.setVisibility(View.GONE); } Loading @@ -1048,6 +1193,24 @@ public class ChooserActivity extends ResolverActivity { } } static class RowViewHolder { final View[] cells; final ViewGroup row; int measuredRowHeight; int itemIndex; public RowViewHolder(ViewGroup row, int cellCount) { this.row = row; this.cells = new View[cellCount]; } public void measure() { final int spec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); row.measure(spec, spec); measuredRowHeight = row.getMeasuredHeight(); } } static class ChooserTargetServiceConnection implements ServiceConnection { private final DisplayResolveInfo mOriginalTarget; private ComponentName mConnectedComponent; Loading Loading @@ -1199,4 +1362,44 @@ public class ChooserActivity extends ResolverActivity { mSelectedTarget = null; } } class OffsetDataSetObserver extends DataSetObserver { private final AbsListView mListView; private int mCachedViewType = -1; private View mCachedView; public OffsetDataSetObserver(AbsListView listView) { mListView = listView; } @Override public void onChanged() { if (mResolverDrawerLayout == null) { return; } final int chooserTargetRows = mChooserRowAdapter.getServiceTargetRowCount(); int offset = 0; for (int i = 0; i < chooserTargetRows; i++) { final int pos = mChooserRowAdapter.getCallerTargetRowCount() + i; final int vt = mChooserRowAdapter.getItemViewType(pos); if (vt != mCachedViewType) { mCachedView = null; } final View v = mChooserRowAdapter.getView(pos, mCachedView, mListView); int height = ((RowViewHolder) (v.getTag())).measuredRowHeight; offset += (int) (height * mChooserRowAdapter.getRowScale(pos) * chooserTargetRows); if (vt >= 0) { mCachedViewType = vt; mCachedView = v; } else { mCachedViewType = -1; } } mResolverDrawerLayout.setCollapsibleHeightReserved(offset); } } }
core/java/com/android/internal/app/ResolverActivity.java +7 −1 Original line number Diff line number Diff line Loading @@ -103,6 +103,8 @@ public class ResolverActivity extends Activity { private ResolverComparator mResolverComparator; private PickTargetOptionRequest mPickOptionRequest; protected ResolverDrawerLayout mResolverDrawerLayout; private boolean mRegistered; private final PackageMonitor mPackageMonitor = new PackageMonitor() { @Override public void onSomePackagesChanged() { Loading Loading @@ -253,6 +255,7 @@ public class ResolverActivity extends Activity { if (isVoiceInteraction()) { rdl.setCollapsed(false); } mResolverDrawerLayout = rdl; } if (title == null) { Loading Loading @@ -1570,7 +1573,10 @@ public class ResolverActivity extends Activity { private void onBindView(View view, TargetInfo info) { final ViewHolder holder = (ViewHolder) view.getTag(); final CharSequence label = info.getDisplayLabel(); if (!TextUtils.equals(holder.text.getText(), label)) { holder.text.setText(info.getDisplayLabel()); } if (showsExtendedInfo(info)) { holder.text2.setVisibility(View.VISIBLE); holder.text2.setText(info.getExtendedInfo()); Loading
core/java/com/android/internal/widget/ResolverDrawerLayout.java +90 −21 Original line number Diff line number Diff line Loading @@ -69,6 +69,12 @@ public class ResolverDrawerLayout extends ViewGroup { private int mCollapsibleHeight; private int mUncollapsibleHeight; /** * The height in pixels of reserved space added to the top of the collapsed UI; * e.g. chooser targets */ private int mCollapsibleHeightReserved; private int mTopOffset; private boolean mIsDragging; Loading Loading @@ -153,12 +159,62 @@ public class ResolverDrawerLayout extends ViewGroup { } } public void setCollapsibleHeightReserved(int heightPixels) { final int oldReserved = mCollapsibleHeightReserved; mCollapsibleHeightReserved = heightPixels; final int dReserved = mCollapsibleHeightReserved - oldReserved; if (dReserved != 0 && mIsDragging) { mLastTouchY -= dReserved; } final int oldCollapsibleHeight = mCollapsibleHeight; mCollapsibleHeight = Math.max(mCollapsibleHeight, getMaxCollapsedHeight()); if (updateCollapseOffset(oldCollapsibleHeight, !isDragging())) { return; } invalidate(); } private boolean isMoving() { return mIsDragging || !mScroller.isFinished(); } private boolean isDragging() { return mIsDragging || getNestedScrollAxes() == SCROLL_AXIS_VERTICAL; } private boolean updateCollapseOffset(int oldCollapsibleHeight, boolean remainClosed) { if (oldCollapsibleHeight == mCollapsibleHeight) { return false; } if (isLaidOut()) { final boolean isCollapsedOld = mCollapseOffset != 0; if (remainClosed && (oldCollapsibleHeight < mCollapsibleHeight && mCollapseOffset == oldCollapsibleHeight)) { // Stay closed even at the new height. mCollapseOffset = mCollapsibleHeight; } else { mCollapseOffset = Math.min(mCollapseOffset, mCollapsibleHeight); } final boolean isCollapsedNew = mCollapseOffset != 0; if (isCollapsedOld != isCollapsedNew) { notifyViewAccessibilityStateChangedIfNeeded( AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED); } } else { // Start out collapsed at first unless we restored state for otherwise mCollapseOffset = mOpenOnLayout ? 0 : mCollapsibleHeight; } return true; } private int getMaxCollapsedHeight() { return isSmallCollapsed() ? mMaxCollapsedHeightSmall : mMaxCollapsedHeight; return (isSmallCollapsed() ? mMaxCollapsedHeightSmall : mMaxCollapsedHeight) + mCollapsibleHeightReserved; } public void setOnDismissedListener(OnDismissedListener listener) { Loading Loading @@ -676,7 +732,7 @@ public class ResolverDrawerLayout extends ViewGroup { final LayoutParams lp = (LayoutParams) child.getLayoutParams(); if (lp.alwaysShow && child.getVisibility() != GONE) { measureChildWithMargins(child, widthSpec, widthPadding, heightSpec, heightUsed); heightUsed += lp.topMargin + child.getMeasuredHeight() + lp.bottomMargin; heightUsed += getHeightUsed(child); } } Loading @@ -688,7 +744,7 @@ public class ResolverDrawerLayout extends ViewGroup { final LayoutParams lp = (LayoutParams) child.getLayoutParams(); if (!lp.alwaysShow && child.getVisibility() != GONE) { measureChildWithMargins(child, widthSpec, widthPadding, heightSpec, heightUsed); heightUsed += lp.topMargin + child.getMeasuredHeight() + lp.bottomMargin; heightUsed += getHeightUsed(child); } } Loading @@ -697,28 +753,41 @@ public class ResolverDrawerLayout extends ViewGroup { heightUsed - alwaysShowHeight - getMaxCollapsedHeight()); mUncollapsibleHeight = heightUsed - mCollapsibleHeight; if (isLaidOut()) { final boolean isCollapsedOld = mCollapseOffset != 0; if (oldCollapsibleHeight < mCollapsibleHeight && mCollapseOffset == oldCollapsibleHeight) { // Stay closed even at the new height. mCollapseOffset = mCollapsibleHeight; } else { mCollapseOffset = Math.min(mCollapseOffset, mCollapsibleHeight); updateCollapseOffset(oldCollapsibleHeight, !isDragging()); mTopOffset = Math.max(0, heightSize - heightUsed) + (int) mCollapseOffset; setMeasuredDimension(sourceWidth, heightSize); } final boolean isCollapsedNew = mCollapseOffset != 0; if (isCollapsedOld != isCollapsedNew) { notifyViewAccessibilityStateChangedIfNeeded( AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED); private int getHeightUsed(View child) { // This method exists because we're taking a fast path at measuring ListViews that // lets us get away with not doing the more expensive wrap_content measurement which // imposes double child view measurement costs. If we're looking at a ListView, we can // check against the lowest child view plus padding and margin instead of the actual // measured height of the ListView. This lets the ListView hang off the edge when // all of the content would fit on-screen. int heightUsed = child.getMeasuredHeight(); if (child instanceof AbsListView) { final AbsListView lv = (AbsListView) child; final int lvPaddingBottom = lv.getPaddingBottom(); int lowest = 0; for (int i = 0, N = lv.getChildCount(); i < N; i++) { final int bottom = lv.getChildAt(i).getBottom() + lvPaddingBottom; if (bottom > lowest) { lowest = bottom; } } else { // Start out collapsed at first unless we restored state for otherwise mCollapseOffset = mOpenOnLayout ? 0 : mCollapsibleHeight; } mTopOffset = Math.max(0, heightSize - heightUsed) + (int) mCollapseOffset; if (lowest < heightUsed) { heightUsed = lowest; } } setMeasuredDimension(sourceWidth, heightSize); final LayoutParams lp = (LayoutParams) child.getLayoutParams(); return lp.topMargin + heightUsed + lp.bottomMargin; } @Override Loading
core/res/res/layout/chooser_grid.xml +1 −1 Original line number Diff line number Diff line Loading @@ -85,7 +85,7 @@ <ListView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_height="match_parent" android:id="@+id/resolver_list" android:clipToPadding="false" android:scrollbarStyle="outsideOverlay" Loading
core/res/res/layout/resolve_grid_item.xml +2 −1 Original line number Diff line number Diff line Loading @@ -70,6 +70,7 @@ android:minLines="2" android:maxLines="2" android:gravity="top|center_horizontal" android:ellipsize="marquee" /> android:ellipsize="marquee" android:visibility="gone" /> </LinearLayout>