/*
 * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * 按照Apache 2.0协议进行授权，意味着代码可以在遵循该协议条款的情况下被使用、分发等。
 * 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 net.micode.notes.data;
// 定义该类所属的包名，用于在Java项目中对类进行组织和管理，方便区分不同功能模块的类等。

import android.content.Context;
// 引入Android系统的上下文类，用于获取系统相关资源、访问系统服务等操作。
import android.database.Cursor;
// 用于处理数据库查询结果集的类，通过它可以遍历查询返回的数据行等。
import android.provider.ContactsContract.CommonDataKinds.Phone;
// 用于访问联系人中电话号码相关信息的常量和接口等，方便操作联系人电话号码相关的数据。
import android.provider.ContactsContract.Data;
// 用于访问联系人数据的相关常量和接口，可用于构建查询联系人相关信息的语句等。
import android.telephony.PhoneNumberUtils;
// 提供了一些处理电话号码相关的实用工具方法，比如格式化、匹配等操作。
import android.util.Log;
// Android系统提供的用于记录日志的类，方便调试和查看程序运行过程中的相关信息。

import java.util.HashMap;
// Java中用于存储键值对数据的集合类，在这里用于缓存联系人相关信息。

public class Contact {
    // 定义Contact类，用于处理与联系人相关的操作，比如根据电话号码查找联系人姓名等。

    private static HashMap<String, String> sContactCache;
    // 定义一个静态的HashMap，用于缓存电话号码和对应的联系人姓名，这样可以避免重复查询数据库，提高效率。
    // 键为电话号码字符串，值为对应的联系人姓名字符串。
    private static final String TAG = "Contact";
    // 定义一个日志标签字符串常量，用于在记录日志时标识是该类产生的日志信息，方便在查看日志时进行筛选和定位。

    private static final String CALLER_ID_SELECTION = "PHONE_NUMBERS_EQUAL(" + Phone.NUMBER
            + ",?) AND " + Data.MIMETYPE + "='" + Phone.CONTENT_ITEM_TYPE + "'"
            + " AND " + Data.RAW_CONTACT_ID + " IN "
            + "(SELECT raw_contact_id "
            + " FROM phone_lookup"
            + " WHERE min_match = '+')";
    // 定义一个静态的字符串常量，用于构建查询联系人信息的SQL语句的选择条件部分。
    // 它通过多个条件组合来筛选出符合特定电话号码对应的联系人信息，例如通过电话号码相等比较、数据类型匹配、原始联系人ID相关条件等进行筛选。

    public static String getContact(Context context, String phoneNumber) {
        // 定义一个静态方法，用于根据给定的上下文（用于访问系统资源等）和电话号码来获取对应的联系人姓名。
        // 参数context是Android系统上下文，phoneNumber是要查找联系人对应的电话号码字符串。

        if (sContactCache == null) {
            // 判断联系人缓存集合是否为空，如果为空则进行初始化操作。
            sContactCache = new HashMap<String, String>();
            // 创建一个新的HashMap实例，用于后续缓存联系人信息。
        }

        if (sContactCache.containsKey(phoneNumber)) {
            // 判断缓存中是否已经存在给定电话号码对应的联系人姓名，如果存在则直接返回缓存中的姓名。
            return sContactCache.get(phoneNumber);
        }

        String selection = CALLER_ID_SELECTION.replace("+",
                PhoneNumberUtils.toCallerIDMinMatch(phoneNumber));
        // 根据给定的电话号码，对之前定义的查询选择条件字符串常量进行替换操作，将其中的 '+' 替换为适合该电话号码的最小匹配格式（通过PhoneNumberUtils工具类方法转换），得到实际用于查询的选择条件字符串。

        Cursor cursor = context.getContentResolver().query(
                Data.CONTENT_URI,
                new String[]{Phone.DISPLAY_NAME},
                selection,
                new String[]{phoneNumber},
                null);
        // 通过上下文的ContentResolver对象发起一个数据库查询操作，查询联系人数据。
        // 参数Data.CONTENT_URI指定了查询的内容提供者的URI，即联系人数据的访问地址。
        // 第二个参数指定了要查询返回的列，这里只查询联系人的显示名称（Phone.DISPLAY_NAME）。
        // 第三个参数是前面构建好的查询选择条件字符串。
        // 第四个参数是查询选择条件中占位符对应的实际值，这里就是要查找联系人对应的电话号码。
        // 最后一个参数为null，表示不进行排序等额外操作。

        if (cursor!= null && cursor.moveToFirst()) {
            // 判断查询返回的游标是否不为空并且游标可以移动到第一条数据（即有查询到符合条件的数据）。
            try {
                String name = cursor.getString(0);
                // 从游标中获取第一条数据（也就是查询到的联系人信息中）的联系人姓名，因为前面只查询了显示名称这一列，所以这里索引为0获取该列的值。
                sContactCache.put(phoneNumber, name);
                // 将获取到的联系人姓名存入缓存集合中，键为传入的电话号码，方便下次查找相同电话号码时直接从缓存获取。
                return name;
                // 返回获取到的联系人姓名。
            } catch (IndexOutOfBoundsException e) {
                // 如果在从游标获取字符串数据时出现越界异常（比如查询结果列数与预期不符等情况），则捕获该异常。
                Log.e(TAG, " Cursor get string error " + e.toString());
                // 使用Log.e记录错误级别的日志，标记是在游标获取字符串时出现错误，并输出异常的字符串表示形式，方便调试查看问题。
                return null;
                // 返回null表示获取联系人姓名失败。
            } finally {
                cursor.close();
                // 无论是否成功获取到联系人姓名，都要关闭游标，释放相关资源，避免内存泄漏等问题。
            }
        } else {
            Log.d(TAG, "No contact matched with number:" + phoneNumber);
            // 如果游标为空或者游标中没有可移动到的第一条数据（即没有查询到符合条件的联系人信息），则记录一条调试级别的日志，提示没有找到与给定电话号码匹配的联系人。
            return null;
            // 返回null表示没有找到对应的联系人姓名。
        }
    }
}
