Loading libs/WindowManager/Shell/res/values/colors.xml +2 −0 Original line number Diff line number Diff line Loading @@ -30,6 +30,8 @@ <color name="bubbles_light">#FFFFFF</color> <color name="bubbles_dark">@color/GM2_grey_800</color> <color name="bubbles_icon_tint">@color/GM2_grey_700</color> <color name="bubble_bar_expanded_view_handle_light">#EBffffff</color> <color name="bubble_bar_expanded_view_handle_dark">#99000000</color> <!-- PiP --> <color name="pip_custom_close_bg">#D93025</color> Loading libs/WindowManager/Shell/res/values/dimen.xml +5 −1 Original line number Diff line number Diff line Loading @@ -229,7 +229,11 @@ <!-- Size of the bubble bar (height), should match transient_taskbar_size in Launcher. --> <dimen name="bubblebar_size">72dp</dimen> <!-- The size of the drag handle / menu shown along with a bubble bar expanded view. --> <dimen name="bubblebar_expanded_view_menu_size">16dp</dimen> <dimen name="bubble_bar_expanded_view_handle_size">40dp</dimen> <!-- The width of the drag handle shown along with a bubble bar expanded view. --> <dimen name="bubble_bar_expanded_view_handle_width">128dp</dimen> <!-- The height of the drag handle shown along with a bubble bar expanded view. --> <dimen name="bubble_bar_expanded_view_handle_height">4dp</dimen> <!-- Bottom and end margin for compat buttons. --> <dimen name="compat_button_margin">24dp</dimen> Loading libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java +44 −31 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package com.android.wm.shell.bubbles.bar; import android.annotation.ColorInt; import android.annotation.Nullable; import android.app.ActivityManager; import android.content.Context; import android.content.res.TypedArray; Loading Loading @@ -46,10 +48,10 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView private BubbleController mController; private BubbleTaskViewHelper mBubbleTaskViewHelper; private HandleView mMenuView; private TaskView mTaskView; private BubbleBarHandleView mHandleView = new BubbleBarHandleView(getContext()); private @Nullable TaskView mTaskView; private int mMenuHeight; private int mHandleHeight; private int mBackgroundColor; private float mCornerRadius = 0f; Loading Loading @@ -83,11 +85,9 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView super.onFinishInflate(); Context context = getContext(); setElevation(getResources().getDimensionPixelSize(R.dimen.bubble_elevation)); mMenuHeight = context.getResources().getDimensionPixelSize( R.dimen.bubblebar_expanded_view_menu_size); mMenuView = new HandleView(context); addView(mMenuView); mHandleHeight = context.getResources().getDimensionPixelSize( R.dimen.bubble_bar_expanded_view_handle_size); addView(mHandleView); applyThemeAttrs(); setClipToOutline(true); setOutlineProvider(new ViewOutlineProvider() { Loading Loading @@ -123,14 +123,12 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView ta.recycle(); mMenuView.setCornerRadius(mCornerRadius); mMenuHeight = getResources().getDimensionPixelSize( R.dimen.bubblebar_expanded_view_menu_size); mHandleHeight = getResources().getDimensionPixelSize( R.dimen.bubble_bar_expanded_view_handle_size); if (mTaskView != null) { mTaskView.setCornerRadius(mCornerRadius); mTaskView.setElevation(150); updateMenuColor(); updateHandleAndBackgroundColor(true /* animated */); } } Loading @@ -138,10 +136,8 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int height = MeasureSpec.getSize(heightMeasureSpec); // Add corner radius here so that the menu extends behind the rounded corners of TaskView. int menuViewHeight = Math.min((int) (mMenuHeight + mCornerRadius), height); measureChild(mMenuView, widthMeasureSpec, MeasureSpec.makeMeasureSpec(menuViewHeight, int menuViewHeight = Math.min(mHandleHeight, height); measureChild(mHandleView, widthMeasureSpec, MeasureSpec.makeMeasureSpec(menuViewHeight, MeasureSpec.getMode(heightMeasureSpec))); if (mTaskView != null) { Loading @@ -153,12 +149,12 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); // Drag handle above final int dragHandleBottom = t + mMenuView.getMeasuredHeight(); mMenuView.layout(l, t, r, dragHandleBottom); final int dragHandleBottom = t + mHandleView.getMeasuredHeight(); mHandleView.layout(l, t, r, dragHandleBottom); if (mTaskView != null) { // Subtract radius so that the menu extends behind the rounded corners of TaskView. mTaskView.layout(l, (int) (dragHandleBottom - mCornerRadius), r, mTaskView.layout(l, dragHandleBottom, r, dragHandleBottom + mTaskView.getMeasuredHeight()); } } Loading @@ -166,7 +162,7 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView @Override public void onTaskCreated() { setContentVisibility(true); updateMenuColor(); updateHandleAndBackgroundColor(false /* animated */); } @Override Loading Loading @@ -218,16 +214,33 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView } } /** Updates the menu bar to be the status bar color specified by the app. */ private void updateMenuColor() { /** * Updates the background color to match with task view status/bg color, and sets handle color * to contrast with the background */ private void updateHandleAndBackgroundColor(boolean animated) { if (mTaskView == null) return; ActivityManager.RunningTaskInfo info = mTaskView.getTaskInfo(); final int taskBgColor = info.taskDescription.getStatusBarColor(); final int color = Color.valueOf(taskBgColor == -1 ? Color.WHITE : taskBgColor).toArgb(); if (color != -1) { mMenuView.setBackgroundColor(color); final int color = getTaskViewColor(); final boolean isRegionDark = Color.luminance(color) <= 0.5; mHandleView.updateHandleColor(isRegionDark, animated); setBackgroundColor(color); } /** * Retrieves task view status/nav bar color or background if available * * TODO (b/283075226): Update with color sampling when * RegionSamplingHelper or alternative is available */ private @ColorInt int getTaskViewColor() { if (mTaskView == null || mTaskView.getTaskInfo() == null) return mBackgroundColor; ActivityManager.TaskDescription taskDescription = mTaskView.getTaskInfo().taskDescription; if (taskDescription.getStatusBarColor() != Color.TRANSPARENT) { return taskDescription.getStatusBarColor(); } else if (taskDescription.getBackgroundColor() != Color.TRANSPARENT) { return taskDescription.getBackgroundColor(); } else { mMenuView.setBackgroundColor(mBackgroundColor); return mBackgroundColor; } } Loading libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleView.java 0 → 100644 +115 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 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.wm.shell.bubbles.bar; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; import android.annotation.Nullable; import android.content.Context; import android.content.res.Resources; import android.graphics.Outline; import android.graphics.Rect; import android.util.AttributeSet; import android.view.View; import android.view.ViewOutlineProvider; import androidx.annotation.ColorInt; import androidx.core.content.ContextCompat; import com.android.wm.shell.R; /** * Handle view to show at the top of a bubble bar expanded view. */ public class BubbleBarHandleView extends View { private static final long COLOR_CHANGE_DURATION = 120; private final int mHandleWidth; private final int mHandleHeight; private final @ColorInt int mHandleLightColor; private final @ColorInt int mHandleDarkColor; private @Nullable ObjectAnimator mColorChangeAnim; public BubbleBarHandleView(Context context) { this(context, null); } public BubbleBarHandleView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public BubbleBarHandleView(Context context, AttributeSet attrs, int defStyleAttr) { this(context, attrs, defStyleAttr, 0); } public BubbleBarHandleView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); Resources resources = context.getResources(); mHandleWidth = resources.getDimensionPixelSize( R.dimen.bubble_bar_expanded_view_handle_width); mHandleHeight = resources.getDimensionPixelSize( R.dimen.bubble_bar_expanded_view_handle_height); mHandleLightColor = ContextCompat.getColor(context, R.color.bubble_bar_expanded_view_handle_light); mHandleDarkColor = ContextCompat.getColor(context, R.color.bubble_bar_expanded_view_handle_dark); setClipToOutline(true); setOutlineProvider(new ViewOutlineProvider() { @Override public void getOutline(View view, Outline outline) { final int handleCenterX = view.getWidth() / 2; final int handleCenterY = view.getHeight() / 2; final float handleRadius = mHandleHeight / 2f; Rect handleBounds = new Rect( handleCenterX - mHandleWidth / 2, handleCenterY - mHandleHeight / 2, handleCenterX + mHandleWidth / 2, handleCenterY + mHandleHeight / 2); outline.setRoundRect(handleBounds, handleRadius); } }); } /** * Updates the handle color. * * @param isRegionDark Whether the background behind the handle is dark, and thus the handle * should be light (and vice versa). * @param animated Whether to animate the change, or apply it immediately. */ public void updateHandleColor(boolean isRegionDark, boolean animated) { int newColor = isRegionDark ? mHandleLightColor : mHandleDarkColor; if (mColorChangeAnim != null) { mColorChangeAnim.cancel(); } if (animated) { mColorChangeAnim = ObjectAnimator.ofArgb(this, "backgroundColor", newColor); mColorChangeAnim.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { mColorChangeAnim = null; } }); mColorChangeAnim.setDuration(COLOR_CHANGE_DURATION); mColorChangeAnim.start(); } else { setBackgroundColor(newColor); } } } libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/HandleView.javadeleted 100644 → 0 +0 −42 Original line number Diff line number Diff line /* * Copyright (C) 2023 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.wm.shell.bubbles.bar; import android.content.Context; import android.view.Gravity; import android.widget.LinearLayout; /** * Handle / menu view to show at the top of a bubble bar expanded view. */ public class HandleView extends LinearLayout { // TODO(b/273307221): implement the manage menu in this view. public HandleView(Context context) { super(context); setOrientation(LinearLayout.HORIZONTAL); setGravity(Gravity.CENTER); } /** * The menu extends past the top of the TaskView because of the rounded corners. This means * to center content in the menu we must subtract the radius (i.e. the amount of space covered * by TaskView). */ public void setCornerRadius(float radius) { setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(), (int) radius); } } Loading
libs/WindowManager/Shell/res/values/colors.xml +2 −0 Original line number Diff line number Diff line Loading @@ -30,6 +30,8 @@ <color name="bubbles_light">#FFFFFF</color> <color name="bubbles_dark">@color/GM2_grey_800</color> <color name="bubbles_icon_tint">@color/GM2_grey_700</color> <color name="bubble_bar_expanded_view_handle_light">#EBffffff</color> <color name="bubble_bar_expanded_view_handle_dark">#99000000</color> <!-- PiP --> <color name="pip_custom_close_bg">#D93025</color> Loading
libs/WindowManager/Shell/res/values/dimen.xml +5 −1 Original line number Diff line number Diff line Loading @@ -229,7 +229,11 @@ <!-- Size of the bubble bar (height), should match transient_taskbar_size in Launcher. --> <dimen name="bubblebar_size">72dp</dimen> <!-- The size of the drag handle / menu shown along with a bubble bar expanded view. --> <dimen name="bubblebar_expanded_view_menu_size">16dp</dimen> <dimen name="bubble_bar_expanded_view_handle_size">40dp</dimen> <!-- The width of the drag handle shown along with a bubble bar expanded view. --> <dimen name="bubble_bar_expanded_view_handle_width">128dp</dimen> <!-- The height of the drag handle shown along with a bubble bar expanded view. --> <dimen name="bubble_bar_expanded_view_handle_height">4dp</dimen> <!-- Bottom and end margin for compat buttons. --> <dimen name="compat_button_margin">24dp</dimen> Loading
libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java +44 −31 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package com.android.wm.shell.bubbles.bar; import android.annotation.ColorInt; import android.annotation.Nullable; import android.app.ActivityManager; import android.content.Context; import android.content.res.TypedArray; Loading Loading @@ -46,10 +48,10 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView private BubbleController mController; private BubbleTaskViewHelper mBubbleTaskViewHelper; private HandleView mMenuView; private TaskView mTaskView; private BubbleBarHandleView mHandleView = new BubbleBarHandleView(getContext()); private @Nullable TaskView mTaskView; private int mMenuHeight; private int mHandleHeight; private int mBackgroundColor; private float mCornerRadius = 0f; Loading Loading @@ -83,11 +85,9 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView super.onFinishInflate(); Context context = getContext(); setElevation(getResources().getDimensionPixelSize(R.dimen.bubble_elevation)); mMenuHeight = context.getResources().getDimensionPixelSize( R.dimen.bubblebar_expanded_view_menu_size); mMenuView = new HandleView(context); addView(mMenuView); mHandleHeight = context.getResources().getDimensionPixelSize( R.dimen.bubble_bar_expanded_view_handle_size); addView(mHandleView); applyThemeAttrs(); setClipToOutline(true); setOutlineProvider(new ViewOutlineProvider() { Loading Loading @@ -123,14 +123,12 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView ta.recycle(); mMenuView.setCornerRadius(mCornerRadius); mMenuHeight = getResources().getDimensionPixelSize( R.dimen.bubblebar_expanded_view_menu_size); mHandleHeight = getResources().getDimensionPixelSize( R.dimen.bubble_bar_expanded_view_handle_size); if (mTaskView != null) { mTaskView.setCornerRadius(mCornerRadius); mTaskView.setElevation(150); updateMenuColor(); updateHandleAndBackgroundColor(true /* animated */); } } Loading @@ -138,10 +136,8 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int height = MeasureSpec.getSize(heightMeasureSpec); // Add corner radius here so that the menu extends behind the rounded corners of TaskView. int menuViewHeight = Math.min((int) (mMenuHeight + mCornerRadius), height); measureChild(mMenuView, widthMeasureSpec, MeasureSpec.makeMeasureSpec(menuViewHeight, int menuViewHeight = Math.min(mHandleHeight, height); measureChild(mHandleView, widthMeasureSpec, MeasureSpec.makeMeasureSpec(menuViewHeight, MeasureSpec.getMode(heightMeasureSpec))); if (mTaskView != null) { Loading @@ -153,12 +149,12 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); // Drag handle above final int dragHandleBottom = t + mMenuView.getMeasuredHeight(); mMenuView.layout(l, t, r, dragHandleBottom); final int dragHandleBottom = t + mHandleView.getMeasuredHeight(); mHandleView.layout(l, t, r, dragHandleBottom); if (mTaskView != null) { // Subtract radius so that the menu extends behind the rounded corners of TaskView. mTaskView.layout(l, (int) (dragHandleBottom - mCornerRadius), r, mTaskView.layout(l, dragHandleBottom, r, dragHandleBottom + mTaskView.getMeasuredHeight()); } } Loading @@ -166,7 +162,7 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView @Override public void onTaskCreated() { setContentVisibility(true); updateMenuColor(); updateHandleAndBackgroundColor(false /* animated */); } @Override Loading Loading @@ -218,16 +214,33 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView } } /** Updates the menu bar to be the status bar color specified by the app. */ private void updateMenuColor() { /** * Updates the background color to match with task view status/bg color, and sets handle color * to contrast with the background */ private void updateHandleAndBackgroundColor(boolean animated) { if (mTaskView == null) return; ActivityManager.RunningTaskInfo info = mTaskView.getTaskInfo(); final int taskBgColor = info.taskDescription.getStatusBarColor(); final int color = Color.valueOf(taskBgColor == -1 ? Color.WHITE : taskBgColor).toArgb(); if (color != -1) { mMenuView.setBackgroundColor(color); final int color = getTaskViewColor(); final boolean isRegionDark = Color.luminance(color) <= 0.5; mHandleView.updateHandleColor(isRegionDark, animated); setBackgroundColor(color); } /** * Retrieves task view status/nav bar color or background if available * * TODO (b/283075226): Update with color sampling when * RegionSamplingHelper or alternative is available */ private @ColorInt int getTaskViewColor() { if (mTaskView == null || mTaskView.getTaskInfo() == null) return mBackgroundColor; ActivityManager.TaskDescription taskDescription = mTaskView.getTaskInfo().taskDescription; if (taskDescription.getStatusBarColor() != Color.TRANSPARENT) { return taskDescription.getStatusBarColor(); } else if (taskDescription.getBackgroundColor() != Color.TRANSPARENT) { return taskDescription.getBackgroundColor(); } else { mMenuView.setBackgroundColor(mBackgroundColor); return mBackgroundColor; } } Loading
libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleView.java 0 → 100644 +115 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 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.wm.shell.bubbles.bar; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; import android.annotation.Nullable; import android.content.Context; import android.content.res.Resources; import android.graphics.Outline; import android.graphics.Rect; import android.util.AttributeSet; import android.view.View; import android.view.ViewOutlineProvider; import androidx.annotation.ColorInt; import androidx.core.content.ContextCompat; import com.android.wm.shell.R; /** * Handle view to show at the top of a bubble bar expanded view. */ public class BubbleBarHandleView extends View { private static final long COLOR_CHANGE_DURATION = 120; private final int mHandleWidth; private final int mHandleHeight; private final @ColorInt int mHandleLightColor; private final @ColorInt int mHandleDarkColor; private @Nullable ObjectAnimator mColorChangeAnim; public BubbleBarHandleView(Context context) { this(context, null); } public BubbleBarHandleView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public BubbleBarHandleView(Context context, AttributeSet attrs, int defStyleAttr) { this(context, attrs, defStyleAttr, 0); } public BubbleBarHandleView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); Resources resources = context.getResources(); mHandleWidth = resources.getDimensionPixelSize( R.dimen.bubble_bar_expanded_view_handle_width); mHandleHeight = resources.getDimensionPixelSize( R.dimen.bubble_bar_expanded_view_handle_height); mHandleLightColor = ContextCompat.getColor(context, R.color.bubble_bar_expanded_view_handle_light); mHandleDarkColor = ContextCompat.getColor(context, R.color.bubble_bar_expanded_view_handle_dark); setClipToOutline(true); setOutlineProvider(new ViewOutlineProvider() { @Override public void getOutline(View view, Outline outline) { final int handleCenterX = view.getWidth() / 2; final int handleCenterY = view.getHeight() / 2; final float handleRadius = mHandleHeight / 2f; Rect handleBounds = new Rect( handleCenterX - mHandleWidth / 2, handleCenterY - mHandleHeight / 2, handleCenterX + mHandleWidth / 2, handleCenterY + mHandleHeight / 2); outline.setRoundRect(handleBounds, handleRadius); } }); } /** * Updates the handle color. * * @param isRegionDark Whether the background behind the handle is dark, and thus the handle * should be light (and vice versa). * @param animated Whether to animate the change, or apply it immediately. */ public void updateHandleColor(boolean isRegionDark, boolean animated) { int newColor = isRegionDark ? mHandleLightColor : mHandleDarkColor; if (mColorChangeAnim != null) { mColorChangeAnim.cancel(); } if (animated) { mColorChangeAnim = ObjectAnimator.ofArgb(this, "backgroundColor", newColor); mColorChangeAnim.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { mColorChangeAnim = null; } }); mColorChangeAnim.setDuration(COLOR_CHANGE_DURATION); mColorChangeAnim.start(); } else { setBackgroundColor(newColor); } } }
libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/HandleView.javadeleted 100644 → 0 +0 −42 Original line number Diff line number Diff line /* * Copyright (C) 2023 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.wm.shell.bubbles.bar; import android.content.Context; import android.view.Gravity; import android.widget.LinearLayout; /** * Handle / menu view to show at the top of a bubble bar expanded view. */ public class HandleView extends LinearLayout { // TODO(b/273307221): implement the manage menu in this view. public HandleView(Context context) { super(context); setOrientation(LinearLayout.HORIZONTAL); setGravity(Gravity.CENTER); } /** * The menu extends past the top of the TaskView because of the rounded corners. This means * to center content in the menu we must subtract the radius (i.e. the amount of space covered * by TaskView). */ public void setCornerRadius(float radius) { setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(), (int) radius); } }