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.dialer.contactsfragment;
18 
19 import android.app.Fragment;
20 import android.app.LoaderManager.LoaderCallbacks;
21 import android.content.Loader;
22 import android.database.Cursor;
23 import android.os.Bundle;
24 import android.support.annotation.Nullable;
25 import android.support.v7.widget.LinearLayoutManager;
26 import android.support.v7.widget.RecyclerView;
27 import android.view.LayoutInflater;
28 import android.view.View;
29 import android.view.View.OnScrollChangeListener;
30 import android.view.ViewGroup;
31 import android.widget.TextView;
32 import com.android.dialer.util.PermissionsUtil;
33 
34 /** Fragment containing a list of all contacts. */
35 public class ContactsFragment extends Fragment
36     implements LoaderCallbacks<Cursor>, OnScrollChangeListener {
37 
38   private TextView anchoredHeader;
39   private RecyclerView recyclerView;
40   private LinearLayoutManager manager;
41   private ContactsAdapter adapter;
42 
43   @Nullable
44   @Override
onCreateView( LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)45   public View onCreateView(
46       LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
47     View view = inflater.inflate(R.layout.fragment_contacts, container, false);
48     anchoredHeader = (TextView) view.findViewById(R.id.header);
49     manager = new LinearLayoutManager(getContext());
50 
51     // TODO: Handle contacts permission denied view
52     // TODO: Handle 0 contacts layout
53     recyclerView = (RecyclerView) view.findViewById(R.id.recycler_view);
54     recyclerView.setLayoutManager(manager);
55     getLoaderManager().initLoader(0, null, this);
56 
57     if (PermissionsUtil.hasContactsReadPermissions(getContext())) {
58       getLoaderManager().initLoader(0, null, this);
59     }
60 
61     return view;
62   }
63 
64   @Override
onCreateLoader(int id, Bundle args)65   public Loader<Cursor> onCreateLoader(int id, Bundle args) {
66     return new ContactsCursorLoader(getContext());
67   }
68 
69   @Override
onLoadFinished(Loader<Cursor> loader, Cursor cursor)70   public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
71     // TODO setup fast scroller.
72     adapter = new ContactsAdapter(getContext(), cursor);
73     recyclerView.setAdapter(adapter);
74     if (adapter.getItemCount() > 1) {
75       recyclerView.setOnScrollChangeListener(this);
76     }
77   }
78 
79   @Override
onLoaderReset(Loader<Cursor> loader)80   public void onLoaderReset(Loader<Cursor> loader) {
81     recyclerView.setAdapter(null);
82     recyclerView.setOnScrollChangeListener(null);
83     adapter = null;
84   }
85 
86   /*
87    * When our recycler view updates, we need to ensure that our row headers and anchored header
88    * are in the correct state.
89    *
90    * The general rule is, when the row headers are shown, our anchored header is hidden. When the
91    * recycler view is scrolling through a sublist that has more than one element, we want to show
92    * out anchored header, to create the illusion that our row header has been anchored. In all
93    * other situations, we want to hide the anchor because that means we are transitioning between
94    * two sublists.
95    */
96   @Override
onScrollChange(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY)97   public void onScrollChange(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
98     int firstVisibleItem = manager.findFirstVisibleItemPosition();
99     int firstCompletelyVisible = manager.findFirstCompletelyVisibleItemPosition();
100 
101     // If the user swipes to the top of the list very quickly, there is some strange behavior
102     // between this method updating headers and adapter#onBindViewHolder updating headers.
103     // To overcome this, we refresh the headers to ensure they are correct.
104     if (firstVisibleItem == firstCompletelyVisible && firstVisibleItem == 0) {
105       adapter.refreshHeaders();
106       anchoredHeader.setVisibility(View.INVISIBLE);
107     } else {
108       boolean showAnchor =
109           adapter.getHeader(firstVisibleItem).equals(adapter.getHeader(firstCompletelyVisible));
110       anchoredHeader.setText(adapter.getHeader(firstCompletelyVisible));
111       anchoredHeader.setVisibility(showAnchor ? View.VISIBLE : View.INVISIBLE);
112 
113       int rowHeaderVisibility = showAnchor ? View.INVISIBLE : View.VISIBLE;
114       adapter.setHeaderVisibility(firstVisibleItem, rowHeaderVisibility);
115       adapter.setHeaderVisibility(firstCompletelyVisible, rowHeaderVisibility);
116     }
117   }
118 }
119