blob: 4e3eff6973a78c11e222977329e79659d18b3032 [file] [log] [blame]
/*
* Copyright (C) 2019 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.telephony.common;
import android.content.Context;
import android.content.res.Resources;
import android.database.Cursor;
import android.os.Parcel;
import android.os.Parcelable;
import android.provider.ContactsContract;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.text.TextUtils;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.util.Objects;
/**
* Contact phone number and its meta data.
*/
public class PhoneNumber implements Parcelable {
private final I18nPhoneNumberWrapper mI18nPhoneNumber;
@NonNull
private final String mAccountName;
@NonNull
private final String mAccountType;
private int mType;
@Nullable
private String mLabel;
private boolean mIsPrimary;
private long mId;
private int mDataVersion;
/** The favorite bit is from local database, presenting a
* {@link com.android.car.dialer.storage.FavoriteNumberEntity}. */
private boolean mIsFavorite;
static PhoneNumber fromCursor(Context context, Cursor cursor) {
int typeColumn = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.TYPE);
int labelColumn = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.LABEL);
int numberColumn = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);
int rawDataIdColumn = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone._ID);
int dataVersionColumn = cursor.getColumnIndex(
ContactsContract.CommonDataKinds.Phone.DATA_VERSION);
// IS_PRIMARY means primary entry of the raw contact and IS_SUPER_PRIMARY means primary
// entry of the aggregated contact. It is guaranteed that only one data entry is super
// primary.
int isPrimaryColumn = cursor.getColumnIndex(
ContactsContract.CommonDataKinds.Phone.IS_SUPER_PRIMARY);
int accountNameColumn = cursor.getColumnIndex(ContactsContract.RawContacts.ACCOUNT_NAME);
int accountTypeColumn = cursor.getColumnIndex(ContactsContract.RawContacts.ACCOUNT_TYPE);
return PhoneNumber.newInstance(
context,
cursor.getString(numberColumn),
cursor.getInt(typeColumn),
cursor.getString(labelColumn),
cursor.getInt(isPrimaryColumn) > 0,
cursor.getLong(rawDataIdColumn),
cursor.getString(accountNameColumn),
cursor.getString(accountTypeColumn),
cursor.getInt(dataVersionColumn));
}
/**
* Creates a new {@link PhoneNumber}.
*
* @param rawNumber A potential phone number.
* @param type The phone number type. See more at {@link Phone#TYPE}
* @param label The user defined label. See more at {@link Phone#LABEL}
* @param isPrimary Whether this is the primary entry of the aggregated contact it belongs
* to. See more at {@link Phone#IS_SUPER_PRIMARY}.
* @param id The unique key for raw contact entry containing the phone number entity.
* See more at {@link Phone#_ID}
* @param dataVersion The dataVersion of the raw contact entry record. See more at {@link
* Phone#DATA_VERSION}
*/
public static PhoneNumber newInstance(Context context, String rawNumber, int type,
@Nullable String label, boolean isPrimary, long id, String accountName,
String accountType, int dataVersion) {
I18nPhoneNumberWrapper i18nPhoneNumber = I18nPhoneNumberWrapper.Factory.INSTANCE.get(
context, rawNumber);
return new PhoneNumber(i18nPhoneNumber, type, label, isPrimary, id, accountName,
accountType, dataVersion);
}
private PhoneNumber(I18nPhoneNumberWrapper i18nNumber, int type, @Nullable String label,
boolean isPrimary, long id, String accountName, String accountType, int dataVersion) {
mI18nPhoneNumber = i18nNumber;
mType = type;
mLabel = label;
mIsPrimary = isPrimary;
mId = id;
mAccountName = TextUtils.emptyIfNull(accountName);
mAccountType = TextUtils.emptyIfNull(accountType);
mDataVersion = dataVersion;
}
@Override
public boolean equals(Object obj) {
return obj instanceof PhoneNumber
&& mI18nPhoneNumber.equals(((PhoneNumber) obj).mI18nPhoneNumber)
&& mAccountName.equals(((PhoneNumber) obj).mAccountName)
&& mAccountType.equals(((PhoneNumber) obj).mAccountType);
}
@Override
public int hashCode() {
return Objects.hash(mI18nPhoneNumber, mAccountName, mAccountType);
}
/**
* Returns if the phone number is the primary entry for the aggregated contact it belongs to.
* See more at {@link Phone#IS_SUPER_PRIMARY}.
*/
public boolean isPrimary() {
return mIsPrimary;
}
/**
* Returns a human readable string label. For example, Home, Work, etc.
*/
public CharSequence getReadableLabel(Resources res) {
return Phone.getTypeLabel(res, mType, mLabel);
}
/**
* Gets a phone number in the international format if valid. Otherwise, returns the raw number.
*/
public String getNumber() {
return mI18nPhoneNumber.getNumber();
}
/**
* Returns the raw number, the number that is input by the user
*/
public String getRawNumber() {
return mI18nPhoneNumber.getRawNumber();
}
/**
* Returns the format independent i18n {@link I18nPhoneNumberWrapper wrapper} class.
*/
public I18nPhoneNumberWrapper getI18nPhoneNumberWrapper() {
return mI18nPhoneNumber;
}
/**
* Gets the type of phone number, for example Home or Work. Possible values are defined in
* {@link Phone}.
*/
public int getType() {
return mType;
}
public long getId() {
return mId;
}
@Nullable
public String getAccountName() {
return mAccountName;
}
@Nullable
public String getAccountType() {
return mAccountType;
}
/**
* Updates the favorite bit, which is local database. See
* {@link com.android.car.dialer.storage.FavoriteNumberDatabase}.
*/
public void setIsFavorite(boolean isFavorite) {
mIsFavorite = isFavorite;
}
/** Returns if the phone number is favorite entry. */
public boolean isFavorite() {
return mIsFavorite;
}
/**
* Each contact may have a few sources with the same phone number. Merge same phone numbers as
* one.
*
* <p>As long as one of those phone numbers is primary entry of the aggregated contact, mark
* the merged phone number as primary.
*/
public PhoneNumber merge(PhoneNumber phoneNumber) {
if (equals(phoneNumber)) {
if (mDataVersion < phoneNumber.mDataVersion) {
mDataVersion = phoneNumber.mDataVersion;
mId = phoneNumber.mId;
mIsPrimary |= phoneNumber.mIsPrimary;
mType = phoneNumber.mType;
mLabel = phoneNumber.mLabel;
}
}
return this;
}
/**
* Gets the user defined label for the the contact method.
*/
@Nullable
public String getLabel() {
return mLabel;
}
@Override
public String toString() {
return getNumber() + " " + mAccountName + " " + mAccountType;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mType);
dest.writeString(mLabel);
dest.writeParcelable(mI18nPhoneNumber, flags);
dest.writeBoolean(mIsPrimary);
dest.writeLong(mId);
dest.writeString(mAccountName);
dest.writeString(mAccountType);
dest.writeInt(mDataVersion);
dest.writeBoolean(mIsFavorite);
}
public static Creator<PhoneNumber> CREATOR = new Creator<PhoneNumber>() {
@Override
public PhoneNumber createFromParcel(Parcel source) {
int type = source.readInt();
String label = source.readString();
I18nPhoneNumberWrapper i18nPhoneNumberWrapper = source.readParcelable(
I18nPhoneNumberWrapper.class.getClassLoader());
boolean isPrimary = source.readBoolean();
long id = source.readLong();
String accountName = source.readString();
String accountType = source.readString();
int dataVersion = source.readInt();
PhoneNumber phoneNumber = new PhoneNumber(i18nPhoneNumberWrapper, type, label,
isPrimary, id, accountName, accountType, dataVersion);
phoneNumber.setIsFavorite(source.readBoolean());
return phoneNumber;
}
@Override
public PhoneNumber[] newArray(int size) {
return new PhoneNumber[size];
}
};
}