feat: 大规模扩充至212个模块/175K行代码 — 覆盖android/java/javax/org/dalvik全包
- android/ 167个模块 (app/content/os/view/widget/graphics/media/hardware/net/telephony/provider等) - java/ 35个模块 (lang/io/util/net/nio/security/crypto/sql/beans/rmi/awt等) - javax/ 4个模块 (net/crypto/security/sql) - org/ 5个模块 (json/w3c_dom/xml_sax/xml_pull/apache_http) - dalvik/ 1个模块 (system) - ~3053个类/接口,方法签名完全对齐Android SDK - 桌面跨平台运行 (Windows/macOS/Linux) - 修复所有导入错误和Python关键字冲突
This commit is contained in:
1 parent
506f9ac725
commit
a51854455b
128 files changed
+100571
-3
No files matched your search
@@ -0,0 +1,776 @@
|
||||
"""
|
||||
Android Provider ContactsContract 扩展组件
|
||||
方法签名完全对齐 Android SDK
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import json
|
||||
import time
|
||||
import threading
|
||||
from typing import Any, Dict, List, Optional, Set, Tuple, Union
|
||||
|
||||
from .._core import Stub, AndroidOnlyError
|
||||
from ..java.lang import RuntimeException
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
# 联系人常量
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
|
||||
TIMES_CONTACTED = "times_contacted"
|
||||
LAST_TIME_CONTACTED = "last_time_contacted"
|
||||
STARRED = "starred"
|
||||
CUSTOM_RINGTONE = "custom_ringtone"
|
||||
SEND_TO_VOICEMAIL = "send_to_voicemail"
|
||||
CONTACT_PRESENCE = "contact_presence"
|
||||
CONTACT_CHAT_CAPABILITY = "contact_chat_capability"
|
||||
CONTACT_STATUS = "contact_status"
|
||||
CONTACT_STATUS_TIMESTAMP = "contact_status_ts"
|
||||
CONTACT_STATUS_RES_PACKAGE = "contact_status_res_package"
|
||||
CONTACT_STATUS_LABEL = "contact_status_label"
|
||||
CONTACT_STATUS_ICON = "contact_status_icon"
|
||||
|
||||
CONTACT_ID = "_id"
|
||||
CONTACT_LOOKUP_KEY = "lookup"
|
||||
CONTACT_DISPLAY_NAME = "display_name"
|
||||
CONTACT_PHOTO_ID = "photo_id"
|
||||
CONTACT_PHOTO_URI = "photo_uri"
|
||||
CONTACT_PHOTO_THUMBNAIL_URI = "photo_thumb_uri"
|
||||
CONTACT_LAST_TIME_CONTACTED = LAST_TIME_CONTACTED
|
||||
CONTACT_TIMES_CONTACTED = TIMES_CONTACTED
|
||||
CONTACT_STARRED = STARRED
|
||||
CONTACT_HAS_PHONE_NUMBER = "has_phone_number"
|
||||
CONTACT_IS_USER_PROFILE = "is_user_profile"
|
||||
CONTACT_IS_PRIVATE = "is_private"
|
||||
CONTACT_NAME_RAW_CONTACT_ID = "name_raw_contact_id"
|
||||
CONTACT_DISPLAY_NAME_SOURCE = "display_name_source"
|
||||
CONTACT_LOOKUP_KEY = CONTACT_LOOKUP_KEY
|
||||
CONTACT_SORT_KEY_PRIMARY = "sort_key"
|
||||
CONTACT_SORT_KEY_ALTERNATIVE = "sort_key_alt"
|
||||
|
||||
LOOKUP_KEY = CONTACT_LOOKUP_KEY
|
||||
LOOKUP_ID = "_id"
|
||||
|
||||
DATA_ID = "_id"
|
||||
DATA_CONTACT_ID = "contact_id"
|
||||
DATA_RAW_CONTACT_ID = "raw_contact_id"
|
||||
DATA_MIMETYPE = "mimetype"
|
||||
DATA_IS_PRIMARY = "is_primary"
|
||||
DATA_IS_SUPER_PRIMARY = "is_super_primary"
|
||||
DATA_DATA_VERSION = "data_version"
|
||||
DATA_DELETED = "deleted"
|
||||
DATA_SYNC1 = "data_sync1"
|
||||
DATA_SYNC2 = "data_sync2"
|
||||
DATA_SYNC3 = "data_sync3"
|
||||
DATA_SYNC4 = "data_sync4"
|
||||
DATA1 = "data1"
|
||||
DATA2 = "data2"
|
||||
DATA3 = "data3"
|
||||
DATA4 = "data4"
|
||||
DATA5 = "data5"
|
||||
DATA6 = "data6"
|
||||
DATA7 = "data7"
|
||||
DATA8 = "data8"
|
||||
DATA9 = "data9"
|
||||
DATA10 = "data10"
|
||||
DATA11 = "data11"
|
||||
DATA12 = "data12"
|
||||
DATA13 = "data13"
|
||||
DATA14 = "data14"
|
||||
DATA15 = "data15"
|
||||
|
||||
RAW_CONTACT_ID = "_id"
|
||||
RAW_CONTACT_CONTACT_ID = "contact_id"
|
||||
RAW_CONTACT_ACCOUNT_NAME = "account_name"
|
||||
RAW_CONTACT_ACCOUNT_TYPE = "account_type"
|
||||
RAW_CONTACT_SOURCE_ID = "sourceid"
|
||||
RAW_CONTACT_VERSION = "version"
|
||||
RAW_CONTACT_DELETED = "deleted"
|
||||
RAW_CONTACT_AGGREGATION_MODE = "aggregation_mode"
|
||||
RAW_CONTACT_AGGREGATION_NEEDED = 0
|
||||
RAW_CONTACT_AGGREGATION_SUSPENDED = 1
|
||||
RAW_CONTACT_AGGREGATION_DEFAULT = 2
|
||||
RAW_CONTACT_SYNC1 = "sync1"
|
||||
RAW_CONTACT_SYNC2 = "sync2"
|
||||
RAW_CONTACT_SYNC3 = "sync3"
|
||||
RAW_CONTACT_SYNC4 = "sync4"
|
||||
RAW_CONTACT_METADATA = "metadata"
|
||||
|
||||
# MIME 类型
|
||||
MIMETYPE_EMAIL = "vnd.android.cursor.item/email_v2"
|
||||
MIMETYPE_PHONE = "vnd.android.cursor.item/phone_v2"
|
||||
MIMETYPE_NAME = "vnd.android.cursor.item/name"
|
||||
MIMETYPE_POSTAL = "vnd.android.cursor.item/postal-address_v2"
|
||||
MIMETYPE_IM = "vnd.android.cursor.item/im"
|
||||
MIMETYPE_NOTE = "vnd.android.cursor.item/note"
|
||||
MIMETYPE_ORGANIZATION = "vnd.android.cursor.item/organization"
|
||||
MIMETYPE_PHOTO = "vnd.android.cursor.item/photo"
|
||||
MIMETYPE_WEBSITE = "vnd.android.cursor.item/website"
|
||||
MIMETYPE_SIP_ADDRESS = "vnd.android.cursor.item/sip_address"
|
||||
MIMETYPE_NICKNAME = "vnd.android.cursor.item/nickname"
|
||||
MIMETYPE_GROUP_MEMBERSHIP = "vnd.android.cursor.item/group_membership"
|
||||
MIMETYPE_EVENT = "vnd.android.cursor.item/contact_event"
|
||||
MIMETYPE_RELATION = "vnd.android.cursor.item/relation"
|
||||
|
||||
FULL_NAME_STYLE_UNDEFINED = 0
|
||||
FULL_NAME_STYLE_WESTERN = 1
|
||||
FULL_NAME_STYLE_EASTERN = 2
|
||||
FULL_NAME_STYLE_CHINESE = 3
|
||||
FULL_NAME_STYLE_JAPANESE = 4
|
||||
FULL_NAME_STYLE_KOREAN = 5
|
||||
|
||||
PHONETIC_NAME_STYLE_UNDEFINED = 0
|
||||
PHONETIC_NAME_STYLE_JAPANESE = 1
|
||||
PHONETIC_NAME_STYLE_KOREAN = 2
|
||||
PHONETIC_NAME_STYLE_PINYIN = 3
|
||||
PHONETIC_NAME_STYLE_ROMANJI = 4
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
# ContactsContract 命名空间容器
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
|
||||
class ContactsContractBase(Stub):
|
||||
"""ContactsContract — 联系人合同基类"""
|
||||
pass
|
||||
|
||||
|
||||
class SyncColumns(Stub):
|
||||
"""ContactsContract.SyncColumns — 同步列"""
|
||||
ACCOUNT_NAME = RAW_CONTACT_ACCOUNT_NAME
|
||||
ACCOUNT_TYPE = RAW_CONTACT_ACCOUNT_TYPE
|
||||
SOURCE_ID = RAW_CONTACT_SOURCE_ID
|
||||
VERSION = RAW_CONTACT_VERSION
|
||||
DIRTY = "dirty"
|
||||
SYNC1 = RAW_CONTACT_SYNC1
|
||||
SYNC2 = RAW_CONTACT_SYNC2
|
||||
SYNC3 = RAW_CONTACT_SYNC3
|
||||
SYNC4 = RAW_CONTACT_SYNC4
|
||||
|
||||
|
||||
class PresenceColumns(Stub):
|
||||
"""ContactsContract.PresenceColumns — 在线状态列"""
|
||||
DATA_ID = "presence_data_id"
|
||||
PROTOCOL = "protocol"
|
||||
CUSTOM_PROTOCOL = "custom_protocol"
|
||||
IM_HANDLE = "im_handle"
|
||||
IM_ACCOUNT = "im_account"
|
||||
STATUS = "mode"
|
||||
STATUS_LABEL = "status_label"
|
||||
STATUS_RES_PACKAGE = "status_res_package"
|
||||
STATUS_ICON = "status_icon"
|
||||
STATUS_TIMESTAMP = "status_ts"
|
||||
|
||||
AVAILABLE = 5
|
||||
AWAY = 2
|
||||
BUSY = 4
|
||||
DO_NOT_DISTURB = 4
|
||||
IDLE = 3
|
||||
INVISIBLE = 1
|
||||
OFFLINE = 0
|
||||
|
||||
|
||||
class Presence(PresenceColumns):
|
||||
"""ContactsContract.Presence — 在线状态"""
|
||||
pass
|
||||
|
||||
|
||||
class FullNameStyle(Stub):
|
||||
"""ContactsContract.FullNameStyle — 全名风格"""
|
||||
UNDEFINED = FULL_NAME_STYLE_UNDEFINED
|
||||
WESTERN = FULL_NAME_STYLE_WESTERN
|
||||
EASTERN = FULL_NAME_STYLE_EASTERN
|
||||
CHINESE = FULL_NAME_STYLE_CHINESE
|
||||
JAPANESE = FULL_NAME_STYLE_JAPANESE
|
||||
KOREAN = FULL_NAME_STYLE_KOREAN
|
||||
|
||||
|
||||
class PhoneticNameStyle(Stub):
|
||||
"""ContactsContract.PhoneticNameStyle — 拼音名称风格"""
|
||||
UNDEFINED = PHONETIC_NAME_STYLE_UNDEFINED
|
||||
JAPANESE = PHONETIC_NAME_STYLE_JAPANESE
|
||||
KOREAN = PHONETIC_NAME_STYLE_KOREAN
|
||||
PINYIN = PHONETIC_NAME_STYLE_PINYIN
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
# ContactsContract.Contacts
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
|
||||
class ContactData(Stub):
|
||||
"""内部联系人数据存储"""
|
||||
def __init__(self):
|
||||
self.id: int = 0
|
||||
self.lookup_key: str = ""
|
||||
self.display_name: str = ""
|
||||
self.photo_id: int = 0
|
||||
self.photo_uri: Optional[str] = None
|
||||
self.photo_thumb_uri: Optional[str] = None
|
||||
self.times_contacted: int = 0
|
||||
self.last_time_contacted: int = 0
|
||||
self.starred: bool = False
|
||||
self.custom_ringtone: Optional[str] = None
|
||||
self.send_to_voicemail: bool = False
|
||||
self.has_phone_number: bool = False
|
||||
self.is_user_profile: bool = False
|
||||
self.name_raw_contact_id: int = 0
|
||||
self.display_name_source: int = 0
|
||||
self.sort_key: str = ""
|
||||
self.sort_key_alt: str = ""
|
||||
|
||||
|
||||
class AggregationSuggestions(Stub):
|
||||
"""ContactsContract.Contacts.AggregationSuggestions — 聚合建议"""
|
||||
|
||||
class Builder(Stub):
|
||||
"""聚合建议构建器"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._contact_id: int = 0
|
||||
self._limit: int = 0
|
||||
self._name: Optional[str] = None
|
||||
|
||||
def setContactId(self, contact_id: int) -> "AggregationSuggestions.Builder":
|
||||
self._contact_id = contact_id
|
||||
return self
|
||||
|
||||
def setLimit(self, limit: int) -> "AggregationSuggestions.Builder":
|
||||
self._limit = limit
|
||||
return self
|
||||
|
||||
def addNameParameter(self, name: str) -> "AggregationSuggestions.Builder":
|
||||
self._name = name
|
||||
return self
|
||||
|
||||
def build(self) -> str:
|
||||
return f"suggestions?contact_id={self._contact_id}&limit={self._limit}"
|
||||
|
||||
CONTENT_DIRECTORY = "suggestions"
|
||||
PARAMETER_MATCH_NAME = "name"
|
||||
|
||||
|
||||
class Photo(Stub):
|
||||
"""ContactsContract.Contacts.Photo — 联系人照片"""
|
||||
CONTENT_DIRECTORY = "photo"
|
||||
PHOTO = "data15"
|
||||
PHOTO_FILE_ID = "data14"
|
||||
DISPLAY_PHOTO = "display_photo"
|
||||
|
||||
|
||||
class Contacts(Stub):
|
||||
"""ContactsContract.Contacts — 联系人表"""
|
||||
|
||||
CONTENT_URI = "content://com.android.contacts/contacts"
|
||||
CONTENT_LOOKUP_URI = "content://com.android.contacts/contacts/lookup"
|
||||
CONTENT_FILTER_URI = "content://com.android.contacts/contacts/filter"
|
||||
CONTENT_STREQUENT_URI = "content://com.android.contacts/contacts/strequent"
|
||||
CONTENT_FREQUENT_URI = "content://com.android.contacts/contacts/frequent"
|
||||
CONTENT_STREQUENT_EMAIL_URI = "content://com.android.contacts/contacts/strequent/email"
|
||||
CONTENT_GROUP_URI = "content://com.android.contacts/contacts/group"
|
||||
|
||||
AggregationSuggestions = AggregationSuggestions
|
||||
Photo = Photo
|
||||
|
||||
# 列
|
||||
_ID = "_id"
|
||||
LOOKUP_KEY = "lookup"
|
||||
DISPLAY_NAME = "display_name"
|
||||
PHOTO_ID = "photo_id"
|
||||
PHOTO_URI = "photo_uri"
|
||||
PHOTO_THUMBNAIL_URI = "photo_thumb_uri"
|
||||
TIMES_CONTACTED = TIMES_CONTACTED
|
||||
LAST_TIME_CONTACTED = LAST_TIME_CONTACTED
|
||||
STARRED = STARRED
|
||||
CUSTOM_RINGTONE = CUSTOM_RINGTONE
|
||||
SEND_TO_VOICEMAIL = SEND_TO_VOICEMAIL
|
||||
HAS_PHONE_NUMBER = "has_phone_number"
|
||||
IS_USER_PROFILE = "is_user_profile"
|
||||
NAME_RAW_CONTACT_ID = "name_raw_contact_id"
|
||||
DISPLAY_NAME_SOURCE = "display_name_source"
|
||||
SORT_KEY_PRIMARY = "sort_key"
|
||||
SORT_KEY_ALTERNATIVE = "sort_key_alt"
|
||||
CONTACT_LAST_UPDATED_TIMESTAMP = "contact_last_updated_timestamp"
|
||||
CONTACT_PRESENCE = CONTACT_PRESENCE
|
||||
CONTACT_CHAT_CAPABILITY = CONTACT_CHAT_CAPABILITY
|
||||
CONTACT_STATUS = CONTACT_STATUS
|
||||
CONTACT_STATUS_TIMESTAMP = CONTACT_STATUS_TIMESTAMP
|
||||
|
||||
@staticmethod
|
||||
def getLookupUri(contact_id: int, lookup_key: str) -> str:
|
||||
return f"{Contacts.CONTENT_LOOKUP_URI}/{lookup_key}/{contact_id}"
|
||||
|
||||
@staticmethod
|
||||
def lookupContact(content_resolver: Any, lookup_uri: str) -> Optional[Dict[str, Any]]:
|
||||
return None
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
# ContactsContract.Data
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
|
||||
class Data(Stub):
|
||||
"""ContactsContract.Data — 数据表"""
|
||||
|
||||
CONTENT_URI = "content://com.android.contacts/data"
|
||||
|
||||
_ID = DATA_ID
|
||||
CONTACT_ID = DATA_CONTACT_ID
|
||||
RAW_CONTACT_ID = DATA_RAW_CONTACT_ID
|
||||
MIMETYPE = DATA_MIMETYPE
|
||||
IS_PRIMARY = DATA_IS_PRIMARY
|
||||
IS_SUPER_PRIMARY = DATA_IS_SUPER_PRIMARY
|
||||
DATA_VERSION = DATA_DATA_VERSION
|
||||
DELETED = DATA_DELETED
|
||||
SYNC1 = DATA_SYNC1
|
||||
SYNC2 = DATA_SYNC2
|
||||
SYNC3 = DATA_SYNC3
|
||||
SYNC4 = DATA_SYNC4
|
||||
DATA1 = DATA1
|
||||
DATA2 = DATA2
|
||||
DATA3 = DATA3
|
||||
DATA4 = DATA4
|
||||
DATA5 = DATA5
|
||||
DATA6 = DATA6
|
||||
DATA7 = DATA7
|
||||
DATA8 = DATA8
|
||||
DATA9 = DATA9
|
||||
DATA10 = DATA10
|
||||
DATA11 = DATA11
|
||||
DATA12 = DATA12
|
||||
DATA13 = DATA13
|
||||
DATA14 = DATA14
|
||||
DATA15 = DATA15
|
||||
|
||||
# 偏好常量
|
||||
CONTACT_PREFERRED_ORDER = "contact_preferred_order"
|
||||
CONTACT_PREFERRED_PHONE = "contact_preferred_phone"
|
||||
CONTACT_PREFERRED_EMAIL = "contact_preferred_email"
|
||||
CONTACT_PREFERRED_PHONE_ORDER = "contact_preferred_phone_order"
|
||||
CONTACT_PREFERRED_EMAIL_ORDER = "contact_preferred_email_order"
|
||||
|
||||
# 可见性
|
||||
VISIBILITY_VISIBLE = 0
|
||||
VISIBILITY_HIDDEN = 1
|
||||
VISIBILITY_DEFAULT = 0
|
||||
VISIBILITY = "visibility"
|
||||
|
||||
@staticmethod
|
||||
def getContactPreferred(data_kind: str, data_set: List[Dict]) -> Optional[Dict]:
|
||||
if not data_set:
|
||||
return None
|
||||
primaries = [d for d in data_set if d.get("is_super_primary") or d.get("is_primary")]
|
||||
if primaries:
|
||||
return primaries[0]
|
||||
return data_set[0]
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
# ContactsContract.CommonDataKinds
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
|
||||
class CommonDataKinds(Stub):
|
||||
"""ContactsContract.CommonDataKinds — 通用数据类型"""
|
||||
|
||||
class Email(Stub):
|
||||
CONTENT_ITEM_TYPE = MIMETYPE_EMAIL
|
||||
TYPE_HOME = 1
|
||||
TYPE_WORK = 2
|
||||
TYPE_OTHER = 3
|
||||
TYPE_MOBILE = 4
|
||||
ADDRESS = DATA1
|
||||
DISPLAY_NAME = DATA4
|
||||
TYPE = DATA2
|
||||
LABEL = DATA3
|
||||
|
||||
class Phone(Stub):
|
||||
CONTENT_ITEM_TYPE = MIMETYPE_PHONE
|
||||
TYPE_HOME = 1
|
||||
TYPE_MOBILE = 2
|
||||
TYPE_WORK = 3
|
||||
TYPE_FAX_WORK = 4
|
||||
TYPE_FAX_HOME = 5
|
||||
TYPE_PAGER = 6
|
||||
TYPE_OTHER = 7
|
||||
TYPE_CALLBACK = 8
|
||||
TYPE_CAR = 9
|
||||
TYPE_COMPANY_MAIN = 10
|
||||
TYPE_ISDN = 11
|
||||
TYPE_MAIN = 12
|
||||
TYPE_OTHER_FAX = 13
|
||||
TYPE_RADIO = 14
|
||||
TYPE_TELEX = 15
|
||||
TYPE_TTY_TDD = 16
|
||||
TYPE_WORK_MOBILE = 17
|
||||
TYPE_WORK_PAGER = 18
|
||||
TYPE_ASSISTANT = 19
|
||||
TYPE_MMS = 20
|
||||
NUMBER = DATA1
|
||||
NUMBER_KEY = "data4"
|
||||
TYPE = DATA2
|
||||
LABEL = DATA3
|
||||
|
||||
class StructuredName(Stub):
|
||||
CONTENT_ITEM_TYPE = MIMETYPE_NAME
|
||||
DISPLAY_NAME = DATA1
|
||||
GIVEN_NAME = DATA2
|
||||
FAMILY_NAME = DATA3
|
||||
PREFIX = DATA4
|
||||
MIDDLE_NAME = DATA5
|
||||
SUFFIX = DATA6
|
||||
PHONETIC_GIVEN_NAME = DATA7
|
||||
PHONETIC_FAMILY_NAME = DATA8
|
||||
PHONETIC_MIDDLE_NAME = DATA9
|
||||
FULL_NAME_STYLE = DATA10
|
||||
PHONETIC_NAME_STYLE = DATA11
|
||||
|
||||
class StructuredPostal(Stub):
|
||||
CONTENT_ITEM_TYPE = MIMETYPE_POSTAL
|
||||
FORMATTED_ADDRESS = DATA1
|
||||
STREET = DATA4
|
||||
POBOX = DATA5
|
||||
NEIGHBORHOOD = DATA6
|
||||
CITY = DATA7
|
||||
REGION = DATA8
|
||||
POSTCODE = DATA9
|
||||
COUNTRY = DATA10
|
||||
TYPE = DATA2
|
||||
LABEL = DATA3
|
||||
TYPE_HOME = 1
|
||||
TYPE_WORK = 2
|
||||
TYPE_OTHER = 3
|
||||
|
||||
class Im(Stub):
|
||||
CONTENT_ITEM_TYPE = MIMETYPE_IM
|
||||
PROTOCOL = DATA5
|
||||
CUSTOM_PROTOCOL = DATA6
|
||||
DATA = DATA1
|
||||
TYPE = DATA2
|
||||
|
||||
PROTOCOL_AIM = 0
|
||||
PROTOCOL_MSN = 1
|
||||
PROTOCOL_YAHOO = 2
|
||||
PROTOCOL_SKYPE = 3
|
||||
PROTOCOL_QQ = 4
|
||||
PROTOCOL_GTALK = 5
|
||||
PROTOCOL_ICQ = 6
|
||||
PROTOCOL_JABBER = 7
|
||||
PROTOCOL_CUSTOM = -1
|
||||
|
||||
class Organization(Stub):
|
||||
CONTENT_ITEM_TYPE = MIMETYPE_ORGANIZATION
|
||||
COMPANY = DATA1
|
||||
TYPE = DATA2
|
||||
LABEL = DATA3
|
||||
TITLE = DATA4
|
||||
DEPARTMENT = DATA5
|
||||
JOB_DESCRIPTION = DATA6
|
||||
SYMBOL = DATA7
|
||||
PHONETIC_NAME = DATA8
|
||||
OFFICE_LOCATION = DATA9
|
||||
PHONETIC_NAME_STYLE = DATA10
|
||||
TYPE_WORK = 1
|
||||
TYPE_OTHER = 2
|
||||
|
||||
class Photo(Stub):
|
||||
CONTENT_ITEM_TYPE = MIMETYPE_PHOTO
|
||||
PHOTO = DATA15
|
||||
PHOTO_FILE_ID = DATA14
|
||||
|
||||
class Note(Stub):
|
||||
CONTENT_ITEM_TYPE = MIMETYPE_NOTE
|
||||
NOTE = DATA1
|
||||
|
||||
class Website(Stub):
|
||||
CONTENT_ITEM_TYPE = MIMETYPE_WEBSITE
|
||||
URL = DATA1
|
||||
TYPE = DATA2
|
||||
TYPE_HOMEPAGE = 1
|
||||
TYPE_BLOG = 2
|
||||
TYPE_PROFILE = 3
|
||||
TYPE_HOME = 4
|
||||
TYPE_WORK = 5
|
||||
TYPE_FTP = 6
|
||||
TYPE_OTHER = 7
|
||||
|
||||
class Event(Stub):
|
||||
CONTENT_ITEM_TYPE = MIMETYPE_EVENT
|
||||
START_DATE = DATA1
|
||||
TYPE = DATA2
|
||||
TYPE_ANNIVERSARY = 1
|
||||
TYPE_OTHER = 2
|
||||
TYPE_BIRTHDAY = 3
|
||||
|
||||
class Relation(Stub):
|
||||
CONTENT_ITEM_TYPE = MIMETYPE_RELATION
|
||||
NAME = DATA1
|
||||
TYPE = DATA2
|
||||
TYPE_ASSISTANT = 1
|
||||
TYPE_BROTHER = 2
|
||||
TYPE_CHILD = 3
|
||||
TYPE_DOMESTIC_PARTNER = 4
|
||||
TYPE_FATHER = 5
|
||||
TYPE_FRIEND = 6
|
||||
TYPE_MANAGER = 7
|
||||
TYPE_MOTHER = 8
|
||||
TYPE_PARENT = 9
|
||||
TYPE_PARTNER = 10
|
||||
TYPE_REFERRED_BY = 11
|
||||
TYPE_RELATIVE = 12
|
||||
TYPE_SISTER = 13
|
||||
TYPE_SPOUSE = 14
|
||||
|
||||
class Nickname(Stub):
|
||||
CONTENT_ITEM_TYPE = MIMETYPE_NICKNAME
|
||||
NAME = DATA1
|
||||
TYPE = DATA2
|
||||
TYPE_DEFAULT = 1
|
||||
TYPE_OTHER_NAME = 2
|
||||
TYPE_MAIDEN_NAME = 3
|
||||
TYPE_SHORT_NAME = 4
|
||||
TYPE_INITIALS = 5
|
||||
|
||||
class SipAddress(Stub):
|
||||
CONTENT_ITEM_TYPE = MIMETYPE_SIP_ADDRESS
|
||||
SIP_ADDRESS = DATA1
|
||||
TYPE = DATA2
|
||||
TYPE_HOME = 1
|
||||
TYPE_WORK = 2
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
# ContactsContract.RawContacts
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
|
||||
class RawContacts(Stub):
|
||||
"""ContactsContract.RawContacts — 原始联系人"""
|
||||
|
||||
CONTENT_URI = "content://com.android.contacts/raw_contacts"
|
||||
|
||||
_ID = RAW_CONTACT_ID
|
||||
CONTACT_ID = RAW_CONTACT_CONTACT_ID
|
||||
ACCOUNT_NAME = RAW_CONTACT_ACCOUNT_NAME
|
||||
ACCOUNT_TYPE = RAW_CONTACT_ACCOUNT_TYPE
|
||||
SOURCE_ID = RAW_CONTACT_SOURCE_ID
|
||||
VERSION = RAW_CONTACT_VERSION
|
||||
DELETED = RAW_CONTACT_DELETED
|
||||
AGGREGATION_MODE = RAW_CONTACT_AGGREGATION_MODE
|
||||
SYNC1 = RAW_CONTACT_SYNC1
|
||||
SYNC2 = RAW_CONTACT_SYNC2
|
||||
SYNC3 = RAW_CONTACT_SYNC3
|
||||
SYNC4 = RAW_CONTACT_SYNC4
|
||||
METADATA = RAW_CONTACT_METADATA
|
||||
RAW_CONTACT_IS_USER_PROFILE = "raw_contact_is_user_profile"
|
||||
RAW_CONTACT_IS_PRIVATE = "raw_contact_is_private"
|
||||
DISPLAY_NAME_SOURCE = "display_name_source"
|
||||
NAME_VERIFIED = "name_verified"
|
||||
GEOLOCATION = "geolocation"
|
||||
|
||||
AGGREGATION_MODE_DEFAULT = 0
|
||||
AGGREGATION_MODE_IMMEDIATE = 1
|
||||
AGGREGATION_MODE_SUSPENDED = 2
|
||||
AGGREGATION_MODE_DISABLED = 3
|
||||
|
||||
@staticmethod
|
||||
def getContactId(raw_contact_id: int) -> int:
|
||||
return 0
|
||||
|
||||
@staticmethod
|
||||
def getLookupUri(raw_contact_id: int, lookup_key: str) -> str:
|
||||
return f"content://com.android.contacts/raw_contacts/{raw_contact_id}"
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
# ContactsContract.StatusUpdates / Profile / Settings
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
|
||||
class StatusUpdates(PresenceColumns):
|
||||
"""ContactsContract.StatusUpdates — 状态更新"""
|
||||
CONTENT_URI = "content://com.android.contacts/status_updates"
|
||||
|
||||
|
||||
class Profile(Stub):
|
||||
"""ContactsContract.Profile — 用户个人资料"""
|
||||
CONTENT_URI = "content://com.android.contacts/profile"
|
||||
CONTENT_VCARD_URI = "content://com.android.contacts/profile/vcard"
|
||||
CONTENT_RAW_CONTACTS_URI = "content://com.android.contacts/profile/raw_contacts"
|
||||
MIN_ID = 1000000000
|
||||
MAX_ID = 2000000000
|
||||
|
||||
|
||||
class Settings(Stub):
|
||||
"""ContactsContract.Settings — 联系人设置"""
|
||||
CONTENT_URI = "content://com.android.contacts/settings"
|
||||
ACCOUNT_NAME = RAW_CONTACT_ACCOUNT_NAME
|
||||
ACCOUNT_TYPE = RAW_CONTACT_ACCOUNT_TYPE
|
||||
SHOULD_SYNC = "should_sync"
|
||||
UNGROUPED_VISIBLE = "ungrouped_visible"
|
||||
ANY_UNGROUPED = "any_ungrouped"
|
||||
UNGROUPED_COUNT = "ungrouped_count"
|
||||
UNGROUPED_WITH_PHONES = "ungrouped_with_phones"
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
# ContactsContract.PhoneLookup / DisplayPhoto / DataUsageFeedback
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
|
||||
class PhoneLookup(Stub):
|
||||
"""ContactsContract.PhoneLookup — 电话查找"""
|
||||
CONTENT_URI = "content://com.android.contacts/phone_lookup"
|
||||
CONTENT_FILTER_URI = "content://com.android.contacts/phone_lookup/filter"
|
||||
QUERY_PARAMETER_SIP_ADDRESS = "sip"
|
||||
NUMBER = "number"
|
||||
TYPE = "type"
|
||||
LABEL = "label"
|
||||
NORMALIZED_NUMBER = "normalized_number"
|
||||
CONTACT_ID = "contact_id"
|
||||
LOOKUP_KEY = "lookup"
|
||||
DISPLAY_NAME = "display_name"
|
||||
PHOTO_ID = "photo_id"
|
||||
PHOTO_URI = "photo_uri"
|
||||
PHOTO_THUMBNAIL_URI = "photo_thumb_uri"
|
||||
|
||||
|
||||
class DisplayPhoto(Stub):
|
||||
"""ContactsContract.DisplayPhoto — 显示照片"""
|
||||
CONTENT_URI = "content://com.android.contacts/display_photo"
|
||||
DISPLAY_MAX_DIM = "display_max_dim"
|
||||
THUMBNAIL_MAX_DIM = "thumbnail_max_dim"
|
||||
CONTENT_DIRECTORY = "display_photo"
|
||||
|
||||
|
||||
class DataUsageFeedback(Stub):
|
||||
"""ContactsContract.DataUsageFeedback — 数据使用反馈"""
|
||||
FEEDBACK_URI = "content://com.android.contacts/data/usagefeedback"
|
||||
USAGE_TYPE = "type"
|
||||
USAGE_TYPE_CALL = "call"
|
||||
USAGE_TYPE_TEXT = "text"
|
||||
USAGE_TYPE_EMAIL = "email"
|
||||
USAGE_TYPE_LONG_TEXT = "long_text"
|
||||
USAGE_TYPE_VIDEO_CALL = "video_call"
|
||||
|
||||
|
||||
class QuickContact(Stub):
|
||||
"""ContactsContract.QuickContact — 快速联系人"""
|
||||
|
||||
ACTION_QUICK_CONTACT = "android.provider.ContactsContract.QuickContact"
|
||||
|
||||
MODE_SMALL = 0
|
||||
MODE_MEDIUM = 1
|
||||
MODE_LARGE = 2
|
||||
|
||||
EXTRA_EXCLUDE_MIMES = "android.provider.extra.EXCLUDE_MIMES"
|
||||
EXTRA_MODE = "android.provider.extra.MODE"
|
||||
EXTRA_PRIORITIZED_MIMETYPE = "android.provider.extra.PRIORITIZED_MIMETYPE"
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
# ContactsContract 控制器 — 模拟 ContentProvider 的数据存储
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
|
||||
class ContactsContract(ContactsContractBase):
|
||||
"""ContactsContract — 联系人合同"""
|
||||
|
||||
Contacts = Contacts
|
||||
Data = Data
|
||||
CommonDataKinds = CommonDataKinds
|
||||
RawContacts = RawContacts
|
||||
StatusUpdates = StatusUpdates
|
||||
Profile = Profile
|
||||
Settings = Settings
|
||||
SyncColumns = SyncColumns
|
||||
Presence = Presence
|
||||
PhoneLookup = PhoneLookup
|
||||
DisplayPhoto = DisplayPhoto
|
||||
FullNameStyle = FullNameStyle
|
||||
PhoneticNameStyle = PhoneticNameStyle
|
||||
QuickContact = QuickContact
|
||||
DataUsageFeedback = DataUsageFeedback
|
||||
|
||||
AUTHORITY = "com.android.contacts"
|
||||
CALLER_IS_SYNCADAPTER = "caller_is_syncadapter"
|
||||
DEFERRED_SNIPPETING = "deferred_snippeting"
|
||||
DEFERRED_SNIPPETING_QUERY = "deferred_snippeting_query"
|
||||
DIRECTORY_CONTENT_URI = "content://com.android.contacts/directories"
|
||||
LIMIT_PARAM_KEY = "limit"
|
||||
PRIMARY_ACCOUNT_NAME = "name_forPrimaryAccount"
|
||||
PRIMARY_ACCOUNT_TYPE = "type_forPrimaryAccount"
|
||||
PROVIDER_INTERFACE = "android.content.ContactsContract"
|
||||
REMOVE_DUPLICATE_ENTRIES = "remove_duplicate_entries"
|
||||
STREQUENT_PHONE_ONLY = "strequent_phone_only"
|
||||
|
||||
_store: Dict[str, List[Dict[str, Any]]] = {
|
||||
"contacts": [],
|
||||
"raw_contacts": [],
|
||||
"data": [],
|
||||
}
|
||||
_lock = threading.Lock()
|
||||
|
||||
@classmethod
|
||||
def addContact(cls, display_name: str = "",
|
||||
phone: str = "", email: str = "") -> int:
|
||||
with cls._lock:
|
||||
contact_id = len(cls._store["contacts"]) + 1
|
||||
contact = {
|
||||
"_id": contact_id,
|
||||
"display_name": display_name,
|
||||
"has_phone_number": bool(phone),
|
||||
"times_contacted": 0,
|
||||
"last_time_contacted": 0,
|
||||
"starred": False,
|
||||
}
|
||||
cls._store["contacts"].append(contact)
|
||||
raw_id = len(cls._store["raw_contacts"]) + 1
|
||||
raw = {
|
||||
"_id": raw_id,
|
||||
"contact_id": contact_id,
|
||||
"aggregation_mode": 0,
|
||||
"deleted": False,
|
||||
"account_name": "",
|
||||
"account_type": "",
|
||||
}
|
||||
cls._store["raw_contacts"].append(raw)
|
||||
if phone:
|
||||
cls._store["data"].append({
|
||||
"_id": len(cls._store["data"]) + 1,
|
||||
"raw_contact_id": raw_id,
|
||||
"mimetype": MIMETYPE_PHONE,
|
||||
"data1": phone,
|
||||
"data2": 2,
|
||||
"is_primary": True,
|
||||
})
|
||||
if email:
|
||||
cls._store["data"].append({
|
||||
"_id": len(cls._store["data"]) + 1,
|
||||
"raw_contact_id": raw_id,
|
||||
"mimetype": MIMETYPE_EMAIL,
|
||||
"data1": email,
|
||||
"data2": 1,
|
||||
"is_primary": True,
|
||||
})
|
||||
return contact_id
|
||||
|
||||
@classmethod
|
||||
def query(cls, table: str, filters: Optional[Dict] = None) -> List[Dict[str, Any]]:
|
||||
with cls._lock:
|
||||
results = list(cls._store.get(table, []))
|
||||
if filters:
|
||||
filtered = []
|
||||
for item in results:
|
||||
match = True
|
||||
for k, v in filters.items():
|
||||
if item.get(k) != v:
|
||||
match = False
|
||||
break
|
||||
if match:
|
||||
filtered.append(item)
|
||||
return filtered
|
||||
return results
|
||||
|
||||
@classmethod
|
||||
def toString(cls) -> str:
|
||||
return (f"ContactsContract(contacts={len(cls._store['contacts'])}, "
|
||||
f"raw={len(cls._store['raw_contacts'])}, "
|
||||
f"data={len(cls._store['data'])})")
|
||||
Reference in new issue
Block a user