Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a671972b authored by Nancy Chen's avatar Nancy Chen Committed by Android (Google) Code Review
Browse files

Merge "Factor out lookup code in DefaultVoicemailNotifier." into ub-contactsdialer-b-dev

parents 94d369a8 f9125c7c
Loading
Loading
Loading
Loading
+264 −0
Original line number Diff line number Diff line
@@ -16,14 +16,94 @@

package com.android.dialer.calllog;

import static android.Manifest.permission.READ_CALL_LOG;
import static android.Manifest.permission.READ_CONTACTS;

import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.provider.CallLog.Calls;
import android.provider.ContactsContract.PhoneLookup;
import android.support.annotation.Nullable;
import android.telephony.PhoneNumberUtils;
import android.text.TextUtils;
import android.util.Log;

import com.android.contacts.common.util.PermissionsUtil;
import com.android.dialer.R;
import com.android.dialer.util.TelecomUtil;

import java.util.ArrayList;
import java.util.List;

/**
 * Helper class operating on call log notifications.
 */
public class CallLogNotificationsHelper {
    private static final String TAG = "CallLogNotifHelper";
    private static CallLogNotificationsHelper sInstance;

    /** Returns the singleton instance of the {@link CallLogNotificationsHelper}. */
    public static CallLogNotificationsHelper getInstance(Context context) {
        if (sInstance == null) {
            ContentResolver contentResolver = context.getContentResolver();
            sInstance = new CallLogNotificationsHelper(context,
                    createNewCallsQuery(context, contentResolver),
                    createNameLookupQuery(context, contentResolver));
        }
        return sInstance;
    }

    private final Context mContext;
    private final NewCallsQuery mNewCallsQuery;
    private final NameLookupQuery mNameLookupQuery;

    CallLogNotificationsHelper(Context context, NewCallsQuery newCallsQuery,
            NameLookupQuery nameLookupQuery) {
        mContext = context;
        mNewCallsQuery = newCallsQuery;
        mNameLookupQuery = nameLookupQuery;
    }

    /**
     * Get all voicemails with the "new" flag set to 1.
     *
     * @return A list of NewCall objects where each object represents a new voicemail.
     */
    @Nullable
    public List<NewCall> getNewVoicemails() {
        return mNewCallsQuery.query(Calls.VOICEMAIL_TYPE);
    }

    /**
     * Given a number and number information (presentation and country ISO), get the best name
     * for display. If the name itself if already available, return that. Otherwise attempt to look
     * it up in the database. If that fails, fall back to displaying the number.
     */
    public String getName(@Nullable String number, int numberPresentation,
                          @Nullable String countryIso) {
        String name = PhoneNumberDisplayUtil.getDisplayName(
                mContext,
                number,
                numberPresentation,
                false).toString();
        if (!TextUtils.isEmpty(name)) {
            return name;
        }
        // Look it up in the database.
        name = mNameLookupQuery.query(number);
        if (!TextUtils.isEmpty(name)) {
            return name;
        }
        if (!TextUtils.isEmpty(number)) {
            // If we cannot lookup the contact, use the number instead.
            return PhoneNumberUtils.formatNumber(number, countryIso);
        }
        return mContext.getResources().getString(R.string.unknown);
    }

