Move the overflow ui to the bubble window

- An activity always runs on the main looper of the process
  and this simplifies threading since all bubble data will
  run on the shell thread
- The BubbleExpandedView will not initialize the task view
  when mIsOverflow is set.  We still snapshot the overflow
  view to keep the existing selection animation logic the
  same between overflow/task bubbles

Bug: 161979899
Test: atest WMShellUnitTests
Test: atest SytemUITests

Change-Id: If3632a3030c58c9ec334afcb06aac4c51e28938b
This commit is contained in:
Winson Chung
2021-01-15 21:51:58 -08:00
parent 7b956dd62f
commit ebf6903682
7 changed files with 108 additions and 101 deletions

View File

@@ -14,7 +14,7 @@
~ limitations under the License
-->
<LinearLayout
<com.android.wm.shell.bubbles.BubbleOverflowContainerView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/bubble_overflow_container"
android:layout_width="match_parent"
@@ -34,8 +34,8 @@
<LinearLayout
android:id="@+id/bubble_overflow_empty_state"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="@dimen/bubble_overflow_empty_state_padding"
android:paddingRight="@dimen/bubble_overflow_empty_state_padding"
android:orientation="vertical"
@@ -69,4 +69,4 @@
android:paddingBottom="@dimen/bubble_empty_overflow_subtitle_padding"
android:gravity="center"/>
</LinearLayout>
</LinearLayout>
</com.android.wm.shell.bubbles.BubbleOverflowContainerView>

View File

