| /* |
| * Copyright (C) 2015 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.car.dialer; |
| |
| import android.content.ContentResolver; |
| import android.content.Context; |
| import android.content.CursorLoader; |
| import android.content.Loader; |
| import android.database.ContentObserver; |
| import android.database.Cursor; |
| import android.graphics.Canvas; |
| import android.net.Uri; |
| import android.os.Bundle; |
| import android.os.Handler; |
| import android.support.annotation.Nullable; |
| import android.support.v4.app.Fragment; |
| import android.support.v4.view.ViewCompat; |
| import android.support.v7.widget.RecyclerView; |
| import android.util.Log; |
| import android.view.LayoutInflater; |
| import android.view.View; |
| import android.view.ViewGroup; |
| import android.widget.LinearLayout; |
| import com.android.car.dialer.telecom.PhoneLoader; |
| import com.android.car.dialer.telecom.UiCallManager; |
| import com.android.car.view.PagedListView; |
| |
| /** |
| * Contains a list of contacts. The call types can be any of the CALL_TYPE_* fields from |
| * {@link PhoneLoader}. |
| */ |
| public class StrequentsFragment extends Fragment { |
| private static final String TAG = "Em.StrequentsFrag"; |
| |
| public static final String KEY_MAX_CLICKS = "max_clicks"; |
| public static final int DEFAULT_MAX_CLICKS = 6; |
| |
| private UiCallManager mUiCallManager; |
| private StrequentsAdapter mAdapter; |
| private CursorLoader mSpeedialCursorLoader; |
| private CursorLoader mCallLogCursorLoader; |
| private Context mContext; |
| private PagedListView mListView; |
| private Cursor mStrequentCursor; |
| private Cursor mCallLogCursor; |
| private boolean mHasLoadedData; |
| |
| public static StrequentsFragment newInstance(UiCallManager callManager) { |
| StrequentsFragment fragment = new StrequentsFragment(); |
| fragment.mUiCallManager = callManager; |
| return fragment; |
| } |
| |
| @Override |
| public void onCreate(@Nullable Bundle savedInstanceState) { |
| super.onCreate(savedInstanceState); |
| if (Log.isLoggable(TAG, Log.DEBUG)) { |
| Log.d(TAG, "onCreate"); |
| } |
| } |
| |
| @Override |
| public View onCreateView(LayoutInflater inflater, ViewGroup container, |
| Bundle savedInstanceState) { |
| if (Log.isLoggable(TAG, Log.DEBUG)) { |
| Log.d(TAG, "onCreateView"); |
| } |
| |
| mContext = getContext(); |
| |
| View view = inflater.inflate(R.layout.strequents_fragment, container, false); |
| mListView = (PagedListView) view.findViewById(R.id.list_view); |
| mListView.getLayoutManager().setOffsetRows(true); |
| |
| Bundle args = getArguments(); |
| mSpeedialCursorLoader = PhoneLoader.registerCallObserver(PhoneLoader.CALL_TYPE_SPEED_DIAL, |
| mContext, (loader, cursor) -> { |
| if (Log.isLoggable(TAG, Log.DEBUG)) { |
| Log.d(TAG, "PhoneLoader: onLoadComplete (CALL_TYPE_SPEED_DIAL)"); |
| } |
| |
| onLoadStrequentCursor(cursor); |
| |
| if (mContext != null) { |
| mListView.setDefaultItemDecoration(new Decoration(mContext)); |
| } |
| }); |
| |
| // Get the latest call log from the call logs history. |
| mCallLogCursorLoader = PhoneLoader.registerCallObserver(PhoneLoader.CALL_TYPE_ALL, mContext, |
| (loader, cursor) -> { |
| if (Log.isLoggable(TAG, Log.DEBUG)) { |
| Log.d(TAG, "PhoneLoader: onLoadComplete (CALL_TYPE_ALL)"); |
| } |
| onLoadCallLogCursor(cursor); |
| }); |
| |
| ContentResolver contentResolver = mContext.getContentResolver(); |
| contentResolver.registerContentObserver(mSpeedialCursorLoader.getUri(), |
| false, new SpeedDialContentObserver(new Handler())); |
| contentResolver.registerContentObserver(mCallLogCursorLoader.getUri(), |
| false, new CallLogContentObserver(new Handler())); |
| |
| // Maximum number of forward acting clicks the user can perform |
| |
| int maxClicks = args.getInt(KEY_MAX_CLICKS, |
| DEFAULT_MAX_CLICKS /* G.maxForwardClicks.get() */); |
| // We want to show one fewer page than max clicks to allow clicking on an item, |
| // but, the first page is "free" since it doesn't take any clicks to show |
| final int maxPages = maxClicks < 0 ? -1 : maxClicks; |
| if (Log.isLoggable(TAG, Log.VERBOSE)) { |
| Log.v(TAG, "Max clicks: " + maxClicks + ", Max pages: " + maxPages); |
| } |
| |
| mListView.removeDefaultItemDecoration(); |
| mListView.setLightMode(); |
| mAdapter = new StrequentsAdapter(mContext, mUiCallManager); |
| mAdapter.setStrequentsListener(viewHolder -> { |
| if (Log.isLoggable(TAG, Log.DEBUG)) { |
| Log.d(TAG, "onContactedClicked"); |
| } |
| |
| mUiCallManager.safePlaceCall((String) viewHolder.itemView.getTag(), false); |
| }); |
| mListView.setMaxPages(maxPages); |
| mListView.setAdapter(mAdapter); |
| |
| if (Log.isLoggable(TAG, Log.DEBUG)) { |
| Log.d(TAG, "setItemAnimator"); |
| } |
| |
| mListView.getRecyclerView().setItemAnimator(new StrequentsItemAnimator()); |
| return view; |
| } |
| |
| @Override |
| public void onDestroyView() { |
| super.onDestroyView(); |
| |
| if (Log.isLoggable(TAG, Log.DEBUG)) { |
| Log.d(TAG, "onDestroyView"); |
| } |
| |
| mAdapter.setStrequentCursor(null); |
| mAdapter.setLastCallCursor(null); |
| mCallLogCursorLoader.reset(); |
| mSpeedialCursorLoader.reset(); |
| mCallLogCursor = null; |
| mStrequentCursor = null; |
| mHasLoadedData = false; |
| mContext = null; |
| } |
| |
| private void loadDataIntoAdapter() { |
| if (Log.isLoggable(TAG, Log.DEBUG)) { |
| Log.d(TAG, "loadDataIntoAdapter"); |
| } |
| |
| mHasLoadedData = true; |
| mAdapter.setLastCallCursor(mCallLogCursor); |
| mAdapter.setStrequentCursor(mStrequentCursor); |
| } |
| |
| private void onLoadStrequentCursor(Cursor cursor) { |
| if (Log.isLoggable(TAG, Log.DEBUG)) { |
| Log.d(TAG, "onLoadStrequentCursor"); |
| } |
| |
| if (cursor == null) { |
| throw new IllegalArgumentException( |
| "cursor was null in on speed dial fetched"); |
| } |
| |
| mStrequentCursor = cursor; |
| if (mCallLogCursor != null) { |
| if (mHasLoadedData) { |
| mAdapter.setStrequentCursor(cursor); |
| } else { |
| loadDataIntoAdapter(); |
| } |
| } |
| } |
| |
| private void onLoadCallLogCursor(Cursor cursor) { |
| if (cursor == null) { |
| throw new IllegalArgumentException( |
| "cursor was null in on calls fetched"); |
| } |
| |
| mCallLogCursor = cursor; |
| if (mStrequentCursor != null) { |
| if (mHasLoadedData) { |
| mAdapter.setLastCallCursor(cursor); |
| } else { |
| loadDataIntoAdapter(); |
| } |
| } |
| } |
| |
| /** |
| * A {@link ContentResolver} that is responsible for reloading the user's starred and frequent |
| * contacts. |
| */ |
| private class SpeedDialContentObserver extends ContentObserver { |
| public SpeedDialContentObserver(Handler handler) { |
| super(handler); |
| } |
| |
| @Override |
| public void onChange(boolean selfChange) { |
| onChange(selfChange, null); |
| } |
| |
| @Override |
| public void onChange(boolean selfChange, Uri uri) { |
| if (Log.isLoggable(TAG, Log.DEBUG)) { |
| Log.d(TAG, "SpeedDialContentObserver onChange() called. Reloading strequents."); |
| } |
| mSpeedialCursorLoader.startLoading(); |
| } |
| } |
| |
| /** |
| * A {@link ContentResolver} that is responsible for reloading the user's recent calls. |
| */ |
| private class CallLogContentObserver extends ContentObserver { |
| public CallLogContentObserver(Handler handler) { |
| super(handler); |
| } |
| |
| @Override |
| public void onChange(boolean selfChange) { |
| onChange(selfChange, null); |
| } |
| |
| @Override |
| public void onChange(boolean selfChange, Uri uri) { |
| if (Log.isLoggable(TAG, Log.DEBUG)) { |
| Log.d(TAG, "CallLogContentObserver onChange() called. Reloading call log."); |
| } |
| mCallLogCursorLoader.startLoading(); |
| } |
| } |
| |
| /** |
| * Decoration for the speed dial cards. This is basically copied from the one in |
| * {@link PagedListView} except it won't show a divider between the dialpad item and the first |
| * speed dial item and the divider is offset but a couple of pixels to offset the fact that |
| * the cards overlap. |
| */ |
| private static class Decoration extends PagedListView.Decoration { |
| private final int mPaintAlpha; |
| |
| public Decoration(Context context) { |
| super(context); |
| mPaintAlpha = mPaint.getAlpha(); |
| } |
| |
| @Override |
| public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) { |
| StrequentsAdapter adapter = (StrequentsAdapter) parent.getAdapter(); |
| |
| if (adapter.getItemCount() <= 0) { |
| return; |
| } |
| |
| final int childCount = parent.getChildCount(); |
| |
| // Don't draw decoration line on last item of the list. |
| for (int i = 0; i < childCount - 1; i++) { |
| final View child = parent.getChildAt(i); |
| |
| // If the child is focused then the decoration will look bad with the focus |
| // highlight so don't draw it. |
| if (child.isFocused()) { |
| continue; |
| } |
| |
| // The left edge of the divider should align with the left edge of text_container. |
| final LinearLayout container = (LinearLayout) child.findViewById(R.id.container); |
| View textContainer = child.findViewById(R.id.text_container); |
| View card = child.findViewById(R.id.call_log_card); |
| |
| int left = textContainer.getLeft() + container.getLeft() + card.getLeft(); |
| int right = left + textContainer.getWidth(); |
| |
| RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) child.getLayoutParams(); |
| int bottom = child.getBottom() + lp.bottomMargin |
| + Math.round(ViewCompat.getTranslationY(child)); |
| int top = bottom - mDividerHeight; |
| |
| if (top >= c.getHeight() || top < 0) { |
| break; |
| } |
| |
| mPaint.setAlpha(Math.round(container.getAlpha() * mPaintAlpha)); |
| c.drawRect(left, top, right, bottom, mPaint); |
| } |
| } |
| } |
| } |