summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/current.txt19
-rw-r--r--core/java/android/widget/GridLayout.java725
-rw-r--r--core/res/res/layout/keyguard_screen_password_landscape.xml2
-rw-r--r--core/res/res/layout/keyguard_screen_tab_unlock_land.xml5
-rw-r--r--core/res/res/layout/keyguard_screen_unlock_landscape.xml8
-rw-r--r--core/res/res/layout/keyguard_screen_unlock_portrait.xml2
-rwxr-xr-xcore/res/res/values/attrs.xml38
-rw-r--r--core/res/res/values/public.xml6
-rwxr-xr-xcore/res/res/values/strings.xml10
-rw-r--r--packages/SystemUI/AndroidManifest.xml5
-rw-r--r--packages/SystemUI/res/drawable-hdpi/global_screenshot_background.9.pngbin0 -> 358 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/global_screenshot_background.9.pngbin0 -> 358 bytes
-rw-r--r--packages/SystemUI/res/layout/global_screenshot.xml40
-rw-r--r--packages/SystemUI/res/values/strings.xml5
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java384
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java50
-rwxr-xr-xpolicy/src/com/android/internal/policy/impl/PhoneWindowManager.java56
-rw-r--r--services/java/com/android/server/usb/UsbDeviceManager.java254
-rw-r--r--services/java/com/android/server/usb/UsbHostManager.java5
-rw-r--r--services/java/com/android/server/usb/UsbSettingsManager.java33
-rw-r--r--tests/GridLayoutTest/res/layout/grid3.xml4
-rw-r--r--tests/GridLayoutTest/src/com/android/test/layout/Activity2.java4
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
new file mode 100644
index 000000000000..e14111dae168
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/global_screenshot_background.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/global_screenshot_background.9.png b/packages/SystemUI/res/drawable-mdpi/global_screenshot_background.9.png
new file mode 100644
index 000000000000..e14111dae168
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/global_screenshot_background.9.png
Binary files differ
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);
}
}