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:
@@ -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>
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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> {
|
||||
@@ -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) {
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user