From b51f11cde8d1455f6dc1a75a147306fcf86c730f Mon Sep 17 00:00:00 2001 From: Danny Baumann Date: Wed, 27 Jun 2018 15:53:56 +0200 Subject: Re-add call statistics. Author: Danny Baumann Date: Mon Nov 11 15:36:21 2019 +0100 Fix crash in call stats. We fed color resources into LinearColorBar now. Change-Id: Ica6870fb6af8b982bc2710e7b63092c513fa4755 Author: Danny Baumann Date: Mon Dec 2 09:34:22 2019 +0100 Beautify call stats details category headers. Make them consistent with how pref category headers are displayed. Change-Id: I4ee8bb2dae11970051552d14df2d58d7ab6a3fdf Change-Id: I9f2b6e912ca69a5aa7a1790bed06304ee953e752 --- .../android/dialer/callstats/CallStatsAdapter.java | 248 +++++++++++++++++++++ 1 file changed, 248 insertions(+) create mode 100644 java/com/android/dialer/callstats/CallStatsAdapter.java (limited to 'java/com/android/dialer/callstats/CallStatsAdapter.java') diff --git a/java/com/android/dialer/callstats/CallStatsAdapter.java b/java/com/android/dialer/callstats/CallStatsAdapter.java new file mode 100644 index 000000000..1d673fc05 --- /dev/null +++ b/java/com/android/dialer/callstats/CallStatsAdapter.java @@ -0,0 +1,248 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * Copyright (C) 2013 Android Open Kang 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.support.v7.widget.RecyclerView; +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 com.android.dialer.R; +import com.android.dialer.app.DialtactsActivity; +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 mAllItems; + private ArrayList mShownItems; + private CallStatsDetails mTotalItem; + private Map 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 mDurationComparator = (o1, o2) -> { + Long duration1 = o1.getRequestedDuration(mType); + Long duration2 = o2.getRequestedDuration(mType); + // sort descending + return duration2.compareTo(duration1); + }; + private final Comparator 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 cache) { + mContext = context; + mContactDisplayPreferences = prefs; + + final String currentCountryIso = GeoUtil.getCurrentCountryIso(mContext); + mContactInfoHelper = new ContactInfoHelper(mContext, currentCountryIso); + + mAllItems = new ArrayList(); + mShownItems = new ArrayList(); + 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 calls, long from, long to) { + mInfoLookup.clear(); + mFilterFrom = from; + mFilterTo = to; + + mAllItems.clear(); + mTotalItem.reset(); + + for (Map.Entry 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); + } +} -- cgit v1.2.3