diff options
Diffstat (limited to 'src/com/android/dialer/calllog/CallLogFragment.java')
-rw-r--r-- | src/com/android/dialer/calllog/CallLogFragment.java | 530 |
1 files changed, 0 insertions, 530 deletions
diff --git a/src/com/android/dialer/calllog/CallLogFragment.java b/src/com/android/dialer/calllog/CallLogFragment.java deleted file mode 100644 index 67b72a5a3..000000000 --- a/src/com/android/dialer/calllog/CallLogFragment.java +++ /dev/null @@ -1,530 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.dialer.calllog; - -import android.app.Activity; -import android.app.Fragment; -import android.app.KeyguardManager; -import android.content.ContentResolver; -import android.content.Context; -import android.content.pm.PackageManager; -import android.database.ContentObserver; -import android.database.Cursor; -import android.os.Bundle; -import android.os.Handler; -import android.os.Message; -import android.provider.CallLog; -import android.provider.CallLog.Calls; -import android.provider.ContactsContract; -import android.support.annotation.Nullable; -import android.support.v13.app.FragmentCompat; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import com.android.contacts.common.GeoUtil; -import com.android.contacts.common.util.PermissionsUtil; -import com.android.dialer.R; -import com.android.dialer.list.ListsFragment; -import com.android.dialer.util.EmptyLoader; -import com.android.dialer.voicemail.VoicemailPlaybackPresenter; -import com.android.dialer.widget.EmptyContentView; -import com.android.dialer.widget.EmptyContentView.OnEmptyViewActionButtonClickedListener; -import com.android.dialerbind.ObjectFactory; - -import static android.Manifest.permission.READ_CALL_LOG; - -/** - * Displays a list of call log entries. To filter for a particular kind of call - * (all, missed or voicemails), specify it in the constructor. - */ -public class CallLogFragment extends Fragment implements CallLogQueryHandler.Listener, - CallLogAdapter.CallFetcher, OnEmptyViewActionButtonClickedListener, - FragmentCompat.OnRequestPermissionsResultCallback { - private static final String TAG = "CallLogFragment"; - - /** - * ID of the empty loader to defer other fragments. - */ - private static final int EMPTY_LOADER_ID = 0; - - private static final String KEY_FILTER_TYPE = "filter_type"; - private static final String KEY_LOG_LIMIT = "log_limit"; - private static final String KEY_DATE_LIMIT = "date_limit"; - private static final String KEY_IS_CALL_LOG_ACTIVITY = "is_call_log_activity"; - - // No limit specified for the number of logs to show; use the CallLogQueryHandler's default. - private static final int NO_LOG_LIMIT = -1; - // No date-based filtering. - private static final int NO_DATE_LIMIT = 0; - - private static final int READ_CALL_LOG_PERMISSION_REQUEST_CODE = 1; - - private static final int EVENT_UPDATE_DISPLAY = 1; - - private static final long MILLIS_IN_MINUTE = 60 * 1000; - - private RecyclerView mRecyclerView; - private LinearLayoutManager mLayoutManager; - private CallLogAdapter mAdapter; - private CallLogQueryHandler mCallLogQueryHandler; - private boolean mScrollToTop; - - - private EmptyContentView mEmptyListView; - private KeyguardManager mKeyguardManager; - - private boolean mEmptyLoaderRunning; - private boolean mCallLogFetched; - private boolean mVoicemailStatusFetched; - - private final Handler mDisplayUpdateHandler = new Handler() { - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case EVENT_UPDATE_DISPLAY: - refreshData(); - rescheduleDisplayUpdate(); - break; - } - } - }; - - private final Handler mHandler = new Handler(); - - protected class CustomContentObserver extends ContentObserver { - public CustomContentObserver() { - super(mHandler); - } - @Override - public void onChange(boolean selfChange) { - mRefreshDataRequired = true; - } - } - - // See issue 6363009 - private final ContentObserver mCallLogObserver = new CustomContentObserver(); - private final ContentObserver mContactsObserver = new CustomContentObserver(); - private boolean mRefreshDataRequired = true; - - private boolean mHasReadCallLogPermission = false; - - // Exactly same variable is in Fragment as a package private. - private boolean mMenuVisible = true; - - // Default to all calls. - private int mCallTypeFilter = CallLogQueryHandler.CALL_TYPE_ALL; - - // Log limit - if no limit is specified, then the default in {@link CallLogQueryHandler} - // will be used. - private int mLogLimit = NO_LOG_LIMIT; - - // Date limit (in millis since epoch) - when non-zero, only calls which occurred on or after - // the date filter are included. If zero, no date-based filtering occurs. - private long mDateLimit = NO_DATE_LIMIT; - - /* - * True if this instance of the CallLogFragment shown in the CallLogActivity. - */ - private boolean mIsCallLogActivity = false; - - public interface HostInterface { - public void showDialpad(); - } - - public CallLogFragment() { - this(CallLogQueryHandler.CALL_TYPE_ALL, NO_LOG_LIMIT); - } - - public CallLogFragment(int filterType) { - this(filterType, NO_LOG_LIMIT); - } - - public CallLogFragment(int filterType, boolean isCallLogActivity) { - this(filterType, NO_LOG_LIMIT); - mIsCallLogActivity = isCallLogActivity; - } - - public CallLogFragment(int filterType, int logLimit) { - this(filterType, logLimit, NO_DATE_LIMIT); - } - - /** - * Creates a call log fragment, filtering to include only calls of the desired type, occurring - * after the specified date. - * @param filterType type of calls to include. - * @param dateLimit limits results to calls occurring on or after the specified date. - */ - public CallLogFragment(int filterType, long dateLimit) { - this(filterType, NO_LOG_LIMIT, dateLimit); - } - - /** - * Creates a call log fragment, filtering to include only calls of the desired type, occurring - * after the specified date. Also provides a means to limit the number of results returned. - * @param filterType type of calls to include. - * @param logLimit limits the number of results to return. - * @param dateLimit limits results to calls occurring on or after the specified date. - */ - public CallLogFragment(int filterType, int logLimit, long dateLimit) { - mCallTypeFilter = filterType; - mLogLimit = logLimit; - mDateLimit = dateLimit; - } - - @Override - public void onCreate(Bundle state) { - super.onCreate(state); - if (state != null) { - mCallTypeFilter = state.getInt(KEY_FILTER_TYPE, mCallTypeFilter); - mLogLimit = state.getInt(KEY_LOG_LIMIT, mLogLimit); - mDateLimit = state.getLong(KEY_DATE_LIMIT, mDateLimit); - mIsCallLogActivity = state.getBoolean(KEY_IS_CALL_LOG_ACTIVITY, mIsCallLogActivity); - } - - final Activity activity = getActivity(); - final ContentResolver resolver = activity.getContentResolver(); - String currentCountryIso = GeoUtil.getCurrentCountryIso(activity); - mCallLogQueryHandler = new CallLogQueryHandler(activity, resolver, this, mLogLimit); - mKeyguardManager = - (KeyguardManager) activity.getSystemService(Context.KEYGUARD_SERVICE); - resolver.registerContentObserver(CallLog.CONTENT_URI, true, mCallLogObserver); - resolver.registerContentObserver(ContactsContract.Contacts.CONTENT_URI, true, - mContactsObserver); - setHasOptionsMenu(true); - } - - /** Called by the CallLogQueryHandler when the list of calls has been fetched or updated. */ - @Override - public boolean onCallsFetched(Cursor cursor) { - if (getActivity() == null || getActivity().isFinishing()) { - // Return false; we did not take ownership of the cursor - return false; - } - mAdapter.invalidatePositions(); - mAdapter.setLoading(false); - mAdapter.changeCursor(cursor); - // This will update the state of the "Clear call log" menu item. - getActivity().invalidateOptionsMenu(); - - boolean showListView = cursor != null && cursor.getCount() > 0; - mRecyclerView.setVisibility(showListView ? View.VISIBLE : View.GONE); - mEmptyListView.setVisibility(!showListView ? View.VISIBLE : View.GONE); - - if (mScrollToTop) { - // The smooth-scroll animation happens over a fixed time period. - // As a result, if it scrolls through a large portion of the list, - // each frame will jump so far from the previous one that the user - // will not experience the illusion of downward motion. Instead, - // if we're not already near the top of the list, we instantly jump - // near the top, and animate from there. - if (mLayoutManager.findFirstVisibleItemPosition() > 5) { - // TODO: Jump to near the top, then begin smooth scroll. - mRecyclerView.smoothScrollToPosition(0); - } - // Workaround for framework issue: the smooth-scroll doesn't - // occur if setSelection() is called immediately before. - mHandler.post(new Runnable() { - @Override - public void run() { - if (getActivity() == null || getActivity().isFinishing()) { - return; - } - mRecyclerView.smoothScrollToPosition(0); - } - }); - - mScrollToTop = false; - } - mCallLogFetched = true; - destroyEmptyLoaderIfAllDataFetched(); - return true; - } - - /** - * Called by {@link CallLogQueryHandler} after a successful query to voicemail status provider. - */ - @Override - public void onVoicemailStatusFetched(Cursor statusCursor) { - Activity activity = getActivity(); - if (activity == null || activity.isFinishing()) { - return; - } - - mVoicemailStatusFetched = true; - destroyEmptyLoaderIfAllDataFetched(); - } - - private void destroyEmptyLoaderIfAllDataFetched() { - if (mCallLogFetched && mVoicemailStatusFetched && mEmptyLoaderRunning) { - mEmptyLoaderRunning = false; - getLoaderManager().destroyLoader(EMPTY_LOADER_ID); - } - } - - @Override - public void onVoicemailUnreadCountFetched(Cursor cursor) {} - - @Override - public void onMissedCallsUnreadCountFetched(Cursor cursor) {} - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) { - View view = inflater.inflate(R.layout.call_log_fragment, container, false); - setupView(view, null); - return view; - } - - protected void setupView( - View view, @Nullable VoicemailPlaybackPresenter voicemailPlaybackPresenter) { - mRecyclerView = (RecyclerView) view.findViewById(R.id.recycler_view); - mRecyclerView.setHasFixedSize(true); - mLayoutManager = new LinearLayoutManager(getActivity()); - mRecyclerView.setLayoutManager(mLayoutManager); - mEmptyListView = (EmptyContentView) view.findViewById(R.id.empty_list_view); - mEmptyListView.setImage(R.drawable.empty_call_log); - mEmptyListView.setActionClickedListener(this); - - int activityType = mIsCallLogActivity ? CallLogAdapter.ACTIVITY_TYPE_CALL_LOG : - CallLogAdapter.ACTIVITY_TYPE_DIALTACTS; - String currentCountryIso = GeoUtil.getCurrentCountryIso(getActivity()); - mAdapter = ObjectFactory.newCallLogAdapter( - getActivity(), - this, - new ContactInfoHelper(getActivity(), currentCountryIso), - voicemailPlaybackPresenter, - activityType); - mRecyclerView.setAdapter(mAdapter); - fetchCalls(); - } - - @Override - public void onViewCreated(View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - updateEmptyMessage(mCallTypeFilter); - mAdapter.onRestoreInstanceState(savedInstanceState); - } - - @Override - public void onStart() { - // Start the empty loader now to defer other fragments. We destroy it when both calllog - // and the voicemail status are fetched. - getLoaderManager().initLoader(EMPTY_LOADER_ID, null, - new EmptyLoader.Callback(getActivity())); - mEmptyLoaderRunning = true; - super.onStart(); - } - - @Override - public void onResume() { - super.onResume(); - final boolean hasReadCallLogPermission = - PermissionsUtil.hasPermission(getActivity(), READ_CALL_LOG); - if (!mHasReadCallLogPermission && hasReadCallLogPermission) { - // We didn't have the permission before, and now we do. Force a refresh of the call log. - // Note that this code path always happens on a fresh start, but mRefreshDataRequired - // is already true in that case anyway. - mRefreshDataRequired = true; - updateEmptyMessage(mCallTypeFilter); - } - - mHasReadCallLogPermission = hasReadCallLogPermission; - refreshData(); - mAdapter.onResume(); - - rescheduleDisplayUpdate(); - } - - @Override - public void onPause() { - cancelDisplayUpdate(); - mAdapter.onPause(); - super.onPause(); - } - - @Override - public void onStop() { - updateOnTransition(); - - super.onStop(); - } - - @Override - public void onDestroy() { - mAdapter.changeCursor(null); - - getActivity().getContentResolver().unregisterContentObserver(mCallLogObserver); - getActivity().getContentResolver().unregisterContentObserver(mContactsObserver); - super.onDestroy(); - } - - @Override - public void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - outState.putInt(KEY_FILTER_TYPE, mCallTypeFilter); - outState.putInt(KEY_LOG_LIMIT, mLogLimit); - outState.putLong(KEY_DATE_LIMIT, mDateLimit); - outState.putBoolean(KEY_IS_CALL_LOG_ACTIVITY, mIsCallLogActivity); - - mAdapter.onSaveInstanceState(outState); - } - - @Override - public void fetchCalls() { - mCallLogQueryHandler.fetchCalls(mCallTypeFilter, mDateLimit); - if (!mIsCallLogActivity) { - ((ListsFragment) getParentFragment()).updateTabUnreadCounts(); - } - } - - private void updateEmptyMessage(int filterType) { - final Context context = getActivity(); - if (context == null) { - return; - } - - if (!PermissionsUtil.hasPermission(context, READ_CALL_LOG)) { - mEmptyListView.setDescription(R.string.permission_no_calllog); - mEmptyListView.setActionLabel(R.string.permission_single_turn_on); - return; - } - - final int messageId; - switch (filterType) { - case Calls.MISSED_TYPE: - messageId = R.string.call_log_missed_empty; - break; - case Calls.VOICEMAIL_TYPE: - messageId = R.string.call_log_voicemail_empty; - break; - case CallLogQueryHandler.CALL_TYPE_ALL: - messageId = R.string.call_log_all_empty; - break; - default: - throw new IllegalArgumentException("Unexpected filter type in CallLogFragment: " - + filterType); - } - mEmptyListView.setDescription(messageId); - if (mIsCallLogActivity) { - mEmptyListView.setActionLabel(EmptyContentView.NO_LABEL); - } else if (filterType == CallLogQueryHandler.CALL_TYPE_ALL) { - mEmptyListView.setActionLabel(R.string.call_log_all_empty_action); - } - } - - CallLogAdapter getAdapter() { - return mAdapter; - } - - @Override - public void setMenuVisibility(boolean menuVisible) { - super.setMenuVisibility(menuVisible); - if (mMenuVisible != menuVisible) { - mMenuVisible = menuVisible; - if (!menuVisible) { - updateOnTransition(); - } else if (isResumed()) { - refreshData(); - } - } - } - - /** Requests updates to the data to be shown. */ - private void refreshData() { - // Prevent unnecessary refresh. - if (mRefreshDataRequired) { - // Mark all entries in the contact info cache as out of date, so they will be looked up - // again once being shown. - mAdapter.invalidateCache(); - mAdapter.setLoading(true); - - fetchCalls(); - mCallLogQueryHandler.fetchVoicemailStatus(); - mCallLogQueryHandler.fetchMissedCallsUnreadCount(); - updateOnTransition(); - mRefreshDataRequired = false; - } else { - // Refresh the display of the existing data to update the timestamp text descriptions. - mAdapter.notifyDataSetChanged(); - } - } - - /** - * Updates the voicemail notification state. - * - * TODO: Move to CallLogActivity - */ - private void updateOnTransition() { - // We don't want to update any call data when keyguard is on because the user has likely not - // seen the new calls yet. - // This might be called before onCreate() and thus we need to check null explicitly. - if (mKeyguardManager != null && !mKeyguardManager.inKeyguardRestrictedInputMode() - && mCallTypeFilter == Calls.VOICEMAIL_TYPE) { - CallLogNotificationsHelper.updateVoicemailNotifications(getActivity()); - } - } - - @Override - public void onEmptyViewActionButtonClicked() { - final Activity activity = getActivity(); - if (activity == null) { - return; - } - - if (!PermissionsUtil.hasPermission(activity, READ_CALL_LOG)) { - FragmentCompat.requestPermissions(this, new String[] {READ_CALL_LOG}, - READ_CALL_LOG_PERMISSION_REQUEST_CODE); - } else if (!mIsCallLogActivity) { - // Show dialpad if we are not in the call log activity. - ((HostInterface) activity).showDialpad(); - } - } - - @Override - public void onRequestPermissionsResult(int requestCode, String[] permissions, - int[] grantResults) { - if (requestCode == READ_CALL_LOG_PERMISSION_REQUEST_CODE) { - if (grantResults.length >= 1 && PackageManager.PERMISSION_GRANTED == grantResults[0]) { - // Force a refresh of the data since we were missing the permission before this. - mRefreshDataRequired = true; - } - } - } - - /** - * Schedules an update to the relative call times (X mins ago). - */ - private void rescheduleDisplayUpdate() { - if (!mDisplayUpdateHandler.hasMessages(EVENT_UPDATE_DISPLAY)) { - long time = System.currentTimeMillis(); - // This value allows us to change the display relatively close to when the time changes - // from one minute to the next. - long millisUtilNextMinute = MILLIS_IN_MINUTE - (time % MILLIS_IN_MINUTE); - mDisplayUpdateHandler.sendEmptyMessageDelayed( - EVENT_UPDATE_DISPLAY, millisUtilNextMinute); - } - } - - /** - * Cancels any pending update requests to update the relative call times (X mins ago). - */ - private void cancelDisplayUpdate() { - mDisplayUpdateHandler.removeMessages(EVENT_UPDATE_DISPLAY); - } -} |