Merge "Add GlobalActionsColumnLayout to replace HardwareUILayout." into qt-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
dc22284752
68
packages/SystemUI/res/layout-land/global_actions_column.xml
Normal file
68
packages/SystemUI/res/layout-land/global_actions_column.xml
Normal file
@@ -0,0 +1,68 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2019 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
|
||||
-->
|
||||
|
||||
<com.android.systemui.globalactions.GlobalActionsColumnLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@id/global_actions_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="horizontal"
|
||||
android:clipToPadding="false"
|
||||
android:theme="@style/qs_theme"
|
||||
android:gravity="center_horizontal | top"
|
||||
android:clipChildren="false"
|
||||
>
|
||||
<LinearLayout
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="wrap_content"
|
||||
android:padding="0dp"
|
||||
android:orientation="horizontal"
|
||||
android:clipChildren="false"
|
||||
android:clipToPadding="false"
|
||||
>
|
||||
<!-- Grid of action items -->
|
||||
<LinearLayout
|
||||
android:id="@android:id/list"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginTop="@dimen/global_actions_grid_side_margin"
|
||||
android:translationZ="@dimen/global_actions_translate"
|
||||
android:paddingLeft="@dimen/global_actions_grid_horizontal_padding"
|
||||
android:paddingRight="@dimen/global_actions_grid_horizontal_padding"
|
||||
android:paddingTop="@dimen/global_actions_grid_vertical_padding"
|
||||
android:paddingBottom="@dimen/global_actions_grid_vertical_padding"
|
||||
android:background="?android:attr/colorBackgroundFloating"
|
||||
/>
|
||||
<!-- For separated items-->
|
||||
<LinearLayout
|
||||
android:id="@+id/separated_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/global_actions_grid_side_margin"
|
||||
android:layout_marginTop="@dimen/global_actions_grid_side_margin"
|
||||
android:paddingLeft="@dimen/global_actions_grid_horizontal_padding"
|
||||
android:paddingRight="@dimen/global_actions_grid_horizontal_padding"
|
||||
android:paddingTop="@dimen/global_actions_grid_vertical_padding"
|
||||
android:paddingBottom="@dimen/global_actions_grid_vertical_padding"
|
||||
android:orientation="horizontal"
|
||||
android:background="?android:attr/colorBackgroundFloating"
|
||||
android:translationZ="@dimen/global_actions_translate"
|
||||
/>
|
||||
</LinearLayout>
|
||||
|
||||
</com.android.systemui.globalactions.GlobalActionsColumnLayout>
|
||||
@@ -0,0 +1,68 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2019 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
|
||||
-->
|
||||
|
||||
<com.android.systemui.globalactions.GlobalActionsColumnLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@id/global_actions_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="horizontal"
|
||||
android:clipToPadding="false"
|
||||
android:theme="@style/qs_theme"
|
||||
android:gravity="center_horizontal | bottom"
|
||||
android:clipChildren="false"
|
||||
>
|
||||
<LinearLayout
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="wrap_content"
|
||||
android:padding="0dp"
|
||||
android:orientation="horizontal"
|
||||
>
|
||||
<!-- Grid of action items -->
|
||||
<com.android.systemui.globalactions.ListGridLayout
|
||||
android:id="@android:id/list"
|
||||
android:layout_gravity="bottom|left"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginBottom="@dimen/global_actions_grid_side_margin"
|
||||
android:translationZ="@dimen/global_actions_translate"
|
||||
android:paddingLeft="@dimen/global_actions_grid_horizontal_padding"
|
||||
android:paddingRight="@dimen/global_actions_grid_horizontal_padding"
|
||||
android:paddingTop="@dimen/global_actions_grid_vertical_padding"
|
||||
android:paddingBottom="@dimen/global_actions_grid_vertical_padding"
|
||||
android:background="?android:attr/colorBackgroundFloating"
|
||||
/>
|
||||
<!-- For separated items-->
|
||||
<LinearLayout
|
||||
android:id="@+id/separated_button"
|
||||
android:layout_gravity="top|left"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginRight="@dimen/global_actions_grid_side_margin"
|
||||
android:layout_marginBottom="@dimen/global_actions_grid_side_margin"
|
||||
android:paddingLeft="@dimen/global_actions_grid_horizontal_padding"
|
||||
android:paddingRight="@dimen/global_actions_grid_horizontal_padding"
|
||||
android:paddingTop="@dimen/global_actions_grid_vertical_padding"
|
||||
android:paddingBottom="@dimen/global_actions_grid_vertical_padding"
|
||||
android:orientation="horizontal"
|
||||
android:background="?android:attr/colorBackgroundFloating"
|
||||
android:translationZ="@dimen/global_actions_translate"
|
||||
/>
|
||||
</LinearLayout>
|
||||
|
||||
</com.android.systemui.globalactions.GlobalActionsColumnLayout>
|
||||
70
packages/SystemUI/res/layout/global_actions_column.xml
Normal file
70
packages/SystemUI/res/layout/global_actions_column.xml
Normal file
@@ -0,0 +1,70 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2019 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
|
||||
-->
|
||||
|
||||
<com.android.systemui.globalactions.GlobalActionsColumnLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@id/global_actions_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:clipToPadding="false"
|
||||
android:theme="@style/qs_theme"
|
||||
android:gravity="center_vertical | right"
|
||||
android:clipChildren="false"
|
||||
>
|
||||
<LinearLayout
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="wrap_content"
|
||||
android:gravity="top | right"
|
||||
android:orientation="vertical"
|
||||
android:padding="0dp"
|
||||
android:layout_marginTop="@dimen/global_actions_grid_container_bottom_margin"
|
||||
android:layout_marginBottom="@dimen/global_actions_grid_container_bottom_margin"
|
||||
>
|
||||
<!-- Global actions is right-aligned to be physically near power button -->
|
||||
<LinearLayout
|
||||
android:id="@android:id/list"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:gravity="right"
|
||||
android:translationZ="@dimen/global_actions_translate"
|
||||
android:layout_marginRight="@dimen/global_actions_grid_side_margin"
|
||||
android:paddingLeft="@dimen/global_actions_grid_horizontal_padding"
|
||||
android:paddingRight="@dimen/global_actions_grid_horizontal_padding"
|
||||
android:paddingTop="@dimen/global_actions_grid_vertical_padding"
|
||||
android:paddingBottom="@dimen/global_actions_grid_vertical_padding"
|
||||
/>
|
||||
|
||||
<!-- For separated items-->
|
||||
<LinearLayout
|
||||
android:id="@+id/separated_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/global_actions_grid_side_margin"
|
||||
android:layout_marginRight="@dimen/global_actions_grid_side_margin"
|
||||
android:paddingLeft="@dimen/global_actions_grid_horizontal_padding"
|
||||
android:paddingRight="@dimen/global_actions_grid_horizontal_padding"
|
||||
android:paddingTop="@dimen/global_actions_grid_vertical_padding"
|
||||
android:paddingBottom="@dimen/global_actions_grid_vertical_padding"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center"
|
||||
android:translationZ="@dimen/global_actions_translate"
|
||||
/>
|
||||
</LinearLayout>
|
||||
|
||||
</com.android.systemui.globalactions.GlobalActionsColumnLayout>
|
||||
@@ -79,16 +79,6 @@ public class HardwareUiLayout extends MultiListLayout implements Tunable {
|
||||
return findViewById(android.R.id.list);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAllItems() {
|
||||
if (mList != null) {
|
||||
mList.removeAllViews();
|
||||
}
|
||||
if (mSeparatedView != null) {
|
||||
mSeparatedView.removeAllViews();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAttachedToWindow() {
|
||||
super.onAttachedToWindow();
|
||||
|
||||
@@ -44,11 +44,6 @@ public abstract class MultiListLayout extends LinearLayout {
|
||||
|
||||
protected abstract ViewGroup getListView();
|
||||
|
||||
/**
|
||||
* Removes all child items from the separated and list views, if they exist.
|
||||
*/
|
||||
protected abstract void removeAllItems();
|
||||
|
||||
/**
|
||||
* Sets the divided view, which may have a differently-colored background.
|
||||
*/
|
||||
@@ -110,6 +105,25 @@ public abstract class MultiListLayout extends LinearLayout {
|
||||
onUpdateList();
|
||||
}
|
||||
|
||||
protected void removeAllSeparatedViews() {
|
||||
ViewGroup separated = getSeparatedView();
|
||||
if (separated != null) {
|
||||
separated.removeAllViews();
|
||||
}
|
||||
}
|
||||
|
||||
protected void removeAllListViews() {
|
||||
ViewGroup list = getListView();
|
||||
if (list != null) {
|
||||
list.removeAllViews();
|
||||
}
|
||||
}
|
||||
|
||||
protected void removeAllItems() {
|
||||
removeAllListViews();
|
||||
removeAllSeparatedViews();
|
||||
}
|
||||
|
||||
protected void onUpdateList() {
|
||||
removeAllItems();
|
||||
setSeparatedViewVisibility(mAdapter.hasSeparatedItems());
|
||||
|
||||
@@ -0,0 +1,195 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.globalactions;
|
||||
|
||||
import static com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE;
|
||||
import static com.android.systemui.util.leak.RotationUtils.ROTATION_NONE;
|
||||
import static com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.systemui.R;
|
||||
|
||||
/**
|
||||
* Grid-based implementation of the button layout created by the global actions dialog.
|
||||
*/
|
||||
public class GlobalActionsColumnLayout extends GlobalActionsLayout {
|
||||
private boolean mLastSnap;
|
||||
|
||||
public GlobalActionsColumnLayout(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int l, int t, int r, int b) {
|
||||
super.onLayout(changed, l, t, r, b);
|
||||
|
||||
post(() -> updateSnap());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
protected boolean shouldReverseListItems() {
|
||||
int rotation = getCurrentRotation();
|
||||
if (rotation == ROTATION_NONE) {
|
||||
return false;
|
||||
}
|
||||
if (getCurrentLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
|
||||
return rotation == ROTATION_LANDSCAPE;
|
||||
}
|
||||
return rotation == ROTATION_SEASCAPE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdateList() {
|
||||
super.onUpdateList();
|
||||
updateChildOrdering();
|
||||
}
|
||||
|
||||
private void updateChildOrdering() {
|
||||
if (shouldReverseListItems()) {
|
||||
getListView().bringToFront();
|
||||
} else {
|
||||
getSeparatedView().bringToFront();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Snap this layout to align with the power button.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
protected void snapToPowerButton() {
|
||||
int offset = getPowerButtonOffsetDistance();
|
||||
switch (getCurrentRotation()) {
|
||||
case (ROTATION_LANDSCAPE):
|
||||
setPadding(offset, 0, 0, 0);
|
||||
setGravity(Gravity.LEFT | Gravity.TOP);
|
||||
break;
|
||||
case (ROTATION_SEASCAPE):
|
||||
setPadding(0, 0, offset, 0);
|
||||
setGravity(Gravity.RIGHT | Gravity.BOTTOM);
|
||||
break;
|
||||
default:
|
||||
setPadding(0, offset, 0, 0);
|
||||
setGravity(Gravity.TOP | Gravity.RIGHT);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Detach this layout from snapping to the power button and instead center along that edge.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
protected void centerAlongEdge() {
|
||||
switch (getCurrentRotation()) {
|
||||
case (ROTATION_LANDSCAPE):
|
||||
setPadding(0, 0, 0, 0);
|
||||
setGravity(Gravity.CENTER_HORIZONTAL | Gravity.TOP);
|
||||
break;
|
||||
case (ROTATION_SEASCAPE):
|
||||
setPadding(0, 0, 0, 0);
|
||||
setGravity(Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM);
|
||||
break;
|
||||
default:
|
||||
setPadding(0, 0, 0, 0);
|
||||
setGravity(Gravity.CENTER_VERTICAL | Gravity.RIGHT);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the distance from the top of the screen to the power button.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
protected int getPowerButtonOffsetDistance() {
|
||||
return Math.round(getContext().getResources().getDimension(
|
||||
R.dimen.global_actions_top_padding));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether there is enough extra space below the dialog such that we can offset the top
|
||||
* of the dialog from the top of the phone to line it up with the power button, then either
|
||||
* snap the dialog to the power button or center it along the edge with snapToPowerButton.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
protected boolean shouldSnapToPowerButton() {
|
||||
int offsetSize = getPowerButtonOffsetDistance();
|
||||
int dialogSize;
|
||||
int screenSize;
|
||||
View wrapper = getWrapper();
|
||||
int rotation = getCurrentRotation();
|
||||
if (rotation == ROTATION_NONE) {
|
||||
dialogSize = wrapper.getMeasuredHeight();
|
||||
screenSize = getMeasuredHeight();
|
||||
} else {
|
||||
dialogSize = wrapper.getMeasuredWidth();
|
||||
screenSize = getMeasuredWidth();
|
||||
}
|
||||
return dialogSize + offsetSize < screenSize;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
protected void updateSnap() {
|
||||
boolean snap = shouldSnapToPowerButton();
|
||||
if (snap != mLastSnap) {
|
||||
if (snap) {
|
||||
snapToPowerButton();
|
||||
} else {
|
||||
centerAlongEdge();
|
||||
}
|
||||
}
|
||||
mLastSnap = snap;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
protected float getGridItemSize() {
|
||||
return getContext().getResources().getDimension(R.dimen.global_actions_grid_item_height);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
protected float getAnimationDistance() {
|
||||
return getGridItemSize() / 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getAnimationOffsetX() {
|
||||
if (getCurrentRotation() == ROTATION_NONE) {
|
||||
return getAnimationDistance();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getAnimationOffsetY() {
|
||||
switch (getCurrentRotation()) {
|
||||
case ROTATION_LANDSCAPE:
|
||||
return -getAnimationDistance();
|
||||
case ROTATION_SEASCAPE:
|
||||
return getAnimationDistance();
|
||||
default: // Portrait
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1583,13 +1583,20 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
|
||||
}
|
||||
|
||||
private int getGlobalActionsLayoutId(Context context) {
|
||||
if (isForceGridEnabled(context) || shouldUsePanel()) {
|
||||
if (RotationUtils.getRotation(context) == RotationUtils.ROTATION_SEASCAPE) {
|
||||
boolean useGridLayout = isForceGridEnabled(context) || shouldUsePanel();
|
||||
if (RotationUtils.getRotation(context) == RotationUtils.ROTATION_SEASCAPE) {
|
||||
if (useGridLayout) {
|
||||
return com.android.systemui.R.layout.global_actions_grid_seascape;
|
||||
} else {
|
||||
return com.android.systemui.R.layout.global_actions_column_seascape;
|
||||
}
|
||||
} else {
|
||||
if (useGridLayout) {
|
||||
return com.android.systemui.R.layout.global_actions_grid;
|
||||
} else {
|
||||
return com.android.systemui.R.layout.global_actions_column;
|
||||
}
|
||||
return com.android.systemui.R.layout.global_actions_grid;
|
||||
}
|
||||
return com.android.systemui.R.layout.global_actions_wrapped;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1712,7 +1719,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
|
||||
}
|
||||
|
||||
public void onRotate(int from, int to) {
|
||||
if (mShowing && (shouldUsePanel() || isForceGridEnabled(mContext))) {
|
||||
if (mShowing) {
|
||||
refreshDialog();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,58 +22,23 @@ import static com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.systemui.HardwareBgDrawable;
|
||||
import com.android.systemui.MultiListLayout;
|
||||
import com.android.systemui.util.leak.RotationUtils;
|
||||
|
||||
/**
|
||||
* Grid-based implementation of the button layout created by the global actions dialog.
|
||||
*/
|
||||
public class GlobalActionsGridLayout extends MultiListLayout {
|
||||
|
||||
boolean mBackgroundsSet;
|
||||
|
||||
public class GlobalActionsGridLayout extends GlobalActionsLayout {
|
||||
public GlobalActionsGridLayout(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
private void setBackgrounds() {
|
||||
int gridBackgroundColor = getResources().getColor(
|
||||
com.android.systemui.R.color.global_actions_grid_background, null);
|
||||
int separatedBackgroundColor = getResources().getColor(
|
||||
com.android.systemui.R.color.global_actions_separated_background, null);
|
||||
HardwareBgDrawable listBackground = new HardwareBgDrawable(true, true, getContext());
|
||||
HardwareBgDrawable separatedBackground = new HardwareBgDrawable(true, true, getContext());
|
||||
listBackground.setTint(gridBackgroundColor);
|
||||
separatedBackground.setTint(separatedBackgroundColor);
|
||||
getListView().setBackground(listBackground);
|
||||
getSeparatedView().setBackground(separatedBackground);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
|
||||
// backgrounds set only once, the first time onMeasure is called after inflation
|
||||
if (getListView() != null && !mBackgroundsSet) {
|
||||
setBackgrounds();
|
||||
mBackgroundsSet = true;
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
protected int getCurrentRotation() {
|
||||
return RotationUtils.getRotation(mContext);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
protected void setupListView(ListGridLayout listView, int itemCount) {
|
||||
listView.setExpectedCount(itemCount);
|
||||
protected void setupListView() {
|
||||
ListGridLayout listView = getListView();
|
||||
listView.setExpectedCount(mAdapter.countListItems());
|
||||
listView.setReverseSublists(shouldReverseSublists());
|
||||
listView.setReverseItems(shouldReverseListItems());
|
||||
listView.setSwapRowsAndColumns(shouldSwapRowsAndColumns());
|
||||
@@ -81,29 +46,8 @@ public class GlobalActionsGridLayout extends MultiListLayout {
|
||||
|
||||
@Override
|
||||
public void onUpdateList() {
|
||||
setupListView();
|
||||
super.onUpdateList();
|
||||
|
||||
ViewGroup separatedView = getSeparatedView();
|
||||
ListGridLayout listView = getListView();
|
||||
setupListView(listView, mAdapter.countListItems());
|
||||
|
||||
for (int i = 0; i < mAdapter.getCount(); i++) {
|
||||
// generate the view item
|
||||
View v;
|
||||
boolean separated = mAdapter.shouldBeSeparated(i);
|
||||
if (separated) {
|
||||
v = mAdapter.getView(i, null, separatedView);
|
||||
} else {
|
||||
v = mAdapter.getView(i, null, listView);
|
||||
}
|
||||
Log.d("GlobalActionsGridLayout", "View: " + v);
|
||||
|
||||
if (separated) {
|
||||
separatedView.addView(v);
|
||||
} else {
|
||||
listView.addItem(v);
|
||||
}
|
||||
}
|
||||
updateSeparatedItemSize();
|
||||
}
|
||||
|
||||
@@ -111,7 +55,8 @@ public class GlobalActionsGridLayout extends MultiListLayout {
|
||||
* If the separated view contains only one item, expand the bounds of that item to take up the
|
||||
* entire view, so that the whole thing is touch-able.
|
||||
*/
|
||||
private void updateSeparatedItemSize() {
|
||||
@VisibleForTesting
|
||||
protected void updateSeparatedItemSize() {
|
||||
ViewGroup separated = getSeparatedView();
|
||||
if (separated.getChildCount() == 0) {
|
||||
return;
|
||||
@@ -129,13 +74,24 @@ public class GlobalActionsGridLayout extends MultiListLayout {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ViewGroup getSeparatedView() {
|
||||
return findViewById(com.android.systemui.R.id.separated_button);
|
||||
protected ListGridLayout getListView() {
|
||||
return (ListGridLayout) super.getListView();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ListGridLayout getListView() {
|
||||
return findViewById(android.R.id.list);
|
||||
protected void removeAllListViews() {
|
||||
ListGridLayout list = getListView();
|
||||
if (list != null) {
|
||||
list.removeAllItems();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addToListView(View v, boolean reverse) {
|
||||
ListGridLayout list = getListView();
|
||||
if (list != null) {
|
||||
list.addItem(v);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -174,12 +130,7 @@ public class GlobalActionsGridLayout extends MultiListLayout {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the ListGridLayout should reverse the ordering of items within sublists.
|
||||
* Used for RTL languages to ensure that items appear in the same positions, without having to
|
||||
* override layoutDirection, which breaks Talkback ordering.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
@Override
|
||||
protected boolean shouldReverseListItems() {
|
||||
int rotation = getCurrentRotation();
|
||||
boolean reverse = false; // should we add items to parents in the reverse order?
|
||||
@@ -187,20 +138,13 @@ public class GlobalActionsGridLayout extends MultiListLayout {
|
||||
|| rotation == ROTATION_SEASCAPE) {
|
||||
reverse = !reverse; // if we're in portrait or seascape, reverse items
|
||||
}
|
||||
if (getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
|
||||
if (getCurrentLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
|
||||
reverse = !reverse; // if we're in an RTL language, reverse items (again)
|
||||
}
|
||||
return reverse;
|
||||
}
|
||||
|
||||
/**
|
||||
* Not ued in this implementation of the Global Actions Menu, but necessary for some others.
|
||||
*/
|
||||
@Override
|
||||
public void setDivisionView(View v) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
protected float getAnimationDistance() {
|
||||
int rows = getListView().getRowCount();
|
||||
float gridItemSize = getContext().getResources().getDimension(
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.globalactions;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.TextUtils;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.systemui.HardwareBgDrawable;
|
||||
import com.android.systemui.MultiListLayout;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.util.leak.RotationUtils;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Grid-based implementation of the button layout created by the global actions dialog.
|
||||
*/
|
||||
public abstract class GlobalActionsLayout extends MultiListLayout {
|
||||
|
||||
boolean mBackgroundsSet;
|
||||
|
||||
public GlobalActionsLayout(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
private void setBackgrounds() {
|
||||
int gridBackgroundColor = getResources().getColor(
|
||||
R.color.global_actions_grid_background, null);
|
||||
int separatedBackgroundColor = getResources().getColor(
|
||||
R.color.global_actions_separated_background, null);
|
||||
HardwareBgDrawable listBackground = new HardwareBgDrawable(true, true, getContext());
|
||||
HardwareBgDrawable separatedBackground = new HardwareBgDrawable(true, true, getContext());
|
||||
listBackground.setTint(gridBackgroundColor);
|
||||
separatedBackground.setTint(separatedBackgroundColor);
|
||||
getListView().setBackground(listBackground);
|
||||
getSeparatedView().setBackground(separatedBackground);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
|
||||
// backgrounds set only once, the first time onMeasure is called after inflation
|
||||
if (getListView() != null && !mBackgroundsSet) {
|
||||
setBackgrounds();
|
||||
mBackgroundsSet = true;
|
||||
}
|
||||
}
|
||||
|
||||
protected void addToListView(View v, boolean reverse) {
|
||||
if (reverse) {
|
||||
getListView().addView(v, 0);
|
||||
} else {
|
||||
getListView().addView(v);
|
||||
}
|
||||
}
|
||||
|
||||
protected void addToSeparatedView(View v, boolean reverse) {
|
||||
if (reverse) {
|
||||
getSeparatedView().addView(v, 0);
|
||||
} else {
|
||||
getSeparatedView().addView(v);
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
protected int getCurrentLayoutDirection() {
|
||||
return TextUtils.getLayoutDirectionFromLocale(Locale.getDefault());
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
protected int getCurrentRotation() {
|
||||
return RotationUtils.getRotation(mContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the ListGridLayout should reverse the ordering of items within sublists.
|
||||
* Used for RTL languages to ensure that items appear in the same positions, without having to
|
||||
* override layoutDirection, which breaks Talkback ordering.
|
||||
*/
|
||||
protected abstract boolean shouldReverseListItems();
|
||||
|
||||
@Override
|
||||
public void onUpdateList() {
|
||||
super.onUpdateList();
|
||||
|
||||
ViewGroup separatedView = getSeparatedView();
|
||||
ViewGroup listView = getListView();
|
||||
|
||||
for (int i = 0; i < mAdapter.getCount(); i++) {
|
||||
// generate the view item
|
||||
View v;
|
||||
boolean separated = mAdapter.shouldBeSeparated(i);
|
||||
if (separated) {
|
||||
v = mAdapter.getView(i, null, separatedView);
|
||||
} else {
|
||||
v = mAdapter.getView(i, null, listView);
|
||||
}
|
||||
if (separated) {
|
||||
addToSeparatedView(v, false);
|
||||
} else {
|
||||
addToListView(v, shouldReverseListItems());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ViewGroup getSeparatedView() {
|
||||
return findViewById(R.id.separated_button);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ViewGroup getListView() {
|
||||
return findViewById(android.R.id.list);
|
||||
}
|
||||
|
||||
protected View getWrapper() {
|
||||
return getChildAt(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Not used in this implementation of the Global Actions Menu, but necessary for some others.
|
||||
*/
|
||||
@Override
|
||||
public void setDivisionView(View v) {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.globalactions;
|
||||
|
||||
import static junit.framework.Assert.assertEquals;
|
||||
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
import com.android.systemui.util.leak.RotationUtils;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
/**
|
||||
* Tests for {@link ListGridLayout}.
|
||||
*/
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
public class GlobalActionsColumnLayoutTest extends SysuiTestCase {
|
||||
|
||||
private GlobalActionsColumnLayout mColumnLayout;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
mColumnLayout = spy((GlobalActionsColumnLayout)
|
||||
LayoutInflater.from(mContext).inflate(R.layout.global_actions_column, null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShouldReverseListItems() {
|
||||
doReturn(View.LAYOUT_DIRECTION_LTR).when(mColumnLayout).getCurrentLayoutDirection();
|
||||
|
||||
doReturn(RotationUtils.ROTATION_LANDSCAPE).when(mColumnLayout).getCurrentRotation();
|
||||
assertEquals(false, mColumnLayout.shouldReverseListItems());
|
||||
|
||||
doReturn(RotationUtils.ROTATION_NONE).when(mColumnLayout).getCurrentRotation();
|
||||
assertEquals(false, mColumnLayout.shouldReverseListItems());
|
||||
|
||||
doReturn(RotationUtils.ROTATION_SEASCAPE).when(mColumnLayout).getCurrentRotation();
|
||||
assertEquals(true, mColumnLayout.shouldReverseListItems());
|
||||
|
||||
doReturn(View.LAYOUT_DIRECTION_RTL).when(mColumnLayout).getCurrentLayoutDirection();
|
||||
|
||||
doReturn(RotationUtils.ROTATION_LANDSCAPE).when(mColumnLayout).getCurrentRotation();
|
||||
assertEquals(true, mColumnLayout.shouldReverseListItems());
|
||||
|
||||
doReturn(RotationUtils.ROTATION_NONE).when(mColumnLayout).getCurrentRotation();
|
||||
assertEquals(false, mColumnLayout.shouldReverseListItems());
|
||||
|
||||
doReturn(RotationUtils.ROTATION_SEASCAPE).when(mColumnLayout).getCurrentRotation();
|
||||
assertEquals(false, mColumnLayout.shouldReverseListItems());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAnimationOffsetX() {
|
||||
doReturn(50f).when(mColumnLayout).getAnimationDistance();
|
||||
|
||||
doReturn(RotationUtils.ROTATION_NONE).when(mColumnLayout).getCurrentRotation();
|
||||
assertEquals(50f, mColumnLayout.getAnimationOffsetX(), .01);
|
||||
|
||||
doReturn(RotationUtils.ROTATION_LANDSCAPE).when(mColumnLayout).getCurrentRotation();
|
||||
assertEquals(0, mColumnLayout.getAnimationOffsetX(), .01);
|
||||
|
||||
doReturn(RotationUtils.ROTATION_SEASCAPE).when(mColumnLayout).getCurrentRotation();
|
||||
assertEquals(0, mColumnLayout.getAnimationOffsetX(), .01);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAnimationOffsetY() {
|
||||
doReturn(50f).when(mColumnLayout).getAnimationDistance();
|
||||
|
||||
doReturn(RotationUtils.ROTATION_NONE).when(mColumnLayout).getCurrentRotation();
|
||||
assertEquals(0, mColumnLayout.getAnimationOffsetY(), .01);
|
||||
|
||||
doReturn(RotationUtils.ROTATION_LANDSCAPE).when(mColumnLayout).getCurrentRotation();
|
||||
assertEquals(-50f, mColumnLayout.getAnimationOffsetY(), .01);
|
||||
|
||||
doReturn(RotationUtils.ROTATION_SEASCAPE).when(mColumnLayout).getCurrentRotation();
|
||||
assertEquals(50f, mColumnLayout.getAnimationOffsetY(), .01);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSnapToPowerButton_portrait() {
|
||||
doReturn(RotationUtils.ROTATION_NONE).when(mColumnLayout).getCurrentRotation();
|
||||
doReturn(50).when(mColumnLayout).getPowerButtonOffsetDistance();
|
||||
|
||||
mColumnLayout.snapToPowerButton();
|
||||
assertEquals(Gravity.TOP | Gravity.RIGHT, mColumnLayout.getGravity());
|
||||
assertEquals(50, mColumnLayout.getPaddingTop(), .01);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCenterAlongEdge_portrait() {
|
||||
doReturn(RotationUtils.ROTATION_NONE).when(mColumnLayout).getCurrentRotation();
|
||||
|
||||
mColumnLayout.centerAlongEdge();
|
||||
assertEquals(Gravity.CENTER_VERTICAL | Gravity.RIGHT, mColumnLayout.getGravity());
|
||||
assertEquals(0, mColumnLayout.getPaddingTop(), .01);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateSnap_initialState() {
|
||||
doReturn(false).when(mColumnLayout).shouldSnapToPowerButton();
|
||||
|
||||
mColumnLayout.updateSnap(); // should do nothing, since snap has not changed from init state
|
||||
|
||||
verify(mColumnLayout, times(0)).snapToPowerButton();
|
||||
verify(mColumnLayout, times(0)).centerAlongEdge();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateSnap_snapThenSnap() {
|
||||
doReturn(true).when(mColumnLayout).shouldSnapToPowerButton();
|
||||
|
||||
mColumnLayout.updateSnap(); // should snap to power button
|
||||
|
||||
verify(mColumnLayout, times(1)).snapToPowerButton();
|
||||
verify(mColumnLayout, times(0)).centerAlongEdge();
|
||||
|
||||
mColumnLayout.updateSnap(); // should do nothing, since this is the same state as last time
|
||||
|
||||
verify(mColumnLayout, times(1)).snapToPowerButton();
|
||||
verify(mColumnLayout, times(0)).centerAlongEdge();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateSnap_snapThenCenter() {
|
||||
doReturn(true).when(mColumnLayout).shouldSnapToPowerButton();
|
||||
|
||||
mColumnLayout.updateSnap(); // should snap to power button
|
||||
|
||||
verify(mColumnLayout, times(1)).snapToPowerButton();
|
||||
verify(mColumnLayout, times(0)).centerAlongEdge();
|
||||
|
||||
doReturn(false).when(mColumnLayout).shouldSnapToPowerButton();
|
||||
|
||||
mColumnLayout.updateSnap(); // should center to edge
|
||||
|
||||
verify(mColumnLayout, times(1)).snapToPowerButton();
|
||||
verify(mColumnLayout, times(1)).centerAlongEdge();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShouldSnapToPowerButton_vertical() {
|
||||
doReturn(RotationUtils.ROTATION_NONE).when(mColumnLayout).getCurrentRotation();
|
||||
doReturn(300).when(mColumnLayout).getPowerButtonOffsetDistance();
|
||||
doReturn(1000).when(mColumnLayout).getMeasuredHeight();
|
||||
View wrapper = spy(new View(mContext, null));
|
||||
doReturn(wrapper).when(mColumnLayout).getWrapper();
|
||||
doReturn(500).when(wrapper).getMeasuredHeight();
|
||||
|
||||
assertEquals(true, mColumnLayout.shouldSnapToPowerButton());
|
||||
|
||||
doReturn(600).when(mColumnLayout).getMeasuredHeight();
|
||||
|
||||
assertEquals(false, mColumnLayout.shouldSnapToPowerButton());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShouldSnapToPowerButton_horizontal() {
|
||||
doReturn(RotationUtils.ROTATION_LANDSCAPE).when(mColumnLayout).getCurrentRotation();
|
||||
doReturn(300).when(mColumnLayout).getPowerButtonOffsetDistance();
|
||||
doReturn(1000).when(mColumnLayout).getMeasuredWidth();
|
||||
View wrapper = spy(new View(mContext, null));
|
||||
doReturn(wrapper).when(mColumnLayout).getWrapper();
|
||||
doReturn(500).when(wrapper).getMeasuredWidth();
|
||||
|
||||
assertEquals(true, mColumnLayout.shouldSnapToPowerButton());
|
||||
|
||||
doReturn(600).when(mColumnLayout).getMeasuredWidth();
|
||||
|
||||
assertEquals(false, mColumnLayout.shouldSnapToPowerButton());
|
||||
}
|
||||
}
|
||||
@@ -18,12 +18,8 @@ package com.android.systemui.globalactions;
|
||||
|
||||
import static junit.framework.Assert.assertEquals;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.view.LayoutInflater;
|
||||
@@ -32,7 +28,6 @@ import android.view.ViewGroup;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.systemui.MultiListLayout;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
import com.android.systemui.util.leak.RotationUtils;
|
||||
@@ -49,61 +44,12 @@ import org.junit.runner.RunWith;
|
||||
public class GlobalActionsGridLayoutTest extends SysuiTestCase {
|
||||
|
||||
private GlobalActionsGridLayout mGridLayout;
|
||||
private TestAdapter mAdapter;
|
||||
private ListGridLayout mListGrid;
|
||||
|
||||
private class TestAdapter extends MultiListLayout.MultiListAdapter {
|
||||
@Override
|
||||
public void onClickItem(int index) { }
|
||||
|
||||
@Override
|
||||
public boolean onLongClickItem(int index) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int countSeparatedItems() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int countListItems() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldBeSeparated(int position) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return countSeparatedItems() + countListItems();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getItem(int position) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
mGridLayout = spy((GlobalActionsGridLayout)
|
||||
LayoutInflater.from(mContext).inflate(R.layout.global_actions_grid, null));
|
||||
mAdapter = spy(new TestAdapter());
|
||||
mGridLayout.setAdapter(mAdapter);
|
||||
mListGrid = spy(mGridLayout.getListView());
|
||||
doReturn(mListGrid).when(mGridLayout).getListView();
|
||||
}
|
||||
@@ -122,7 +68,7 @@ public class GlobalActionsGridLayoutTest extends SysuiTestCase {
|
||||
|
||||
@Test
|
||||
public void testShouldReverseListItems() {
|
||||
doReturn(View.LAYOUT_DIRECTION_LTR).when(mGridLayout).getLayoutDirection();
|
||||
doReturn(View.LAYOUT_DIRECTION_LTR).when(mGridLayout).getCurrentLayoutDirection();
|
||||
|
||||
doReturn(RotationUtils.ROTATION_LANDSCAPE).when(mGridLayout).getCurrentRotation();
|
||||
assertEquals(false, mGridLayout.shouldReverseListItems());
|
||||
@@ -133,7 +79,7 @@ public class GlobalActionsGridLayoutTest extends SysuiTestCase {
|
||||
doReturn(RotationUtils.ROTATION_SEASCAPE).when(mGridLayout).getCurrentRotation();
|
||||
assertEquals(true, mGridLayout.shouldReverseListItems());
|
||||
|
||||
doReturn(View.LAYOUT_DIRECTION_RTL).when(mGridLayout).getLayoutDirection();
|
||||
doReturn(View.LAYOUT_DIRECTION_RTL).when(mGridLayout).getCurrentLayoutDirection();
|
||||
|
||||
doReturn(RotationUtils.ROTATION_LANDSCAPE).when(mGridLayout).getCurrentRotation();
|
||||
assertEquals(true, mGridLayout.shouldReverseListItems());
|
||||
@@ -185,123 +131,26 @@ public class GlobalActionsGridLayoutTest extends SysuiTestCase {
|
||||
assertEquals(0f, mGridLayout.getAnimationOffsetY(), .01);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void testOnUpdateList_noAdapter() {
|
||||
mGridLayout.setAdapter(null);
|
||||
mGridLayout.updateList();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnUpdateList_noItems() {
|
||||
doReturn(0).when(mAdapter).countSeparatedItems();
|
||||
doReturn(0).when(mAdapter).countListItems();
|
||||
mGridLayout.updateList();
|
||||
|
||||
ViewGroup separatedView = mGridLayout.getSeparatedView();
|
||||
ListGridLayout listView = mGridLayout.getListView();
|
||||
|
||||
assertEquals(0, separatedView.getChildCount());
|
||||
assertEquals(View.GONE, separatedView.getVisibility());
|
||||
|
||||
verify(mListGrid, times(0)).addItem(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnUpdateList_resizesFirstSeparatedItem() {
|
||||
doReturn(1).when(mAdapter).countSeparatedItems();
|
||||
doReturn(0).when(mAdapter).countListItems();
|
||||
public void testUpdateSeparatedItemSize() {
|
||||
View firstView = new View(mContext, null);
|
||||
View secondView = new View(mContext, null);
|
||||
|
||||
doReturn(firstView).when(mAdapter).getView(eq(0), any(), any());
|
||||
doReturn(true).when(mAdapter).shouldBeSeparated(0);
|
||||
ViewGroup separatedView = mGridLayout.getSeparatedView();
|
||||
separatedView.addView(firstView);
|
||||
|
||||
mGridLayout.updateList();
|
||||
mGridLayout.updateSeparatedItemSize();
|
||||
|
||||
ViewGroup.LayoutParams childParams = firstView.getLayoutParams();
|
||||
assertEquals(ViewGroup.LayoutParams.MATCH_PARENT, childParams.width);
|
||||
assertEquals(ViewGroup.LayoutParams.MATCH_PARENT, childParams.height);
|
||||
|
||||
doReturn(2).when(mAdapter).countSeparatedItems();
|
||||
doReturn(secondView).when(mAdapter).getView(eq(1), any(), any());
|
||||
doReturn(true).when(mAdapter).shouldBeSeparated(1);
|
||||
separatedView.addView(secondView);
|
||||
|
||||
mGridLayout.updateList();
|
||||
mGridLayout.updateSeparatedItemSize();
|
||||
|
||||
childParams = firstView.getLayoutParams();
|
||||
assertEquals(ViewGroup.LayoutParams.WRAP_CONTENT, childParams.width);
|
||||
assertEquals(ViewGroup.LayoutParams.WRAP_CONTENT, childParams.height);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnUpdateList_onlySeparatedItems() {
|
||||
doReturn(1).when(mAdapter).countSeparatedItems();
|
||||
doReturn(0).when(mAdapter).countListItems();
|
||||
View testView = new View(mContext, null);
|
||||
doReturn(testView).when(mAdapter).getView(eq(0), any(), any());
|
||||
doReturn(true).when(mAdapter).shouldBeSeparated(0);
|
||||
|
||||
mGridLayout.updateList();
|
||||
|
||||
verify(mListGrid, times(0)).addItem(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnUpdateList_oneSeparatedOneList() {
|
||||
doReturn(1).when(mAdapter).countSeparatedItems();
|
||||
doReturn(1).when(mAdapter).countListItems();
|
||||
View view1 = new View(mContext, null);
|
||||
View view2 = new View(mContext, null);
|
||||
|
||||
doReturn(view1).when(mAdapter).getView(eq(0), any(), any());
|
||||
doReturn(true).when(mAdapter).shouldBeSeparated(0);
|
||||
|
||||
doReturn(view2).when(mAdapter).getView(eq(1), any(), any());
|
||||
doReturn(false).when(mAdapter).shouldBeSeparated(1);
|
||||
|
||||
mGridLayout.updateList();
|
||||
|
||||
ViewGroup separatedView = mGridLayout.getSeparatedView();
|
||||
|
||||
assertEquals(1, separatedView.getChildCount());
|
||||
assertEquals(View.VISIBLE, separatedView.getVisibility());
|
||||
assertEquals(view1, separatedView.getChildAt(0));
|
||||
|
||||
verify(mListGrid, times(1)).addItem(view2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnUpdateList_fourInList() {
|
||||
doReturn(0).when(mAdapter).countSeparatedItems();
|
||||
doReturn(4).when(mAdapter).countListItems();
|
||||
View view1 = new View(mContext, null);
|
||||
View view2 = new View(mContext, null);
|
||||
View view3 = new View(mContext, null);
|
||||
View view4 = new View(mContext, null);
|
||||
|
||||
doReturn(view1).when(mAdapter).getView(eq(0), any(), any());
|
||||
doReturn(false).when(mAdapter).shouldBeSeparated(0);
|
||||
|
||||
doReturn(view2).when(mAdapter).getView(eq(1), any(), any());
|
||||
doReturn(false).when(mAdapter).shouldBeSeparated(1);
|
||||
|
||||
doReturn(view3).when(mAdapter).getView(eq(2), any(), any());
|
||||
doReturn(false).when(mAdapter).shouldBeSeparated(2);
|
||||
|
||||
doReturn(view4).when(mAdapter).getView(eq(3), any(), any());
|
||||
doReturn(false).when(mAdapter).shouldBeSeparated(3);
|
||||
|
||||
mGridLayout.updateList();
|
||||
|
||||
ViewGroup separatedView = mGridLayout.getSeparatedView();
|
||||
assertEquals(0, separatedView.getChildCount());
|
||||
assertEquals(View.GONE, separatedView.getVisibility());
|
||||
|
||||
verify(mListGrid, times(1)).addItem(view1);
|
||||
verify(mListGrid, times(1)).addItem(view2);
|
||||
verify(mListGrid, times(1)).addItem(view3);
|
||||
verify(mListGrid, times(1)).addItem(view4);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,303 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.globalactions;
|
||||
|
||||
import static junit.framework.Assert.assertEquals;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.spy;
|
||||
|
||||
import android.content.Context;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.systemui.MultiListLayout;
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Tests for {@link ListGridLayout}.
|
||||
*/
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
public class GlobalActionsLayoutTest extends SysuiTestCase {
|
||||
|
||||
private TestLayout mLayout;
|
||||
private TestAdapter mAdapter;
|
||||
|
||||
private class TestAdapter extends MultiListLayout.MultiListAdapter {
|
||||
@Override
|
||||
public void onClickItem(int index) { }
|
||||
|
||||
@Override
|
||||
public boolean onLongClickItem(int index) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int countSeparatedItems() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int countListItems() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldBeSeparated(int position) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return countSeparatedItems() + countListItems();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getItem(int position) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private class TestLayout extends GlobalActionsLayout {
|
||||
ArrayList<View> mSeparatedViews = new ArrayList<>();
|
||||
ArrayList<View> mListViews = new ArrayList<>();
|
||||
boolean mSeparatedViewVisible = false;
|
||||
|
||||
TestLayout(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean shouldReverseListItems() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getAnimationOffsetX() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getAnimationOffsetY() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addToListView(View v, boolean reverse) {
|
||||
if (reverse) {
|
||||
mListViews.add(0, v);
|
||||
} else {
|
||||
mListViews.add(v);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addToSeparatedView(View v, boolean reverse) {
|
||||
if (reverse) {
|
||||
mSeparatedViews.add(0, v);
|
||||
} else {
|
||||
mSeparatedViews.add(v);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setSeparatedViewVisibility(boolean visible) {
|
||||
mSeparatedViewVisible = visible;
|
||||
}
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
mLayout = spy(new TestLayout(mContext, null));
|
||||
mAdapter = spy(new TestAdapter());
|
||||
mLayout.setAdapter(mAdapter);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void testOnUpdateList_noAdapter() {
|
||||
mLayout.setAdapter(null);
|
||||
mLayout.updateList();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnUpdateList_noItems() {
|
||||
doReturn(0).when(mAdapter).countSeparatedItems();
|
||||
doReturn(0).when(mAdapter).countListItems();
|
||||
mLayout.updateList();
|
||||
|
||||
assertEquals(0, mLayout.mSeparatedViews.size());
|
||||
assertEquals(0, mLayout.mListViews.size());
|
||||
|
||||
assertEquals(false, mLayout.mSeparatedViewVisible);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnUpdateList_oneSeparatedOneList() {
|
||||
doReturn(1).when(mAdapter).countSeparatedItems();
|
||||
doReturn(1).when(mAdapter).countListItems();
|
||||
View view1 = new View(mContext, null);
|
||||
View view2 = new View(mContext, null);
|
||||
|
||||
doReturn(view1).when(mAdapter).getView(eq(0), any(), any());
|
||||
doReturn(true).when(mAdapter).shouldBeSeparated(0);
|
||||
|
||||
doReturn(view2).when(mAdapter).getView(eq(1), any(), any());
|
||||
doReturn(false).when(mAdapter).shouldBeSeparated(1);
|
||||
|
||||
mLayout.updateList();
|
||||
|
||||
assertEquals(1, mLayout.mSeparatedViews.size());
|
||||
assertEquals(1, mLayout.mListViews.size());
|
||||
assertEquals(view1, mLayout.mSeparatedViews.get(0));
|
||||
assertEquals(view2, mLayout.mListViews.get(0));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testOnUpdateList_twoSeparatedItems() {
|
||||
doReturn(2).when(mAdapter).countSeparatedItems();
|
||||
doReturn(0).when(mAdapter).countListItems();
|
||||
View view1 = new View(mContext, null);
|
||||
View view2 = new View(mContext, null);
|
||||
|
||||
doReturn(view1).when(mAdapter).getView(eq(0), any(), any());
|
||||
doReturn(true).when(mAdapter).shouldBeSeparated(0);
|
||||
doReturn(view2).when(mAdapter).getView(eq(1), any(), any());
|
||||
doReturn(true).when(mAdapter).shouldBeSeparated(1);
|
||||
|
||||
mLayout.updateList();
|
||||
|
||||
assertEquals(2, mLayout.mSeparatedViews.size());
|
||||
assertEquals(0, mLayout.mListViews.size());
|
||||
|
||||
assertEquals(view1, mLayout.mSeparatedViews.get(0));
|
||||
assertEquals(view2, mLayout.mSeparatedViews.get(1));
|
||||
|
||||
// if separated view has items in it, should be made visible
|
||||
assertEquals(true, mLayout.mSeparatedViewVisible);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnUpdateList_twoSeparatedItems_reverse() {
|
||||
doReturn(2).when(mAdapter).countSeparatedItems();
|
||||
doReturn(0).when(mAdapter).countListItems();
|
||||
doReturn(true).when(mLayout).shouldReverseListItems();
|
||||
View view1 = new View(mContext, null);
|
||||
View view2 = new View(mContext, null);
|
||||
|
||||
doReturn(view1).when(mAdapter).getView(eq(0), any(), any());
|
||||
doReturn(true).when(mAdapter).shouldBeSeparated(0);
|
||||
|
||||
doReturn(view2).when(mAdapter).getView(eq(1), any(), any());
|
||||
doReturn(true).when(mAdapter).shouldBeSeparated(1);
|
||||
|
||||
mLayout.updateList();
|
||||
|
||||
assertEquals(2, mLayout.mSeparatedViews.size());
|
||||
assertEquals(0, mLayout.mListViews.size());
|
||||
|
||||
// separated view items are not reversed in current implementation, and this is intentional!
|
||||
assertEquals(view1, mLayout.mSeparatedViews.get(0));
|
||||
assertEquals(view2, mLayout.mSeparatedViews.get(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnUpdateList_fourInList() {
|
||||
doReturn(0).when(mAdapter).countSeparatedItems();
|
||||
doReturn(4).when(mAdapter).countListItems();
|
||||
View view1 = new View(mContext, null);
|
||||
View view2 = new View(mContext, null);
|
||||
View view3 = new View(mContext, null);
|
||||
View view4 = new View(mContext, null);
|
||||
|
||||
doReturn(view1).when(mAdapter).getView(eq(0), any(), any());
|
||||
doReturn(false).when(mAdapter).shouldBeSeparated(0);
|
||||
|
||||
doReturn(view2).when(mAdapter).getView(eq(1), any(), any());
|
||||
doReturn(false).when(mAdapter).shouldBeSeparated(1);
|
||||
|
||||
doReturn(view3).when(mAdapter).getView(eq(2), any(), any());
|
||||
doReturn(false).when(mAdapter).shouldBeSeparated(2);
|
||||
|
||||
doReturn(view4).when(mAdapter).getView(eq(3), any(), any());
|
||||
doReturn(false).when(mAdapter).shouldBeSeparated(3);
|
||||
|
||||
mLayout.updateList();
|
||||
|
||||
assertEquals(0, mLayout.mSeparatedViews.size());
|
||||
assertEquals(4, mLayout.mListViews.size());
|
||||
assertEquals(view1, mLayout.mListViews.get(0));
|
||||
assertEquals(view2, mLayout.mListViews.get(1));
|
||||
assertEquals(view3, mLayout.mListViews.get(2));
|
||||
assertEquals(view4, mLayout.mListViews.get(3));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnUpdateList_fourInList_reverse() {
|
||||
doReturn(0).when(mAdapter).countSeparatedItems();
|
||||
doReturn(4).when(mAdapter).countListItems();
|
||||
doReturn(true).when(mLayout).shouldReverseListItems();
|
||||
View view1 = new View(mContext, null);
|
||||
View view2 = new View(mContext, null);
|
||||
View view3 = new View(mContext, null);
|
||||
View view4 = new View(mContext, null);
|
||||
|
||||
doReturn(view1).when(mAdapter).getView(eq(0), any(), any());
|
||||
doReturn(false).when(mAdapter).shouldBeSeparated(0);
|
||||
|
||||
doReturn(view2).when(mAdapter).getView(eq(1), any(), any());
|
||||
doReturn(false).when(mAdapter).shouldBeSeparated(1);
|
||||
|
||||
doReturn(view3).when(mAdapter).getView(eq(2), any(), any());
|
||||
doReturn(false).when(mAdapter).shouldBeSeparated(2);
|
||||
|
||||
doReturn(view4).when(mAdapter).getView(eq(3), any(), any());
|
||||
doReturn(false).when(mAdapter).shouldBeSeparated(3);
|
||||
|
||||
mLayout.updateList();
|
||||
|
||||
assertEquals(0, mLayout.mSeparatedViews.size());
|
||||
assertEquals(4, mLayout.mListViews.size());
|
||||
assertEquals(view1, mLayout.mListViews.get(3));
|
||||
assertEquals(view2, mLayout.mListViews.get(2));
|
||||
assertEquals(view3, mLayout.mListViews.get(1));
|
||||
assertEquals(view4, mLayout.mListViews.get(0));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user