blob: e3bdd0f3e17f69891d1aa061e106c2ad4bc1067c [file] [log] [blame]
/*
* Copyright (C) 2011 The Android Open Source Project
* Copyright (C) 2013 Android Open Kang Project
* Copyright (C) 2023 The LineageOS 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.callstats;
import android.content.Context;
import android.content.Intent;
import android.text.TextUtils;
import android.view.ContextMenu;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import androidx.recyclerview.widget.RecyclerView;
import com.android.dialer.R;
import com.android.dialer.app.contactinfo.ContactInfoCache;
import com.android.dialer.app.contactinfo.NumberWithCountryIso;
import com.android.dialer.clipboard.ClipboardUtils;
import com.android.dialer.contacts.displaypreference.ContactDisplayPreferences;
import com.android.dialer.location.GeoUtil;
import com.android.dialer.phonenumbercache.ContactInfo;
import com.android.dialer.phonenumbercache.ContactInfoHelper;
import com.android.dialer.phonenumberutil.PhoneNumberHelper;
import com.android.dialer.util.CallUtil;
import com.android.dialer.util.DialerUtils;
import com.android.dialer.util.ExpirableCache;
import com.android.dialer.util.PermissionsUtil;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* Adapter class to hold and handle call stat entries
*/
class CallStatsAdapter extends RecyclerView.Adapter {
private final Context mContext;
private final ContactInfoHelper mContactInfoHelper;
private final ContactInfoCache mContactInfoCache;
private final ContactDisplayPreferences mContactDisplayPreferences;
private ArrayList<CallStatsDetails> mAllItems;
private ArrayList<CallStatsDetails> mShownItems;
private CallStatsDetails mTotalItem;
private Map<CallStatsDetails, ContactInfo> mInfoLookup;
private int mType = -1;
private long mFilterFrom;
private long mFilterTo;
private boolean mSortByDuration;
/**
* Listener that is triggered to populate the context menu with actions to perform on the call's
* number, when the call log entry is long pressed.
*/
private final View.OnCreateContextMenuListener mContextMenuListener = (menu, v, menuInfo) -> {
final CallStatsListItemViewHolder vh = (CallStatsListItemViewHolder) v.getTag();
if (TextUtils.isEmpty(vh.details.number)) {
return;
}
menu.setHeaderTitle(vh.details.number);
final MenuItem copyItem = menu.add(ContextMenu.NONE, R.id.context_menu_copy_to_clipboard,
ContextMenu.NONE, R.string.action_copy_number_text);
copyItem.setOnMenuItemClickListener(item -> {
ClipboardUtils.copyText(CallStatsAdapter.this.mContext, null, vh.details.number, true);
return true;
});
// The edit number before call does not show up if any of the conditions apply:
// 1) Number cannot be called
// 2) Number is the voicemail number
// 3) Number is a SIP address
boolean canPlaceCallsTo = PhoneNumberHelper.canPlaceCallsTo(vh.details.number,
vh.details.numberPresentation);
if (!canPlaceCallsTo || PhoneNumberHelper.isSipNumber(vh.details.number)) {
return;
}
final MenuItem editItem = menu.add(ContextMenu.NONE, R.id.context_menu_edit_before_call,
ContextMenu.NONE, R.string.action_edit_number_before_call);
editItem.setOnMenuItemClickListener(item -> {
final Intent intent = new Intent(Intent.ACTION_DIAL, CallUtil.getCallUri(vh.details.number));
DialerUtils.startActivityWithErrorToast(v.getContext(), intent);
return true;
});
};
private final Comparator<CallStatsDetails> mDurationComparator = (o1, o2) -> {
Long duration1 = o1.getRequestedDuration(mType);
Long duration2 = o2.getRequestedDuration(mType);
// sort descending
return duration2.compareTo(duration1);
};
private final Comparator<CallStatsDetails> mCountComparator = (o1, o2) -> {
Integer count1 = o1.getRequestedCount(mType);
Integer count2 = o2.getRequestedCount(mType);
// sort descending
return count2.compareTo(count1);
};
CallStatsAdapter(Context context, ContactDisplayPreferences prefs,
ExpirableCache<NumberWithCountryIso,ContactInfo> cache) {
mContext = context;
mContactDisplayPreferences = prefs;
final String currentCountryIso = GeoUtil.getCurrentCountryIso(mContext);
mContactInfoHelper = new ContactInfoHelper(mContext, currentCountryIso);
mAllItems = new ArrayList<CallStatsDetails>();
mShownItems = new ArrayList<CallStatsDetails>();
mTotalItem = new CallStatsDetails(null, 0, null, null, null, null, null, 0);
mInfoLookup = new ConcurrentHashMap<>();
mContactInfoCache = new ContactInfoCache(cache,
mContactInfoHelper, () -> notifyDataSetChanged());
if (!PermissionsUtil.hasContactsReadPermissions(context)) {
mContactInfoCache.disableRequestProcessing();
}
}
public void updateData(Map<ContactInfo, CallStatsDetails> calls, long from, long to) {
mInfoLookup.clear();
mFilterFrom = from;
mFilterTo = to;
mAllItems.clear();
mTotalItem.reset();
for (Map.Entry<ContactInfo, CallStatsDetails> entry : calls.entrySet()) {
final CallStatsDetails call = entry.getValue();
mAllItems.add(call);
mTotalItem.mergeWith(call);
mInfoLookup.put(call, entry.getKey());
}
}
public void updateDisplayedData(int type, boolean sortByDuration) {
mType = type;
mSortByDuration = sortByDuration;
mShownItems.clear();
for (CallStatsDetails call : mAllItems) {
if (sortByDuration && call.getRequestedDuration(type) > 0) {
mShownItems.add(call);
} else if (!sortByDuration && call.getRequestedCount(type) > 0) {
mShownItems.add(call);
}
}
Collections.sort(mShownItems, sortByDuration ? mDurationComparator : mCountComparator);
notifyDataSetChanged();
}
public void invalidateCache() {
mContactInfoCache.invalidate();
}
public void startCache() {
if (PermissionsUtil.hasPermission(mContext, android.Manifest.permission.READ_CONTACTS)) {
mContactInfoCache.start();
}
}
public void pauseCache() {
mContactInfoCache.stop();
}
@Override
public int getItemCount() {
return mShownItems.size();
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(mContext);
View view = inflater.inflate(R.layout.call_stats_list_item, parent, false);
CallStatsListItemViewHolder viewHolder = CallStatsListItemViewHolder.create(view,
mContactInfoHelper);
viewHolder.mPrimaryActionView.setOnCreateContextMenuListener(mContextMenuListener);
viewHolder.mPrimaryActionView.setTag(viewHolder);
return viewHolder;
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
CallStatsDetails details = mShownItems.get(position);
CallStatsDetails first = mShownItems.get(0);
CallStatsListItemViewHolder views = (CallStatsListItemViewHolder) viewHolder;
if (PhoneNumberHelper.canPlaceCallsTo(details.number, details.numberPresentation)
&& !details.isVoicemailNumber) {
ContactInfo info = mContactInfoCache.getValue(details.number + details.postDialDigits,
details.countryIso, mInfoLookup.get(details), false);
if (info != null) {
details.updateFromInfo(info);
}
}
views.setDetails(details, first, mTotalItem, mType,
mSortByDuration, mContactDisplayPreferences.getDisplayOrder());
views.clickIntent = getItemClickIntent(details);
}
private Intent getItemClickIntent(CallStatsDetails details) {
Intent intent = new Intent(mContext, CallStatsDetailActivity.class);
intent.putExtra(CallStatsDetailActivity.EXTRA_DETAILS, details);
intent.putExtra(CallStatsDetailActivity.EXTRA_TOTAL, mTotalItem);
intent.putExtra(CallStatsDetailActivity.EXTRA_FROM, mFilterFrom);
intent.putExtra(CallStatsDetailActivity.EXTRA_TO, mFilterTo);
return intent;
}
public String getTotalCallCountString() {
return CallStatsListItemViewHolder.getCallCountString(
mContext, mTotalItem.getRequestedCount(mType));
}
public String getFullDurationString(boolean withSeconds) {
final long duration = mTotalItem.getRequestedDuration(mType);
return CallStatsListItemViewHolder.getDurationString(
mContext, duration, withSeconds);
}
}