@@ -24,7 +24,6 @@ import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import static com.android.wm.shell.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_EXPANDED_VIEW;
import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES;
import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.wm.shell.bubbles.BubbleOverflowActivity.EXTRA_BUBBLE_CONTROLLER;
import android.annotation.NonNull;
import android.annotation.SuppressLint;
@@ -37,14 +36,16 @@ import android.content.Intent;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.Outline;
import android.graphics.Picture;
import android.graphics.Rect;
import android.graphics.drawable.ShapeDrawable;
import android.os.Bundle;
import android.os.RemoteException;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.SurfaceControl;
import android.view.View;
import android.view.ViewGroup;
@@ -77,6 +78,7 @@ public class BubbleExpandedView extends LinearLayout {
private AlphaOptimizedButton mSettingsIcon;
private TaskView mTaskView;
private BubbleOverflowContainerView mOverflowView;
private int mTaskId = INVALID_TASK_ID;
@@ -125,6 +127,7 @@ public class BubbleExpandedView extends LinearLayout {
if (mDestroyed || mInitialized) {
return;
}
// Custom options so there is no activity transition animation
ActivityOptions options = ActivityOptions.makeCustomAnimation(getContext(),
0 /* enterResId */, 0 /* exitResId */);
@@ -307,15 +310,25 @@ public class BubbleExpandedView extends LinearLayout {
* Initialize {@link BubbleController} and {@link BubbleStackView} here, this method must need
* to be called after view inflate.
*/
void initialize(BubbleController controller, BubbleStackView stackView) {
void initialize(BubbleController controller, BubbleStackView stackView, boolean isOverflow) {
mController = controller;
mStackView = stackView;
mTaskView = new TaskView(mContext, mController.getTaskOrganizer());
mExpandedViewContainer.addView(mTaskView);
bringChildToFront(mTaskView);
mTaskView.setListener(mContext.getMainExecutor(), mTaskViewListener);
mIsOverflow = isOverflow;
mPositioner = mController.getPositioner();
if (mIsOverflow) {
mOverflowView = (BubbleOverflowContainerView) LayoutInflater.from(getContext()).inflate(
R.layout.bubble_overflow_container, null /* root */);
mOverflowView.setBubbleController(mController);
mExpandedViewContainer.addView(mOverflowView);
bringChildToFront(mOverflowView);
mSettingsIcon.setVisibility(GONE);
} else {
mTaskView = new TaskView(mContext, mController.getTaskOrganizer());
mTaskView.setListener(mContext.getMainExecutor(), mTaskViewListener);
mExpandedViewContainer.addView(mTaskView);
bringChildToFront(mTaskView);
}
}
void updateDimensions() {
@@ -390,6 +403,17 @@ public class BubbleExpandedView extends LinearLayout {
/** Return a GraphicBuffer with the contents of the task view surface. */
@Nullable
SurfaceControl.ScreenshotHardwareBuffer snapshotActivitySurface() {
if (mIsOverflow) {
// For now, just snapshot the view and return it as a hw buffer so that the animation
// code for both the tasks and overflow can be the same
Picture p = new Picture();
mOverflowView.draw(
p.beginRecording(mOverflowView.getWidth(), mOverflowView.getHeight()));
p.endRecording();
Bitmap snapshot = Bitmap.createBitmap(p);
return new SurfaceControl.ScreenshotHardwareBuffer(snapshot.getHardwareBuffer(),
snapshot.getColorSpace(), false /* containsSecureLayers */);
}
if (mTaskView == null || mTaskView.getSurfaceControl() == null) {
return null;
}
@@ -400,6 +424,11 @@ public class BubbleExpandedView extends LinearLayout {
}
int[] getTaskViewLocationOnScreen() {
if (mIsOverflow) {
// This is only used for animating away the surface when switching bubbles, just use the
// view location on screen for now to allow us to use the same animation code with tasks
return mOverflowView.getLocationOnScreen();
}
if (mTaskView != null) {
return mTaskView.getLocationOnScreen();
} else {
@@ -450,10 +479,7 @@ public class BubbleExpandedView extends LinearLayout {
final float alpha = visibility ? 1f : 0f;
mPointerView.setAlpha(alpha);
if (mTaskView == null) {
return;
}
if (alpha != mTaskView.getAlpha()) {
if (mTaskView != null) {
mTaskView.setAlpha(alpha);
}
}
@@ -467,19 +493,6 @@ public class BubbleExpandedView extends LinearLayout {
return mTaskId;
}
public void setOverflow(boolean overflow) {
mIsOverflow = overflow;
Intent target = new Intent(mContext, BubbleOverflowActivity.class);
target.addFlags(FLAG_ACTIVITY_NEW_DOCUMENT | FLAG_ACTIVITY_MULTIPLE_TASK);
Bundle extras = new Bundle();
extras.putBinder(EXTRA_BUBBLE_CONTROLLER, ObjectWrapper.wrap(mController));
target.putExtras(extras);
mPendingIntent = PendingIntent.getActivity(mContext, 0 /* requestCode */,
target, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
mSettingsIcon.setVisibility(GONE);
}
/**
* Sets the bubble used to populate this view.
*/
@@ -511,7 +524,8 @@ public class BubbleExpandedView extends LinearLayout {
if (isNew) {
mPendingIntent = mBubble.getBubbleIntent();
if (mPendingIntent != null || mBubble.hasMetadataShortcutId()) {
if ((mPendingIntent != null || mBubble.hasMetadataShortcutId())
&& mTaskView != null) {
setContentVisibility(false);
mTaskView.setVisibility(VISIBLE);
}
@@ -552,13 +566,19 @@ public class BubbleExpandedView extends LinearLayout {
desiredHeight = Math.max(desiredHeight, mMinHeight);
float height = Math.min(desiredHeight, getMaxExpandedHeight());
height = Math.max(height, mMinHeight);
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mTaskView.getLayoutParams();
FrameLayout.LayoutParams lp = mIsOverflow
? (FrameLayout.LayoutParams) mOverflowView.getLayoutParams()
: (FrameLayout.LayoutParams) mTaskView.getLayoutParams();
mNeedsNewHeight = lp.height != height;
if (!mImeVisible) {
// If the ime is visible... don't adjust the height because that will cause
// a configuration change and the ime will be lost.
lp.height = (int) height;
mTaskView.setLayoutParams(lp);
if (mIsOverflow) {
mOverflowView.setLayoutParams(lp);
} else {
mTaskView.setLayoutParams(lp);
}
mNeedsNewHeight = false;
}
if (DEBUG_BUBBLE_EXPANDED_VIEW) {
@@ -602,6 +622,9 @@ public class BubbleExpandedView extends LinearLayout {
updateHeight();
mTaskView.onLocationChanged();
}
if (mIsOverflow) {
mOverflowView.show();
}
}
/**
@@ -637,8 +660,8 @@ public class BubbleExpandedView extends LinearLayout {
/**
* Cleans up anything related to the task and TaskView. If this view should be reused after this
* method is called, then {@link #initialize(BubbleController, BubbleStackView)} must be invoked
* first.
* method is called, then {@link #initialize(BubbleController, BubbleStackView, boolean)} must
* be invoked first.
*/
public void cleanUpExpandedState() {
if (DEBUG_BUBBLE_EXPANDED_VIEW) {
@@ -653,8 +676,6 @@ public class BubbleExpandedView extends LinearLayout {
}
if (mTaskView != null) {
mTaskView.release();
}
if (mTaskView != null) {
removeView(mTaskView);
mTaskView = null;
}

View File

@@ -65,8 +65,7 @@ class BubbleOverflow(
/** Call before use and again if cleanUpExpandedState was called. */
fun initialize(controller: BubbleController) {
getExpandedView()?.initialize(controller, controller.stackView)
getExpandedView()?.setOverflow(true)
getExpandedView()?.initialize(controller, controller.stackView, true /* isOverflow */)
}
fun cleanUpExpandedState() {

View File

@@ -29,16 +29,21 @@ import android.content.res.TypedArray;
import android.graphics.Color;
import android.os.Bundle;
import android.os.IBinder;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
@@ -50,10 +55,9 @@ import java.util.List;
import java.util.function.Consumer;
/**
* Activity for showing aged out bubbles.
* Must be public to be accessible to androidx...AppComponentFactory
* Container view for showing aged out bubbles.
*/
public class BubbleOverflowActivity extends Activity {
public class BubbleOverflowContainerView extends LinearLayout {
static final String EXTRA_BUBBLE_CONTROLLER = "bubble_controller";
private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleOverflowActivity" : TAG_BUBBLES;
@@ -95,37 +99,55 @@ public class BubbleOverflowActivity extends Activity {
}
}
public BubbleOverflowContainerView(Context context) {
super(context);
}
public BubbleOverflowContainerView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public BubbleOverflowContainerView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public BubbleOverflowContainerView(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
public void setBubbleController(BubbleController controller) {
mController = controller;
}
public void show() {
setVisibility(View.VISIBLE);
updateOverflow();
}
public void hide() {
setVisibility(View.INVISIBLE);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.bubble_overflow_activity);
protected void onFinishInflate() {
super.onFinishInflate();
mRecyclerView = findViewById(R.id.bubble_overflow_recycler);
mEmptyState = findViewById(R.id.bubble_overflow_empty_state);
mEmptyStateTitle = findViewById(R.id.bubble_overflow_empty_title);
mEmptyStateSubtitle = findViewById(R.id.bubble_overflow_empty_subtitle);
mEmptyStateImage = findViewById(R.id.bubble_overflow_empty_state_image);
Intent intent = getIntent();
if (intent != null && intent.getExtras() != null) {
IBinder binder = intent.getExtras().getBinder(EXTRA_BUBBLE_CONTROLLER);
if (binder instanceof ObjectWrapper) {
mController = ((ObjectWrapper<BubbleController>) binder).get();
updateOverflow();
}
} else {
Log.w(TAG, "Bubble overflow activity created without bubble controller!");
}
}
void updateOverflow() {
Resources res = getResources();
final int columns = res.getInteger(R.integer.bubbles_overflow_columns);
mRecyclerView.setLayoutManager(
new NoScrollGridLayoutManager(getApplicationContext(), columns));
new NoScrollGridLayoutManager(getContext(), columns));
DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
getContext().getDisplay().getMetrics(displayMetrics);
final int overflowPadding = res.getDimensionPixelSize(R.dimen.bubble_overflow_padding);
final int recyclerViewWidth = displayMetrics.widthPixels - (overflowPadding * 2);
@@ -137,7 +159,7 @@ public class BubbleOverflowActivity extends Activity {
- res.getDimensionPixelSize(R.dimen.bubble_overflow_padding);
final int viewHeight = recyclerViewHeight / rows;
mAdapter = new BubbleOverflowAdapter(getApplicationContext(), mOverflowBubbles,
mAdapter = new BubbleOverflowAdapter(getContext(), mOverflowBubbles,
mController::promoteBubbleFromOverflow,
mController.getPositioner(),
viewWidth, viewHeight);
@@ -146,6 +168,11 @@ public class BubbleOverflowActivity extends Activity {
mOverflowBubbles.clear();
mOverflowBubbles.addAll(mController.getOverflowBubbles());
mAdapter.notifyDataSetChanged();
// Currently BubbleExpandedView.mExpandedViewContainer is WRAP_CONTENT so use the same
// width we would use for the recycler view
LayoutParams lp = (LayoutParams) mEmptyState.getLayoutParams();
lp.width = recyclerViewWidth;
updateEmptyStateVisibility();
mController.setOverflowListener(mDataListener);
@@ -172,12 +199,12 @@ public class BubbleOverflowActivity extends Activity {
? res.getDrawable(R.drawable.bubble_ic_empty_overflow_dark)
: res.getDrawable(R.drawable.bubble_ic_empty_overflow_light));
findViewById(android.R.id.content)
findViewById(R.id.bubble_overflow_container)
.setBackgroundColor(isNightMode
? res.getColor(R.color.bubbles_dark)
: res.getColor(R.color.bubbles_light));
final TypedArray typedArray = getApplicationContext().obtainStyledAttributes(
final TypedArray typedArray = getContext().obtainStyledAttributes(
new int[]{android.R.attr.colorBackgroundFloating,
android.R.attr.textColorSecondary});
int bgColor = typedArray.getColor(0, isNightMode ? Color.BLACK : Color.WHITE);
@@ -222,36 +249,6 @@ public class BubbleOverflowActivity extends Activity {
}
}
};
@Override
public void onStart() {
super.onStart();
}
@Override
public void onRestart() {
super.onRestart();
}
@Override
public void onResume() {
super.onResume();
updateOverflow();
}
@Override
public void onPause() {
super.onPause();
}
@Override
public void onStop() {
super.onStop();
}
public void onDestroy() {
super.onDestroy();
}
}
class BubbleOverflowAdapter extends RecyclerView.Adapter<BubbleOverflowAdapter.ViewHolder> {

View File

@@ -138,7 +138,7 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask
info.expandedView = (BubbleExpandedView) inflater.inflate(
R.layout.bubble_expanded_view, stackView, false /* attachToRoot */);
info.expandedView.initialize(controller, stackView);
info.expandedView.initialize(controller, stackView, false /* isOverflow */);
}
if (b.getShortcutInfo() != null) {

View File

@@ -353,14 +353,6 @@
android:permission="android.permission.BIND_WALLPAPER"
android:exported="true" />
<activity
android:name="com.android.wm.shell.bubbles.BubbleOverflowActivity"
android:theme="@style/BubbleOverflow"
android:excludeFromRecents="true"
android:documentLaunchMode="always"
android:resizeableActivity="true">
</activity>
<activity android:name=".tuner.TunerActivity"
android:enabled="false"
android:icon="@drawable/tuner"

View File

@@ -15,8 +15,6 @@
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<style name="BubbleOverflow" parent="@android:style/Theme.NoTitleBar"></style>
<style name="ClearAllButtonDefaultMargins">
<item name="android:layout_marginStart">0dp</item>
<item name="android:layout_marginTop">0dp</item>