    /** Removes the missed call notifications. */
    public static void removeMissedCallNotifications(Context context) {
        TelecomUtil.cancelMissedCallsNotification(context);
@@ -33,4 +113,188 @@ public class CallLogNotificationsHelper {
    public static void updateVoicemailNotifications(Context context) {
        CallLogNotificationsService.updateVoicemailNotifications(context, null);
    }

    /** Information about a new voicemail. */
    public static final class NewCall {
        public final Uri callsUri;
        public final Uri voicemailUri;
        public final String number;
        public final int numberPresentation;
        public final String accountComponentName;
        public final String accountId;
        public final String transcription;
        public final String countryIso;
        public final long dateMs;

        public NewCall(
                Uri callsUri,
                Uri voicemailUri,
                String number,
                int numberPresentation,
                String accountComponentName,
                String accountId,
                String transcription,
                String countryIso,
                long dateMs) {
            this.callsUri = callsUri;
            this.voicemailUri = voicemailUri;
            this.number = number;
            this.numberPresentation = numberPresentation;
            this.accountComponentName = accountComponentName;
            this.accountId = accountId;
            this.transcription = transcription;
            this.countryIso = countryIso;
            this.dateMs = dateMs;
        }
    }

    /** Allows determining the new calls for which a notification should be generated. */
    public interface NewCallsQuery {
        /**
         * Returns the new calls of a certain type for which a notification should be generated.
         */
        @Nullable
        public List<NewCall> query(int type);
    }

    /** Create a new instance of {@link NewCallsQuery}. */
    public static NewCallsQuery createNewCallsQuery(Context context,
            ContentResolver contentResolver) {

        return new DefaultNewCallsQuery(context.getApplicationContext(), contentResolver);
    }

    /**
     * Default implementation of {@link NewCallsQuery} that looks up the list of new calls to
     * notify about in the call log.
     */
    private static final class DefaultNewCallsQuery implements NewCallsQuery {
        private static final String[] PROJECTION = {
            Calls._ID,
            Calls.NUMBER,
            Calls.VOICEMAIL_URI,
            Calls.NUMBER_PRESENTATION,
            Calls.PHONE_ACCOUNT_COMPONENT_NAME,
            Calls.PHONE_ACCOUNT_ID,
            Calls.TRANSCRIPTION,
            Calls.COUNTRY_ISO,
            Calls.DATE
        };
        private static final int ID_COLUMN_INDEX = 0;
        private static final int NUMBER_COLUMN_INDEX = 1;
        private static final int VOICEMAIL_URI_COLUMN_INDEX = 2;
        private static final int NUMBER_PRESENTATION_COLUMN_INDEX = 3;
        private static final int PHONE_ACCOUNT_COMPONENT_NAME_COLUMN_INDEX = 4;
        private static final int PHONE_ACCOUNT_ID_COLUMN_INDEX = 5;
        private static final int TRANSCRIPTION_COLUMN_INDEX = 6;
        private static final int COUNTRY_ISO_COLUMN_INDEX = 7;
        private static final int DATE_COLUMN_INDEX = 8;

        private final ContentResolver mContentResolver;
        private final Context mContext;

        private DefaultNewCallsQuery(Context context, ContentResolver contentResolver) {
            mContext = context;
            mContentResolver = contentResolver;
        }

        @Override
        @Nullable
        public List<NewCall> query(int type) {
            if (!PermissionsUtil.hasPermission(mContext, READ_CALL_LOG)) {
                Log.w(TAG, "No READ_CALL_LOG permission, returning null for calls lookup.");
                return null;
            }
            final String selection = String.format("%s = 1 AND %s = ?", Calls.NEW, Calls.TYPE);
            final String[] selectionArgs = new String[]{ Integer.toString(type) };
            try (Cursor cursor = mContentResolver.query(Calls.CONTENT_URI_WITH_VOICEMAIL,
                    PROJECTION, selection, selectionArgs, Calls.DEFAULT_SORT_ORDER)) {
                if (cursor == null) {
                    return null;
                }
                List<NewCall> newCalls = new ArrayList<>();
                while (cursor.moveToNext()) {
                    newCalls.add(createNewCallsFromCursor(cursor));
                }
                return newCalls;
            } catch (RuntimeException e) {
                Log.w(TAG, "Exception when querying Contacts Provider for calls lookup");
                return null;
            }
        }

        /** Returns an instance of {@link NewCall} created by using the values of the cursor. */
        private NewCall createNewCallsFromCursor(Cursor cursor) {
            String voicemailUriString = cursor.getString(VOICEMAIL_URI_COLUMN_INDEX);
            Uri callsUri = ContentUris.withAppendedId(
                    Calls.CONTENT_URI_WITH_VOICEMAIL, cursor.getLong(ID_COLUMN_INDEX));
            Uri voicemailUri = voicemailUriString == null ? null : Uri.parse(voicemailUriString);
            return new NewCall(
                    callsUri,
                    voicemailUri,
                    cursor.getString(NUMBER_COLUMN_INDEX),
                    cursor.getInt(NUMBER_PRESENTATION_COLUMN_INDEX),
                    cursor.getString(PHONE_ACCOUNT_COMPONENT_NAME_COLUMN_INDEX),
                    cursor.getString(PHONE_ACCOUNT_ID_COLUMN_INDEX),
                    cursor.getString(TRANSCRIPTION_COLUMN_INDEX),
                    cursor.getString(COUNTRY_ISO_COLUMN_INDEX),
                    cursor.getLong(DATE_COLUMN_INDEX));
        }
    }

    /** Allows determining the name associated with a given phone number. */
    public interface NameLookupQuery {
        /**
         * Returns the name associated with the given number in the contacts database, or null if
         * the number does not correspond to any of the contacts.
         * <p>
         * If there are multiple contacts with the same phone number, it will return the name of one
         * of the matching contacts.
         */
        @Nullable
        public String query(@Nullable String number);
    }

    /** Create a new instance of {@link NameLookupQuery}. */
    public static NameLookupQuery createNameLookupQuery(Context context,
            ContentResolver contentResolver) {
        return new DefaultNameLookupQuery(context.getApplicationContext(), contentResolver);
    }

    /**
     * Default implementation of {@link NameLookupQuery} that looks up the name of a contact in the
     * contacts database.
     */
    private static final class DefaultNameLookupQuery implements NameLookupQuery {
        private static final String[] PROJECTION = { PhoneLookup.DISPLAY_NAME };
        private static final int DISPLAY_NAME_COLUMN_INDEX = 0;

        private final ContentResolver mContentResolver;
        private final Context mContext;

        private DefaultNameLookupQuery(Context context, ContentResolver contentResolver) {
            mContext = context;
            mContentResolver = contentResolver;
        }

        @Override
        @Nullable
        public String query(@Nullable String number) {
            if (!PermissionsUtil.hasPermission(mContext, READ_CONTACTS)) {
                Log.w(TAG, "No READ_CONTACTS permission, returning null for name lookup.");
                return null;
            }
            try (Cursor cursor =  mContentResolver.query(
                    Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(number)),
                    PROJECTION, null, null, null)) {
                if (cursor == null || !cursor.moveToFirst()) {
                    return null;
                }
                return cursor.getString(DISPLAY_NAME_COLUMN_INDEX);
            } catch (RuntimeException e) {
                Log.w(TAG, "Exception when querying Contacts Provider for name lookup");
                return null;
            }
        }
    }
}
+16 −233

File changed.

Preview size limit exceeded, changes collapsed.