SearchDropTargetBar.java revision a6427b15c18d5b8f3078f553d78f8432de9f46e9
14c98d9235d164680186180974719f551cf935d08Winson Chung/* 24c98d9235d164680186180974719f551cf935d08Winson Chung * Copyright (C) 2011 The Android Open Source Project 34c98d9235d164680186180974719f551cf935d08Winson Chung * 44c98d9235d164680186180974719f551cf935d08Winson Chung * Licensed under the Apache License, Version 2.0 (the "License"); 54c98d9235d164680186180974719f551cf935d08Winson Chung * you may not use this file except in compliance with the License. 64c98d9235d164680186180974719f551cf935d08Winson Chung * You may obtain a copy of the License at 74c98d9235d164680186180974719f551cf935d08Winson Chung * 84c98d9235d164680186180974719f551cf935d08Winson Chung * http://www.apache.org/licenses/LICENSE-2.0 94c98d9235d164680186180974719f551cf935d08Winson Chung * 104c98d9235d164680186180974719f551cf935d08Winson Chung * Unless required by applicable law or agreed to in writing, software 114c98d9235d164680186180974719f551cf935d08Winson Chung * distributed under the License is distributed on an "AS IS" BASIS, 124c98d9235d164680186180974719f551cf935d08Winson Chung * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 134c98d9235d164680186180974719f551cf935d08Winson Chung * See the License for the specific language governing permissions and 144c98d9235d164680186180974719f551cf935d08Winson Chung * limitations under the License. 154c98d9235d164680186180974719f551cf935d08Winson Chung */ 164c98d9235d164680186180974719f551cf935d08Winson Chung 174c98d9235d164680186180974719f551cf935d08Winson Chungpackage com.android.launcher2; 184c98d9235d164680186180974719f551cf935d08Winson Chung 19201bc828d5a0078f505e8e37152156a0cb43c10aWinson Chungimport android.animation.Animator; 20201bc828d5a0078f505e8e37152156a0cb43c10aWinson Chungimport android.animation.AnimatorListenerAdapter; 21a62e9fd95ae10cff4676f2cc1f4d68334ca27a0bWinson Chungimport android.animation.AnimatorSet; 22201bc828d5a0078f505e8e37152156a0cb43c10aWinson Chungimport android.animation.ObjectAnimator; 234c98d9235d164680186180974719f551cf935d08Winson Chungimport android.content.Context; 24a62e9fd95ae10cff4676f2cc1f4d68334ca27a0bWinson Chungimport android.content.res.Configuration; 254c98d9235d164680186180974719f551cf935d08Winson Chungimport android.util.AttributeSet; 264c98d9235d164680186180974719f551cf935d08Winson Chungimport android.view.View; 27a62e9fd95ae10cff4676f2cc1f4d68334ca27a0bWinson Chungimport android.view.animation.AccelerateInterpolator; 28a62e9fd95ae10cff4676f2cc1f4d68334ca27a0bWinson Chungimport android.view.animation.DecelerateInterpolator; 294c98d9235d164680186180974719f551cf935d08Winson Chungimport android.widget.FrameLayout; 304c98d9235d164680186180974719f551cf935d08Winson Chung 314c98d9235d164680186180974719f551cf935d08Winson Chungimport com.android.launcher.R; 324c98d9235d164680186180974719f551cf935d08Winson Chung 334c98d9235d164680186180974719f551cf935d08Winson Chung/* 344c98d9235d164680186180974719f551cf935d08Winson Chung * Ths bar will manage the transition between the QSB search bar and the delete drop 354c98d9235d164680186180974719f551cf935d08Winson Chung * targets so that each of the individual IconDropTargets don't have to. 364c98d9235d164680186180974719f551cf935d08Winson Chung */ 374c98d9235d164680186180974719f551cf935d08Winson Chungpublic class SearchDropTargetBar extends FrameLayout implements DragController.DragListener { 384c98d9235d164680186180974719f551cf935d08Winson Chung 39a62e9fd95ae10cff4676f2cc1f4d68334ca27a0bWinson Chung private static final int sTransitionInDuration = 200; 40a62e9fd95ae10cff4676f2cc1f4d68334ca27a0bWinson Chung private static final int sTransitionOutDuration = 175; 414c98d9235d164680186180974719f551cf935d08Winson Chung 42a62e9fd95ae10cff4676f2cc1f4d68334ca27a0bWinson Chung private AnimatorSet mDropTargetBarFadeInAnim; 43a62e9fd95ae10cff4676f2cc1f4d68334ca27a0bWinson Chung private AnimatorSet mDropTargetBarFadeOutAnim; 44201bc828d5a0078f505e8e37152156a0cb43c10aWinson Chung private ObjectAnimator mQSBSearchBarFadeInAnim; 45201bc828d5a0078f505e8e37152156a0cb43c10aWinson Chung private ObjectAnimator mQSBSearchBarFadeOutAnim; 46201bc828d5a0078f505e8e37152156a0cb43c10aWinson Chung 47f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung private boolean mIsSearchBarHidden; 484c98d9235d164680186180974719f551cf935d08Winson Chung private View mQSBSearchBar; 494c98d9235d164680186180974719f551cf935d08Winson Chung private View mDropTargetBar; 5061fa4197c4316bb0f9b05fcefb676f86197a2273Winson Chung private ButtonDropTarget mInfoDropTarget; 5161fa4197c4316bb0f9b05fcefb676f86197a2273Winson Chung private ButtonDropTarget mDeleteDropTarget; 52a62e9fd95ae10cff4676f2cc1f4d68334ca27a0bWinson Chung private int mBarHeight; 53d4d7aa551ffdc80d810ff970fa72a6509960401eAdam Cohen private boolean mDeferOnDragEnd = false; 544c98d9235d164680186180974719f551cf935d08Winson Chung 554c98d9235d164680186180974719f551cf935d08Winson Chung public SearchDropTargetBar(Context context, AttributeSet attrs) { 564c98d9235d164680186180974719f551cf935d08Winson Chung this(context, attrs, 0); 574c98d9235d164680186180974719f551cf935d08Winson Chung } 584c98d9235d164680186180974719f551cf935d08Winson Chung 594c98d9235d164680186180974719f551cf935d08Winson Chung public SearchDropTargetBar(Context context, AttributeSet attrs, int defStyle) { 604c98d9235d164680186180974719f551cf935d08Winson Chung super(context, attrs, defStyle); 614c98d9235d164680186180974719f551cf935d08Winson Chung } 624c98d9235d164680186180974719f551cf935d08Winson Chung 634c98d9235d164680186180974719f551cf935d08Winson Chung public void setup(Launcher launcher, DragController dragController) { 644c98d9235d164680186180974719f551cf935d08Winson Chung dragController.addDragListener(this); 654c98d9235d164680186180974719f551cf935d08Winson Chung dragController.addDragListener(mInfoDropTarget); 664c98d9235d164680186180974719f551cf935d08Winson Chung dragController.addDragListener(mDeleteDropTarget); 674c98d9235d164680186180974719f551cf935d08Winson Chung dragController.addDropTarget(mInfoDropTarget); 684c98d9235d164680186180974719f551cf935d08Winson Chung dragController.addDropTarget(mDeleteDropTarget); 694c98d9235d164680186180974719f551cf935d08Winson Chung mInfoDropTarget.setLauncher(launcher); 704c98d9235d164680186180974719f551cf935d08Winson Chung mDeleteDropTarget.setLauncher(launcher); 714c98d9235d164680186180974719f551cf935d08Winson Chung } 724c98d9235d164680186180974719f551cf935d08Winson Chung 734c98d9235d164680186180974719f551cf935d08Winson Chung @Override 744c98d9235d164680186180974719f551cf935d08Winson Chung protected void onFinishInflate() { 754c98d9235d164680186180974719f551cf935d08Winson Chung super.onFinishInflate(); 764c98d9235d164680186180974719f551cf935d08Winson Chung 774c98d9235d164680186180974719f551cf935d08Winson Chung // Get the individual components 784c98d9235d164680186180974719f551cf935d08Winson Chung mQSBSearchBar = findViewById(R.id.qsb_search_bar); 794c98d9235d164680186180974719f551cf935d08Winson Chung mDropTargetBar = findViewById(R.id.drag_target_bar); 80a6427b15c18d5b8f3078f553d78f8432de9f46e9Winson Chung mInfoDropTarget = (ButtonDropTarget) mDropTargetBar.findViewById(R.id.info_target_text); 81a6427b15c18d5b8f3078f553d78f8432de9f46e9Winson Chung mDeleteDropTarget = (ButtonDropTarget) mDropTargetBar.findViewById(R.id.delete_target_text); 82a62e9fd95ae10cff4676f2cc1f4d68334ca27a0bWinson Chung mBarHeight = getResources().getDimensionPixelSize(R.dimen.qsb_bar_height); 83a62e9fd95ae10cff4676f2cc1f4d68334ca27a0bWinson Chung 84d4d7aa551ffdc80d810ff970fa72a6509960401eAdam Cohen mInfoDropTarget.setSearchDropTargetBar(this); 85d4d7aa551ffdc80d810ff970fa72a6509960401eAdam Cohen mDeleteDropTarget.setSearchDropTargetBar(this); 86d4d7aa551ffdc80d810ff970fa72a6509960401eAdam Cohen 87a62e9fd95ae10cff4676f2cc1f4d68334ca27a0bWinson Chung boolean enableDropDownDropTargets = 88a62e9fd95ae10cff4676f2cc1f4d68334ca27a0bWinson Chung getResources().getBoolean(R.bool.config_useDropTargetDownTransition); 89201bc828d5a0078f505e8e37152156a0cb43c10aWinson Chung 90201bc828d5a0078f505e8e37152156a0cb43c10aWinson Chung // Create the various fade animations 91a62e9fd95ae10cff4676f2cc1f4d68334ca27a0bWinson Chung mDropTargetBar.setAlpha(0f); 92a62e9fd95ae10cff4676f2cc1f4d68334ca27a0bWinson Chung ObjectAnimator fadeInAlphaAnim = ObjectAnimator.ofFloat(mDropTargetBar, "alpha", 1f); 93a62e9fd95ae10cff4676f2cc1f4d68334ca27a0bWinson Chung fadeInAlphaAnim.setInterpolator(new DecelerateInterpolator()); 94a62e9fd95ae10cff4676f2cc1f4d68334ca27a0bWinson Chung mDropTargetBarFadeInAnim = new AnimatorSet(); 95a62e9fd95ae10cff4676f2cc1f4d68334ca27a0bWinson Chung AnimatorSet.Builder fadeInAnimators = mDropTargetBarFadeInAnim.play(fadeInAlphaAnim); 96a62e9fd95ae10cff4676f2cc1f4d68334ca27a0bWinson Chung if (enableDropDownDropTargets) { 97a62e9fd95ae10cff4676f2cc1f4d68334ca27a0bWinson Chung mDropTargetBar.setTranslationY(-mBarHeight); 98a62e9fd95ae10cff4676f2cc1f4d68334ca27a0bWinson Chung fadeInAnimators.with(ObjectAnimator.ofFloat(mDropTargetBar, "translationY", 0f)); 99a62e9fd95ae10cff4676f2cc1f4d68334ca27a0bWinson Chung } 100201bc828d5a0078f505e8e37152156a0cb43c10aWinson Chung mDropTargetBarFadeInAnim.setDuration(sTransitionInDuration); 101201bc828d5a0078f505e8e37152156a0cb43c10aWinson Chung mDropTargetBarFadeInAnim.addListener(new AnimatorListenerAdapter() { 102201bc828d5a0078f505e8e37152156a0cb43c10aWinson Chung @Override 103201bc828d5a0078f505e8e37152156a0cb43c10aWinson Chung public void onAnimationStart(Animator animation) { 104201bc828d5a0078f505e8e37152156a0cb43c10aWinson Chung mDropTargetBar.setVisibility(View.VISIBLE); 105201bc828d5a0078f505e8e37152156a0cb43c10aWinson Chung } 106201bc828d5a0078f505e8e37152156a0cb43c10aWinson Chung }); 107a62e9fd95ae10cff4676f2cc1f4d68334ca27a0bWinson Chung ObjectAnimator fadeOutAlphaAnim = ObjectAnimator.ofFloat(mDropTargetBar, "alpha", 0f); 108a62e9fd95ae10cff4676f2cc1f4d68334ca27a0bWinson Chung fadeOutAlphaAnim.setInterpolator(new AccelerateInterpolator()); 109a62e9fd95ae10cff4676f2cc1f4d68334ca27a0bWinson Chung mDropTargetBarFadeOutAnim = new AnimatorSet(); 110a62e9fd95ae10cff4676f2cc1f4d68334ca27a0bWinson Chung AnimatorSet.Builder fadeOutAnimators = mDropTargetBarFadeOutAnim.play(fadeOutAlphaAnim); 111a62e9fd95ae10cff4676f2cc1f4d68334ca27a0bWinson Chung if (enableDropDownDropTargets) { 112a62e9fd95ae10cff4676f2cc1f4d68334ca27a0bWinson Chung fadeOutAnimators.with(ObjectAnimator.ofFloat(mDropTargetBar, "translationY", 113a62e9fd95ae10cff4676f2cc1f4d68334ca27a0bWinson Chung -mBarHeight)); 114a62e9fd95ae10cff4676f2cc1f4d68334ca27a0bWinson Chung } 115201bc828d5a0078f505e8e37152156a0cb43c10aWinson Chung mDropTargetBarFadeOutAnim.setDuration(sTransitionOutDuration); 116201bc828d5a0078f505e8e37152156a0cb43c10aWinson Chung mDropTargetBarFadeOutAnim.addListener(new AnimatorListenerAdapter() { 117201bc828d5a0078f505e8e37152156a0cb43c10aWinson Chung @Override 118201bc828d5a0078f505e8e37152156a0cb43c10aWinson Chung public void onAnimationEnd(Animator animation) { 119201bc828d5a0078f505e8e37152156a0cb43c10aWinson Chung mDropTargetBar.setVisibility(View.GONE); 120a62e9fd95ae10cff4676f2cc1f4d68334ca27a0bWinson Chung mDropTargetBar.setLayerType(View.LAYER_TYPE_NONE, null); 121201bc828d5a0078f505e8e37152156a0cb43c10aWinson Chung } 122201bc828d5a0078f505e8e37152156a0cb43c10aWinson Chung }); 123201bc828d5a0078f505e8e37152156a0cb43c10aWinson Chung mQSBSearchBarFadeInAnim = ObjectAnimator.ofFloat(mQSBSearchBar, "alpha", 1f); 124201bc828d5a0078f505e8e37152156a0cb43c10aWinson Chung mQSBSearchBarFadeInAnim.setDuration(sTransitionInDuration); 125201bc828d5a0078f505e8e37152156a0cb43c10aWinson Chung mQSBSearchBarFadeInAnim.addListener(new AnimatorListenerAdapter() { 126201bc828d5a0078f505e8e37152156a0cb43c10aWinson Chung @Override 127201bc828d5a0078f505e8e37152156a0cb43c10aWinson Chung public void onAnimationStart(Animator animation) { 128201bc828d5a0078f505e8e37152156a0cb43c10aWinson Chung mQSBSearchBar.setVisibility(View.VISIBLE); 129201bc828d5a0078f505e8e37152156a0cb43c10aWinson Chung } 130201bc828d5a0078f505e8e37152156a0cb43c10aWinson Chung }); 131201bc828d5a0078f505e8e37152156a0cb43c10aWinson Chung mQSBSearchBarFadeOutAnim = ObjectAnimator.ofFloat(mQSBSearchBar, "alpha", 0f); 132201bc828d5a0078f505e8e37152156a0cb43c10aWinson Chung mQSBSearchBarFadeOutAnim.setDuration(sTransitionOutDuration); 133201bc828d5a0078f505e8e37152156a0cb43c10aWinson Chung mQSBSearchBarFadeOutAnim.addListener(new AnimatorListenerAdapter() { 134201bc828d5a0078f505e8e37152156a0cb43c10aWinson Chung @Override 135201bc828d5a0078f505e8e37152156a0cb43c10aWinson Chung public void onAnimationEnd(Animator animation) { 136201bc828d5a0078f505e8e37152156a0cb43c10aWinson Chung mQSBSearchBar.setVisibility(View.GONE); 137201bc828d5a0078f505e8e37152156a0cb43c10aWinson Chung } 138201bc828d5a0078f505e8e37152156a0cb43c10aWinson Chung }); 139201bc828d5a0078f505e8e37152156a0cb43c10aWinson Chung } 140201bc828d5a0078f505e8e37152156a0cb43c10aWinson Chung 141201bc828d5a0078f505e8e37152156a0cb43c10aWinson Chung private void cancelAnimations() { 142201bc828d5a0078f505e8e37152156a0cb43c10aWinson Chung mDropTargetBarFadeInAnim.cancel(); 143201bc828d5a0078f505e8e37152156a0cb43c10aWinson Chung mDropTargetBarFadeOutAnim.cancel(); 144201bc828d5a0078f505e8e37152156a0cb43c10aWinson Chung mQSBSearchBarFadeInAnim.cancel(); 145201bc828d5a0078f505e8e37152156a0cb43c10aWinson Chung mQSBSearchBarFadeOutAnim.cancel(); 1464c98d9235d164680186180974719f551cf935d08Winson Chung } 1474c98d9235d164680186180974719f551cf935d08Winson Chung 1484c98d9235d164680186180974719f551cf935d08Winson Chung /* 149f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung * Shows and hides the search bar. 150f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung */ 151f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung public void showSearchBar(boolean animated) { 152201bc828d5a0078f505e8e37152156a0cb43c10aWinson Chung cancelAnimations(); 153f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung if (animated) { 154201bc828d5a0078f505e8e37152156a0cb43c10aWinson Chung mQSBSearchBarFadeInAnim.start(); 155f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung } else { 156201bc828d5a0078f505e8e37152156a0cb43c10aWinson Chung mQSBSearchBar.setVisibility(View.VISIBLE); 157f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung mQSBSearchBar.setAlpha(1f); 158f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung } 159f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung mIsSearchBarHidden = false; 160f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung } 161f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung public void hideSearchBar(boolean animated) { 162201bc828d5a0078f505e8e37152156a0cb43c10aWinson Chung cancelAnimations(); 163f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung if (animated) { 164201bc828d5a0078f505e8e37152156a0cb43c10aWinson Chung mQSBSearchBarFadeOutAnim.start(); 165f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung } else { 166201bc828d5a0078f505e8e37152156a0cb43c10aWinson Chung mQSBSearchBar.setVisibility(View.GONE); 167f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung mQSBSearchBar.setAlpha(0f); 168f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung } 169f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung mIsSearchBarHidden = true; 170f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung } 171f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung 172f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung /* 173f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung * Gets various transition durations. 174f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung */ 175f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung public int getTransitionInDuration() { 176f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung return sTransitionInDuration; 177f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung } 178f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung public int getTransitionOutDuration() { 179f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung return sTransitionOutDuration; 180f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung } 181f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung 182f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung /* 1834c98d9235d164680186180974719f551cf935d08Winson Chung * DragController.DragListener implementation 1844c98d9235d164680186180974719f551cf935d08Winson Chung */ 1854c98d9235d164680186180974719f551cf935d08Winson Chung @Override 1864c98d9235d164680186180974719f551cf935d08Winson Chung public void onDragStart(DragSource source, Object info, int dragAction) { 1874c98d9235d164680186180974719f551cf935d08Winson Chung // Animate out the QSB search bar, and animate in the drop target bar 188a62e9fd95ae10cff4676f2cc1f4d68334ca27a0bWinson Chung mDropTargetBar.setLayerType(View.LAYER_TYPE_HARDWARE, null); 189a62e9fd95ae10cff4676f2cc1f4d68334ca27a0bWinson Chung mDropTargetBar.buildLayer(); 190a62e9fd95ae10cff4676f2cc1f4d68334ca27a0bWinson Chung mDropTargetBarFadeOutAnim.cancel(); 191201bc828d5a0078f505e8e37152156a0cb43c10aWinson Chung mDropTargetBarFadeInAnim.start(); 192f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung if (!mIsSearchBarHidden) { 193a62e9fd95ae10cff4676f2cc1f4d68334ca27a0bWinson Chung mQSBSearchBarFadeInAnim.cancel(); 194201bc828d5a0078f505e8e37152156a0cb43c10aWinson Chung mQSBSearchBarFadeOutAnim.start(); 195f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung } 1964c98d9235d164680186180974719f551cf935d08Winson Chung } 1974c98d9235d164680186180974719f551cf935d08Winson Chung 198d4d7aa551ffdc80d810ff970fa72a6509960401eAdam Cohen public void deferOnDragEnd() { 199d4d7aa551ffdc80d810ff970fa72a6509960401eAdam Cohen mDeferOnDragEnd = true; 200d4d7aa551ffdc80d810ff970fa72a6509960401eAdam Cohen } 201d4d7aa551ffdc80d810ff970fa72a6509960401eAdam Cohen 2024c98d9235d164680186180974719f551cf935d08Winson Chung @Override 2034c98d9235d164680186180974719f551cf935d08Winson Chung public void onDragEnd() { 204d4d7aa551ffdc80d810ff970fa72a6509960401eAdam Cohen if (!mDeferOnDragEnd) { 205d4d7aa551ffdc80d810ff970fa72a6509960401eAdam Cohen // Restore the QSB search bar, and animate out the drop target bar 206d4d7aa551ffdc80d810ff970fa72a6509960401eAdam Cohen mDropTargetBarFadeInAnim.cancel(); 207d4d7aa551ffdc80d810ff970fa72a6509960401eAdam Cohen mDropTargetBarFadeOutAnim.start(); 208d4d7aa551ffdc80d810ff970fa72a6509960401eAdam Cohen if (!mIsSearchBarHidden) { 209d4d7aa551ffdc80d810ff970fa72a6509960401eAdam Cohen mQSBSearchBarFadeOutAnim.cancel(); 210d4d7aa551ffdc80d810ff970fa72a6509960401eAdam Cohen mQSBSearchBarFadeInAnim.start(); 211d4d7aa551ffdc80d810ff970fa72a6509960401eAdam Cohen } 212d4d7aa551ffdc80d810ff970fa72a6509960401eAdam Cohen } else { 213d4d7aa551ffdc80d810ff970fa72a6509960401eAdam Cohen mDeferOnDragEnd = false; 214f0ea4d3378be7b962c8e0bce2392df5e82491fb8Winson Chung } 2154c98d9235d164680186180974719f551cf935d08Winson Chung } 2164c98d9235d164680186180974719f551cf935d08Winson Chung} 217