• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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.settings.datetime.timezone;
18 
19 import android.content.res.Configuration;
20 import android.os.Bundle;
21 import android.view.LayoutInflater;
22 import android.view.Menu;
23 import android.view.MenuInflater;
24 import android.view.MenuItem;
25 import android.view.View;
26 import android.view.ViewGroup;
27 import android.widget.LinearLayout;
28 import android.widget.SearchView;
29 import android.widget.TextView;
30 
31 import androidx.annotation.NonNull;
32 import androidx.coordinatorlayout.widget.CoordinatorLayout;
33 import androidx.core.view.ViewCompat;
34 import androidx.recyclerview.widget.LinearLayoutManager;
35 import androidx.recyclerview.widget.RecyclerView;
36 
37 import com.android.settings.R;
38 import com.android.settings.core.InstrumentedFragment;
39 import com.android.settings.datetime.timezone.model.TimeZoneData;
40 import com.android.settings.datetime.timezone.model.TimeZoneDataLoader;
41 
42 import com.google.android.material.appbar.AppBarLayout;
43 
44 import java.util.Locale;
45 
46 /**
47  * It's abstract class. Subclass should use it with {@class BaseTimeZoneAdapter} and
48  * {@class AdapterItem} to provide a list view with text search capability.
49  * The search matches the prefix of words in the search text.
50  */
51 public abstract class BaseTimeZonePicker extends InstrumentedFragment
52         implements SearchView.OnQueryTextListener, MenuItem.OnActionExpandListener {
53 
54     public static final String EXTRA_RESULT_REGION_ID =
55             "com.android.settings.datetime.timezone.result_region_id";
56     public static final String EXTRA_RESULT_TIME_ZONE_ID =
57             "com.android.settings.datetime.timezone.result_time_zone_id";
58 
59     protected AppBarLayout mAppBarLayout;
60 
61     private final int mTitleResId;
62     private final int mSearchHintResId;
63     private final boolean mSearchEnabled;
64     private final boolean mDefaultExpandSearch;
65 
66     protected Locale mLocale;
67     private BaseTimeZoneAdapter mAdapter;
68     private RecyclerView mRecyclerView;
69     private TimeZoneData mTimeZoneData;
70 
71     private SearchView mSearchView;
72 
73     /**
74      * Constructor called by subclass.
75      * @param defaultExpandSearch whether expand the search view when first launching the fragment
76      */
BaseTimeZonePicker(int titleResId, int searchHintResId, boolean searchEnabled, boolean defaultExpandSearch)77     protected BaseTimeZonePicker(int titleResId, int searchHintResId,
78             boolean searchEnabled, boolean defaultExpandSearch) {
79         mTitleResId = titleResId;
80         mSearchHintResId = searchHintResId;
81         mSearchEnabled = searchEnabled;
82         mDefaultExpandSearch = defaultExpandSearch;
83     }
84 
85     @Override
onCreate(Bundle savedInstanceState)86     public void onCreate(Bundle savedInstanceState) {
87         super.onCreate(savedInstanceState);
88         setHasOptionsMenu(true);
89         getActivity().setTitle(mTitleResId);
90     }
91 
92     @Override
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)93     public View onCreateView(LayoutInflater inflater, ViewGroup container,
94             Bundle savedInstanceState) {
95         final View view = inflater.inflate(R.layout.recycler_view, container, false);
96         mRecyclerView = view.findViewById(R.id.recycler_view);
97         mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext(),
98                 LinearLayoutManager.VERTICAL, /* reverseLayout */ false));
99         mRecyclerView.setAdapter(mAdapter);
100         mAppBarLayout = getActivity().findViewById(R.id.app_bar);
101         autoSetCollapsingToolbarLayoutScrolling();
102 
103         // Initialize TimeZoneDataLoader only when mRecyclerView is ready to avoid race
104         // during onDateLoaderReady callback.
105         getLoaderManager().initLoader(0, null, new TimeZoneDataLoader.LoaderCreator(
106                 getContext(), this::onTimeZoneDataReady));
107         return view;
108     }
109 
onTimeZoneDataReady(TimeZoneData timeZoneData)110     public void onTimeZoneDataReady(TimeZoneData timeZoneData) {
111         if (mTimeZoneData == null && timeZoneData != null) {
112             mTimeZoneData = timeZoneData;
113             mAdapter = createAdapter(mTimeZoneData);
114             if (mRecyclerView != null) {
115                 mRecyclerView.setAdapter(mAdapter);
116             }
117         }
118     }
119 
getLocale()120     protected Locale getLocale() {
121         return getContext().getResources().getConfiguration().getLocales().get(0);
122     }
123 
124     /**
125      * Called when TimeZoneData is ready.
126      */
createAdapter(TimeZoneData timeZoneData)127     protected abstract BaseTimeZoneAdapter createAdapter(TimeZoneData timeZoneData);
128 
129     @Override
onCreateOptionsMenu(Menu menu, MenuInflater inflater)130     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
131         if (mSearchEnabled) {
132             inflater.inflate(R.menu.time_zone_base_search_menu, menu);
133 
134             final MenuItem searchMenuItem = menu.findItem(R.id.time_zone_search_menu);
135             searchMenuItem.setOnActionExpandListener(this);
136             mSearchView = (SearchView) searchMenuItem.getActionView();
137 
138             mSearchView.setQueryHint(getText(mSearchHintResId));
139             mSearchView.setOnQueryTextListener(this);
140             mSearchView.setMaxWidth(Integer.MAX_VALUE);
141 
142             if (mDefaultExpandSearch) {
143                 searchMenuItem.expandActionView();
144                 mSearchView.setIconified(false);
145                 mSearchView.setActivated(true);
146                 mSearchView.setQuery("", true /* submit */);
147             }
148 
149             // Set zero margin and padding to align with the text horizontally in the preference
150             final TextView searchViewView = (TextView) mSearchView.findViewById(
151                     com.android.internal.R.id.search_src_text);
152             searchViewView.setPadding(0, searchViewView.getPaddingTop(), 0,
153                     searchViewView.getPaddingBottom());
154             final View editFrame = mSearchView.findViewById(
155                     com.android.internal.R.id.search_edit_frame);
156             final LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) editFrame
157                     .getLayoutParams();
158             params.setMarginStart(0);
159             params.setMarginEnd(0);
160             editFrame.setLayoutParams(params);
161         }
162     }
163 
164     @Override
onMenuItemActionExpand(MenuItem item)165     public boolean onMenuItemActionExpand(MenuItem item) {
166         // To prevent a large space on tool bar.
167         if (mAppBarLayout != null) {
168             mAppBarLayout.setExpanded(false /*expanded*/, false /*animate*/);
169         }
170         // To prevent user can expand the collapsing tool bar view.
171         ViewCompat.setNestedScrollingEnabled(mRecyclerView, false);
172         return true;
173     }
174 
175     @Override
onMenuItemActionCollapse(MenuItem item)176     public boolean onMenuItemActionCollapse(MenuItem item) {
177         // We keep the collapsed status after user cancel the search function.
178         if (mAppBarLayout != null) {
179             mAppBarLayout.setExpanded(false /*expanded*/, false /*animate*/);
180         }
181         ViewCompat.setNestedScrollingEnabled(mRecyclerView, true);
182         return true;
183     }
184 
185     @Override
onQueryTextSubmit(String query)186     public boolean onQueryTextSubmit(String query) {
187         return false;
188     }
189 
190     @Override
onQueryTextChange(String newText)191     public boolean onQueryTextChange(String newText) {
192         if (mAdapter != null) {
193             mAdapter.getFilter().filter(newText);
194         }
195         return false;
196     }
197 
198     public interface OnListItemClickListener<T extends BaseTimeZoneAdapter.AdapterItem> {
onListItemClick(T item)199         void onListItemClick(T item);
200     }
201 
autoSetCollapsingToolbarLayoutScrolling()202     private void autoSetCollapsingToolbarLayoutScrolling() {
203         if (mAppBarLayout == null) {
204             return;
205         }
206 
207         CoordinatorLayout.LayoutParams params =
208                 (CoordinatorLayout.LayoutParams) mAppBarLayout.getLayoutParams();
209         AppBarLayout.Behavior behavior = new AppBarLayout.Behavior();
210         behavior.setDragCallback(
211                 new AppBarLayout.Behavior.DragCallback() {
212                     @Override
213                     public boolean canDrag(@NonNull AppBarLayout appBarLayout) {
214                         return appBarLayout.getResources().getConfiguration().orientation
215                                 == Configuration.ORIENTATION_LANDSCAPE;
216                     }
217                 });
218         params.setBehavior(behavior);
219     }
220 }
221