blob: 06141eda042595e237adbd58f9522adf23f6bdf1 [file] [log] [blame]
/*
* 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.database.ContentObserver;
import android.database.Cursor;
import android.graphics.Rect;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.android.car.dialer.telecom.PhoneLoader;
import com.android.car.dialer.telecom.UiCallManager;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.car.widget.PagedListView;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
/**
* 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";
private static final String KEY_MAX_CLICKS = "max_clicks";
private 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() {
return new StrequentsFragment();
}
@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();
mUiCallManager = UiCallManager.get();
View view = inflater.inflate(R.layout.strequents_fragment, container, false);
mListView = view.findViewById(R.id.list_view);
int numOfColumn = getContext().getResources().getInteger(
R.integer.favorite_fragment_grid_column);
mListView.getRecyclerView().setLayoutManager(
new GridLayoutManager(getContext(), numOfColumn));
mListView.getRecyclerView().addItemDecoration(new ItemSpacingDecoration());
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);
});
// 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
Bundle args = getArguments();
int maxClicks = args == null
? DEFAULT_MAX_CLICKS
: 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);
}
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(null);
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();
}
}
private class ItemSpacingDecoration extends RecyclerView.ItemDecoration {
@Override
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view,
@NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
int carPadding1 = mContext.getResources().getDimensionPixelOffset(
R.dimen.car_padding_1);
int leftPadding = 0;
int rightPadding = 0;
if (parent.getChildAdapterPosition(view) % 2 == 0) {
rightPadding = carPadding1;
} else {
leftPadding = carPadding1;
}
outRect.set(leftPadding, carPadding1, rightPadding, carPadding1);
}
}
}