183a6621b38457a517bc842dc6df902ad8a370adfDoris Ling/* 283a6621b38457a517bc842dc6df902ad8a370adfDoris Ling * Copyright (C) 2017 The Android Open Source Project 383a6621b38457a517bc842dc6df902ad8a370adfDoris Ling * 483a6621b38457a517bc842dc6df902ad8a370adfDoris Ling * Licensed under the Apache License, Version 2.0 (the "License"); 583a6621b38457a517bc842dc6df902ad8a370adfDoris Ling * you may not use this file except in compliance with the License. 683a6621b38457a517bc842dc6df902ad8a370adfDoris Ling * You may obtain a copy of the License at 783a6621b38457a517bc842dc6df902ad8a370adfDoris Ling * 883a6621b38457a517bc842dc6df902ad8a370adfDoris Ling * http://www.apache.org/licenses/LICENSE-2.0 983a6621b38457a517bc842dc6df902ad8a370adfDoris Ling * 1083a6621b38457a517bc842dc6df902ad8a370adfDoris Ling * Unless required by applicable law or agreed to in writing, software 1183a6621b38457a517bc842dc6df902ad8a370adfDoris Ling * distributed under the License is distributed on an "AS IS" BASIS, 1283a6621b38457a517bc842dc6df902ad8a370adfDoris Ling * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1383a6621b38457a517bc842dc6df902ad8a370adfDoris Ling * See the License for the specific language governing permissions and 1483a6621b38457a517bc842dc6df902ad8a370adfDoris Ling * limitations under the License. 1583a6621b38457a517bc842dc6df902ad8a370adfDoris Ling */ 1683a6621b38457a517bc842dc6df902ad8a370adfDoris Lingpackage com.android.settings.dashboard.suggestions; 1783a6621b38457a517bc842dc6df902ad8a370adfDoris Ling 18b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Lingimport android.content.ComponentName; 1983a6621b38457a517bc842dc6df902ad8a370adfDoris Lingimport android.content.Context; 20b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Lingimport android.content.Intent; 2183a6621b38457a517bc842dc6df902ad8a370adfDoris Lingimport android.graphics.drawable.Icon; 2283a6621b38457a517bc842dc6df902ad8a370adfDoris Lingimport android.view.LayoutInflater; 2383a6621b38457a517bc842dc6df902ad8a370adfDoris Lingimport android.view.View; 24b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Lingimport android.view.ContextThemeWrapper; 25b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Lingimport android.view.ViewGroup; 26b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Lingimport android.widget.Button; 27b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Lingimport android.widget.FrameLayout; 2883a6621b38457a517bc842dc6df902ad8a370adfDoris Lingimport android.widget.LinearLayout; 29b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Lingimport android.widget.RemoteViews; 30b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Lingimport android.widget.TextView; 3183a6621b38457a517bc842dc6df902ad8a370adfDoris Ling 3283a6621b38457a517bc842dc6df902ad8a370adfDoris Lingimport com.android.settings.R; 33b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Lingimport com.android.settings.SettingsActivity; 349f1e911759dc6fedaac9fa65afb79f6a93022bf4Andrew Sappersteinimport com.android.settings.testutils.SettingsRobolectricTestRunner; 3583a6621b38457a517bc842dc6df902ad8a370adfDoris Lingimport com.android.settings.TestConfig; 3683a6621b38457a517bc842dc6df902ad8a370adfDoris Lingimport com.android.settings.dashboard.DashboardAdapter; 37b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Lingimport com.android.settings.testutils.FakeFeatureFactory; 3883a6621b38457a517bc842dc6df902ad8a370adfDoris Lingimport com.android.settingslib.drawer.Tile; 3983a6621b38457a517bc842dc6df902ad8a370adfDoris Ling 4083a6621b38457a517bc842dc6df902ad8a370adfDoris Lingimport java.util.ArrayList; 4183a6621b38457a517bc842dc6df902ad8a370adfDoris Lingimport java.util.List; 4283a6621b38457a517bc842dc6df902ad8a370adfDoris Ling 4383a6621b38457a517bc842dc6df902ad8a370adfDoris Lingimport org.junit.Before; 4483a6621b38457a517bc842dc6df902ad8a370adfDoris Lingimport org.junit.Test; 4583a6621b38457a517bc842dc6df902ad8a370adfDoris Lingimport org.junit.runner.RunWith; 46b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Lingimport org.mockito.Answers; 4783a6621b38457a517bc842dc6df902ad8a370adfDoris Lingimport org.mockito.Mock; 4883a6621b38457a517bc842dc6df902ad8a370adfDoris Lingimport org.mockito.MockitoAnnotations; 4983a6621b38457a517bc842dc6df902ad8a370adfDoris Lingimport org.robolectric.RuntimeEnvironment; 5083a6621b38457a517bc842dc6df902ad8a370adfDoris Lingimport org.robolectric.annotation.Config; 51b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Lingimport org.robolectric.shadows.ShadowApplication; 5283a6621b38457a517bc842dc6df902ad8a370adfDoris Ling 5383a6621b38457a517bc842dc6df902ad8a370adfDoris Lingimport static com.google.common.truth.Truth.assertThat; 5483a6621b38457a517bc842dc6df902ad8a370adfDoris Lingimport static org.mockito.Mockito.any; 55b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Lingimport static org.mockito.Mockito.doReturn; 5683a6621b38457a517bc842dc6df902ad8a370adfDoris Lingimport static org.mockito.Mockito.mock; 57b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Lingimport static org.mockito.Mockito.never; 5883a6621b38457a517bc842dc6df902ad8a370adfDoris Lingimport static org.mockito.Mockito.spy; 59b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Lingimport static org.mockito.Mockito.when; 6083a6621b38457a517bc842dc6df902ad8a370adfDoris Lingimport static org.mockito.Mockito.verify; 6183a6621b38457a517bc842dc6df902ad8a370adfDoris Ling 6283a6621b38457a517bc842dc6df902ad8a370adfDoris Ling@RunWith(SettingsRobolectricTestRunner.class) 6383a6621b38457a517bc842dc6df902ad8a370adfDoris Ling@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) 6483a6621b38457a517bc842dc6df902ad8a370adfDoris Lingpublic class SuggestionAdapterTest { 6583a6621b38457a517bc842dc6df902ad8a370adfDoris Ling @Mock 6683a6621b38457a517bc842dc6df902ad8a370adfDoris Ling private Tile mSuggestion1; 6783a6621b38457a517bc842dc6df902ad8a370adfDoris Ling @Mock 6883a6621b38457a517bc842dc6df902ad8a370adfDoris Ling private Tile mSuggestion2; 69b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling @Mock(answer = Answers.RETURNS_DEEP_STUBS) 70b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling private SettingsActivity mActivity; 7183a6621b38457a517bc842dc6df902ad8a370adfDoris Ling 7283a6621b38457a517bc842dc6df902ad8a370adfDoris Ling private Context mContext; 7383a6621b38457a517bc842dc6df902ad8a370adfDoris Ling private SuggestionAdapter mSuggestionAdapter; 74b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling private DashboardAdapter.DashboardItemHolder mSuggestionHolder; 7583a6621b38457a517bc842dc6df902ad8a370adfDoris Ling private List<Tile> mOneSuggestion; 7683a6621b38457a517bc842dc6df902ad8a370adfDoris Ling private List<Tile> mTwoSuggestions; 7783a6621b38457a517bc842dc6df902ad8a370adfDoris Ling 7883a6621b38457a517bc842dc6df902ad8a370adfDoris Ling @Before 7983a6621b38457a517bc842dc6df902ad8a370adfDoris Ling public void setUp() { 8083a6621b38457a517bc842dc6df902ad8a370adfDoris Ling MockitoAnnotations.initMocks(this); 8183a6621b38457a517bc842dc6df902ad8a370adfDoris Ling mContext = RuntimeEnvironment.application; 82b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling FakeFeatureFactory.setupForTest(mActivity); 83b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling 8483a6621b38457a517bc842dc6df902ad8a370adfDoris Ling mSuggestion1.title = "Test Suggestion 1"; 8583a6621b38457a517bc842dc6df902ad8a370adfDoris Ling mSuggestion1.icon = mock(Icon.class); 8683a6621b38457a517bc842dc6df902ad8a370adfDoris Ling mSuggestion2.title = "Test Suggestion 2"; 8783a6621b38457a517bc842dc6df902ad8a370adfDoris Ling mSuggestion2.icon = mock(Icon.class); 8883a6621b38457a517bc842dc6df902ad8a370adfDoris Ling mOneSuggestion = new ArrayList<>(); 8983a6621b38457a517bc842dc6df902ad8a370adfDoris Ling mOneSuggestion.add(mSuggestion1); 9083a6621b38457a517bc842dc6df902ad8a370adfDoris Ling mTwoSuggestions = new ArrayList<>(); 9183a6621b38457a517bc842dc6df902ad8a370adfDoris Ling mTwoSuggestions.add(mSuggestion1); 9283a6621b38457a517bc842dc6df902ad8a370adfDoris Ling mTwoSuggestions.add(mSuggestion2); 9383a6621b38457a517bc842dc6df902ad8a370adfDoris Ling } 9483a6621b38457a517bc842dc6df902ad8a370adfDoris Ling 9583a6621b38457a517bc842dc6df902ad8a370adfDoris Ling @Test 9683a6621b38457a517bc842dc6df902ad8a370adfDoris Ling public void getItemCount_shouldReturnListSize() { 9783a6621b38457a517bc842dc6df902ad8a370adfDoris Ling mSuggestionAdapter = new SuggestionAdapter(mContext, mOneSuggestion, new ArrayList<>()); 9883a6621b38457a517bc842dc6df902ad8a370adfDoris Ling assertThat(mSuggestionAdapter.getItemCount()).isEqualTo(1); 9983a6621b38457a517bc842dc6df902ad8a370adfDoris Ling 10083a6621b38457a517bc842dc6df902ad8a370adfDoris Ling mSuggestionAdapter = new SuggestionAdapter(mContext, mTwoSuggestions, new ArrayList<>()); 10183a6621b38457a517bc842dc6df902ad8a370adfDoris Ling assertThat(mSuggestionAdapter.getItemCount()).isEqualTo(2); 10283a6621b38457a517bc842dc6df902ad8a370adfDoris Ling } 10383a6621b38457a517bc842dc6df902ad8a370adfDoris Ling 10483a6621b38457a517bc842dc6df902ad8a370adfDoris Ling @Test 10583a6621b38457a517bc842dc6df902ad8a370adfDoris Ling public void getItemViewType_shouldReturnSuggestionTile() { 10683a6621b38457a517bc842dc6df902ad8a370adfDoris Ling mSuggestionAdapter = new SuggestionAdapter(mContext, mOneSuggestion, new ArrayList<>()); 10783a6621b38457a517bc842dc6df902ad8a370adfDoris Ling assertThat(mSuggestionAdapter.getItemViewType(0)) 1081b6685f4a4a9f18d557f356f0af2b34e5e247a5eDoris Ling .isEqualTo(R.layout.suggestion_tile); 10983a6621b38457a517bc842dc6df902ad8a370adfDoris Ling } 11083a6621b38457a517bc842dc6df902ad8a370adfDoris Ling 11183a6621b38457a517bc842dc6df902ad8a370adfDoris Ling @Test 11283a6621b38457a517bc842dc6df902ad8a370adfDoris Ling public void onBindViewHolder_shouldSetListener() { 11383a6621b38457a517bc842dc6df902ad8a370adfDoris Ling final View view = spy(LayoutInflater.from(mContext).inflate( 1141b6685f4a4a9f18d557f356f0af2b34e5e247a5eDoris Ling R.layout.suggestion_tile, new LinearLayout(mContext), true)); 115b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling mSuggestionHolder = new DashboardAdapter.DashboardItemHolder(view); 11683a6621b38457a517bc842dc6df902ad8a370adfDoris Ling mSuggestionAdapter = new SuggestionAdapter(mContext, mOneSuggestion, new ArrayList<>()); 11783a6621b38457a517bc842dc6df902ad8a370adfDoris Ling 118b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling mSuggestionAdapter.onBindViewHolder(mSuggestionHolder, 0); 11983a6621b38457a517bc842dc6df902ad8a370adfDoris Ling 12083a6621b38457a517bc842dc6df902ad8a370adfDoris Ling verify(view).setOnClickListener(any(View.OnClickListener.class)); 12183a6621b38457a517bc842dc6df902ad8a370adfDoris Ling } 12283a6621b38457a517bc842dc6df902ad8a370adfDoris Ling 123b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling @Test 124b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling public void onBindViewHolder_shouldInflateRemoteView() { 125b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling List<Tile> packages = makeSuggestions("pkg1"); 126b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling RemoteViews remoteViews = mock(RemoteViews.class); 127b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling TextView textView = new TextView(RuntimeEnvironment.application); 128b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling doReturn(textView).when(remoteViews).apply(any(Context.class), any(ViewGroup.class)); 129b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling packages.get(0).remoteViews = remoteViews; 130b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling setupSuggestions(mActivity, packages); 131b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling 132b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling mSuggestionAdapter.onBindViewHolder(mSuggestionHolder, 0); 133b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling 134b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling assertThat(textView.getParent()).isSameAs(mSuggestionHolder.itemView); 135b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling mSuggestionHolder.itemView.performClick(); 136b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling 137b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling verify(mActivity).startSuggestion(any(Intent.class)); 138b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling } 139b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling 140b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling @Test 141b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling public void onBindViewHolder_primaryViewShouldHandleClick() { 142b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling Context context = 143b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling new ContextThemeWrapper(RuntimeEnvironment.application, R.style.Theme_Settings); 144b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling 145b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling List<Tile> packages = makeSuggestions("pkg1"); 146b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling RemoteViews remoteViews = mock(RemoteViews.class); 147b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling FrameLayout layout = new FrameLayout(context); 148b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling Button primary = new Button(context); 149b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling primary.setId(android.R.id.primary); 150b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling layout.addView(primary); 151b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling doReturn(layout).when(remoteViews).apply(any(Context.class), any(ViewGroup.class)); 152b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling packages.get(0).remoteViews = remoteViews; 153b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling setupSuggestions(mActivity, packages); 154b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling 155b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling mSuggestionAdapter.onBindViewHolder(mSuggestionHolder, 0); 156b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling mSuggestionHolder.itemView.performClick(); 157b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling 158b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling assertThat(ShadowApplication.getInstance().getNextStartedActivity()).isNull(); 159b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling verify(mActivity, never()).startSuggestion(any(Intent.class)); 160b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling 161b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling primary.performClick(); 162b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling 163b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling verify(mActivity).startSuggestion(any(Intent.class)); 164b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling } 165b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling 166b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling @Test 167b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling public void onBindViewHolder_viewsShouldClearOnRebind() { 168b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling Context context = 169b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling new ContextThemeWrapper(RuntimeEnvironment.application, R.style.Theme_Settings); 170b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling 171b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling List<Tile> packages = makeSuggestions("pkg1"); 172b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling RemoteViews remoteViews = mock(RemoteViews.class); 173b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling FrameLayout layout = new FrameLayout(context); 174b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling Button primary = new Button(context); 175b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling primary.setId(android.R.id.primary); 176b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling layout.addView(primary); 177b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling doReturn(layout).when(remoteViews).apply(any(Context.class), any(ViewGroup.class)); 178b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling packages.get(0).remoteViews = remoteViews; 179b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling setupSuggestions(mActivity, packages); 180b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling 181b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling mSuggestionAdapter.onBindViewHolder(mSuggestionHolder, 0); 182b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling mSuggestionAdapter.onBindViewHolder(mSuggestionHolder, 0); 183b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling 184b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling ViewGroup itemView = (ViewGroup) mSuggestionHolder.itemView; 185b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling assertThat(itemView.getChildCount()).isEqualTo(1); 186b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling } 187b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling 188b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling private void setupSuggestions(Context context, List<Tile> suggestions) { 189b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling mSuggestionAdapter = new SuggestionAdapter(context, suggestions, new ArrayList<>()); 190b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling mSuggestionHolder = mSuggestionAdapter.onCreateViewHolder( 191b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling new FrameLayout(RuntimeEnvironment.application), 192b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling mSuggestionAdapter.getItemViewType(0)); 193b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling } 194b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling 195b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling private List<Tile> makeSuggestions(String... pkgNames) { 196b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling final List<Tile> suggestions = new ArrayList<>(); 197b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling for (String pkgName : pkgNames) { 198b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling Tile suggestion = new Tile(); 199b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling suggestion.intent = new Intent("action"); 200b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling suggestion.intent.setComponent(new ComponentName(pkgName, "cls")); 201b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling suggestions.add(suggestion); 202b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling suggestion.icon = mock(Icon.class); 203b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling } 204b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling return suggestions; 205b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling } 206b99af0e4a210f68b8fda8486d3f7fa33645fb5aaDoris Ling 20783a6621b38457a517bc842dc6df902ad8a370adfDoris Ling} 208