1ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian/*
2ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * Copyright (C) 2011 The Android Open Source Project
3ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian *
4ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * Licensed under the Apache License, Version 2.0 (the "License");
5ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * you may not use this file except in compliance with the License.
6ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * You may obtain a copy of the License at
7ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian *
8ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian *      http://www.apache.org/licenses/LICENSE-2.0
9ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian *
10ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * Unless required by applicable law or agreed to in writing, software
11ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * distributed under the License is distributed on an "AS IS" BASIS,
12ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * See the License for the specific language governing permissions and
14ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * limitations under the License.
15ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian */
16ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
17ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianpackage com.android.dialer.app.calllog;
18ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
19ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport static android.Manifest.permission.READ_CALL_LOG;
20ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
21ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.app.Activity;
22ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.app.Fragment;
23ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.app.KeyguardManager;
24ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.content.ContentResolver;
25ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.content.Context;
26ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.content.pm.PackageManager;
27ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.database.ContentObserver;
28ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.database.Cursor;
29ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.os.Bundle;
30ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.os.Handler;
31ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.os.Message;
32ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.provider.CallLog;
33ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.provider.CallLog.Calls;
34ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.provider.ContactsContract;
35ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.support.annotation.CallSuper;
36ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.support.annotation.Nullable;
37ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.support.v13.app.FragmentCompat;
38ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.support.v7.app.AppCompatActivity;
39ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.support.v7.widget.LinearLayoutManager;
40ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.support.v7.widget.RecyclerView;
41ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.view.LayoutInflater;
42ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.view.View;
43ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.view.ViewGroup;
44ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport com.android.dialer.app.Bindings;
45ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport com.android.dialer.app.R;
46ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport com.android.dialer.app.calllog.calllogcache.CallLogCache;
47ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport com.android.dialer.app.contactinfo.ContactInfoCache;
48ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport com.android.dialer.app.contactinfo.ContactInfoCache.OnContactInfoChangedListener;
49ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport com.android.dialer.app.contactinfo.ExpirableCacheHeadlessFragment;
50ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport com.android.dialer.app.list.ListsFragment;
51ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport com.android.dialer.app.voicemail.VoicemailPlaybackPresenter;
52ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport com.android.dialer.app.widget.EmptyContentView;
53ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport com.android.dialer.app.widget.EmptyContentView.OnEmptyViewActionButtonClickedListener;
54d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport com.android.dialer.blocking.FilteredNumberAsyncQueryHandler;
55d8046e520a866b9948ee9ba47cf642b441ca8e23Eric Erfanianimport com.android.dialer.common.Assert;
56ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport com.android.dialer.common.LogUtil;
57ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport com.android.dialer.database.CallLogQueryHandler;
5810b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanianimport com.android.dialer.location.GeoUtil;
59ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport com.android.dialer.phonenumbercache.ContactInfoHelper;
60ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport com.android.dialer.util.PermissionsUtil;
61ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
62ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian/**
63ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * Displays a list of call log entries. To filter for a particular kind of call (all, missed or
64ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * voicemails), specify it in the constructor.
65ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian */
66ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianpublic class CallLogFragment extends Fragment
67d8046e520a866b9948ee9ba47cf642b441ca8e23Eric Erfanian    implements CallLogQueryHandler.Listener,
68ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        CallLogAdapter.CallFetcher,
69ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        OnEmptyViewActionButtonClickedListener,
70ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        FragmentCompat.OnRequestPermissionsResultCallback,
71ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        CallLogModalAlertManager.Listener {
72ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private static final String KEY_FILTER_TYPE = "filter_type";
73d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  private static final String KEY_LOG_LIMIT = "log_limit";
74d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  private static final String KEY_DATE_LIMIT = "date_limit";
75d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  private static final String KEY_IS_CALL_LOG_ACTIVITY = "is_call_log_activity";
76ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private static final String KEY_HAS_READ_CALL_LOG_PERMISSION = "has_read_call_log_permission";
77ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private static final String KEY_REFRESH_DATA_REQUIRED = "refresh_data_required";
78ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
79d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  // No limit specified for the number of logs to show; use the CallLogQueryHandler's default.
80d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  private static final int NO_LOG_LIMIT = -1;
81d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  // No date-based filtering.
82d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  private static final int NO_DATE_LIMIT = 0;
83d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian
84ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private static final int READ_CALL_LOG_PERMISSION_REQUEST_CODE = 1;
85ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
86ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private static final int EVENT_UPDATE_DISPLAY = 1;
87ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
88ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private static final long MILLIS_IN_MINUTE = 60 * 1000;
89ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private final Handler mHandler = new Handler();
90ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  // See issue 6363009
91ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private final ContentObserver mCallLogObserver = new CustomContentObserver();
92ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private final ContentObserver mContactsObserver = new CustomContentObserver();
93ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private RecyclerView mRecyclerView;
94ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private LinearLayoutManager mLayoutManager;
95ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private CallLogAdapter mAdapter;
96ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private CallLogQueryHandler mCallLogQueryHandler;
97ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private boolean mScrollToTop;
98ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private EmptyContentView mEmptyListView;
99ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private KeyguardManager mKeyguardManager;
100ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private ContactInfoCache mContactInfoCache;
101ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private final OnContactInfoChangedListener mOnContactInfoChangedListener =
102ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      new OnContactInfoChangedListener() {
103ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        @Override
104ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        public void onContactInfoChanged() {
105ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian          if (mAdapter != null) {
106ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            mAdapter.notifyDataSetChanged();
107ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian          }
108ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        }
109ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      };
110ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private boolean mRefreshDataRequired;
111ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private boolean mHasReadCallLogPermission;
112ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  // Exactly same variable is in Fragment as a package private.
113ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private boolean mMenuVisible = true;
114ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  // Default to all calls.
115d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  private int mCallTypeFilter = CallLogQueryHandler.CALL_TYPE_ALL;
116d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  // Log limit - if no limit is specified, then the default in {@link CallLogQueryHandler}
117d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  // will be used.
118d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  private int mLogLimit = NO_LOG_LIMIT;
119d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  // Date limit (in millis since epoch) - when non-zero, only calls which occurred on or after
120d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  // the date filter are included.  If zero, no date-based filtering occurs.
121d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  private long mDateLimit = NO_DATE_LIMIT;
122d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  /*
123d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian   * True if this instance of the CallLogFragment shown in the CallLogActivity.
124d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian   */
125d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  private boolean mIsCallLogActivity = false;
126ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private final Handler mDisplayUpdateHandler =
127ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      new Handler() {
128ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        @Override
129ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        public void handleMessage(Message msg) {
130ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian          switch (msg.what) {
131ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            case EVENT_UPDATE_DISPLAY:
132ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian              refreshData();
133ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian              rescheduleDisplayUpdate();
134ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian              break;
135d8046e520a866b9948ee9ba47cf642b441ca8e23Eric Erfanian            default:
136d8046e520a866b9948ee9ba47cf642b441ca8e23Eric Erfanian              throw Assert.createAssertionFailException("Invalid message: " + msg);
137ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian          }
138ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        }
139ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      };
140ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  protected CallLogModalAlertManager mModalAlertManager;
141ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private ViewGroup mModalAlertView;
142ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
143d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  public CallLogFragment() {
144d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    this(CallLogQueryHandler.CALL_TYPE_ALL, NO_LOG_LIMIT);
145d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  }
146d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian
147d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  public CallLogFragment(int filterType) {
148d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    this(filterType, NO_LOG_LIMIT);
149d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  }
150d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian
151d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  public CallLogFragment(int filterType, boolean isCallLogActivity) {
152d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    this(filterType, NO_LOG_LIMIT);
153d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    mIsCallLogActivity = isCallLogActivity;
154d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  }
155d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian
156d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  public CallLogFragment(int filterType, int logLimit) {
157d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    this(filterType, logLimit, NO_DATE_LIMIT);
158d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  }
159d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian
160d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  /**
161d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian   * Creates a call log fragment, filtering to include only calls of the desired type, occurring
162d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian   * after the specified date.
163d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian   *
164d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian   * @param filterType type of calls to include.
165d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian   * @param dateLimit limits results to calls occurring on or after the specified date.
166d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian   */
167d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  public CallLogFragment(int filterType, long dateLimit) {
168d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    this(filterType, NO_LOG_LIMIT, dateLimit);
169d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  }
170d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian
171d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  /**
172d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian   * Creates a call log fragment, filtering to include only calls of the desired type, occurring
173d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian   * after the specified date. Also provides a means to limit the number of results returned.
174d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian   *
175d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian   * @param filterType type of calls to include.
176d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian   * @param logLimit limits the number of results to return.
177d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian   * @param dateLimit limits results to calls occurring on or after the specified date.
178d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian   */
179d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  public CallLogFragment(int filterType, int logLimit, long dateLimit) {
180d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    mCallTypeFilter = filterType;
181d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    mLogLimit = logLimit;
182d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    mDateLimit = dateLimit;
183d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian  }
184d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian
185ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  @Override
186ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public void onCreate(Bundle state) {
187ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    LogUtil.d("CallLogFragment.onCreate", toString());
188ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    super.onCreate(state);
189ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    mRefreshDataRequired = true;
190ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (state != null) {
191ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      mCallTypeFilter = state.getInt(KEY_FILTER_TYPE, mCallTypeFilter);
192d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian      mLogLimit = state.getInt(KEY_LOG_LIMIT, mLogLimit);
193d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian      mDateLimit = state.getLong(KEY_DATE_LIMIT, mDateLimit);
194d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian      mIsCallLogActivity = state.getBoolean(KEY_IS_CALL_LOG_ACTIVITY, mIsCallLogActivity);
195ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      mHasReadCallLogPermission = state.getBoolean(KEY_HAS_READ_CALL_LOG_PERMISSION, false);
196ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      mRefreshDataRequired = state.getBoolean(KEY_REFRESH_DATA_REQUIRED, mRefreshDataRequired);
197ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
198ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
199ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    final Activity activity = getActivity();
200ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    final ContentResolver resolver = activity.getContentResolver();
201d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    mCallLogQueryHandler = new CallLogQueryHandler(activity, resolver, this, mLogLimit);
202ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    mKeyguardManager = (KeyguardManager) activity.getSystemService(Context.KEYGUARD_SERVICE);
203c857f90590e7d7fcffa89511982eb33afd34805fEric Erfanian
204c857f90590e7d7fcffa89511982eb33afd34805fEric Erfanian    if (PermissionsUtil.hasCallLogReadPermissions(getContext())) {
205c857f90590e7d7fcffa89511982eb33afd34805fEric Erfanian      resolver.registerContentObserver(CallLog.CONTENT_URI, true, mCallLogObserver);
206c857f90590e7d7fcffa89511982eb33afd34805fEric Erfanian    } else {
207c857f90590e7d7fcffa89511982eb33afd34805fEric Erfanian      LogUtil.w("CallLogFragment.onCreate", "call log permission not available");
208c857f90590e7d7fcffa89511982eb33afd34805fEric Erfanian    }
209c857f90590e7d7fcffa89511982eb33afd34805fEric Erfanian    if (PermissionsUtil.hasContactsReadPermissions(getContext())) {
210c857f90590e7d7fcffa89511982eb33afd34805fEric Erfanian      resolver.registerContentObserver(
211c857f90590e7d7fcffa89511982eb33afd34805fEric Erfanian          ContactsContract.Contacts.CONTENT_URI, true, mContactsObserver);
212c857f90590e7d7fcffa89511982eb33afd34805fEric Erfanian    } else {
213c857f90590e7d7fcffa89511982eb33afd34805fEric Erfanian      LogUtil.w("CallLogFragment.onCreate", "contacts permission not available.");
214c857f90590e7d7fcffa89511982eb33afd34805fEric Erfanian    }
215ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    setHasOptionsMenu(true);
216ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
217ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
218ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /** Called by the CallLogQueryHandler when the list of calls has been fetched or updated. */
219ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  @Override
220ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public boolean onCallsFetched(Cursor cursor) {
221ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (getActivity() == null || getActivity().isFinishing()) {
222ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      // Return false; we did not take ownership of the cursor
223ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      return false;
224ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
225ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    mAdapter.invalidatePositions();
226ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    mAdapter.setLoading(false);
227ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    mAdapter.changeCursor(cursor);
228ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // This will update the state of the "Clear call log" menu item.
229ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    getActivity().invalidateOptionsMenu();
230ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
231ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (cursor != null && cursor.getCount() > 0) {
232ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      mRecyclerView.setPaddingRelative(
233ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian          mRecyclerView.getPaddingStart(),
234ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian          0,
235ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian          mRecyclerView.getPaddingEnd(),
236ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian          getResources().getDimensionPixelSize(R.dimen.floating_action_button_list_bottom_padding));
237ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      mEmptyListView.setVisibility(View.GONE);
238ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    } else {
239ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      mRecyclerView.setPaddingRelative(
240ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian          mRecyclerView.getPaddingStart(), 0, mRecyclerView.getPaddingEnd(), 0);
241ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      mEmptyListView.setVisibility(View.VISIBLE);
242ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
243ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (mScrollToTop) {
244ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      // The smooth-scroll animation happens over a fixed time period.
245ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      // As a result, if it scrolls through a large portion of the list,
246ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      // each frame will jump so far from the previous one that the user
247ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      // will not experience the illusion of downward motion.  Instead,
248ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      // if we're not already near the top of the list, we instantly jump
249ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      // near the top, and animate from there.
250ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      if (mLayoutManager.findFirstVisibleItemPosition() > 5) {
251ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        // TODO: Jump to near the top, then begin smooth scroll.
252ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        mRecyclerView.smoothScrollToPosition(0);
253ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      }
254ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      // Workaround for framework issue: the smooth-scroll doesn't
255ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      // occur if setSelection() is called immediately before.
256ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      mHandler.post(
257ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian          new Runnable() {
258ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            @Override
259ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            public void run() {
260ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian              if (getActivity() == null || getActivity().isFinishing()) {
261ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian                return;
262ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian              }
263ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian              mRecyclerView.smoothScrollToPosition(0);
264ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            }
265ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian          });
266ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
267ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      mScrollToTop = false;
268ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
269ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    return true;
270ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
271ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
272ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  @Override
273ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public void onVoicemailStatusFetched(Cursor statusCursor) {}
274ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
275ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  @Override
276ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public void onVoicemailUnreadCountFetched(Cursor cursor) {}
277ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
278ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  @Override
279ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public void onMissedCallsUnreadCountFetched(Cursor cursor) {}
280ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
281ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  @Override
282ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) {
283ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    View view = inflater.inflate(R.layout.call_log_fragment, container, false);
284ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    setupView(view);
285ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    return view;
286ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
287ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
288ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  protected void setupView(View view) {
289ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    mRecyclerView = (RecyclerView) view.findViewById(R.id.recycler_view);
290ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    mRecyclerView.setHasFixedSize(true);
291ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    mLayoutManager = new LinearLayoutManager(getActivity());
292ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    mRecyclerView.setLayoutManager(mLayoutManager);
293ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    mEmptyListView = (EmptyContentView) view.findViewById(R.id.empty_list_view);
294ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    mEmptyListView.setImage(R.drawable.empty_call_log);
295ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    mEmptyListView.setActionClickedListener(this);
296ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    mModalAlertView = (ViewGroup) view.findViewById(R.id.modal_message_container);
297ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    mModalAlertManager =
298ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        new CallLogModalAlertManager(LayoutInflater.from(getContext()), mModalAlertView, this);
299ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
300ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
301ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  protected void setupData() {
302d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    int activityType =
303d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian        mIsCallLogActivity
304d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian            ? CallLogAdapter.ACTIVITY_TYPE_CALL_LOG
305d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian            : CallLogAdapter.ACTIVITY_TYPE_DIALTACTS;
306ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    String currentCountryIso = GeoUtil.getCurrentCountryIso(getActivity());
307ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
308ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    mContactInfoCache =
309ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        new ContactInfoCache(
310ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            ExpirableCacheHeadlessFragment.attach((AppCompatActivity) getActivity())
311ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian                .getRetainedCache(),
312ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            new ContactInfoHelper(getActivity(), currentCountryIso),
313ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            mOnContactInfoChangedListener);
314ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    mAdapter =
315ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        Bindings.getLegacy(getActivity())
316ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            .newCallLogAdapter(
317ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian                getActivity(),
318ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian                mRecyclerView,
319ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian                this,
320ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian                CallLogCache.getCallLogCache(getActivity()),
321ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian                mContactInfoCache,
322ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian                getVoicemailPlaybackPresenter(),
323d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian                new FilteredNumberAsyncQueryHandler(getActivity()),
324ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian                activityType);
325ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    mRecyclerView.setAdapter(mAdapter);
326ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    fetchCalls();
327ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
328ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
329ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  @Nullable
330ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  protected VoicemailPlaybackPresenter getVoicemailPlaybackPresenter() {
331ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    return null;
332ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
333ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
334ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  @Override
335ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public void onActivityCreated(Bundle savedInstanceState) {
336ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    super.onActivityCreated(savedInstanceState);
337ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    setupData();
338ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    mAdapter.onRestoreInstanceState(savedInstanceState);
339ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
340ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
341ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  @Override
342ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public void onViewCreated(View view, Bundle savedInstanceState) {
343ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    super.onViewCreated(view, savedInstanceState);
344ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    updateEmptyMessage(mCallTypeFilter);
345ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
346ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
347ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  @Override
348ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public void onResume() {
349ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    LogUtil.d("CallLogFragment.onResume", toString());
350ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    super.onResume();
351ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    final boolean hasReadCallLogPermission =
352ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        PermissionsUtil.hasPermission(getActivity(), READ_CALL_LOG);
353ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (!mHasReadCallLogPermission && hasReadCallLogPermission) {
354ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      // We didn't have the permission before, and now we do. Force a refresh of the call log.
355ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      // Note that this code path always happens on a fresh start, but mRefreshDataRequired
356ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      // is already true in that case anyway.
357ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      mRefreshDataRequired = true;
358ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      updateEmptyMessage(mCallTypeFilter);
359ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
360ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
361ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    mHasReadCallLogPermission = hasReadCallLogPermission;
362ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
363ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    /*
364ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian     * Always clear the filtered numbers cache since users could have blocked/unblocked numbers
365ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian     * from the settings page
366ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian     */
367ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    mAdapter.clearFilteredNumbersCache();
368ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    refreshData();
369ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    mAdapter.onResume();
370ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
371ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    rescheduleDisplayUpdate();
372ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
373ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
374ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  @Override
375ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public void onPause() {
376ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    LogUtil.d("CallLogFragment.onPause", toString());
377ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    cancelDisplayUpdate();
378ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    mAdapter.onPause();
379ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    super.onPause();
380ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
381ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
382ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  @Override
383ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public void onStop() {
384ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    updateOnTransition();
385ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
386ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    super.onStop();
387ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    mAdapter.onStop();
38810b34a5ebf12e97ecba0caf3c8e30b476b038a96Eric Erfanian    mContactInfoCache.stop();
389ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
390ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
391ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  @Override
392ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public void onDestroy() {
393ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    LogUtil.d("CallLogFragment.onDestroy", toString());
394ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    mAdapter.changeCursor(null);
395ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
396ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    getActivity().getContentResolver().unregisterContentObserver(mCallLogObserver);
397ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    getActivity().getContentResolver().unregisterContentObserver(mContactsObserver);
398ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    super.onDestroy();
399ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
400ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
401ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  @Override
402ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public void onSaveInstanceState(Bundle outState) {
403ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    super.onSaveInstanceState(outState);
404ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    outState.putInt(KEY_FILTER_TYPE, mCallTypeFilter);
405d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    outState.putInt(KEY_LOG_LIMIT, mLogLimit);
406d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    outState.putLong(KEY_DATE_LIMIT, mDateLimit);
407d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    outState.putBoolean(KEY_IS_CALL_LOG_ACTIVITY, mIsCallLogActivity);
408ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    outState.putBoolean(KEY_HAS_READ_CALL_LOG_PERMISSION, mHasReadCallLogPermission);
409ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    outState.putBoolean(KEY_REFRESH_DATA_REQUIRED, mRefreshDataRequired);
410ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
411ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    mAdapter.onSaveInstanceState(outState);
412ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
413ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
414ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  @Override
415ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public void fetchCalls() {
416d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    mCallLogQueryHandler.fetchCalls(mCallTypeFilter, mDateLimit);
417d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    if (!mIsCallLogActivity) {
418d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian      ((ListsFragment) getParentFragment()).updateTabUnreadCounts();
419d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    }
420ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
421ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
422ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private void updateEmptyMessage(int filterType) {
423ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    final Context context = getActivity();
424ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (context == null) {
425ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      return;
426ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
427ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
428ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (!PermissionsUtil.hasPermission(context, READ_CALL_LOG)) {
429ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      mEmptyListView.setDescription(R.string.permission_no_calllog);
430ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      mEmptyListView.setActionLabel(R.string.permission_single_turn_on);
431ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      return;
432ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
433ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
434ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    final int messageId;
435ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    switch (filterType) {
436ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      case Calls.MISSED_TYPE:
437ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        messageId = R.string.call_log_missed_empty;
438ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        break;
439ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      case Calls.VOICEMAIL_TYPE:
440ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        messageId = R.string.call_log_voicemail_empty;
441ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        break;
442ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      case CallLogQueryHandler.CALL_TYPE_ALL:
443ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        messageId = R.string.call_log_all_empty;
444ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        break;
445ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      default:
446ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        throw new IllegalArgumentException(
447ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            "Unexpected filter type in CallLogFragment: " + filterType);
448ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
449ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    mEmptyListView.setDescription(messageId);
450d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    if (mIsCallLogActivity) {
451d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian      mEmptyListView.setActionLabel(EmptyContentView.NO_LABEL);
452d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    } else if (filterType == CallLogQueryHandler.CALL_TYPE_ALL) {
453ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      mEmptyListView.setActionLabel(R.string.call_log_all_empty_action);
454ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
455ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
456ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
457ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public CallLogAdapter getAdapter() {
458ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    return mAdapter;
459ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
460ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
461ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  @Override
462ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public void setMenuVisibility(boolean menuVisible) {
463ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    super.setMenuVisibility(menuVisible);
464ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (mMenuVisible != menuVisible) {
465ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      mMenuVisible = menuVisible;
466ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      if (!menuVisible) {
467ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        updateOnTransition();
468ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      } else if (isResumed()) {
469ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        refreshData();
470ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      }
471ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
472ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
473ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
474ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /** Requests updates to the data to be shown. */
475ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private void refreshData() {
476ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // Prevent unnecessary refresh.
477ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (mRefreshDataRequired) {
478ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      // Mark all entries in the contact info cache as out of date, so they will be looked up
479ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      // again once being shown.
480ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      mContactInfoCache.invalidate();
481ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      mAdapter.setLoading(true);
482ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
483ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      fetchCalls();
484ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      mCallLogQueryHandler.fetchVoicemailStatus();
485ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      mCallLogQueryHandler.fetchMissedCallsUnreadCount();
486ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      updateOnTransition();
487ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      mRefreshDataRequired = false;
488ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    } else {
489ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      // Refresh the display of the existing data to update the timestamp text descriptions.
490ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      mAdapter.notifyDataSetChanged();
491ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
492ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
493ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
494ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /**
495ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * Updates the voicemail notification state.
496ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   *
497ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * <p>TODO: Move to CallLogActivity
498ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   */
499ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private void updateOnTransition() {
500ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // We don't want to update any call data when keyguard is on because the user has likely not
501ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // seen the new calls yet.
502ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // This might be called before onCreate() and thus we need to check null explicitly.
503ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (mKeyguardManager != null
504ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        && !mKeyguardManager.inKeyguardRestrictedInputMode()
505ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        && mCallTypeFilter == Calls.VOICEMAIL_TYPE) {
506c857f90590e7d7fcffa89511982eb33afd34805fEric Erfanian      CallLogNotificationsService.markNewVoicemailsAsOld(getActivity(), null);
507ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
508ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
509ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
510ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  @Override
511ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public void onEmptyViewActionButtonClicked() {
512ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    final Activity activity = getActivity();
513ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (activity == null) {
514ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      return;
515ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
516ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
517ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (!PermissionsUtil.hasPermission(activity, READ_CALL_LOG)) {
518ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      FragmentCompat.requestPermissions(
519ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian          this, new String[] {READ_CALL_LOG}, READ_CALL_LOG_PERMISSION_REQUEST_CODE);
520d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian    } else if (!mIsCallLogActivity) {
521d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian      // Show dialpad if we are not in the call log activity.
522ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      ((HostInterface) activity).showDialpad();
523ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
524ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
525ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
526ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  @Override
527ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public void onRequestPermissionsResult(
528ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      int requestCode, String[] permissions, int[] grantResults) {
529ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (requestCode == READ_CALL_LOG_PERMISSION_REQUEST_CODE) {
530ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      if (grantResults.length >= 1 && PackageManager.PERMISSION_GRANTED == grantResults[0]) {
531ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        // Force a refresh of the data since we were missing the permission before this.
532ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        mRefreshDataRequired = true;
533ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      }
534ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
535ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
536ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
537ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /** Schedules an update to the relative call times (X mins ago). */
538ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private void rescheduleDisplayUpdate() {
539ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (!mDisplayUpdateHandler.hasMessages(EVENT_UPDATE_DISPLAY)) {
540ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      long time = System.currentTimeMillis();
541ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      // This value allows us to change the display relatively close to when the time changes
542ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      // from one minute to the next.
543ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      long millisUtilNextMinute = MILLIS_IN_MINUTE - (time % MILLIS_IN_MINUTE);
544ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      mDisplayUpdateHandler.sendEmptyMessageDelayed(EVENT_UPDATE_DISPLAY, millisUtilNextMinute);
545ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
546ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
547ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
548ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /** Cancels any pending update requests to update the relative call times (X mins ago). */
549ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private void cancelDisplayUpdate() {
550ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    mDisplayUpdateHandler.removeMessages(EVENT_UPDATE_DISPLAY);
551ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
552ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
553ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  @CallSuper
554d8046e520a866b9948ee9ba47cf642b441ca8e23Eric Erfanian  public void onVisible() {
555d8046e520a866b9948ee9ba47cf642b441ca8e23Eric Erfanian    LogUtil.enterBlock("CallLogFragment.onPageSelected");
556d8046e520a866b9948ee9ba47cf642b441ca8e23Eric Erfanian    if (getActivity() != null) {
557d8046e520a866b9948ee9ba47cf642b441ca8e23Eric Erfanian      ((HostInterface) getActivity())
558ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian          .enableFloatingButton(mModalAlertManager == null || mModalAlertManager.isEmpty());
559ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
560ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
561ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
562ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  @CallSuper
563d8046e520a866b9948ee9ba47cf642b441ca8e23Eric Erfanian  public void onNotVisible() {
564d8046e520a866b9948ee9ba47cf642b441ca8e23Eric Erfanian    LogUtil.enterBlock("CallLogFragment.onPageUnselected");
565ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
566ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
567ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  @Override
568ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public void onShowModalAlert(boolean show) {
569ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    LogUtil.d(
570ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        "CallLogFragment.onShowModalAlert",
571ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        "show: %b, fragment: %s, isVisible: %b",
572ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        show,
573ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        this,
574ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        getUserVisibleHint());
575ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    getAdapter().notifyDataSetChanged();
576ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    HostInterface hostInterface = (HostInterface) getActivity();
577ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (show) {
578ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      mRecyclerView.setVisibility(View.GONE);
579ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      mModalAlertView.setVisibility(View.VISIBLE);
580ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      if (hostInterface != null && getUserVisibleHint()) {
581ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        hostInterface.enableFloatingButton(false);
582ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      }
583ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    } else {
584ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      mRecyclerView.setVisibility(View.VISIBLE);
585ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      mModalAlertView.setVisibility(View.GONE);
586ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      if (hostInterface != null && getUserVisibleHint()) {
587ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        hostInterface.enableFloatingButton(true);
588ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      }
589ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
590ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
591ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
592ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public interface HostInterface {
593ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
594ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    void showDialpad();
595ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
596ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    void enableFloatingButton(boolean enabled);
597ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
598ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
599ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  protected class CustomContentObserver extends ContentObserver {
600ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
601ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    public CustomContentObserver() {
602ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      super(mHandler);
603ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
604ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
605ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    @Override
606ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    public void onChange(boolean selfChange) {
607ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      mRefreshDataRequired = true;
608ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
609ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
610ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian}
611