• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 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 package com.android.car.dialer;
17 
18 import android.content.ContentResolver;
19 import android.content.Context;
20 import android.content.CursorLoader;
21 import android.database.ContentObserver;
22 import android.database.Cursor;
23 import android.graphics.Rect;
24 import android.net.Uri;
25 import android.os.Bundle;
26 import android.os.Handler;
27 import android.support.annotation.NonNull;
28 import android.support.annotation.Nullable;
29 import android.support.v4.app.Fragment;
30 import android.support.v7.widget.GridLayoutManager;
31 import android.support.v7.widget.RecyclerView;
32 import android.util.Log;
33 import android.view.LayoutInflater;
34 import android.view.View;
35 import android.view.ViewGroup;
36 
37 import androidx.car.widget.DayNightStyle;
38 import androidx.car.widget.PagedListView;
39 
40 import com.android.car.dialer.telecom.PhoneLoader;
41 import com.android.car.dialer.telecom.UiCallManager;
42 
43 /**
44  * Contains a list of contacts. The call types can be any of the CALL_TYPE_* fields from
45  * {@link PhoneLoader}.
46  */
47 public class StrequentsFragment extends Fragment {
48     private static final String TAG = "Em.StrequentsFrag";
49 
50     private static final String KEY_MAX_CLICKS = "max_clicks";
51     private static final int DEFAULT_MAX_CLICKS = 6;
52 
53     private UiCallManager mUiCallManager;
54     private StrequentsAdapter mAdapter;
55     private CursorLoader mSpeedialCursorLoader;
56     private CursorLoader mCallLogCursorLoader;
57     private Context mContext;
58     private PagedListView mListView;
59     private Cursor mStrequentCursor;
60     private Cursor mCallLogCursor;
61     private boolean mHasLoadedData;
62 
newInstance()63     public static StrequentsFragment newInstance() {
64         return new StrequentsFragment();
65     }
66 
67     @Override
onCreate(@ullable Bundle savedInstanceState)68     public void onCreate(@Nullable Bundle savedInstanceState) {
69         super.onCreate(savedInstanceState);
70         if (Log.isLoggable(TAG, Log.DEBUG)) {
71             Log.d(TAG, "onCreate");
72         }
73     }
74 
75     @Override
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)76     public View onCreateView(LayoutInflater inflater, ViewGroup container,
77             Bundle savedInstanceState) {
78         if (Log.isLoggable(TAG, Log.DEBUG)) {
79             Log.d(TAG, "onCreateView");
80         }
81 
82         mContext = getContext();
83         mUiCallManager = UiCallManager.get();
84 
85         View view = inflater.inflate(R.layout.strequents_fragment, container, false);
86         mListView = view.findViewById(R.id.list_view);
87         int numOfColumn = getContext().getResources().getInteger(
88                 R.integer.favorite_fragment_grid_column);
89         mListView.getRecyclerView().setLayoutManager(
90                 new GridLayoutManager(getContext(), numOfColumn));
91         mListView.getRecyclerView().addItemDecoration(new ItemSpacingDecoration());
92 
93         mSpeedialCursorLoader = PhoneLoader.registerCallObserver(PhoneLoader.CALL_TYPE_SPEED_DIAL,
94                 mContext, (loader, cursor) -> {
95                     if (Log.isLoggable(TAG, Log.DEBUG)) {
96                         Log.d(TAG, "PhoneLoader: onLoadComplete (CALL_TYPE_SPEED_DIAL)");
97                     }
98 
99                     onLoadStrequentCursor(cursor);
100                 });
101 
102         // Get the latest call log from the call logs history.
103         mCallLogCursorLoader = PhoneLoader.registerCallObserver(PhoneLoader.CALL_TYPE_ALL, mContext,
104                 (loader, cursor) -> {
105                     if (Log.isLoggable(TAG, Log.DEBUG)) {
106                         Log.d(TAG, "PhoneLoader: onLoadComplete (CALL_TYPE_ALL)");
107                     }
108                     onLoadCallLogCursor(cursor);
109                 });
110 
111         ContentResolver contentResolver = mContext.getContentResolver();
112         contentResolver.registerContentObserver(mSpeedialCursorLoader.getUri(),
113                 false, new SpeedDialContentObserver(new Handler()));
114         contentResolver.registerContentObserver(mCallLogCursorLoader.getUri(),
115                 false, new CallLogContentObserver(new Handler()));
116 
117         // Maximum number of forward acting clicks the user can perform
118         Bundle args = getArguments();
119         int maxClicks = args == null
120                 ? DEFAULT_MAX_CLICKS
121                 : args.getInt(KEY_MAX_CLICKS, DEFAULT_MAX_CLICKS /* G.maxForwardClicks.get() */);
122         // We want to show one fewer page than max clicks to allow clicking on an item,
123         // but, the first page is "free" since it doesn't take any clicks to show
124         final int maxPages = maxClicks < 0 ? -1 : maxClicks;
125         if (Log.isLoggable(TAG, Log.VERBOSE)) {
126             Log.v(TAG, "Max clicks: " + maxClicks + ", Max pages: " + maxPages);
127         }
128 
129         mAdapter = new StrequentsAdapter(mContext, mUiCallManager);
130         mAdapter.setStrequentsListener(viewHolder -> {
131             if (Log.isLoggable(TAG, Log.DEBUG)) {
132                 Log.d(TAG, "onContactedClicked");
133             }
134 
135             mUiCallManager.safePlaceCall((String) viewHolder.itemView.getTag(), false);
136         });
137         mListView.setMaxPages(maxPages);
138         mListView.setAdapter(mAdapter);
139 
140         if (Log.isLoggable(TAG, Log.DEBUG)) {
141             Log.d(TAG, "setItemAnimator");
142         }
143 
144         mListView.getRecyclerView().setItemAnimator(null);
145         return view;
146     }
147 
148     @Override
onDestroyView()149     public void onDestroyView() {
150         super.onDestroyView();
151 
152         if (Log.isLoggable(TAG, Log.DEBUG)) {
153             Log.d(TAG, "onDestroyView");
154         }
155 
156         mAdapter.setStrequentCursor(null);
157         mAdapter.setLastCallCursor(null);
158         mCallLogCursorLoader.reset();
159         mSpeedialCursorLoader.reset();
160         mCallLogCursor = null;
161         mStrequentCursor = null;
162         mHasLoadedData = false;
163         mContext = null;
164     }
165 
loadDataIntoAdapter()166     private void loadDataIntoAdapter() {
167         if (Log.isLoggable(TAG, Log.DEBUG)) {
168             Log.d(TAG, "loadDataIntoAdapter");
169         }
170 
171         mHasLoadedData = true;
172         mAdapter.setLastCallCursor(mCallLogCursor);
173         mAdapter.setStrequentCursor(mStrequentCursor);
174     }
175 
onLoadStrequentCursor(Cursor cursor)176     private void onLoadStrequentCursor(Cursor cursor) {
177         if (Log.isLoggable(TAG, Log.DEBUG)) {
178             Log.d(TAG, "onLoadStrequentCursor");
179         }
180 
181         if (cursor == null) {
182             throw new IllegalArgumentException(
183                     "cursor was null in on speed dial fetched");
184         }
185 
186         mStrequentCursor = cursor;
187         if (mCallLogCursor != null) {
188             if (mHasLoadedData) {
189                 mAdapter.setStrequentCursor(cursor);
190             } else {
191                 loadDataIntoAdapter();
192             }
193         }
194     }
195 
onLoadCallLogCursor(Cursor cursor)196     private void onLoadCallLogCursor(Cursor cursor) {
197         if (cursor == null) {
198             throw new IllegalArgumentException(
199                     "cursor was null in on calls fetched");
200         }
201 
202         mCallLogCursor = cursor;
203         if (mStrequentCursor != null) {
204             if (mHasLoadedData) {
205                 mAdapter.setLastCallCursor(cursor);
206             } else {
207                 loadDataIntoAdapter();
208             }
209         }
210     }
211 
212     /**
213      * A {@link ContentResolver} that is responsible for reloading the user's starred and frequent
214      * contacts.
215      */
216     private class SpeedDialContentObserver extends ContentObserver {
SpeedDialContentObserver(Handler handler)217         public SpeedDialContentObserver(Handler handler) {
218             super(handler);
219         }
220 
221         @Override
onChange(boolean selfChange)222         public void onChange(boolean selfChange) {
223             onChange(selfChange, null);
224         }
225 
226         @Override
onChange(boolean selfChange, Uri uri)227         public void onChange(boolean selfChange, Uri uri) {
228             if (Log.isLoggable(TAG, Log.DEBUG)) {
229                 Log.d(TAG, "SpeedDialContentObserver onChange() called. Reloading strequents.");
230             }
231             mSpeedialCursorLoader.startLoading();
232         }
233     }
234 
235     /**
236      * A {@link ContentResolver} that is responsible for reloading the user's recent calls.
237      */
238     private class CallLogContentObserver extends ContentObserver {
CallLogContentObserver(Handler handler)239         public CallLogContentObserver(Handler handler) {
240             super(handler);
241         }
242 
243         @Override
onChange(boolean selfChange)244         public void onChange(boolean selfChange) {
245             onChange(selfChange, null);
246         }
247 
248         @Override
onChange(boolean selfChange, Uri uri)249         public void onChange(boolean selfChange, Uri uri) {
250             if (Log.isLoggable(TAG, Log.DEBUG)) {
251                 Log.d(TAG, "CallLogContentObserver onChange() called. Reloading call log.");
252             }
253             mCallLogCursorLoader.startLoading();
254         }
255     }
256 
257     private class ItemSpacingDecoration extends RecyclerView.ItemDecoration {
258 
259         @Override
getItemOffsets(@onNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state)260         public void getItemOffsets(@NonNull Rect outRect, @NonNull View view,
261                 @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
262             super.getItemOffsets(outRect, view, parent, state);
263             int carPadding1 = mContext.getResources().getDimensionPixelOffset(
264                     R.dimen.car_padding_1);
265 
266             int leftPadding = 0;
267             int rightPadding = 0;
268             if (parent.getChildAdapterPosition(view) % 2 == 0) {
269                 rightPadding = carPadding1;
270             } else {
271                 leftPadding = carPadding1;
272             }
273 
274             outRect.set(leftPadding, carPadding1, rightPadding, carPadding1);
275         }
276     }
277 }
278