1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.car.dialer.ui.search; 18 19 import android.os.Bundle; 20 import android.text.TextUtils; 21 import android.view.View; 22 23 import androidx.annotation.NonNull; 24 import androidx.annotation.Nullable; 25 import androidx.fragment.app.Fragment; 26 import androidx.lifecycle.ViewModelProvider; 27 28 import com.android.car.dialer.R; 29 import com.android.car.dialer.log.L; 30 import com.android.car.dialer.ui.common.DialerListBaseFragment; 31 import com.android.car.dialer.ui.contact.ContactDetailsFragment; 32 import com.android.car.telephony.common.Contact; 33 import com.android.car.ui.recyclerview.CarUiRecyclerView; 34 import com.android.car.ui.toolbar.Toolbar; 35 import com.android.car.ui.toolbar.ToolbarController; 36 import com.android.car.uxr.LifeCycleObserverUxrContentLimiter; 37 import com.android.car.uxr.UxrContentLimiterImpl; 38 39 import dagger.hilt.android.AndroidEntryPoint; 40 41 /** 42 * A fragment that will take a search query, look up contacts that match and display those 43 * results as a list. 44 */ 45 @AndroidEntryPoint(DialerListBaseFragment.class) 46 public class ContactResultsFragment extends Hilt_ContactResultsFragment implements 47 Toolbar.OnSearchListener { 48 49 /** 50 * Creates a new instance of the {@link ContactResultsFragment}. 51 * 52 * @param initialSearchQuery An optional search query that will be inputted when the fragment 53 * starts up. 54 */ newInstance(@ullable String initialSearchQuery)55 public static ContactResultsFragment newInstance(@Nullable String initialSearchQuery) { 56 ContactResultsFragment fragment = new ContactResultsFragment(); 57 Bundle args = new Bundle(); 58 args.putString(SEARCH_QUERY, initialSearchQuery); 59 fragment.setArguments(args); 60 return fragment; 61 } 62 63 public static final String FRAGMENT_TAG = "ContactResultsFragment"; 64 65 private static final String TAG = "CD.ContactResultsFragment"; 66 private static final String SEARCH_QUERY = "SearchQuery"; 67 68 private ContactResultsViewModel mContactResultsViewModel; 69 private final ContactResultsAdapter mAdapter = new ContactResultsAdapter( 70 contactResult -> onShowContactDetail(contactResult.getContact())); 71 72 private CarUiRecyclerView.OnScrollListener mOnScrollChangeListener; 73 private ToolbarController mToolbar; 74 75 private LifeCycleObserverUxrContentLimiter mUxrContentLimiter; 76 77 @Override onCreate(Bundle savedInstanceState)78 public void onCreate(Bundle savedInstanceState) { 79 super.onCreate(savedInstanceState); 80 81 mContactResultsViewModel = new ViewModelProvider(this).get( 82 ContactResultsViewModel.class); 83 mContactResultsViewModel.getContactSearchResults().observe(this, 84 contactResults -> { 85 mAdapter.setData(contactResults); 86 showContent(); 87 }); 88 mContactResultsViewModel.getSortOrderLiveData().observe(this, 89 v -> mAdapter.setSortMethod(v)); 90 91 // Set the initial search query, if one was provided from a Intent.ACTION_SEARCH 92 if (getArguments() != null) { 93 String initialQuery = getArguments().getString(SEARCH_QUERY); 94 if (!TextUtils.isEmpty(initialQuery)) { 95 setSearchQuery(initialQuery); 96 } 97 getArguments().clear(); 98 } 99 100 mUxrContentLimiter = new LifeCycleObserverUxrContentLimiter( 101 new UxrContentLimiterImpl(getContext(), R.xml.uxr_config)); 102 getLifecycle().addObserver(mUxrContentLimiter); 103 104 mOnScrollChangeListener = new CarUiRecyclerView.OnScrollListener() { 105 @Override 106 public void onScrollStateChanged(@NonNull CarUiRecyclerView recyclerView, 107 int newState) { 108 } 109 110 @Override 111 public void onScrolled(@NonNull CarUiRecyclerView recyclerView, int dx, int dy) { 112 if (dy != 0) { 113 // Clear the focus to dismiss the keyboard in touch mode. 114 View focusedView = getActivity().getCurrentFocus(); 115 if (focusedView != null && focusedView.isInTouchMode()) { 116 focusedView.clearFocus(); 117 } 118 } 119 } 120 }; 121 } 122 123 @Override onViewCreated(View view, Bundle savedInstanceState)124 public void onViewCreated(View view, Bundle savedInstanceState) { 125 super.onViewCreated(view, savedInstanceState); 126 getRecyclerView().setAdapter(mAdapter); 127 mUxrContentLimiter.setAdapter(mAdapter); 128 } 129 130 @Override onDestroyView()131 public void onDestroyView() { 132 getRecyclerView().removeOnScrollListener(mOnScrollChangeListener); 133 if (mToolbar != null) { 134 mToolbar.unregisterOnSearchListener(this); 135 } 136 super.onDestroyView(); 137 } 138 139 @Override setupToolbar(@onNull ToolbarController toolbar)140 protected void setupToolbar(@NonNull ToolbarController toolbar) { 141 super.setupToolbar(toolbar); 142 mToolbar = toolbar; 143 mToolbar.registerOnSearchListener(this); 144 mToolbar.setSearchIcon(R.drawable.ic_app_icon); 145 setSearchQuery(mContactResultsViewModel.getSearchQuery()); 146 147 if (mToolbar.canShowSearchResultsView()) { 148 mToolbar.setSearchResultsView(getRecyclerView().getView()); 149 } else { 150 // Widescreen IME list should not set the scroll listener to dismiss the keyboard. 151 getRecyclerView().addOnScrollListener(mOnScrollChangeListener); 152 } 153 } 154 155 /** Sets the search query that should be used to filter contacts. */ setSearchQuery(String query)156 public void setSearchQuery(String query) { 157 if (mToolbar != null) { 158 mToolbar.setSearchQuery(query); 159 } else { 160 onSearch(query); 161 } 162 } 163 164 /** Triggered by search view text change. */ 165 @Override onSearch(String newQuery)166 public void onSearch(String newQuery) { 167 L.d(TAG, "onSearch: %s", newQuery); 168 mContactResultsViewModel.setSearchQuery(newQuery); 169 } 170 onShowContactDetail(Contact contact)171 protected void onShowContactDetail(Contact contact) { 172 Fragment contactDetailsFragment = ContactDetailsFragment.newInstance(contact); 173 pushContentFragment(contactDetailsFragment, ContactDetailsFragment.FRAGMENT_TAG); 174 } 175 176 @Override getToolbarState()177 protected Toolbar.State getToolbarState() { 178 return Toolbar.State.SEARCH; 179 } 180 } 181