| OLD | NEW | 
|---|
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be | 
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. | 
| 4 | 4 | 
| 5 #include "ui/views/animation/ink_drop_animation_controller.h" | 5 #include "ui/views/animation/ink_drop_animation_controller_impl.h" | 
| 6 | 6 | 
| 7 #include "base/command_line.h" | 7 #include "base/command_line.h" | 
| 8 #include "ui/base/ui_base_switches.h" | 8 #include "ui/base/ui_base_switches.h" | 
| 9 #include "ui/compositor/layer.h" | 9 #include "ui/compositor/layer.h" | 
| 10 #include "ui/compositor/layer_animation_observer.h" | 10 #include "ui/compositor/layer_animation_observer.h" | 
| 11 #include "ui/compositor/layer_animation_sequence.h" | 11 #include "ui/compositor/layer_animation_sequence.h" | 
| 12 #include "ui/compositor/paint_recorder.h" | 12 #include "ui/compositor/paint_recorder.h" | 
| 13 #include "ui/compositor/scoped_layer_animation_settings.h" | 13 #include "ui/compositor/scoped_layer_animation_settings.h" | 
| 14 #include "ui/events/event.h" | 14 #include "ui/events/event.h" | 
| 15 #include "ui/gfx/canvas.h" | 15 #include "ui/gfx/canvas.h" | 
|  | 16 #include "ui/gfx/geometry/size.h" | 
|  | 17 #include "ui/views/animation/ink_drop_delegate.h" | 
|  | 18 #include "ui/views/animation/ink_drop_host.h" | 
| 16 #include "ui/views/view.h" | 19 #include "ui/views/view.h" | 
| 17 | 20 | 
| 18 namespace { | 21 namespace { | 
| 19 | 22 | 
| 20 // Animation constants | 23 // Animation constants | 
| 21 const float kMinimumScale = 0.1f; | 24 const float kMinimumScale = 0.1f; | 
| 22 const float kMinimumScaleCenteringOffset = 0.5f - kMinimumScale / 2.0f; | 25 const float kMinimumScaleCenteringOffset = 0.5f - kMinimumScale / 2.0f; | 
| 23 | 26 | 
| 24 const int kHideAnimationDurationFastMs = 100; | 27 const int kHideAnimationDurationFastMs = 100; | 
| 25 const int kHideAnimationDurationSlowMs = 1000; | 28 const int kHideAnimationDurationSlowMs = 1000; | 
| (...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 106   ui::Layer* background_layer_; | 109   ui::Layer* background_layer_; | 
| 107 | 110 | 
| 108   // If true the hide animation will immediately be scheduled upon completion of | 111   // If true the hide animation will immediately be scheduled upon completion of | 
| 109   // the observed animation. | 112   // the observed animation. | 
| 110   bool hide_; | 113   bool hide_; | 
| 111 | 114 | 
| 112   DISALLOW_COPY_AND_ASSIGN(AppearAnimationObserver); | 115   DISALLOW_COPY_AND_ASSIGN(AppearAnimationObserver); | 
| 113 }; | 116 }; | 
| 114 | 117 | 
| 115 AppearAnimationObserver::AppearAnimationObserver(ui::Layer* layer, bool hide) | 118 AppearAnimationObserver::AppearAnimationObserver(ui::Layer* layer, bool hide) | 
| 116     : layer_(layer), background_layer_(nullptr), hide_(hide) { | 119     : layer_(layer), background_layer_(nullptr), hide_(hide) {} | 
| 117 } |  | 
| 118 | 120 | 
| 119 AppearAnimationObserver::~AppearAnimationObserver() { | 121 AppearAnimationObserver::~AppearAnimationObserver() { | 
| 120   StopObserving(); | 122   StopObserving(); | 
| 121 } | 123 } | 
| 122 | 124 | 
| 123 bool AppearAnimationObserver::IsAnimationActive() { | 125 bool AppearAnimationObserver::IsAnimationActive() { | 
| 124   // Initial animation ongoing | 126   // Initial animation ongoing | 
| 125   if (!attached_sequences().empty()) | 127   if (!attached_sequences().empty()) | 
| 126     return true; | 128     return true; | 
| 127   // Maintain the animation until told to hide. | 129   // Maintain the animation until told to hide. | 
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 175     StartHideAnimation(); | 177     StartHideAnimation(); | 
| 176 } | 178 } | 
| 177 | 179 | 
| 178 bool AppearAnimationObserver::RequiresNotificationWhenAnimatorDestroyed() | 180 bool AppearAnimationObserver::RequiresNotificationWhenAnimatorDestroyed() | 
| 179     const { | 181     const { | 
| 180   // Ensures that OnImplicitAnimationsCompleted is called even if the observed | 182   // Ensures that OnImplicitAnimationsCompleted is called even if the observed | 
| 181   // animation is deleted. Allows for setting the proper state on |layer_|. | 183   // animation is deleted. Allows for setting the proper state on |layer_|. | 
| 182   return true; | 184   return true; | 
| 183 } | 185 } | 
| 184 | 186 | 
| 185 // Renders the visual feedback. Will render a circle if |circle_| is true, | 187 InkDropAnimationControllerImpl::InkDropAnimationControllerImpl( | 
| 186 // otherwise renders a rounded rectangle. | 188     InkDropHost* ink_drop_host) | 
| 187 class InkDropDelegate : public ui::LayerDelegate { | 189     : ink_drop_host_(ink_drop_host), | 
| 188  public: | 190       appear_animation_observer_(nullptr), | 
| 189   InkDropDelegate(ui::Layer* layer, SkColor color); | 191       long_press_animation_observer_(nullptr), | 
| 190   ~InkDropDelegate() override; | 192       ink_drop_bounds_(0, 0, 0, 0) { | 
|  | 193   root_layer_owner_.SetLayer(new ui::Layer(ui::LAYER_NOT_DRAWN)); | 
|  | 194   ink_drop_layer_owner_.SetLayer(new ui::Layer()); | 
|  | 195   long_press_layer_owner_.SetLayer(new ui::Layer()); | 
| 191 | 196 | 
| 192   // Sets the visual style of the feedback. | 197   ink_drop_delegate_.reset(new InkDropDelegate(ink_drop_layer_owner_.layer(), | 
| 193   void set_should_render_circle(bool should_render_circle) { | 198                                                kInkDropColor, kCircleRadius, | 
| 194     should_render_circle_ = should_render_circle; | 199                                                kRoundedRectCorners)); | 
| 195   } | 200   long_press_delegate_.reset( | 
|  | 201       new InkDropDelegate(long_press_layer_owner_.layer(), kLongPressColor, | 
|  | 202                           kCircleRadius, kRoundedRectCorners)), | 
| 196 | 203 | 
| 197   // ui::LayerDelegate: | 204       SetupAnimationLayer(long_press_layer_owner_.layer(), | 
| 198   void OnPaintLayer(const ui::PaintContext& context) override; | 205                           long_press_delegate_.get()); | 
| 199   void OnDelegatedFrameDamage(const gfx::Rect& damage_rect_in_dip) override; | 206   SetupAnimationLayer(ink_drop_layer_owner_.layer(), ink_drop_delegate_.get()); | 
| 200   void OnDeviceScaleFactorChanged(float device_scale_factor) override; |  | 
| 201   base::Closure PrepareForLayerBoundsChange() override; |  | 
| 202 | 207 | 
| 203  private: | 208   root_layer_owner_.layer()->Add(ink_drop_layer_owner_.layer()); | 
| 204   // The ui::Layer being rendered to. | 209   root_layer_owner_.layer()->Add(long_press_layer_owner_.layer()); | 
| 205   ui::Layer* layer_; |  | 
| 206 | 210 | 
| 207   // The color to paint. | 211   ink_drop_host_->AddInkDropLayer(root_layer_owner_.layer()); | 
| 208   SkColor color_; |  | 
| 209 |  | 
| 210   // When true renders a circle, otherwise renders a rounded rectangle. |  | 
| 211   bool should_render_circle_; |  | 
| 212 |  | 
| 213   DISALLOW_COPY_AND_ASSIGN(InkDropDelegate); |  | 
| 214 }; |  | 
| 215 |  | 
| 216 InkDropDelegate::InkDropDelegate(ui::Layer* layer, SkColor color) |  | 
| 217     : layer_(layer), color_(color), should_render_circle_(true) { |  | 
| 218 } | 212 } | 
| 219 | 213 | 
| 220 InkDropDelegate::~InkDropDelegate() { | 214 InkDropAnimationControllerImpl::~InkDropAnimationControllerImpl() { | 
|  | 215   // TODO(bruthig): Change this to be called when the ink drop becomes hidden. | 
|  | 216   ink_drop_host_->RemoveInkDropLayer(root_layer_owner_.layer()); | 
| 221 } | 217 } | 
| 222 | 218 | 
| 223 void InkDropDelegate::OnPaintLayer(const ui::PaintContext& context) { | 219 void InkDropAnimationControllerImpl::AnimateToState(InkDropState state) { | 
| 224   SkPaint paint; | 220   // TODO(bruthig): Do not transition if we are already in |state| and restrict | 
| 225   paint.setColor(color_); | 221   // any state transition that don't make sense or wouldn't look visually | 
| 226   paint.setFlags(SkPaint::kAntiAlias_Flag); | 222   // appealing. | 
| 227   paint.setStyle(SkPaint::kFill_Style); | 223   switch (state) { | 
| 228 | 224     case InkDropState::HIDDEN: | 
| 229   gfx::Rect bounds = layer_->bounds(); | 225       AnimateHide(); | 
| 230 | 226       break; | 
| 231   ui::PaintRecorder recorder(context, layer_->size()); | 227     case InkDropState::ACTION_PENDING: | 
| 232   gfx::Canvas* canvas = recorder.canvas(); | 228       AnimateTapDown(); | 
| 233   if (should_render_circle_) { | 229       break; | 
| 234     gfx::Point midpoint(bounds.width() * 0.5f, bounds.height() * 0.5f); | 230     case InkDropState::QUICK_ACTION: | 
| 235     canvas->DrawCircle(midpoint, kCircleRadius, paint); | 231       AnimateTapDown(); | 
| 236   } else { | 232       AnimateHide(); | 
| 237     canvas->DrawRoundRect(bounds, kRoundedRectCorners, paint); | 233       break; | 
|  | 234     case InkDropState::SLOW_ACTION: | 
|  | 235       AnimateLongPress(); | 
|  | 236       break; | 
|  | 237     case InkDropState::ACTIVATED: | 
|  | 238       AnimateLongPress(); | 
|  | 239       break; | 
| 238   } | 240   } | 
| 239 } | 241 } | 
| 240 | 242 | 
| 241 void InkDropDelegate::OnDelegatedFrameDamage( | 243 void InkDropAnimationControllerImpl::SetInkDropSize(const gfx::Size& size) { | 
| 242     const gfx::Rect& damage_rect_in_dip) { | 244   SetInkDropBounds(gfx::Rect(ink_drop_bounds_.origin(), size)); | 
| 243 } | 245 } | 
| 244 | 246 | 
| 245 void InkDropDelegate::OnDeviceScaleFactorChanged(float device_scale_factor) { | 247 gfx::Rect InkDropAnimationControllerImpl::GetInkDropBounds() const { | 
|  | 248   return ink_drop_bounds_; | 
| 246 } | 249 } | 
| 247 | 250 | 
| 248 base::Closure InkDropDelegate::PrepareForLayerBoundsChange() { | 251 void InkDropAnimationControllerImpl::SetInkDropBounds(const gfx::Rect& bounds) { | 
| 249   return base::Closure(); | 252   ink_drop_bounds_ = bounds; | 
|  | 253   SetLayerBounds(ink_drop_layer_owner_.layer()); | 
|  | 254   SetLayerBounds(long_press_layer_owner_.layer()); | 
| 250 } | 255 } | 
| 251 | 256 | 
| 252 InkDropAnimationController::InkDropAnimationController(views::View* view) | 257 void InkDropAnimationControllerImpl::AnimateTapDown() { | 
| 253     : ink_drop_layer_(new ui::Layer()), |  | 
| 254       ink_drop_delegate_( |  | 
| 255           new InkDropDelegate(ink_drop_layer_.get(), kInkDropColor)), |  | 
| 256       appear_animation_observer_(nullptr), |  | 
| 257       long_press_layer_(new ui::Layer()), |  | 
| 258       long_press_delegate_( |  | 
| 259           new InkDropDelegate(long_press_layer_.get(), kLongPressColor)), |  | 
| 260       long_press_animation_observer_(nullptr), |  | 
| 261       view_(view) { |  | 
| 262   view_->SetPaintToLayer(true); |  | 
| 263   view_->AddPreTargetHandler(this); |  | 
| 264   ui::Layer* layer = view_->layer(); |  | 
| 265   layer->SetMasksToBounds(!UseCircularFeedback()); |  | 
| 266   SetupAnimationLayer(layer, long_press_layer_.get(), |  | 
| 267                       long_press_delegate_.get()); |  | 
| 268   SetupAnimationLayer(layer, ink_drop_layer_.get(), ink_drop_delegate_.get()); |  | 
| 269   ink_drop_delegate_->set_should_render_circle(UseCircularFeedback()); |  | 
| 270 } |  | 
| 271 |  | 
| 272 InkDropAnimationController::~InkDropAnimationController() { |  | 
| 273   view_->RemovePreTargetHandler(this); |  | 
| 274 } |  | 
| 275 |  | 
| 276 void InkDropAnimationController::AnimateTapDown() { |  | 
| 277   if ((appear_animation_observer_ && | 258   if ((appear_animation_observer_ && | 
| 278        appear_animation_observer_->IsAnimationActive()) || | 259        appear_animation_observer_->IsAnimationActive()) || | 
| 279       (long_press_animation_observer_ && | 260       (long_press_animation_observer_ && | 
| 280        long_press_animation_observer_->IsAnimationActive())) { | 261        long_press_animation_observer_->IsAnimationActive())) { | 
| 281     // Only one animation at a time. Subsequent tap downs are ignored until the | 262     // Only one animation at a time. Subsequent tap downs are ignored until the | 
| 282     // current animation completes. | 263     // current animation completes. | 
| 283     return; | 264     return; | 
| 284   } | 265   } | 
| 285   appear_animation_observer_.reset( | 266   appear_animation_observer_.reset( | 
| 286       new AppearAnimationObserver(ink_drop_layer_.get(), false)); | 267       new AppearAnimationObserver(ink_drop_layer_owner_.layer(), false)); | 
| 287   AnimateShow(ink_drop_layer_.get(), appear_animation_observer_.get(), | 268   AnimateShow(ink_drop_layer_owner_.layer(), appear_animation_observer_.get(), | 
| 288               UseCircularFeedback(), |  | 
| 289               base::TimeDelta::FromMilliseconds( | 269               base::TimeDelta::FromMilliseconds( | 
| 290                   (UseFastAnimations() ? kShowInkDropAnimationDurationFastMs | 270                   (UseFastAnimations() ? kShowInkDropAnimationDurationFastMs | 
| 291                                        : kShowInkDropAnimationDurationSlowMs))); | 271                                        : kShowInkDropAnimationDurationSlowMs))); | 
| 292 } | 272 } | 
| 293 | 273 | 
| 294 void InkDropAnimationController::AnimateHide() { | 274 void InkDropAnimationControllerImpl::AnimateHide() { | 
| 295   if (appear_animation_observer_) | 275   if (appear_animation_observer_ && | 
|  | 276       appear_animation_observer_->IsAnimationActive()) { | 
| 296     appear_animation_observer_->HideNowIfDoneOrOnceCompleted(); | 277     appear_animation_observer_->HideNowIfDoneOrOnceCompleted(); | 
|  | 278   } else if (long_press_animation_observer_) { | 
|  | 279     long_press_animation_observer_->HideNowIfDoneOrOnceCompleted(); | 
|  | 280   } | 
| 297 } | 281 } | 
| 298 | 282 | 
| 299 void InkDropAnimationController::AnimateLongPress() { | 283 void InkDropAnimationControllerImpl::AnimateLongPress() { | 
| 300   // Only one animation at a time. Subsequent long presses are ignored until the | 284   // Only one animation at a time. Subsequent long presses are ignored until the | 
| 301   // current animation completes. | 285   // current animation completes. | 
| 302   if (long_press_animation_observer_ && | 286   if (long_press_animation_observer_ && | 
| 303       long_press_animation_observer_->IsAnimationActive()) { | 287       long_press_animation_observer_->IsAnimationActive()) { | 
| 304     return; | 288     return; | 
| 305   } | 289   } | 
| 306   appear_animation_observer_.reset(); | 290   appear_animation_observer_.reset(); | 
| 307   long_press_animation_observer_.reset( | 291   long_press_animation_observer_.reset( | 
| 308       new AppearAnimationObserver(long_press_layer_.get(), true)); | 292       new AppearAnimationObserver(long_press_layer_owner_.layer(), false)); | 
| 309   long_press_animation_observer_->SetBackgroundToHide(ink_drop_layer_.get()); | 293   long_press_animation_observer_->SetBackgroundToHide( | 
| 310   AnimateShow(long_press_layer_.get(), long_press_animation_observer_.get(), | 294       ink_drop_layer_owner_.layer()); | 
| 311               true, | 295   AnimateShow(long_press_layer_owner_.layer(), | 
|  | 296               long_press_animation_observer_.get(), | 
| 312               base::TimeDelta::FromMilliseconds( | 297               base::TimeDelta::FromMilliseconds( | 
| 313                   UseFastAnimations() ? kShowLongPressAnimationDurationFastMs | 298                   UseFastAnimations() ? kShowLongPressAnimationDurationFastMs | 
| 314                                       : kShowLongPressAnimationDurationSlowMs)); | 299                                       : kShowLongPressAnimationDurationSlowMs)); | 
| 315 } | 300 } | 
| 316 | 301 | 
| 317 void InkDropAnimationController::AnimateShow(ui::Layer* layer, | 302 void InkDropAnimationControllerImpl::AnimateShow( | 
| 318                                              AppearAnimationObserver* observer, | 303     ui::Layer* layer, | 
| 319                                              bool circle, | 304     AppearAnimationObserver* observer, | 
| 320                                              base::TimeDelta duration) { | 305     base::TimeDelta duration) { | 
| 321   SetLayerBounds(layer, circle, view_->width(), view_->height()); |  | 
| 322   layer->SetVisible(true); | 306   layer->SetVisible(true); | 
| 323   layer->SetOpacity(1.0f); | 307   layer->SetOpacity(1.0f); | 
| 324 | 308 | 
| 325   float start_x = layer->bounds().width() * kMinimumScaleCenteringOffset; | 309   float start_x = ink_drop_bounds_.x() + | 
| 326   float start_y = layer->bounds().height() * kMinimumScaleCenteringOffset; | 310                   layer->bounds().width() * kMinimumScaleCenteringOffset; | 
|  | 311   float start_y = ink_drop_bounds_.y() + | 
|  | 312                   layer->bounds().height() * kMinimumScaleCenteringOffset; | 
| 327 | 313 | 
| 328   gfx::Transform initial_transform; | 314   gfx::Transform initial_transform; | 
| 329   initial_transform.Translate(start_x, start_y); | 315   initial_transform.Translate(start_x, start_y); | 
| 330   initial_transform.Scale(kMinimumScale, kMinimumScale); | 316   initial_transform.Scale(kMinimumScale, kMinimumScale); | 
| 331   layer->SetTransform(initial_transform); | 317   layer->SetTransform(initial_transform); | 
| 332 | 318 | 
| 333   ui::LayerAnimator* animator = layer->GetAnimator(); | 319   ui::LayerAnimator* animator = layer->GetAnimator(); | 
| 334   ui::ScopedLayerAnimationSettings animation(animator); | 320   ui::ScopedLayerAnimationSettings animation(animator); | 
| 335   animation.SetPreemptionStrategy( | 321   animation.SetPreemptionStrategy( | 
| 336       ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); | 322       ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); | 
| 337 | 323 | 
| 338   gfx::Transform identity_transform; | 324   gfx::Transform target_transform; | 
|  | 325   target_transform.Translate(ink_drop_bounds_.x(), ink_drop_bounds_.y()); | 
| 339   ui::LayerAnimationElement* element = | 326   ui::LayerAnimationElement* element = | 
| 340       ui::LayerAnimationElement::CreateTransformElement(identity_transform, | 327       ui::LayerAnimationElement::CreateTransformElement(target_transform, | 
| 341                                                         duration); | 328                                                         duration); | 
| 342   ui::LayerAnimationSequence* sequence = | 329   ui::LayerAnimationSequence* sequence = | 
| 343       new ui::LayerAnimationSequence(element); | 330       new ui::LayerAnimationSequence(element); | 
| 344   sequence->AddObserver(observer); | 331   sequence->AddObserver(observer); | 
| 345   animator->StartAnimation(sequence); | 332   animator->StartAnimation(sequence); | 
| 346 } | 333 } | 
| 347 | 334 | 
| 348 void InkDropAnimationController::SetLayerBounds(ui::Layer* layer, | 335 void InkDropAnimationControllerImpl::SetLayerBounds(ui::Layer* layer) { | 
| 349                                                 bool circle, | 336   bool circle = UseCircularFeedback(); | 
| 350                                                 int width, | 337   gfx::Size size = ink_drop_bounds_.size(); | 
| 351                                                 int height) { | 338   float circle_width = circle ? 2.0f * kCircleRadius : size.width(); | 
| 352   float circle_width = circle ? 2.0f * kCircleRadius : width; | 339   float circle_height = circle ? 2.0f * kCircleRadius : size.height(); | 
| 353   float circle_height = circle ? 2.0f * kCircleRadius : height; | 340   float circle_x = circle ? (size.width() - circle_width) * 0.5f : 0; | 
| 354   float circle_x = circle ? (width - circle_width) * 0.5f : 0; | 341   float circle_y = circle ? (size.height() - circle_height) * 0.5f : 0; | 
| 355   float circle_y = circle ? (height - circle_height) * 0.5f : 0; |  | 
| 356   layer->SetBounds(gfx::Rect(circle_x, circle_y, circle_width, circle_height)); | 342   layer->SetBounds(gfx::Rect(circle_x, circle_y, circle_width, circle_height)); | 
| 357 } | 343 } | 
| 358 | 344 | 
| 359 void InkDropAnimationController::SetupAnimationLayer( | 345 void InkDropAnimationControllerImpl::SetupAnimationLayer( | 
| 360     ui::Layer* parent, |  | 
| 361     ui::Layer* layer, | 346     ui::Layer* layer, | 
| 362     InkDropDelegate* delegate) { | 347     InkDropDelegate* delegate) { | 
| 363   layer->SetFillsBoundsOpaquely(false); | 348   layer->SetFillsBoundsOpaquely(false); | 
| 364   layer->set_delegate(delegate); | 349   layer->set_delegate(delegate); | 
| 365   layer->SetVisible(false); | 350   layer->SetVisible(false); | 
| 366   layer->SetBounds(gfx::Rect()); | 351   layer->SetBounds(gfx::Rect()); | 
| 367   parent->Add(layer); | 352   delegate->set_should_render_circle(UseCircularFeedback()); | 
| 368   parent->StackAtBottom(layer); |  | 
| 369 } |  | 
| 370 |  | 
| 371 void InkDropAnimationController::OnGestureEvent(ui::GestureEvent* event) { |  | 
| 372   if (event->target() != view_) |  | 
| 373     return; |  | 
| 374 |  | 
| 375   switch (event->type()) { |  | 
| 376     case ui::ET_GESTURE_TAP_DOWN: |  | 
| 377       AnimateTapDown(); |  | 
| 378       break; |  | 
| 379     case ui::ET_GESTURE_LONG_PRESS: |  | 
| 380       AnimateLongPress(); |  | 
| 381       break; |  | 
| 382     case ui::ET_GESTURE_END: |  | 
| 383     case ui::ET_GESTURE_TAP_CANCEL: |  | 
| 384     case ui::ET_GESTURE_TAP: |  | 
| 385       AnimateHide(); |  | 
| 386       break; |  | 
| 387     default: |  | 
| 388       break; |  | 
| 389   } |  | 
| 390 } | 353 } | 
| 391 | 354 | 
| 392 }  // namespace views | 355 }  // namespace views | 
| OLD | NEW | 
|---|