diff options
22 files changed, 1177 insertions, 478 deletions
diff --git a/api/current.txt b/api/current.txt index 5053859bc69c..cf0eda375e7e 100644 --- a/api/current.txt +++ b/api/current.txt @@ -596,10 +596,10 @@ package android { field public static final int layout_centerInParent = 16843151; // 0x101018f field public static final int layout_centerVertical = 16843153; // 0x1010191 field public static final int layout_column = 16843084; // 0x101014c - field public static final int layout_columnSpan = 16843646; // 0x101037e - field public static final int layout_columnWeight = 16843647; // 0x101037f + field public static final int layout_columnSpan = 16843645; // 0x101037d field public static final int layout_gravity = 16842931; // 0x10100b3 field public static final int layout_height = 16842997; // 0x10100f5 + field public static final int layout_heightSpec = 16843647; // 0x101037f field public static final int layout_margin = 16842998; // 0x10100f6 field public static final int layout_marginBottom = 16843002; // 0x10100fa field public static final int layout_marginEnd = 16843675; // 0x101039b @@ -609,13 +609,13 @@ package android { field public static final int layout_marginTop = 16843000; // 0x10100f8 field public static final int layout_row = 16843643; // 0x101037b field public static final int layout_rowSpan = 16843644; // 0x101037c - field public static final int layout_rowWeight = 16843645; // 0x101037d field public static final int layout_scale = 16843155; // 0x1010193 field public static final int layout_span = 16843085; // 0x101014d field public static final int layout_toLeftOf = 16843138; // 0x1010182 field public static final int layout_toRightOf = 16843139; // 0x1010183 field public static final int layout_weight = 16843137; // 0x1010181 field public static final int layout_width = 16842996; // 0x10100f4 + field public static final int layout_widthSpec = 16843646; // 0x101037e field public static final int layout_x = 16843135; // 0x101017f field public static final int layout_y = 16843136; // 0x1010180 field public static final int left = 16843181; // 0x10101ad @@ -24892,9 +24892,9 @@ package android.widget { } public class GridLayout extends android.view.ViewGroup { - ctor public GridLayout(android.content.Context); ctor public GridLayout(android.content.Context, android.util.AttributeSet, int); ctor public GridLayout(android.content.Context, android.util.AttributeSet); + ctor public GridLayout(android.content.Context); method public int getAlignmentMode(); method public int getColumnCount(); method public int getOrientation(); @@ -24914,8 +24914,11 @@ package android.widget { field public static final int ALIGN_MARGINS = 1; // 0x1 field public static final android.widget.GridLayout.Alignment BASELINE; field public static final android.widget.GridLayout.Alignment BOTTOM; + field public static final android.widget.GridLayout.Spec CAN_SHRINK; + field public static final android.widget.GridLayout.Spec CAN_STRETCH; field public static final android.widget.GridLayout.Alignment CENTER; field public static final android.widget.GridLayout.Alignment FILL; + field public static final android.widget.GridLayout.Spec FIXED; field public static final int HORIZONTAL = 0; // 0x0 field public static final android.widget.GridLayout.Alignment LEFT; field public static final android.widget.GridLayout.Alignment RIGHT; @@ -24942,9 +24945,13 @@ package android.widget { ctor public GridLayout.LayoutParams(android.content.Context, android.util.AttributeSet); method public void setGravity(int); field public android.widget.GridLayout.Group columnGroup; - field public float columnWeight; + field public android.widget.GridLayout.Spec heightSpec; field public android.widget.GridLayout.Group rowGroup; - field public float rowWeight; + field public android.widget.GridLayout.Spec widthSpec; + } + + public static abstract class GridLayout.Spec { + ctor public GridLayout.Spec(); } public class GridView extends android.widget.AbsListView { diff --git a/core/java/android/widget/GridLayout.java b/core/java/android/widget/GridLayout.java index 15702244596f..7c0470e17434 100644 --- a/core/java/android/widget/GridLayout.java +++ b/core/java/android/widget/GridLayout.java @@ -24,6 +24,7 @@ import android.graphics.Paint; import android.graphics.Rect; import android.util.AttributeSet; import android.util.Log; +import android.util.Pair; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; @@ -33,7 +34,6 @@ import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -67,7 +67,7 @@ import static java.lang.Math.min; * * <h4>Default Cell Assignment</h4> * - * If no child specifies the row and column indices of the cell it + * If a child does not specify the row and column indices of the cell it * wishes to occupy, GridLayout assigns cell locations automatically using its: * {@link GridLayout#setOrientation(int) orientation}, * {@link GridLayout#setRowCount(int) rowCount} and @@ -94,8 +94,8 @@ import static java.lang.Math.min; * * Like {@link LinearLayout}, a child's ability to stretch is controlled * using <em>weights</em>, which are specified using the - * {@link GridLayout.LayoutParams#rowWeight rowWeight} and - * {@link GridLayout.LayoutParams#columnWeight columnWeight} layout parameters. + * {@link GridLayout.LayoutParams#widthSpec widthSpec} and + * {@link GridLayout.LayoutParams#heightSpec heightSpec} layout parameters. * <p> * <p> * See {@link GridLayout.LayoutParams} for a full description of the @@ -171,9 +171,7 @@ public class GridLayout extends ViewGroup { private static final String TAG = GridLayout.class.getName(); private static final boolean DEBUG = false; private static final double GOLDEN_RATIO = (1 + Math.sqrt(5)) / 2; - private static final int MIN = 0; private static final int PRF = 1; - private static final int MAX = 2; // Defaults @@ -184,6 +182,7 @@ public class GridLayout extends ViewGroup { private static final int DEFAULT_ALIGNMENT_MODE = ALIGN_MARGINS; // todo remove this private static final int DEFAULT_CONTAINER_MARGIN = 20; + private static final int MAX_SIZE = 100000; // TypedArray indices @@ -205,36 +204,16 @@ public class GridLayout extends ViewGroup { private int mAlignmentMode = DEFAULT_ALIGNMENT_MODE; private int mDefaultGravity = Gravity.NO_GRAVITY; - /* package */ boolean accommodateBothMinAndMax = false; - // Constructors /** * {@inheritDoc} */ - public GridLayout(Context context) { - this(context, null, 0); - } - - /** - * {@inheritDoc} - */ public GridLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); if (DEBUG) { setWillNotDraw(false); } - processAttributes(context, attrs); - } - - /** - * {@inheritDoc} - */ - public GridLayout(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - private void processAttributes(Context context, AttributeSet attrs) { TypedArray a = context.obtainStyledAttributes(attrs, styleable.GridLayout); try { setRowCount(a.getInt(ROW_COUNT, DEFAULT_COUNT)); @@ -249,6 +228,20 @@ public class GridLayout extends ViewGroup { } } + /** + * {@inheritDoc} + */ + public GridLayout(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + /** + * {@inheritDoc} + */ + public GridLayout(Context context) { + this(context, null); + } + // Implementation /** @@ -527,11 +520,10 @@ public class GridLayout extends ViewGroup { return result; } - private static int sum(float[] a) { - int result = 0; - for (int i = 0, length = a.length; i < length; i++) { - result += a[i]; - } + private static <T> T[] append(T[] a, T[] b) { + T[] result = (T[]) Array.newInstance(a.getClass().getComponentType(), a.length + b.length); + System.arraycopy(a, 0, result, 0, a.length); + System.arraycopy(b, 0, result, a.length, b.length); return result; } @@ -603,13 +595,13 @@ public class GridLayout extends ViewGroup { if (isGone(c)) continue; LayoutParams lp = getLayoutParams1(c); - Group colGroup = lp.columnGroup; - Interval cols = colGroup.span; - int colSpan = cols.size(); + final Group colGroup = lp.columnGroup; + final Interval cols = colGroup.span; + final int colSpan = cols.size(); - Group rowGroup = lp.rowGroup; - Interval rows = rowGroup.span; - int rowSpan = rows.size(); + final Group rowGroup = lp.rowGroup; + final Interval rows = rowGroup.span; + final int rowSpan = rows.size(); if (horizontal) { row = valueIfDefined2(rows.min, row); @@ -700,8 +692,8 @@ public class GridLayout extends ViewGroup { } private void drawRectangle(Canvas graphics, int x1, int y1, int x2, int y2, Paint paint) { - // x2 = x2 - 1; - // y2 = y2 - 1; + x2 = x2 - 1; + y2 = y2 - 1; graphics.drawLine(x1, y1, x1, y2, paint); graphics.drawLine(x1, y1, x2, y1, paint); graphics.drawLine(x1, y2, x2, y2, paint); @@ -734,9 +726,9 @@ public class GridLayout extends ViewGroup { drawLine(canvas, 0, y, width - 1, y, paint); } } + // Draw bounds paint.setColor(Color.BLUE); - for (int i = 0; i < getChildCount(); i++) { View c = getChildAt(i); drawRectangle(canvas, @@ -748,7 +740,6 @@ public class GridLayout extends ViewGroup { // Draw margins paint.setColor(Color.YELLOW); - for (int i = 0; i < getChildCount(); i++) { View c = getChildAt(i); drawRectangle(canvas, @@ -819,11 +810,11 @@ public class GridLayout extends ViewGroup { protected void onMeasure(int widthSpec, int heightSpec) { measureChildrenWithMargins(widthSpec, heightSpec); - int computedWidth = getPaddingLeft() + mHorizontalAxis.getMin() + getPaddingRight(); - int computedHeight = getPaddingTop() + mVerticalAxis.getMin() + getPaddingBottom(); + int width = getPaddingLeft() + mHorizontalAxis.getMeasure(widthSpec) + getPaddingRight(); + int height = getPaddingTop() + mVerticalAxis.getMeasure(heightSpec) + getPaddingBottom(); - int measuredWidth = Math.max(computedWidth, getSuggestedMinimumWidth()); - int measuredHeight = Math.max(computedHeight, getSuggestedMinimumHeight()); + int measuredWidth = Math.max(width, getSuggestedMinimumWidth()); + int measuredHeight = Math.max(height, getSuggestedMinimumHeight()); setMeasuredDimension( resolveSizeAndState(measuredWidth, widthSpec, 0), @@ -834,12 +825,12 @@ public class GridLayout extends ViewGroup { return (alignment == UNDEFINED) ? 0 : alignment; } - private int getMeasurement(View c, boolean horizontal, int measurementType) { + private int getMeasurement(View c, boolean horizontal) { return horizontal ? c.getMeasuredWidth() : c.getMeasuredHeight(); } - private int getMeasurementIncludingMargin(View c, boolean horizontal, int measurementType) { - int result = getMeasurement(c, horizontal, measurementType); + private int getMeasurementIncludingMargin(View c, boolean horizontal) { + int result = getMeasurement(c, horizontal); if (mAlignmentMode == ALIGN_MARGINS) { return result + getTotalMargin(c, horizontal); } @@ -889,17 +880,17 @@ public class GridLayout extends ViewGroup { Interval colSpan = columnGroup.span; Interval rowSpan = rowGroup.span; - int x1 = mHorizontalAxis.getLocationIncludingMargin(c, true, colSpan.min); - int y1 = mVerticalAxis.getLocationIncludingMargin(c, true, rowSpan.min); + int x1 = mHorizontalAxis.getLocationIncludingMargin(true, colSpan.min); + int y1 = mVerticalAxis.getLocationIncludingMargin(true, rowSpan.min); - int x2 = mHorizontalAxis.getLocationIncludingMargin(c, false, colSpan.max); - int y2 = mVerticalAxis.getLocationIncludingMargin(c, false, rowSpan.max); + int x2 = mHorizontalAxis.getLocationIncludingMargin(false, colSpan.max); + int y2 = mVerticalAxis.getLocationIncludingMargin(false, rowSpan.max); int cellWidth = x2 - x1; int cellHeight = y2 - y1; - int pWidth = getMeasurement(c, true, PRF); - int pHeight = getMeasurement(c, false, PRF); + int pWidth = getMeasurement(c, true); + int pHeight = getMeasurement(c, false); Alignment hAlign = columnGroup.alignment; Alignment vAlign = rowGroup.alignment; @@ -910,9 +901,8 @@ public class GridLayout extends ViewGroup { Bounds rowBounds = mVerticalAxis.getGroupBounds().getValue(i); // Gravity offsets: the location of the alignment group relative to its cell group. - int type = PRF; - int c2ax = protect(hAlign.getAlignmentValue(null, cellWidth - colBounds.size(), type)); - int c2ay = protect(vAlign.getAlignmentValue(null, cellHeight - rowBounds.size(), type)); + int c2ax = protect(hAlign.getAlignmentValue(null, cellWidth - colBounds.size(true))); + int c2ay = protect(vAlign.getAlignmentValue(null, cellHeight - rowBounds.size(true))); if (mAlignmentMode == ALIGN_MARGINS) { int leftMargin = getMargin(c, true, true); @@ -925,8 +915,8 @@ public class GridLayout extends ViewGroup { int mHeight = topMargin + pHeight + bottomMargin; // Alignment offsets: the location of the view relative to its alignment group. - int a2vx = colBounds.getOffset(c, hAlign, type, mWidth); - int a2vy = rowBounds.getOffset(c, vAlign, type, mHeight); + int a2vx = colBounds.getOffset(c, hAlign, mWidth); + int a2vy = rowBounds.getOffset(c, vAlign, mHeight); dx = c2ax + a2vx + leftMargin; dy = c2ay + a2vy + topMargin; @@ -935,13 +925,14 @@ public class GridLayout extends ViewGroup { cellHeight -= topMargin + bottomMargin; } else { // Alignment offsets: the location of the view relative to its alignment group. - int a2vx = colBounds.getOffset(c, hAlign, type, pWidth); - int a2vy = rowBounds.getOffset(c, vAlign, type, pHeight); + int a2vx = colBounds.getOffset(c, hAlign, pWidth); + int a2vy = rowBounds.getOffset(c, vAlign, pHeight); dx = c2ax + a2vx; dy = c2ay + a2vy; } + int type = PRF; int width = hAlign.getSizeInCell(c, pWidth, cellWidth, type); int height = vAlign.getSizeInCell(c, pHeight, cellHeight, type); @@ -962,7 +953,7 @@ public class GridLayout extends ViewGroup { private class Axis { private static final int MIN_VALUE = -1000000; - private static final int UNVISITED = 0; + private static final int NEW = 0; private static final int PENDING = 1; private static final int COMPLETE = 2; @@ -975,8 +966,11 @@ public class GridLayout extends ViewGroup { PackedMap<Group, Bounds> groupBounds; public boolean groupBoundsValid = false; - PackedMap<Interval, MutableInt> spanSizes; - public boolean spanSizesValid = false; + PackedMap<Interval, MutableInt> forwardLinks; + public boolean forwardLinksValid = false; + + PackedMap<Interval, MutableInt> backwardLinks; + public boolean backwardLinksValid = false; public int[] leadingMargins; public boolean leadingMarginsValid = false; @@ -987,14 +981,14 @@ public class GridLayout extends ViewGroup { public Arc[] arcs; public boolean arcsValid = false; - public int[] minima; - public boolean minimaValid = false; - - public float[] weights; public int[] locations; + public boolean locationsValid = false; private boolean mOrderPreserved = DEFAULT_ORDER_PRESERVED; + private MutableInt parentMin = new MutableInt(0); + private MutableInt parentMax = new MutableInt(-MAX_SIZE); + private Axis(boolean horizontal) { this.horizontal = horizontal; } @@ -1036,22 +1030,19 @@ public class GridLayout extends ViewGroup { } private PackedMap<Group, Bounds> createGroupBounds() { - int N = getChildCount(); - Group[] groups = new Group[N]; - Arrays.fill(groups, Group.GONE); - Bounds[] bounds = new Bounds[N]; - Arrays.fill(bounds, Bounds.GONE); - for (int i = 0; i < N; i++) { + Assoc<Group, Bounds> assoc = Assoc.of(Group.class, Bounds.class); + for (int i = 0, N = getChildCount(); i < N; i++) { View c = getChildAt(i); - if (isGone(c)) continue; - LayoutParams lp = getLayoutParams(c); - Group group = horizontal ? lp.columnGroup : lp.rowGroup; - - groups[i] = group; - bounds[i] = group.alignment.getBounds(); + if (isGone(c)) { + assoc.put(Group.GONE, Bounds.GONE); + } else { + LayoutParams lp = getLayoutParams(c); + Group group = horizontal ? lp.columnGroup : lp.rowGroup; + Bounds bounds = group.alignment.getBounds(); + assoc.put(group, bounds); + } } - - return new PackedMap<Group, Bounds>(groups, bounds); + return assoc.pack(); } private void computeGroupBounds() { @@ -1064,13 +1055,7 @@ public class GridLayout extends ViewGroup { if (isGone(c)) continue; LayoutParams lp = getLayoutParams(c); Group g = horizontal ? lp.columnGroup : lp.rowGroup; - - Bounds bounds = groupBounds.getValue(i); - - int size = getMeasurementIncludingMargin(c, horizontal, PRF); - // todo test this works correctly when the returned value is UNDEFINED - int before = g.alignment.getAlignmentValue(c, size, PRF); - bounds.include(before, size - before); + groupBounds.getValue(i).include(c, g, GridLayout.this, this, lp); } } @@ -1086,80 +1071,91 @@ public class GridLayout extends ViewGroup { } // Add values computed by alignment - taking the max of all alignments in each span - private PackedMap<Interval, MutableInt> createSpanSizes() { - PackedMap<Group, Bounds> groupBounds = getGroupBounds(); - int N = groupBounds.keys.length; - Interval[] spans = new Interval[N]; - MutableInt[] values = new MutableInt[N]; - for (int i = 0; i < N; i++) { - Interval key = groupBounds.keys[i].span; - - spans[i] = key; - values[i] = new MutableInt(); + private PackedMap<Interval, MutableInt> createLinks(boolean min) { + Assoc<Interval, MutableInt> result = Assoc.of(Interval.class, MutableInt.class); + Group[] keys = getGroupBounds().keys; + for (int i = 0, N = keys.length; i < N; i++) { + Interval span = min ? keys[i].span : keys[i].span.inverse(); + result.put(span, new MutableInt()); } - return new PackedMap<Interval, MutableInt>(spans, values); + return result.pack(); } - private void computeSpanSizes() { - MutableInt[] spans = spanSizes.values; + private void computeLinks(PackedMap<Interval, MutableInt> links, boolean min) { + MutableInt[] spans = links.values; for (int i = 0; i < spans.length; i++) { spans[i].reset(); } - Bounds[] bounds = getGroupBounds().values; // use getter to trigger a re-evaluation + // use getter to trigger a re-evaluation + Bounds[] bounds = getGroupBounds().values; for (int i = 0; i < bounds.length; i++) { - int value = bounds[i].size(); - - MutableInt valueHolder = spanSizes.getValue(i); + int size = bounds[i].size(min); + int value = min ? size : -size; + MutableInt valueHolder = links.getValue(i); valueHolder.value = max(valueHolder.value, value); } } - private PackedMap<Interval, MutableInt> getSpanSizes() { - if (spanSizes == null) { - spanSizes = createSpanSizes(); + private PackedMap<Interval, MutableInt> getForwardLinks() { + if (forwardLinks == null) { + forwardLinks = createLinks(true); } - if (!spanSizesValid) { - computeSpanSizes(); - spanSizesValid = true; + if (!forwardLinksValid) { + computeLinks(forwardLinks, true); + forwardLinksValid = true; } - return spanSizes; + return forwardLinks; } - private void include(List<Arc> arcs, Interval key, MutableInt size) { - // this bit below should really be computed outside here - - // its just to stop default (col>0) constraints obliterating valid entries - for (Arc arc : arcs) { - Interval span = arc.span; - if (span.equals(key)) { - return; - } + private PackedMap<Interval, MutableInt> getBackwardLinks() { + if (backwardLinks == null) { + backwardLinks = createLinks(false); } - arcs.add(new Arc(key, size)); + if (!backwardLinksValid) { + computeLinks(backwardLinks, false); + backwardLinksValid = true; + } + return backwardLinks; } - private void include2(List<Arc> arcs, Interval span, MutableInt min, MutableInt max, - boolean both) { - include(arcs, span, min); - if (both) { - // todo -// include(arcs, span.inverse(), max.neg()); + private void include(List<Arc> arcs, Interval key, MutableInt size, + boolean ignoreIfAlreadyPresent) { + /* + Remove self referential links. + These appear: + . as parental constraints when GridLayout has no children + . when components have been marked as GONE + */ + if (key.size() == 0) { + return; } + // this bit below should really be computed outside here - + // its just to stop default (row/col > 0) constraints obliterating valid entries + if (ignoreIfAlreadyPresent) { + for (Arc arc : arcs) { + Interval span = arc.span; + if (span.equals(key)) { + return; + } + } + } + arcs.add(new Arc(key, size)); } - private void include2(List<Arc> arcs, Interval span, int min, int max, boolean both) { - include2(arcs, span, new MutableInt(min), new MutableInt(max), both); + private void include(List<Arc> arcs, Interval key, MutableInt size) { + include(arcs, key, size, true); } // Group arcs by their first vertex, returning an array of arrays. // This is linear in the number of arcs. private Arc[][] groupArcsByFirstVertex(Arc[] arcs) { - int N = getCount() + 1;// the number of vertices + int N = getCount() + 1; // the number of vertices Arc[][] result = new Arc[N][]; int[] sizes = new int[N]; for (Arc arc : arcs) { sizes[arc.span.min]++; - } + } for (int i = 0; i < sizes.length; i++) { result[i] = new Arc[sizes[i]]; } @@ -1173,38 +1169,46 @@ public class GridLayout extends ViewGroup { return result; } - private Arc[] topologicalSort(final Arc[] arcs, int start) { - // todo ensure the <start> vertex is added in edge cases - final List<Arc> result = new ArrayList<Arc>(); - new Object() { - Arc[][] arcsByFirstVertex = groupArcsByFirstVertex(arcs); + private Arc[] topologicalSort(final Arc[] arcs) { + return new Object() { + Arc[] result = new Arc[arcs.length]; + int cursor = result.length - 1; + Arc[][] arcsByVertex = groupArcsByFirstVertex(arcs); int[] visited = new int[getCount() + 1]; - boolean completesCycle(int loc) { - int state = visited[loc]; - if (state == UNVISITED) { - visited[loc] = PENDING; - for (Arc arc : arcsByFirstVertex[loc]) { - Interval span = arc.span; - // the recursive call - if (completesCycle(span.max)) { - // which arcs get set here is dependent on the order - // in which we explore nodes - arc.completesCycle = true; + void walk(int loc) { + switch (visited[loc]) { + case NEW: { + visited[loc] = PENDING; + for (Arc arc : arcsByVertex[loc]) { + walk(arc.span.max); + result[cursor--] = arc; } - result.add(arc); + visited[loc] = COMPLETE; + break; + } + case PENDING: { + assert false; + break; + } + case COMPLETE: { + break; } - visited[loc] = COMPLETE; - } else if (state == PENDING) { - return true; - } else if (state == COMPLETE) { } - return false; } - }.completesCycle(start); - Collections.reverse(result); - assert arcs.length == result.size(); - return result.toArray(new Arc[result.size()]); + + Arc[] sort() { + for (int loc = 0, N = arcsByVertex.length; loc < N; loc++) { + walk(loc); + } + assert cursor == -1; + return result; + } + }.sort(); + } + + private Arc[] topologicalSort(List<Arc> arcs) { + return topologicalSort(arcs.toArray(new Arc[arcs.size()])); } private boolean[] findUsed(Collection<Arc> arcs) { @@ -1254,43 +1258,64 @@ public class GridLayout extends ViewGroup { return result; } + private void addComponentSizes(List<Arc> result, PackedMap<Interval, MutableInt> links) { + for (int i = 0; i < links.keys.length; i++) { + Interval key = links.keys[i]; + include(result, key, links.values[i], false); + } + } + private Arc[] createArcs() { - List<Arc> result = new ArrayList<Arc>(); + List<Arc> mins = new ArrayList<Arc>(); + List<Arc> maxs = new ArrayList<Arc>(); - // Add all the preferred elements that were not defined by the user. - PackedMap<Interval, MutableInt> spanSizes = getSpanSizes(); - for (int i = 0; i < spanSizes.keys.length; i++) { - Interval key = spanSizes.keys[i]; - if (key == Interval.GONE) continue; - MutableInt value = spanSizes.values[i]; - // todo remove value duplicate - include2(result, key, value, value, accommodateBothMinAndMax); - } + // Add the minimum values from the components. + addComponentSizes(mins, getForwardLinks()); + // Add the maximum values from the components. + addComponentSizes(maxs, getBackwardLinks()); // Find redundant rows/cols and glue them together with 0-length arcs to link the tree - boolean[] used = findUsed(result); + boolean[] used = findUsed(mins); for (int i = 0; i < getCount(); i++) { if (!used[i]) { Interval span = new Interval(i, i + 1); - include(result, span, new MutableInt(0)); - include(result, span.inverse(), new MutableInt(0)); + include(mins, span, new MutableInt(0)); + include(maxs, span.inverse(), new MutableInt(0)); } } + // Add ordering constraints to prevent row/col sizes from going negative if (mOrderPreserved) { - // Add preferred gaps + // Add a constraint for every row/col for (int i = 0; i < getCount(); i++) { if (used[i]) { - include2(result, new Interval(i, i + 1), 0, 0, false); + include(mins, new Interval(i, i + 1), new MutableInt(0)); } } } else { + // Add a constraint for each row/col that separates opposing component edges for (Interval gap : getSpacers()) { - include2(result, gap, 0, 0, false); + include(mins, gap, new MutableInt(0)); } } - Arc[] arcs = result.toArray(new Arc[result.size()]); - return topologicalSort(arcs, 0); + + // Add the container constraints. Use the version of include that allows + // duplicate entries in case a child spans the entire grid. + int N = getCount(); + include(mins, new Interval(0, N), parentMin, false); + include(maxs, new Interval(N, 0), parentMax, false); + + // Sort + Arc[] sMins = topologicalSort(mins); + Arc[] sMaxs = topologicalSort(maxs); + + return append(sMins, sMaxs); + } + + private void computeArcs() { + // getting the links validates the values that are shared by the arc list + getForwardLinks(); + getBackwardLinks(); } public Arc[] getArcs() { @@ -1298,13 +1323,16 @@ public class GridLayout extends ViewGroup { arcs = createArcs(); } if (!arcsValid) { - getSpanSizes(); + computeArcs(); arcsValid = true; } return arcs; } private boolean relax(int[] locations, Arc entry) { + if (!entry.valid) { + return false; + } Interval span = entry.span; int u = span.min; int v = span.max; @@ -1351,7 +1379,8 @@ public class GridLayout extends ViewGroup { typical layout problems complete after the first iteration and the algorithm completes in O(N) steps with very low constants. */ - private int[] solve(Arc[] arcs, int[] locations) { + private void solve(Arc[] arcs, int[] locations) { + String axis = horizontal ? "horizontal" : "vertical"; int N = getCount() + 1; // The number of vertices is the number of columns/rows + 1. boolean changed = false; @@ -1359,20 +1388,44 @@ public class GridLayout extends ViewGroup { for (int i = 0; i < N; i++) { changed = false; for (int j = 0, length = arcs.length; j < length; j++) { - changed = changed | relax(locations, arcs[j]); + changed |= relax(locations, arcs[j]); } if (!changed) { if (DEBUG) { - Log.d(TAG, "Iteration " + - " completed after " + (1 + i) + " steps out of " + N); + Log.d(TAG, axis + " iteration completed in " + (1 + i) + " steps of " + N); } - break; + return; } } - if (changed) { - Log.d(TAG, "*** Algorithm failed to terminate ***"); + + Log.d(TAG, "The " + axis + " constraints contained a contradiction. Resolving... "); + Log.d(TAG, Arrays.toString(arcs)); + + boolean[] culprits = new boolean[arcs.length]; + for (int i = 0; i < N; i++) { + for (int j = 0, length = arcs.length; j < length; j++) { + culprits[j] |= relax(locations, arcs[j]); + } } - return locations; + for (int i = 0; i < culprits.length; i++) { + if (culprits[i]) { + Arc arc = arcs[i]; + // Only remove max values, min values alone cannot be inconsistent + if (arc.span.min < arc.span.max) { + continue; + } + Log.d(TAG, "Removing: " + arc); + arc.valid = false; + break; + } + } + solve1(arcs, locations); + } + + private void solve1(Arc[] arcs, int[] a) { + Arrays.fill(a, MIN_VALUE); + a[0] = 0; + solve(arcs, a); } private void computeMargins(boolean leading) { @@ -1418,11 +1471,11 @@ public class GridLayout extends ViewGroup { for (int i = 0, N = getCount(); i < N; i++) { int margins = leadingMargins[i] + trailingMargins[i + 1]; delta += margins; - minima[i + 1] += delta; + locations[i + 1] += delta; } } - private int getLocationIncludingMargin(View view, boolean leading, int index) { + private int getLocationIncludingMargin(boolean leading, int index) { int location = locations[index]; int margin; if (mAlignmentMode != ALIGN_MARGINS) { @@ -1433,53 +1486,22 @@ public class GridLayout extends ViewGroup { return leading ? (location + margin) : (location - margin); } - private void computeMinima(int[] a) { - Arrays.fill(a, MIN_VALUE); - a[0] = 0; - solve(getArcs(), a); + private void computeLocations(int[] a) { + solve1(getArcs(), a); if (mAlignmentMode != ALIGN_MARGINS) { addMargins(); } } - private int[] getMinima() { - if (minima == null) { - int N = getCount() + 1; - minima = new int[N]; - } - if (!minimaValid) { - computeMinima(minima); - minimaValid = true; - } - return minima; - } - - private void computeWeights() { - for (int i = 0, N = getChildCount(); i < N; i++) { - View c = getChildAt(i); - if (isGone(c)) continue; - LayoutParams lp = getLayoutParams(c); - Group g = horizontal ? lp.columnGroup : lp.rowGroup; - Interval span = g.span; - int penultimateIndex = span.max - 1; - weights[penultimateIndex] += horizontal ? lp.columnWeight : lp.rowWeight; - } - } - - private float[] getWeights() { - if (weights == null) { - int N = getCount(); - weights = new float[N]; - } - computeWeights(); - return weights; - } - private int[] getLocations() { if (locations == null) { int N = getCount() + 1; locations = new int[N]; } + if (!locationsValid) { + computeLocations(locations); + locationsValid = true; + } return locations; } @@ -1489,48 +1511,53 @@ public class GridLayout extends ViewGroup { return max2(locations, 0) - locations[0]; } - private int getMin() { - return size(getMinima()); + private void setParentConstraints(int min, int max) { + parentMin.value = min; + parentMax.value = -max; + locationsValid = false; } - private void layout(int targetSize) { - int[] mins = getMinima(); - - int totalDelta = max(0, targetSize - size(mins)); // confine to expansion - - float[] weights = getWeights(); - float totalWeight = sum(weights); + private int getMeasure(int min, int max) { + setParentConstraints(min, max); + return size(getLocations()); + } - if (totalWeight == 0f && weights.length > 0) { - weights[weights.length - 1] = 1; - totalWeight = 1; + private int getMeasure(int measureSpec) { + int mode = MeasureSpec.getMode(measureSpec); + int size = MeasureSpec.getSize(measureSpec); + switch (mode) { + case MeasureSpec.UNSPECIFIED: { + return getMeasure(0, MAX_SIZE); + } + case MeasureSpec.EXACTLY: { + return getMeasure(size, size); + } + case MeasureSpec.AT_MOST: { + return getMeasure(0, size); + } + default: { + assert false; + return 0; + } } + } - int[] locations = getLocations(); - int cumulativeDelta = 0; - - // note |weights| = |locations| - 1 - for (int i = 0; i < weights.length; i++) { - float weight = weights[i]; - int delta = (int) (totalDelta * weight / totalWeight); - cumulativeDelta += delta; - locations[i + 1] = mins[i + 1] + cumulativeDelta; - - totalDelta -= delta; - totalWeight -= weight; - } + private void layout(int size) { + setParentConstraints(size, size); + getLocations(); } private void invalidateStructure() { countValid = false; groupBounds = null; - spanSizes = null; + forwardLinks = null; + backwardLinks = null; + leadingMargins = null; trailingMargins = null; arcs = null; - minima = null; - weights = null; + locations = null; invalidateValues(); @@ -1538,11 +1565,14 @@ public class GridLayout extends ViewGroup { private void invalidateValues() { groupBoundsValid = false; - spanSizesValid = false; - arcsValid = false; + forwardLinksValid = false; + backwardLinksValid = false; + leadingMarginsValid = false; trailingMarginsValid = false; - minimaValid = false; + arcsValid = false; + + locationsValid = false; } } @@ -1592,16 +1622,16 @@ public class GridLayout extends ViewGroup { * <li>{@link #rowGroup}{@code .alignment} = {@link #BASELINE} </li> * <li>{@link #columnGroup}{@code .span} = {@code [0, 1]} </li> * <li>{@link #columnGroup}{@code .alignment} = {@link #LEFT} </li> - * <li>{@link #rowWeight} = {@code 0f} </li> - * <li>{@link #columnWeight} = {@code 0f} </li> + * <li>{@link #widthSpec} = {@link #FIXED} </li> + * <li>{@link #heightSpec} = {@link #FIXED} </li> * </ul> * * @attr ref android.R.styleable#GridLayout_Layout_layout_row * @attr ref android.R.styleable#GridLayout_Layout_layout_rowSpan - * @attr ref android.R.styleable#GridLayout_Layout_layout_rowWeight + * @attr ref android.R.styleable#GridLayout_Layout_layout_heightSpec * @attr ref android.R.styleable#GridLayout_Layout_layout_column * @attr ref android.R.styleable#GridLayout_Layout_layout_columnSpan - * @attr ref android.R.styleable#GridLayout_Layout_layout_columnWeight + * @attr ref android.R.styleable#GridLayout_Layout_layout_widthSpec * @attr ref android.R.styleable#GridLayout_Layout_layout_gravity */ public static class LayoutParams extends MarginLayoutParams { @@ -1621,14 +1651,15 @@ public class GridLayout extends ViewGroup { new Group(DEFAULT_SPAN, DEFAULT_COLUMN_ALIGNMENT); private static final Group DEFAULT_ROW_GROUP = new Group(DEFAULT_SPAN, DEFAULT_ROW_ALIGNMENT); - private static final int DEFAULT_WEIGHT_0 = 0; - private static final int DEFAULT_WEIGHT_1 = 1; + private static final Spec DEFAULT_SPEC = FIXED; + private static final int DEFAULT_SPEC_INDEX = 0; // Misc private static final Rect CONTAINER_BOUNDS = new Rect(0, 0, 2, 2); private static final Alignment[] COLUMN_ALIGNMENTS = { LEFT, CENTER, RIGHT }; private static final Alignment[] ROW_ALIGNMENTS = { TOP, CENTER, BOTTOM }; + private static final Spec[] SPECS = { FIXED, CAN_SHRINK, CAN_STRETCH }; // TypedArray indices @@ -1641,10 +1672,10 @@ public class GridLayout extends ViewGroup { 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_WEIGHT = styleable.GridLayout_Layout_layout_columnWeight; + private static final int WIDTH_SPEC = styleable.GridLayout_Layout_layout_widthSpec; 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_WEIGHT = styleable.GridLayout_Layout_layout_rowWeight; + private static final int HEIGHT_SPEC = styleable.GridLayout_Layout_layout_heightSpec; private static final int GRAVITY = styleable.GridLayout_Layout_layout_gravity; // Instance variables @@ -1660,28 +1691,29 @@ public class GridLayout extends ViewGroup { */ public Group columnGroup; /** - * The proportional space that should be taken by the associated row group + * The proportional space that should be taken by the associated column group * during excess space distribution. */ - public float rowWeight; + public Spec widthSpec; /** - * The proportional space that should be taken by the associated column group + * The proportional space that should be taken by the associated row group * during excess space distribution. */ - public float columnWeight; + public Spec heightSpec; // Constructors private LayoutParams( int width, int height, int left, int top, int right, int bottom, - Group rowGroup, Group columnGroup, float rowWeight, float columnWeight) { + Group rowGroup, Group columnGroup, + Spec widthSpec, Spec heightSpec) { super(width, height); setMargins(left, top, right, bottom); this.rowGroup = rowGroup; this.columnGroup = columnGroup; - this.rowWeight = rowWeight; - this.columnWeight = columnWeight; + this.heightSpec = heightSpec; + this.widthSpec = widthSpec; } /** @@ -1695,7 +1727,7 @@ public class GridLayout extends ViewGroup { public LayoutParams(Group rowGroup, Group columnGroup) { this(DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_MARGIN, DEFAULT_MARGIN, DEFAULT_MARGIN, DEFAULT_MARGIN, - rowGroup, columnGroup, DEFAULT_WEIGHT_0, DEFAULT_WEIGHT_0); + rowGroup, columnGroup, DEFAULT_SPEC, DEFAULT_SPEC); } /** @@ -1728,8 +1760,8 @@ public class GridLayout extends ViewGroup { super(that); this.columnGroup = that.columnGroup; this.rowGroup = that.rowGroup; - this.columnWeight = that.columnWeight; - this.rowWeight = that.rowWeight; + this.widthSpec = that.widthSpec; + this.heightSpec = that.heightSpec; } // AttributeSet constructors @@ -1813,11 +1845,6 @@ public class GridLayout extends ViewGroup { !definesVertical(gravity), defaultAlignment); } - private int getDefaultWeight(int size) { - //return (size == MATCH_PARENT) ? DEFAULT_WEIGHT_1 : DEFAULT_WEIGHT_0; - return DEFAULT_WEIGHT_0; - } - private void init(Context context, AttributeSet attrs, int defaultGravity) { TypedArray a = context.obtainStyledAttributes(attrs, styleable.GridLayout_Layout); try { @@ -1827,13 +1854,13 @@ public class GridLayout extends ViewGroup { int columnSpan = a.getInt(COLUMN_SPAN, DEFAULT_SPAN_SIZE); Interval hSpan = new Interval(column, column + columnSpan); this.columnGroup = new Group(hSpan, getColumnAlignment(gravity, width)); - this.columnWeight = a.getFloat(COLUMN_WEIGHT, getDefaultWeight(width)); + this.widthSpec = SPECS[a.getInt(WIDTH_SPEC, DEFAULT_SPEC_INDEX)]; int row = a.getInt(ROW, DEFAULT_ROW); int rowSpan = a.getInt(ROW_SPAN, DEFAULT_SPAN_SIZE); Interval vSpan = new Interval(row, row + rowSpan); this.rowGroup = new Group(vSpan, getRowAlignment(gravity, height)); - this.rowWeight = a.getFloat(ROW_WEIGHT, getDefaultWeight(height)); + this.heightSpec = SPECS[a.getInt(HEIGHT_SPEC, DEFAULT_SPEC_INDEX)]; } finally { a.recycle(); } @@ -1874,7 +1901,7 @@ public class GridLayout extends ViewGroup { private static class Arc { public final Interval span; public final MutableInt value; - public boolean completesCycle; + public boolean valid = true; public Arc(Interval span, MutableInt value) { this.span = span; @@ -1883,7 +1910,7 @@ public class GridLayout extends ViewGroup { @Override public String toString() { - return span + " " + (completesCycle ? "+>" : "->") + " " + value; + return span + " " + (!valid ? "+>" : "->") + " " + value; } } @@ -1903,6 +1930,41 @@ public class GridLayout extends ViewGroup { private void reset() { value = Integer.MIN_VALUE; } + + @Override + public String toString() { + return Integer.toString(value); + } + } + + private static class Assoc<K, V> extends ArrayList<Pair<K, V>> { + private final Class<K> keyType; + private final Class<V> valueType; + + private Assoc(Class<K> keyType, Class<V> valueType) { + this.keyType = keyType; + this.valueType = valueType; + } + + private static <K, V> Assoc<K, V> of(Class<K> keyType, Class<V> valueType) { + return new Assoc<K, V>(keyType, valueType); + } + + public void put(K key, V value) { + add(Pair.create(key, value)); + } + + @SuppressWarnings(value = "unchecked") + public PackedMap<K, V> pack() { + int N = size(); + K[] keys = (K[]) Array.newInstance(keyType, N); + V[] values = (V[]) Array.newInstance(valueType, N); + for (int i = 0; i < N; i++) { + keys[i] = get(i).first; + values[i] = get(i).second; + } + return new PackedMap<K, V>(keys, values); + } } /* @@ -1989,6 +2051,7 @@ public class GridLayout extends ViewGroup { public int before; public int after; + public boolean canStretch; private Bounds() { reset(); @@ -1997,6 +2060,7 @@ public class GridLayout extends ViewGroup { protected void reset() { before = Integer.MIN_VALUE; after = Integer.MIN_VALUE; + canStretch = false; } protected void include(int before, int after) { @@ -2004,12 +2068,26 @@ public class GridLayout extends ViewGroup { this.after = max(this.after, after); } - protected int size() { + protected int size(boolean min) { + if (!min && canStretch) { + return MAX_SIZE; + } return before + after; } - protected int getOffset(View c, Alignment alignment, int type, int size) { - return before - alignment.getAlignmentValue(c, size, type); + protected int getOffset(View c, Alignment alignment, int size) { + return before - alignment.getAlignmentValue(c, size); + } + + protected void include(View c, Group g, GridLayout gridLayout, Axis axis, LayoutParams lp) { + Spec spec = axis.horizontal ? lp.widthSpec : lp.heightSpec; + if (spec == CAN_STRETCH) { + canStretch = true; + } + int size = gridLayout.getMeasurementIncludingMargin(c, axis.horizontal); + // todo test this works correctly when the returned value is UNDEFINED + int before = g.alignment.getAlignmentValue(c, size); + include(before, size - before); } @Override @@ -2032,7 +2110,7 @@ public class GridLayout extends ViewGroup { * Intervals are often written as {@code [min, max]} and represent the set of values * {@code x} such that {@code min <= x < max}. */ - /* package */ static class Interval { + static class Interval { private static final Interval GONE = new Interval(UNDEFINED, UNDEFINED); /** @@ -2129,7 +2207,7 @@ public class GridLayout extends ViewGroup { * See {@link GridLayout} for a description of the conventions used by GridLayout * for grid indices. */ - /* package */ final Interval span; + final Interval span; /** * Specifies how cells should be aligned in this group. * For row groups, this specifies the vertical alignment. @@ -2147,7 +2225,7 @@ public class GridLayout extends ViewGroup { * @param span the span * @param alignment the alignment */ - /* package */ Group(Interval span, Alignment alignment) { + Group(Interval span, Alignment alignment) { this.span = span; this.alignment = alignment; } @@ -2248,17 +2326,16 @@ public class GridLayout extends ViewGroup { * so that the locations defined by the alignment values * are the same for all of the views in a group. * <p> - */ public static abstract class Alignment { private static final Alignment GONE = new Alignment() { - public int getAlignmentValue(View view, int viewSize, int measurementType) { + public int getAlignmentValue(View view, int viewSize) { assert false; return 0; } }; - /*pp*/ Alignment() { + Alignment() { } /** @@ -2269,12 +2346,9 @@ public class GridLayout extends ViewGroup { * * @param view the view to which this alignment should be applied * @param viewSize the measured size of the view - * @param measurementType This parameter is currently unused as GridLayout only supports - * one type of measurement: {@link View#measure(int, int)}. - * * @return the alignment value */ - /*pp*/ abstract int getAlignmentValue(View view, int viewSize, int measurementType); + abstract int getAlignmentValue(View view, int viewSize); /** * Returns the size of the view specified by this alignment. @@ -2291,24 +2365,24 @@ public class GridLayout extends ViewGroup { * * @return the aligned size */ - /*pp*/ int getSizeInCell(View view, int viewSize, int cellSize, int measurementType) { + int getSizeInCell(View view, int viewSize, int cellSize, int measurementType) { return viewSize; } - /*pp*/ Bounds getBounds() { + Bounds getBounds() { return new Bounds(); } } private static final Alignment LEADING = new Alignment() { - public int getAlignmentValue(View view, int viewSize, int measurementType) { + public int getAlignmentValue(View view, int viewSize) { return 0; } }; private static final Alignment TRAILING = new Alignment() { - public int getAlignmentValue(View view, int viewSize, int measurementType) { + public int getAlignmentValue(View view, int viewSize) { return viewSize; } }; @@ -2343,7 +2417,7 @@ public class GridLayout extends ViewGroup { * LayoutParams#columnGroup columnGroups}. */ public static final Alignment CENTER = new Alignment() { - public int getAlignmentValue(View view, int viewSize, int measurementType) { + public int getAlignmentValue(View view, int viewSize) { return viewSize >> 1; } }; @@ -2356,7 +2430,7 @@ public class GridLayout extends ViewGroup { * @see View#getBaseline() */ public static final Alignment BASELINE = new Alignment() { - public int getAlignmentValue(View view, int viewSize, int measurementType) { + public int getAlignmentValue(View view, int viewSize) { if (view == null) { return UNDEFINED; } @@ -2378,7 +2452,7 @@ public class GridLayout extends ViewGroup { @Override protected void reset() { super.reset(); - size = 0; + size = Integer.MIN_VALUE; } @Override @@ -2388,13 +2462,13 @@ public class GridLayout extends ViewGroup { } @Override - protected int size() { - return max(super.size(), size); + protected int size(boolean min) { + return max(super.size(min), size); } @Override - protected int getOffset(View c, Alignment alignment, int type, int size) { - return max(0, super.getOffset(c, alignment, type, size)); + protected int getOffset(View c, Alignment alignment, int size) { + return max(0, super.getOffset(c, alignment, size)); } }; } @@ -2406,7 +2480,7 @@ public class GridLayout extends ViewGroup { * {@link LayoutParams#columnGroup columnGroups}. */ public static final Alignment FILL = new Alignment() { - public int getAlignmentValue(View view, int viewSize, int measurementType) { + public int getAlignmentValue(View view, int viewSize) { return UNDEFINED; } @@ -2415,4 +2489,41 @@ public class GridLayout extends ViewGroup { return cellSize; } }; + + /** + * Spec's tell GridLayout how to derive minimum and maximum size values for a + * component. Specifications are made with respect to a child's 'measured size'. + * A child's measured size is, in turn, controlled by its height and width + * layout parameters which either specify a size or, in the case of + * WRAP_CONTENT, defer to the computed size of the component. + */ + public static abstract class Spec { + } + + /** + * Indicates that a view requests precisely the size specified by its layout parameters. + * + * @see Spec + */ + public static final Spec FIXED = new Spec() { + }; + + /** + * Indicates that a view's size should lie between its minimum and the size specified by + * its layout parameters. + * + * @see Spec + */ + public static final Spec CAN_SHRINK = new Spec() { + }; + + /** + * Indicates that a view's size should be greater than or equal to the size specified by + * its layout parameters. + * + * @see Spec + */ + public static final Spec CAN_STRETCH = new Spec() { + }; + } diff --git a/core/res/res/layout/keyguard_screen_password_landscape.xml b/core/res/res/layout/keyguard_screen_password_landscape.xml index 8ba08f68f17f..30df91b24450 100644 --- a/core/res/res/layout/keyguard_screen_password_landscape.xml +++ b/core/res/res/layout/keyguard_screen_password_landscape.xml @@ -156,7 +156,7 @@ /> <!-- Column 1 --> - <Space android:layout_columnWeight="1" android:layout_rowSpan="11" /> + <Space android:layout_widthSpec="canStretch" android:layout_rowSpan="11" /> <!-- Column 2 - password entry field and PIN keyboard --> <LinearLayout diff --git a/core/res/res/layout/keyguard_screen_tab_unlock_land.xml b/core/res/res/layout/keyguard_screen_tab_unlock_land.xml index 5588adc88a81..c8597209e763 100644 --- a/core/res/res/layout/keyguard_screen_tab_unlock_land.xml +++ b/core/res/res/layout/keyguard_screen_tab_unlock_land.xml @@ -169,7 +169,10 @@ </LinearLayout> <!-- Column 1 --> - <Space android:width="20dip" android:layout_columnWeight="1" android:layout_rowSpan="10" /> + <Space + android:width="20dip" + android:layout_heightSpec="canStretch" + android:layout_rowSpan="10" /> <!-- Column 2 --> <com.android.internal.widget.multiwaveview.MultiWaveView diff --git a/core/res/res/layout/keyguard_screen_unlock_landscape.xml b/core/res/res/layout/keyguard_screen_unlock_landscape.xml index d0538dd09b63..0070ed0ec87b 100644 --- a/core/res/res/layout/keyguard_screen_unlock_landscape.xml +++ b/core/res/res/layout/keyguard_screen_unlock_landscape.xml @@ -100,9 +100,11 @@ <!-- TODO: remove hard coded height since layout_rowWeight doesn't seem to be working --> <Space - android:layout_height="43dip" - android:layout_gravity="fill" - android:layout_rowWeight="1" android:layout_columnWeight="1" /> + android:layout_height="43dip" + android:layout_gravity="fill" + android:layout_heightSpec="canStretch" + android:layout_widthSpec="canStretch" + /> <TextView android:id="@+id/carrier" android:layout_gravity="right" diff --git a/core/res/res/layout/keyguard_screen_unlock_portrait.xml b/core/res/res/layout/keyguard_screen_unlock_portrait.xml index 774f830925f5..28c530291bcd 100644 --- a/core/res/res/layout/keyguard_screen_unlock_portrait.xml +++ b/core/res/res/layout/keyguard_screen_unlock_portrait.xml @@ -125,7 +125,7 @@ android:layout_marginBottom="4dip" android:layout_marginLeft="8dip" android:layout_gravity="center|bottom" - android:layout_rowWeight="1" + android:layout_heightSpec="canStretch" /> <TextView diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index db33d1c4e19f..e3887fe4871a 100755 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -3324,11 +3324,6 @@ The default is one. See {@link android.widget.GridLayout.Group}. --> <attr name="layout_rowSpan" format="integer" min="1" /> - <!-- A number indicating the relative proportion of available space that - should be taken by this group of cells. - The default is zero. - See {@link android.widget.GridLayout.LayoutParams#columnWeight}. --> - <attr name="layout_rowWeight" format="float" /> <!-- The column boundary delimiting the left of the group of cells occupied by this view. --> <attr name="layout_column" /> @@ -3337,15 +3332,38 @@ The default is one. See {@link android.widget.GridLayout.Group}. --> <attr name="layout_columnSpan" format="integer" min="1" /> - <!-- A number indicating the relative proportion of available space that - should be taken by this group of cells. - The default is zero. - See {@link android.widget.GridLayout.LayoutParams#columnWeight}.--> - <attr name="layout_columnWeight" format="float" /> <!-- Gravity specifies how a component should be placed in its group of cells. The default is LEFT | BASELINE. See {@link android.widget.GridLayout.LayoutParams#setGravity(int)}. --> <attr name="layout_gravity" /> + <!-- A value specifying how much deficit or excess width this component can accomodate. + The default is FIXED. + See {@link android.widget.GridLayout.LayoutParams#widthSpec}.--> + <attr name="layout_widthSpec" > + <!-- If possible, width should be exactly as specified. + See {@link android.widget.GridLayout#FIXED}. --> + <enum name="fixed" value="0" /> + <!-- If possible, width should be less than or equal to the specified width. + See {@link android.widget.GridLayout#CAN_SHRINK}. --> + <enum name="canShrink" value="1" /> + <!-- If possible, width should be greater than or equal to the specified width. + See {@link android.widget.GridLayout#CAN_STRETCH}. --> + <enum name="canStretch" value="2" /> + </attr> + <!-- A value specifying how much deficit or excess height this component can accomodate. + The default is FIXED. + See {@link android.widget.GridLayout.LayoutParams#heightSpec}.--> + <attr name="layout_heightSpec" > + <!-- If possible, height should be exactly as specified. + See {@link android.widget.GridLayout#FIXED}. --> + <enum name="fixed" value="0" /> + <!-- If possible, height should be less than or equal to the specified height. + See {@link android.widget.GridLayout#CAN_SHRINK}. --> + <enum name="canShrink" value="1" /> + <!-- If possible, height should be greater than or equal to the specified height. + See {@link android.widget.GridLayout#CAN_STRETCH}. --> + <enum name="canStretch" value="2" /> + </attr> </declare-styleable> <declare-styleable name="FrameLayout_Layout"> <attr name="layout_gravity" /> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index db6f98fa3400..945e0c483fad 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -1738,9 +1738,11 @@ <public type="attr" name="layout_row" /> <public type="attr" name="layout_rowSpan" /> - <public type="attr" name="layout_rowWeight" /> <public type="attr" name="layout_columnSpan" /> - <public type="attr" name="layout_columnWeight" /> + + <public type="attr" name="layout_widthSpec" /> + <public type="attr" name="layout_heightSpec" /> + <public type="attr" name="actionModeSelectAllDrawable" /> <public type="attr" name="isAuxiliary" /> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 29fca74c4e14..c8b3b4fbfa81 100755 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -2625,10 +2625,14 @@ <!-- USB_STORAGE_ERROR dialog ok button--> <string name="dlg_ok">OK</string> - <!-- USB_PREFERENCES: When the user connects the phone to a computer via USB, we show a notification asking if he wants to share files across. This is the title --> - <string name="usb_preferences_notification_title">USB connected</string> + <!-- USB_PREFERENCES: Notification for wehen the user connects the phone to a computer via USB in MTP mode. This is the title --> + <string name="usb_mtp_notification_title">Connected as a media device</string> + <!-- USB_PREFERENCES: Notification for wehen the user connects the phone to a computer via USB in PTP mode. This is the title --> + <string name="usb_ptp_notification_title">Connected as a camera</string> + <!-- USB_PREFERENCES: Notification for wehen the user connects the phone to a computer via USB in mass storage mode (for installer CD image). This is the title --> + <string name="usb_cd_installer_notification_title">Connected as an installer</string> <!-- See USB_PREFERENCES. This is the message. --> - <string name="usb_preferece_notification_message">Select to configure USB file transfer.</string> + <string name="usb_notification_message">Touch for other USB options</string> <!-- External media format dialog strings --> <!-- This is the label for the activity, and should never be visible to the user. --> diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 6d8eab63c074..f42cbbfc4f7a 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -25,6 +25,11 @@ android:exported="true" /> + <!-- started from PhoneWindowManager + TODO: Should have an android:permission attribute --> + <service android:name=".screenshot.TakeScreenshotService" + android:exported="false" /> + <activity android:name=".usb.UsbPreferenceActivity" android:theme="@*android:style/Theme.Holo.Dialog.Alert" android:excludeFromRecents="true"> diff --git a/packages/SystemUI/res/drawable-hdpi/global_screenshot_background.9.png b/packages/SystemUI/res/drawable-hdpi/global_screenshot_background.9.png Binary files differnew file mode 100644 index 000000000000..e14111dae168 --- /dev/null +++ b/packages/SystemUI/res/drawable-hdpi/global_screenshot_background.9.png diff --git a/packages/SystemUI/res/drawable-mdpi/global_screenshot_background.9.png b/packages/SystemUI/res/drawable-mdpi/global_screenshot_background.9.png Binary files differnew file mode 100644 index 000000000000..e14111dae168 --- /dev/null +++ b/packages/SystemUI/res/drawable-mdpi/global_screenshot_background.9.png diff --git a/packages/SystemUI/res/layout/global_screenshot.xml b/packages/SystemUI/res/layout/global_screenshot.xml new file mode 100644 index 000000000000..6cb8799eaeb3 --- /dev/null +++ b/packages/SystemUI/res/layout/global_screenshot.xml @@ -0,0 +1,40 @@ +<?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:layout_width="match_parent" + android:layout_height="match_parent"> + <ImageView android:id="@+id/global_screenshot_background" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="#FF000000" + android:visibility="gone" /> + <FrameLayout + android:id="@+id/global_screenshot_container" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:background="@drawable/global_screenshot_background" + android:visibility="gone"> + <ImageView android:id="@+id/global_screenshot" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:adjustViewBounds="true" /> + </FrameLayout> + <ImageView android:id="@+id/global_screenshot_flash" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="#FFFFFFFF" + android:visibility="gone" /> +</FrameLayout> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 86e0cd014302..70f9b7521959 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -165,4 +165,9 @@ <string name="use_ptp_button_title">Mount as a camera (PTP)</string> <!-- Label for the installer CD image option in UsbPreferenceActivity. [CHAR LIMIT=50] --> <string name="installer_cd_button_title">Install Android File Transfer application for Mac</string> + + <!-- toast message displayed when a screenshot is saved to the Gallery. --> + <string name="screenshot_saving_toast">Screenshot saved to Gallery</string> + <!-- toast message displayed when we fail to take a screenshot. --> + <string name="screenshot_failed_toast">Could not save screenshot</string> </resources> diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java new file mode 100644 index 000000000000..83a5578cfa36 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java @@ -0,0 +1,384 @@ +/* + * 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.systemui.screenshot; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; +import android.animation.TimeInterpolator; +import android.animation.ValueAnimator; +import android.animation.ValueAnimator.AnimatorUpdateListener; +import android.app.Activity; +import android.content.ContentValues; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Matrix; +import android.graphics.PixelFormat; +import android.media.MediaScannerConnection; +import android.net.Uri; +import android.os.AsyncTask; +import android.os.Binder; +import android.os.Environment; +import android.os.ServiceManager; +import android.provider.MediaStore; +import android.util.DisplayMetrics; +import android.util.Log; +import android.view.Display; +import android.view.IWindowManager; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.Surface; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.TextView; +import android.widget.Toast; + +import com.android.systemui.R; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.lang.Thread; +import java.text.SimpleDateFormat; +import java.util.Date; + +/** + * POD used in the AsyncTask which saves an image in the background. + */ +class SaveImageInBackgroundData { + Context context; + Bitmap image; + int result; +} + +/** + * An AsyncTask that saves an image to the media store in the background. + */ +class SaveImageInBackgroundTask extends AsyncTask<SaveImageInBackgroundData, Void, + SaveImageInBackgroundData> { + private static final String TAG = "SaveImageInBackgroundTask"; + private static final String SCREENSHOTS_DIR_NAME = "Screenshots"; + private static final String SCREENSHOT_FILE_PATH_TEMPLATE = "%s/%s/Screenshot_%s-%d.png"; + + @Override + protected SaveImageInBackgroundData doInBackground(SaveImageInBackgroundData... params) { + if (params.length != 1) return null; + + Context context = params[0].context; + Bitmap image = params[0].image; + + try{ + long currentTime = System.currentTimeMillis(); + String date = new SimpleDateFormat("MM-dd-yy-kk-mm-ss").format(new Date(currentTime)); + String imageDir = Environment.getExternalStoragePublicDirectory( + Environment.DIRECTORY_PICTURES).getAbsolutePath(); + String imageFilePath = String.format(SCREENSHOT_FILE_PATH_TEMPLATE, + imageDir, SCREENSHOTS_DIR_NAME, + date, currentTime % 1000); + + // Save the screenshot to the MediaStore + ContentValues values = new ContentValues(); + values.put(MediaStore.Images.ImageColumns.DATA, imageFilePath); + values.put(MediaStore.Images.ImageColumns.TITLE, "Screenshot"); + values.put(MediaStore.Images.ImageColumns.DISPLAY_NAME, "Screenshot"); + values.put(MediaStore.Images.ImageColumns.DATE_TAKEN, currentTime); + values.put(MediaStore.Images.ImageColumns.DATE_ADDED, currentTime); + values.put(MediaStore.Images.ImageColumns.DATE_MODIFIED, currentTime); + values.put(MediaStore.Images.ImageColumns.MIME_TYPE, "image/png"); + Uri uri = context.getContentResolver().insert( + MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values); + + OutputStream out = context.getContentResolver().openOutputStream(uri); + image.compress(Bitmap.CompressFormat.PNG, 100, out); + out.flush(); + out.close(); + + params[0].result = 0; + }catch(IOException e){ + params[0].result = 1; + } + + return params[0]; + }; + + @Override + protected void onPostExecute(SaveImageInBackgroundData params) { + if (params.result > 0) { + // Show a message that we've failed to save the image to disk + Toast.makeText(params.context, R.string.screenshot_failed_toast, + Toast.LENGTH_SHORT).show(); + } else { + // Show a message that we've saved the screenshot to disk + Toast.makeText(params.context, R.string.screenshot_saving_toast, + Toast.LENGTH_SHORT).show(); + } + }; +} + +/** + * TODO: + * - Performance when over gl surfaces? Ie. Gallery + * - what do we say in the Toast? Which icon do we get if the user uses another + * type of gallery? + */ +class GlobalScreenshot { + private static final String TAG = "GlobalScreenshot"; + private static final int SCREENSHOT_FADE_IN_DURATION = 900; + private static final int SCREENSHOT_FADE_OUT_DELAY = 1000; + private static final int SCREENSHOT_FADE_OUT_DURATION = 450; + private static final int TOAST_FADE_IN_DURATION = 500; + private static final int TOAST_FADE_OUT_DELAY = 1000; + private static final int TOAST_FADE_OUT_DURATION = 500; + private static final float BACKGROUND_ALPHA = 0.65f; + private static final float SCREENSHOT_SCALE = 0.85f; + private static final float SCREENSHOT_MIN_SCALE = 0.7f; + private static final float SCREENSHOT_ROTATION = -6.75f; // -12.5f; + + private Context mContext; + private LayoutInflater mLayoutInflater; + private IWindowManager mIWindowManager; + private WindowManager mWindowManager; + private WindowManager.LayoutParams mWindowLayoutParams; + private Display mDisplay; + private DisplayMetrics mDisplayMetrics; + private Matrix mDisplayMatrix; + + private Bitmap mScreenBitmap; + private View mScreenshotLayout; + private ImageView mBackgroundView; + private FrameLayout mScreenshotContainerView; + private ImageView mScreenshotView; + + private AnimatorSet mScreenshotAnimation; + + // General use cubic interpolator + final TimeInterpolator mCubicInterpolator = new TimeInterpolator() { + public float getInterpolation(float t) { + return t*t*t; + } + }; + // The interpolator used to control the background alpha at the start of the animation + final TimeInterpolator mBackgroundViewAlphaInterpolator = new TimeInterpolator() { + public float getInterpolation(float t) { + float tStep = 0.35f; + if (t < tStep) { + return t * (1f / tStep); + } else { + return 1f; + } + } + }; + + /** + * @param context everything needs a context :( + */ + public GlobalScreenshot(Context context) { + mContext = context; + mLayoutInflater = (LayoutInflater) + context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + + // Inflate the screenshot layout + mDisplayMetrics = new DisplayMetrics(); + mDisplayMatrix = new Matrix(); + mScreenshotLayout = mLayoutInflater.inflate(R.layout.global_screenshot, null); + mBackgroundView = (ImageView) mScreenshotLayout.findViewById(R.id.global_screenshot_background); + mScreenshotContainerView = (FrameLayout) mScreenshotLayout.findViewById(R.id.global_screenshot_container); + mScreenshotView = (ImageView) mScreenshotLayout.findViewById(R.id.global_screenshot); + mScreenshotLayout.setFocusable(true); + mScreenshotLayout.setOnTouchListener(new View.OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + // Intercept and ignore all touch events + return true; + } + }); + + // Setup the window that we are going to use + mIWindowManager = IWindowManager.Stub.asInterface( + ServiceManager.getService(Context.WINDOW_SERVICE)); + mWindowLayoutParams = new WindowManager.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, 0, 0, + WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY, + WindowManager.LayoutParams.FLAG_FULLSCREEN + | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED + | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED_SYSTEM + | WindowManager.LayoutParams.FLAG_KEEP_SURFACE_WHILE_ANIMATING + | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN + | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED, + PixelFormat.TRANSLUCENT); + mWindowLayoutParams.token = new Binder(); + mWindowLayoutParams.setTitle("ScreenshotAnimation"); + mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); + mDisplay = mWindowManager.getDefaultDisplay(); + } + + /** + * Creates a new worker thread and saves the screenshot to the media store. + */ + private void saveScreenshotInWorkerThread() { + SaveImageInBackgroundData data = new SaveImageInBackgroundData(); + data.context = mContext; + data.image = mScreenBitmap; + new SaveImageInBackgroundTask().execute(data); + } + + /** + * @return the current display rotation in degrees + */ + private float getDegreesForRotation(int value) { + switch (value) { + case Surface.ROTATION_90: + return 90f; + case Surface.ROTATION_180: + return 180f; + case Surface.ROTATION_270: + return 270f; + } + return 0f; + } + + /** + * Takes a screenshot of the current display and shows an animation. + */ + void takeScreenshot() { + // We need to orient the screenshot correctly (and the Surface api seems to take screenshots + // only in the natural orientation of the device :!) + mDisplay.getRealMetrics(mDisplayMetrics); + float[] dims = {mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels}; + float degrees = getDegreesForRotation(mDisplay.getRotation()); + boolean requiresRotation = (degrees > 0); + if (requiresRotation) { + // Get the dimensions of the device in its native orientation + mDisplayMatrix.reset(); + mDisplayMatrix.preRotate(-degrees); + mDisplayMatrix.mapPoints(dims); + dims[0] = Math.abs(dims[0]); + dims[1] = Math.abs(dims[1]); + } + mScreenBitmap = Surface.screenshot((int) dims[0], (int) dims[1]); + if (requiresRotation) { + // Rotate the screenshot to the current orientation + Bitmap ss = Bitmap.createBitmap(mDisplayMetrics.widthPixels, + mDisplayMetrics.heightPixels, Bitmap.Config.ARGB_8888); + Canvas c = new Canvas(ss); + c.translate(ss.getWidth() / 2, ss.getHeight() / 2); + c.rotate(360f - degrees); + c.translate(-dims[0] / 2, -dims[1] / 2); + c.drawBitmap(mScreenBitmap, 0, 0, null); + mScreenBitmap = ss; + } + + // If we couldn't take the screenshot, notify the user + if (mScreenBitmap == null) { + Toast.makeText(mContext, R.string.screenshot_failed_toast, + Toast.LENGTH_SHORT).show(); + return; + } + + // Start the post-screenshot animation + startAnimation(); + } + + + /** + * Starts the animation after taking the screenshot + */ + private void startAnimation() { + // Add the view for the animation + mScreenshotView.setImageBitmap(mScreenBitmap); + mScreenshotLayout.requestFocus(); + + // Setup the animation with the screenshot just taken + if (mScreenshotAnimation != null) { + mScreenshotAnimation.end(); + } + + mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams); + ValueAnimator screenshotFadeInAnim = createScreenshotFadeInAnimation(); + ValueAnimator screenshotFadeOutAnim = createScreenshotFadeOutAnimation(); + mScreenshotAnimation = new AnimatorSet(); + mScreenshotAnimation.play(screenshotFadeInAnim).before(screenshotFadeOutAnim); + mScreenshotAnimation.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + // Save the screenshot once we have a bit of time now + saveScreenshotInWorkerThread(); + + mWindowManager.removeView(mScreenshotLayout); + } + }); + mScreenshotAnimation.start(); + } + private ValueAnimator createScreenshotFadeInAnimation() { + ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f); + anim.setInterpolator(mCubicInterpolator); + anim.setDuration(SCREENSHOT_FADE_IN_DURATION); + anim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animation) { + mBackgroundView.setVisibility(View.VISIBLE); + mScreenshotContainerView.setVisibility(View.VISIBLE); + } + }); + anim.addUpdateListener(new AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + float t = ((Float) animation.getAnimatedValue()).floatValue(); + mBackgroundView.setAlpha(mBackgroundViewAlphaInterpolator.getInterpolation(t) * + BACKGROUND_ALPHA); + float scaleT = SCREENSHOT_SCALE + (1f - t) * SCREENSHOT_SCALE; + mScreenshotContainerView.setAlpha(t*t*t*t); + mScreenshotContainerView.setScaleX(scaleT); + mScreenshotContainerView.setScaleY(scaleT); + mScreenshotContainerView.setRotation(t * SCREENSHOT_ROTATION); + } + }); + return anim; + } + private ValueAnimator createScreenshotFadeOutAnimation() { + ValueAnimator anim = ValueAnimator.ofFloat(1f, 0f); + anim.setInterpolator(mCubicInterpolator); + anim.setStartDelay(SCREENSHOT_FADE_OUT_DELAY); + anim.setDuration(SCREENSHOT_FADE_OUT_DURATION); + anim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mBackgroundView.setVisibility(View.GONE); + mScreenshotContainerView.setVisibility(View.GONE); + } + }); + anim.addUpdateListener(new AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + float t = ((Float) animation.getAnimatedValue()).floatValue(); + float scaleT = SCREENSHOT_MIN_SCALE + + t*(SCREENSHOT_SCALE - SCREENSHOT_MIN_SCALE); + mScreenshotContainerView.setAlpha(t); + mScreenshotContainerView.setScaleX(scaleT); + mScreenshotContainerView.setScaleY(scaleT); + mBackgroundView.setAlpha(t * t * BACKGROUND_ALPHA); + } + }); + return anim; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java new file mode 100644 index 000000000000..35eaedf1f941 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java @@ -0,0 +1,50 @@ +/* + * 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.systemui.screenshot; + +import android.app.Service; +import android.app.AlertDialog; +import android.content.ActivityNotFoundException; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.net.Uri; +import android.hardware.usb.UsbAccessory; +import android.hardware.usb.UsbManager; +import android.os.Bundle; +import android.os.IBinder; +import android.util.Log; + +import com.android.internal.app.AlertActivity; +import com.android.internal.app.AlertController; + +import com.android.systemui.R; + +public class TakeScreenshotService extends Service { + private static final String TAG = "TakeScreenshotService"; + + private static GlobalScreenshot mScreenshot; + + @Override + public IBinder onBind(Intent intent) { + if (mScreenshot == null) { + mScreenshot = new GlobalScreenshot(this); + } + mScreenshot.takeScreenshot(); + return null; + } +} diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java index b52e7e1c42a2..ad6cebb60b78 100755 --- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java @@ -28,6 +28,7 @@ import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.ServiceConnection; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.res.CompatibilityInfo; @@ -372,6 +373,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { // What we do when the user long presses on home private int mLongPressOnHomeBehavior = -1; + // Screenshot trigger states + private boolean mVolumeDownTriggered; + private boolean mPowerDownTriggered; + ShortcutManager mShortcutManager; PowerManager.WakeLock mBroadcastWakeLock; @@ -2339,6 +2344,26 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } + private void takeScreenshot() { + mHandler.post(new Runnable() { + @Override + public void run() { + ComponentName cn = new ComponentName("com.android.systemui", + "com.android.systemui.screenshot.TakeScreenshotService"); + Intent intent = new Intent(); + intent.setComponent(cn); + ServiceConnection conn = new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName name, IBinder service) {} + @Override + public void onServiceDisconnected(ComponentName name) {} + }; + mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE); + mContext.unbindService(conn); + } + }); + } + /** {@inheritDoc} */ @Override public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags, boolean isScreenOn) { @@ -2398,6 +2423,24 @@ public class PhoneWindowManager implements WindowManagerPolicy { // Handle special keys. switch (keyCode) { case KeyEvent.KEYCODE_VOLUME_DOWN: + if (down) { + // If the power key down was already triggered, take the screenshot + if (mPowerDownTriggered) { + // Dismiss the power-key longpress + mHandler.removeCallbacks(mPowerLongPress); + mPowerKeyHandled = true; + + // Take the screenshot + takeScreenshot(); + + // Prevent the event from being passed through to the current activity + result &= ~ACTION_PASS_TO_USER; + break; + } + mVolumeDownTriggered = true; + } else { + mVolumeDownTriggered = false; + } case KeyEvent.KEYCODE_VOLUME_UP: case KeyEvent.KEYCODE_VOLUME_MUTE: { if (down) { @@ -2478,6 +2521,18 @@ public class PhoneWindowManager implements WindowManagerPolicy { case KeyEvent.KEYCODE_POWER: { result &= ~ACTION_PASS_TO_USER; if (down) { + // If the volume down key has been triggered, then just take the screenshot + if (mVolumeDownTriggered) { + // Take the screenshot + takeScreenshot(); + mPowerKeyHandled = true; + + // Prevent the event from being passed through to the current activity + break; + } + mPowerDownTriggered = true; + + ITelephony telephonyService = getTelephonyService(); boolean hungUp = false; if (telephonyService != null) { @@ -2499,6 +2554,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } interceptPowerKeyDown(!isScreenOn || hungUp); } else { + mPowerDownTriggered = false; if (interceptPowerKeyUp(canceled)) { result = (result & ~ACTION_POKE_USER_ACTIVITY) | ACTION_GO_TO_SLEEP; } diff --git a/services/java/com/android/server/usb/UsbDeviceManager.java b/services/java/com/android/server/usb/UsbDeviceManager.java index 918f1b6b8355..13a76b379264 100644 --- a/services/java/com/android/server/usb/UsbDeviceManager.java +++ b/services/java/com/android/server/usb/UsbDeviceManager.java @@ -45,7 +45,6 @@ import android.os.storage.StorageVolume; import android.os.SystemProperties; import android.os.UEventObserver; import android.provider.Settings; -import android.util.Log; import android.util.Slog; import java.io.File; @@ -62,7 +61,7 @@ import java.util.List; public class UsbDeviceManager { private static final String TAG = UsbDeviceManager.class.getSimpleName(); - private static final boolean LOG = false; + private static final boolean DEBUG = false; private static final String USB_STATE_MATCH = "DEVPATH=/devices/virtual/android_usb/android0"; @@ -93,18 +92,9 @@ public class UsbDeviceManager { private final UsbSettingsManager mSettingsManager; private NotificationManager mNotificationManager; private final boolean mHasUsbAccessory; - - // for USB connected notification - private boolean mUsbNotificationShown; private boolean mUseUsbNotification; - private Notification mUsbNotification; - - // for adb connected notification - private boolean mAdbNotificationShown; - private Notification mAdbNotification; private boolean mAdbEnabled; - private class AdbSettingsObserver extends ContentObserver { public AdbSettingsObserver() { super(null); @@ -117,115 +107,20 @@ public class UsbDeviceManager { } } - private void updateUsbNotification(boolean connected) { - if (mNotificationManager == null || !mUseUsbNotification) return; - if (connected) { - if (!mUsbNotificationShown) { - Resources r = mContext.getResources(); - CharSequence title = r.getText( - com.android.internal.R.string.usb_preferences_notification_title); - CharSequence message = r.getText( - com.android.internal.R.string.usb_preferece_notification_message); - - if (mUsbNotification == null) { - mUsbNotification = new Notification(); - mUsbNotification.icon = com.android.internal.R.drawable.stat_sys_data_usb; - mUsbNotification.when = 0; - mUsbNotification.flags = Notification.FLAG_ONGOING_EVENT; - mUsbNotification.tickerText = title; - mUsbNotification.defaults = 0; // please be quiet - mUsbNotification.sound = null; - mUsbNotification.vibrate = null; - } - - Intent intent = new Intent(); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | - Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); - intent.setClassName("com.android.systemui", - "com.android.systemui.usb.UsbPreferenceActivity"); - PendingIntent pi = PendingIntent.getActivity(mContext, 0, - intent, 0); - - mUsbNotification.setLatestEventInfo(mContext, title, message, pi); - - mUsbNotificationShown = true; - mNotificationManager.notify( - com.android.internal.R.string.usb_preferences_notification_title, - mUsbNotification); - } - - } else if (mUsbNotificationShown) { - mUsbNotificationShown = false; - mNotificationManager.cancel( - com.android.internal.R.string.usb_preferences_notification_title); - } - } - - private void updateAdbNotification(boolean adbEnabled) { - if (mNotificationManager == null) return; - if (adbEnabled) { - if ("0".equals(SystemProperties.get("persist.adb.notify"))) return; - - if (!mAdbNotificationShown) { - Resources r = mContext.getResources(); - CharSequence title = r.getText( - com.android.internal.R.string.adb_active_notification_title); - CharSequence message = r.getText( - com.android.internal.R.string.adb_active_notification_message); - - if (mAdbNotification == null) { - mAdbNotification = new Notification(); - mAdbNotification.icon = com.android.internal.R.drawable.stat_sys_adb; - mAdbNotification.when = 0; - mAdbNotification.flags = Notification.FLAG_ONGOING_EVENT; - mAdbNotification.tickerText = title; - mAdbNotification.defaults = 0; // please be quiet - mAdbNotification.sound = null; - mAdbNotification.vibrate = null; - } - - Intent intent = new Intent( - Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | - Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); - // Note: we are hard-coding the component because this is - // an important security UI that we don't want anyone - // intercepting. - intent.setComponent(new ComponentName("com.android.settings", - "com.android.settings.DevelopmentSettings")); - PendingIntent pi = PendingIntent.getActivity(mContext, 0, - intent, 0); - - mAdbNotification.setLatestEventInfo(mContext, title, message, pi); - - mAdbNotificationShown = true; - mNotificationManager.notify( - com.android.internal.R.string.adb_active_notification_title, - mAdbNotification); - } - } else if (mAdbNotificationShown) { - mAdbNotificationShown = false; - mNotificationManager.cancel( - com.android.internal.R.string.adb_active_notification_title); - } - } - /* * Listens for uevent messages from the kernel to monitor the USB state */ private final UEventObserver mUEventObserver = new UEventObserver() { @Override public void onUEvent(UEventObserver.UEvent event) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Slog.v(TAG, "USB UEVENT: " + event.toString()); - } + if (DEBUG) Slog.v(TAG, "USB UEVENT: " + event.toString()); String state = event.get("USB_STATE"); String accessory = event.get("ACCESSORY"); if (state != null) { mHandler.updateState(state); } else if ("START".equals(accessory)) { - Slog.d(TAG, "got accessory start"); + if (DEBUG) Slog.d(TAG, "got accessory start"); setCurrentFunction(UsbManager.USB_FUNCTION_ACCESSORY, false); } } @@ -319,16 +214,32 @@ public class UsbDeviceManager { private String mDefaultFunctions; private UsbAccessory mCurrentAccessory; private boolean mDeferAccessoryAttached; + private int mUsbNotificationId; + private boolean mAdbNotificationShown; + + private static final int NOTIFICATION_NONE = 0; + private static final int NOTIFICATION_MTP = 1; + private static final int NOTIFICATION_PTP = 2; + private static final int NOTIFICATION_INSTALLER = 3; + private static final int NOTIFICATION_ADB = 4; public UsbHandler() { - // Read initial USB state try { + // sanity check the sys.usb.config system property + // this may be necessary if we crashed while switching USB configurations + String config = SystemProperties.get("sys.usb.config", "none"); + if (config.equals("none")) { + String persistConfig = SystemProperties.get("persist.sys.usb.config", "none"); + Slog.w(TAG, "resetting config to persistent property: " + persistConfig); + SystemProperties.set("sys.usb.config", persistConfig); + } + + // Read initial USB state mCurrentFunctions = FileUtils.readTextFile( new File(FUNCTIONS_PATH), 0, null).trim(); mDefaultFunctions = mCurrentFunctions; String state = FileUtils.readTextFile(new File(STATE_PATH), 0, null).trim(); updateState(state); - mAdbEnabled = containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_ADB); // Upgrade step for previous versions that used persist.service.adb.enable @@ -414,12 +325,12 @@ public class UsbDeviceManager { } catch (InterruptedException e) { } } - Log.e(TAG, "waitForState(" + state + ") FAILED"); + Slog.e(TAG, "waitForState(" + state + ") FAILED"); return false; } private boolean setUsbConfig(String config) { - Log.d(TAG, "setUsbConfig(" + config + ")"); + if (DEBUG) Slog.d(TAG, "setUsbConfig(" + config + ")"); // set the new configuration SystemProperties.set("sys.usb.config", config); return waitForState(config); @@ -428,7 +339,7 @@ public class UsbDeviceManager { private void doSetCurrentFunctions(String functions) { if (!mCurrentFunctions.equals(functions)) { if (!setUsbConfig("none") || !setUsbConfig(functions)) { - Log.e(TAG, "Failed to switch USB configuration to " + functions); + Slog.e(TAG, "Failed to switch USB configuration to " + functions); // revert to previous configuration if we fail setUsbConfig(mCurrentFunctions); } else { @@ -438,6 +349,7 @@ public class UsbDeviceManager { } private void setAdbEnabled(boolean enable) { + if (DEBUG) Slog.d(TAG, "setAdbEnabled: " + enable); if (enable != mAdbEnabled) { mAdbEnabled = enable; String functions; @@ -449,7 +361,7 @@ public class UsbDeviceManager { functions = removeFunction(mDefaultFunctions, UsbManager.USB_FUNCTION_ADB); } setCurrentFunction(functions, true); - updateAdbNotification(mAdbEnabled && mConnected); + updateAdbNotification(); } } @@ -469,7 +381,7 @@ public class UsbDeviceManager { String[] strings = nativeGetAccessoryStrings(); if (strings != null) { mCurrentAccessory = new UsbAccessory(strings); - Log.d(TAG, "entering USB accessory mode: " + mCurrentAccessory); + Slog.d(TAG, "entering USB accessory mode: " + mCurrentAccessory); // defer accessoryAttached if system is not ready if (mSystemReady) { mSettingsManager.accessoryAttached(mCurrentAccessory); @@ -477,12 +389,12 @@ public class UsbDeviceManager { mDeferAccessoryAttached = true; } } else { - Log.e(TAG, "nativeGetAccessoryStrings failed"); + Slog.e(TAG, "nativeGetAccessoryStrings failed"); } } else if (!mConnected) { // make sure accessory mode is off // and restore default functions - Log.d(TAG, "exited USB accessory mode"); + Slog.d(TAG, "exited USB accessory mode"); setEnabledFunctions(mDefaultFunctions); if (mCurrentAccessory != null) { @@ -517,8 +429,8 @@ public class UsbDeviceManager { case MSG_UPDATE_STATE: mConnected = (msg.arg1 == 1); mConfigured = (msg.arg2 == 1); - updateUsbNotification(mConnected); - updateAdbNotification(mAdbEnabled && mConnected); + updateUsbNotification(); + updateAdbNotification(); if (containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_ACCESSORY)) { updateCurrentAccessory(); @@ -562,8 +474,8 @@ public class UsbDeviceManager { } break; case MSG_SYSTEM_READY: - updateUsbNotification(mConnected); - updateAdbNotification(mAdbEnabled && mConnected); + updateUsbNotification(); + updateAdbNotification(); updateUsbState(); if (mCurrentAccessory != null && mDeferAccessoryAttached) { mSettingsManager.accessoryAttached(mCurrentAccessory); @@ -576,6 +488,105 @@ public class UsbDeviceManager { return mCurrentAccessory; } + private void updateUsbNotification() { + if (mNotificationManager == null || !mUseUsbNotification) return; + if (mConnected) { + Resources r = mContext.getResources(); + CharSequence title = null; + int id = NOTIFICATION_NONE; + if (containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_MTP)) { + title = r.getText( + com.android.internal.R.string.usb_mtp_notification_title); + id = NOTIFICATION_MTP; + } else if (containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_PTP)) { + title = r.getText( + com.android.internal.R.string.usb_ptp_notification_title); + id = NOTIFICATION_PTP; + } else if (containsFunction(mCurrentFunctions, + UsbManager.USB_FUNCTION_MASS_STORAGE)) { + title = r.getText( + com.android.internal.R.string.usb_cd_installer_notification_title); + id = NOTIFICATION_INSTALLER; + } else { + Slog.e(TAG, "No known USB function in updateUsbNotification"); + } + if (id != mUsbNotificationId) { + // clear notification if title needs changing + if (mUsbNotificationId != NOTIFICATION_NONE) { + mNotificationManager.cancel(mUsbNotificationId); + mUsbNotificationId = NOTIFICATION_NONE; + } + } + if (mUsbNotificationId == NOTIFICATION_NONE) { + CharSequence message = r.getText( + com.android.internal.R.string.usb_notification_message); + + Notification notification = new Notification(); + notification.icon = com.android.internal.R.drawable.stat_sys_data_usb; + notification.when = 0; + notification.flags = Notification.FLAG_ONGOING_EVENT; + notification.tickerText = title; + notification.defaults = 0; // please be quiet + notification.sound = null; + notification.vibrate = null; + + Intent intent = new Intent(); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | + Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); + intent.setClassName("com.android.systemui", + "com.android.systemui.usb.UsbPreferenceActivity"); + PendingIntent pi = PendingIntent.getActivity(mContext, 0, + intent, 0); + notification.setLatestEventInfo(mContext, title, message, pi); + mNotificationManager.notify(id, notification); + mUsbNotificationId = id; + } + + } else if (mUsbNotificationId != NOTIFICATION_NONE) { + mNotificationManager.cancel(mUsbNotificationId); + mUsbNotificationId = NOTIFICATION_NONE; + } + } + + private void updateAdbNotification() { + if (mNotificationManager == null) return; + if (mAdbEnabled && mConnected) { + if ("0".equals(SystemProperties.get("persist.adb.notify"))) return; + + if (!mAdbNotificationShown) { + Resources r = mContext.getResources(); + CharSequence title = r.getText( + com.android.internal.R.string.adb_active_notification_title); + CharSequence message = r.getText( + com.android.internal.R.string.adb_active_notification_message); + + Notification notification = new Notification(); + notification.icon = com.android.internal.R.drawable.stat_sys_adb; + notification.when = 0; + notification.flags = Notification.FLAG_ONGOING_EVENT; + notification.tickerText = title; + notification.defaults = 0; // please be quiet + notification.sound = null; + notification.vibrate = null; + + Intent intent = new Intent( + Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | + Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); + intent.setComponent(new ComponentName("com.android.settings", + "com.android.settings.DevelopmentSettings")); + PendingIntent pi = PendingIntent.getActivity(mContext, 0, + intent, 0); + notification.setLatestEventInfo(mContext, title, message, pi); + mAdbNotificationShown = true; + mNotificationManager.notify(NOTIFICATION_ADB, notification); + } + } else if (mAdbNotificationShown) { + mAdbNotificationShown = false; + mNotificationManager.cancel(NOTIFICATION_ADB); + } + } + public void dump(FileDescriptor fd, PrintWriter pw) { pw.println(" USB Device State:"); pw.println(" Current Functions: " + mCurrentFunctions); @@ -608,6 +619,7 @@ public class UsbDeviceManager { } public void setCurrentFunction(String function, boolean makeDefault) { + if (DEBUG) Slog.d(TAG, "setCurrentFunction(" + function + ") default: " + makeDefault); mHandler.sendMessage(MSG_SET_CURRENT_FUNCTION, function, makeDefault); } diff --git a/services/java/com/android/server/usb/UsbHostManager.java b/services/java/com/android/server/usb/UsbHostManager.java index 923b0494f597..0a0ff598d7d8 100644 --- a/services/java/com/android/server/usb/UsbHostManager.java +++ b/services/java/com/android/server/usb/UsbHostManager.java @@ -37,7 +37,6 @@ import android.os.Parcelable; import android.os.ParcelFileDescriptor; import android.os.UEventObserver; import android.provider.Settings; -import android.util.Log; import android.util.Slog; import java.io.File; @@ -112,7 +111,7 @@ public class UsbHostManager { synchronized (mLock) { if (mDevices.get(deviceName) != null) { - Log.w(TAG, "device already on mDevices list: " + deviceName); + Slog.w(TAG, "device already on mDevices list: " + deviceName); return; } @@ -148,7 +147,7 @@ public class UsbHostManager { } catch (Exception e) { // beware of index out of bound exceptions, which might happen if // a device does not set bNumEndpoints correctly - Log.e(TAG, "error parsing USB descriptors", e); + Slog.e(TAG, "error parsing USB descriptors", e); return; } diff --git a/services/java/com/android/server/usb/UsbSettingsManager.java b/services/java/com/android/server/usb/UsbSettingsManager.java index 911367711faf..0baafbb2bf72 100644 --- a/services/java/com/android/server/usb/UsbSettingsManager.java +++ b/services/java/com/android/server/usb/UsbSettingsManager.java @@ -35,7 +35,7 @@ import android.hardware.usb.UsbManager; import android.os.Binder; import android.os.FileUtils; import android.os.Process; -import android.util.Log; +import android.util.Slog; import android.util.SparseBooleanArray; import android.util.Xml; @@ -62,6 +62,7 @@ import java.util.List; class UsbSettingsManager { private static final String TAG = "UsbSettingsManager"; + private static final boolean DEBUG = false; private static final File sSettingsFile = new File("/data/system/usb_device_manager.xml"); private final Context mContext; @@ -410,9 +411,9 @@ class UsbSettingsManager { } } } catch (FileNotFoundException e) { - Log.w(TAG, "settings file not found"); + if (DEBUG) Slog.d(TAG, "settings file not found"); } catch (Exception e) { - Log.e(TAG, "error reading settings file, deleting to start fresh", e); + Slog.e(TAG, "error reading settings file, deleting to start fresh", e); sSettingsFile.delete(); } finally { if (stream != null) { @@ -428,7 +429,7 @@ class UsbSettingsManager { FileOutputStream fos = null; try { FileOutputStream fstr = new FileOutputStream(sSettingsFile); - Log.d(TAG, "writing settings to " + fstr); + if (DEBUG) Slog.d(TAG, "writing settings to " + fstr); BufferedOutputStream str = new BufferedOutputStream(fstr); FastXmlSerializer serializer = new FastXmlSerializer(); serializer.setOutput(str, "utf-8"); @@ -457,7 +458,7 @@ class UsbSettingsManager { FileUtils.sync(fstr); str.close(); } catch (Exception e) { - Log.e(TAG, "error writing settings file, deleting to start fresh", e); + Slog.e(TAG, "error writing settings file, deleting to start fresh", e); sSettingsFile.delete(); } } @@ -472,7 +473,7 @@ class UsbSettingsManager { try { parser = ai.loadXmlMetaData(mPackageManager, metaDataName); if (parser == null) { - Log.w(TAG, "no meta-data for " + info); + Slog.w(TAG, "no meta-data for " + info); return false; } @@ -494,7 +495,7 @@ class UsbSettingsManager { XmlUtils.nextElement(parser); } } catch (Exception e) { - Log.w(TAG, "Unable to load component info " + info.toString(), e); + Slog.w(TAG, "Unable to load component info " + info.toString(), e); } finally { if (parser != null) parser.close(); } @@ -553,7 +554,7 @@ class UsbSettingsManager { Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_DETACHED); intent.putExtra(UsbManager.EXTRA_DEVICE, device); - Log.d(TAG, "usbDeviceRemoved, sending " + intent); + if (DEBUG) Slog.d(TAG, "usbDeviceRemoved, sending " + intent); mContext.sendBroadcast(intent); } @@ -604,7 +605,7 @@ class UsbSettingsManager { try { mContext.startActivity(dialogIntent); } catch (ActivityNotFoundException e) { - Log.e(TAG, "unable to start UsbAccessoryUriActivity"); + Slog.e(TAG, "unable to start UsbAccessoryUriActivity"); } } } @@ -652,7 +653,7 @@ class UsbSettingsManager { defaultRI.activityInfo.name)); mContext.startActivity(intent); } catch (ActivityNotFoundException e) { - Log.e(TAG, "startActivity failed", e); + Slog.e(TAG, "startActivity failed", e); } } else { Intent resolverIntent = new Intent(); @@ -679,7 +680,7 @@ class UsbSettingsManager { try { mContext.startActivity(resolverIntent); } catch (ActivityNotFoundException e) { - Log.e(TAG, "unable to start activity " + resolverIntent); + Slog.e(TAG, "unable to start activity " + resolverIntent); } } } @@ -733,7 +734,7 @@ class UsbSettingsManager { XmlUtils.nextElement(parser); } } catch (Exception e) { - Log.w(TAG, "Unable to load component info " + aInfo.toString(), e); + Slog.w(TAG, "Unable to load component info " + aInfo.toString(), e); } finally { if (parser != null) parser.close(); } @@ -751,7 +752,7 @@ class UsbSettingsManager { info = mPackageManager.getPackageInfo(packageName, PackageManager.GET_ACTIVITIES | PackageManager.GET_META_DATA); } catch (NameNotFoundException e) { - Log.e(TAG, "handlePackageUpdate could not find package " + packageName, e); + Slog.e(TAG, "handlePackageUpdate could not find package " + packageName, e); return; } @@ -831,7 +832,7 @@ class UsbSettingsManager { try { mContext.startActivity(intent); } catch (ActivityNotFoundException e) { - Log.e(TAG, "unable to start UsbPermissionActivity"); + Slog.e(TAG, "unable to start UsbPermissionActivity"); } finally { Binder.restoreCallingIdentity(identity); } @@ -847,7 +848,7 @@ class UsbSettingsManager { try { pi.send(mContext, 0, intent); } catch (PendingIntent.CanceledException e) { - Log.w(TAG, "requestPermission PendingIntent was cancelled"); + if (DEBUG) Slog.d(TAG, "requestPermission PendingIntent was cancelled"); } return; } @@ -867,7 +868,7 @@ class UsbSettingsManager { try { pi.send(mContext, 0, intent); } catch (PendingIntent.CanceledException e) { - Log.w(TAG, "requestPermission PendingIntent was cancelled"); + if (DEBUG) Slog.d(TAG, "requestPermission PendingIntent was cancelled"); } return; } diff --git a/tests/GridLayoutTest/res/layout/grid3.xml b/tests/GridLayoutTest/res/layout/grid3.xml index ba911c269e4e..536be7eefa03 100644 --- a/tests/GridLayoutTest/res/layout/grid3.xml +++ b/tests/GridLayoutTest/res/layout/grid3.xml @@ -66,8 +66,8 @@ <Space android:layout_row="4" android:layout_column="2" - android:layout_rowWeight="1" - android:layout_columnWeight="1" + android:layout_heightSpec="canStretch" + android:layout_widthSpec="canStretch" /> <Button diff --git a/tests/GridLayoutTest/src/com/android/test/layout/Activity2.java b/tests/GridLayoutTest/src/com/android/test/layout/Activity2.java index e010a007529f..cba98c2b7538 100644 --- a/tests/GridLayoutTest/src/com/android/test/layout/Activity2.java +++ b/tests/GridLayoutTest/src/com/android/test/layout/Activity2.java @@ -97,8 +97,8 @@ public class Activity2 extends Activity { Space v = new Space(context); { LayoutParams lp = new LayoutParams(row5, col3); - lp.rowWeight = 1; - lp.columnWeight = 1; + lp.widthSpec = CAN_STRETCH; + lp.heightSpec = CAN_STRETCH; vg.addView(v, lp); } } |