简介

CSipSimple是Android手机上支持Sip的网络电话软件 , 基于的是pjsip开源sip协议栈

需要了解pjsip , 可以下载PJSUA开发文档中文版看看

源码流程

代码的大致流程如下 :

        中间肯定有SipService/PjsioService和DBProvider的交互

                        UI
        ===================================
                ^               |
                |               |
                |               V
              Intent        SipService
            DBProvider          |
                ^               |
                |               V
                |           PjsipService
          UAStateReciver        |
                ^               |
                |               V
        ===================================
                     pjsip底层库

注册流程分析

 UI界面 : 
    public class BasePrefsWizard extends GenericPrefs{
        /**
         * 保存账户并将账户保存或更新到数据库
         * @param wizardId
         */
        private void saveAccount(String wizardId) {//BASIC
            boolean needRestart = false;

            PreferencesWrapper prefs = new PreferencesWrapper(getApplicationContext());
            account = wizard.buildAccount(account);//1 . 创建账户 , 策略设计模式
            account.wizard = wizardId;
            if (account.id == SipProfile.INVALID_ID) {//2 . 创建新的账户时为true
                // This account does not exists yet
                prefs.startEditing();
                wizard.setDefaultParams(prefs);
                prefs.endEditing();
                applyNewAccountDefault(account);
                //3 . 重点 : 将用户信息插入到数据库
                Uri uri = getContentResolver().insert(SipProfile.ACCOUNT_URI, account.getDbContentValues());
                // After insert, add filters for this wizard 
                account.id = ContentUris.parseId(uri);
                List<Filter> filters = wizard.getDefaultFilters(account);//null
                if (filters != null) {
                    for (Filter filter : filters) { //4 . 如果在设置模块的过滤器进行过配置 , 会执行以下代码
                        // Ensure the correct id if not done by the wizard
                        filter.account = (int) account.id;
                        getContentResolver().insert(SipManager.FILTER_URI, filter.getDbContentValues());
                    }
                }
                // Check if we have to restart
                needRestart = wizard.needRestart();

            } else {//2 . 更新账户信息时执行
                // TODO : should not be done there but if not we should add an
                // option to re-apply default params
                prefs.startEditing();
                wizard.setDefaultParams(prefs);
                prefs.endEditing();
                //3 . 重点 : 将账户信息更新到数据库中
                getContentResolver().update(ContentUris.withAppendedId(SipProfile.ACCOUNT_ID_URI_BASE, account.id), account.getDbContentValues(), null, null);
            }

            // Mainly if global preferences were changed, we have to restart sip stack 
            if (needRestart) { 
                //5 . 在创建账户或更新账户时 , 修改过一些设置 , 会执行以下代码 , 进行重启协议栈
                // 协议栈会更新配置信息
                Intent intent = new Intent(SipManager.ACTION_SIP_REQUEST_RESTART);
                sendBroadcast(intent);
            }
        }
    }

    /**
     * 在上面的[1 . 创建账户]中 , 会使用该类
     * 因为在选择向导的时候选择的是Basic  , 因此这里选择Basic类进行分析
     */
    public class Basic extends BaseImplementation {
        /**
         * 创建一个账户
         */
        public SipProfile buildAccount(SipProfile account) {
            Log.d(THIS_FILE, "begin of save ....");
            account.display_name = accountDisplayName.getText().trim();//用户名

            String[] serverParts = accountServer.getText().split(":");//服务器
            account.acc_id = "<sip:" + SipUri.encodeUser(accountUserName.getText().trim()) + "@"+serverParts[0].trim()+">";
            String regUri = "sip:" + accountServer.getText();//
            account.reg_uri = regUri;
            account.proxies = new String[] { regUri } ;


            account.realm = "*";
            account.username = getText(accountUserName).trim();
            account.data = getText(accountPassword);
            account.scheme = SipProfile.CRED_SCHEME_DIGEST;
            account.datatype = SipProfile.CRED_DATA_PLAIN_PASSWD;
            //By default auto transport
            account.transport = SipProfile.TRANSPORT_UDP;
            return account;
        }
    }

    /**
     * 在上面[3 .  重点 : 将用户信息插入到数据库] 和 
     * [3 . 重点 : 将账户信息更新到数据库中]可以知道 ,
     * 最终的用户信息是保存到数据库中的 , 因此下面分析数据库
     */
    public class DBProvider extends ContentProvider {
        /**
         * 将用户信息插入到数据库
         */
        @Override
        public Uri insert(Uri uri, ContentValues initialValues){
            ... //省略 代码太多 , 看下重点代码

            case ACCOUNTS_STATUS_ID:
                long id = ContentUris.parseId(uri);
                synchronized (profilesStatus) {
                    SipProfileState ps = new SipProfileState();
                    if (profilesStatus.containsKey(id)) {
                        ContentValues currentValues = profilesStatus.get(id);
                        ps.createFromContentValue(currentValues);
                    }
                    ps.createFromContentValue(initialValues);
                    ContentValues cv = ps.getAsContentValue();
                    cv.put(SipProfileState.ACCOUNT_ID, id);
                    profilesStatus.put(id, cv);
                    Log.d(THIS_FILE, "Added " + cv);
                }
                // 1. 通知内容观察者有新的账户注册
                getContext().getContentResolver().notifyChange(uri, null);

            ...// 省略

            if (rowId >= 0) {
                // TODO : for inserted account register it here
                // 2. 重点 , 通知内容观察者有新的账户注册 , 并通过Uri携带注册账户的ID 
                // 后续可以通过该ID值 , 获取账户的状态信息等
                Uri retUri = ContentUris.withAppendedId(baseInsertedUri, rowId);
                getContext().getContentResolver().notifyChange(retUri, null);

                if (matched == ACCOUNTS || matched == ACCOUNTS_ID) {
                    broadcastAccountChange(rowId);// 3 . 重点通过广播通知账户信息发生改变
                }

                ... //省略

            }


        }

        /**
         * 更新用户数据
         */
        @Override
        public int update(Uri uri, ContentValues values, String where,String[] whereArgs) { 
            ...// 省略
            // 1 . 通知内容观察者有用户的信息发生改变
            getContext().getContentResolver().notifyChange(uri, null);

            long rowId = -1;
            if (matched == ACCOUNTS_ID || matched == ACCOUNTS_STATUS_ID) {
                rowId = ContentUris.parseId(uri); // 2. 获取账户的ID
            }
            if (rowId >= 0) {
                if (matched == ACCOUNTS_ID) {
                    // Don't broadcast if we only changed wizard or only changed
                    // priority
                    boolean doBroadcast = true;
                    if (values.size() == 1) {
                        if (values.containsKey(SipProfile.FIELD_WIZARD)) {
                            doBroadcast = false;
                        } else if (values.containsKey(SipProfile.FIELD_PRIORITY)) {
                            doBroadcast = false;
                        }
                    }
                    if (doBroadcast) {
                        broadcastAccountChange(rowId);// 2. 重点 通过广播通知账户信息发生改变
                    }
                } else if (matched == ACCOUNTS_STATUS_ID) {
                    broadcastRegistrationChange(rowId);
                }

                ... // 省略

             }
           }
        }   

        /**
         * 通过广播通知账户信息发生改变
         */
         private void broadcastAccountChange(long accountId) {
             Intent publishIntent = new Intent(SipManager.ACTION_SIP_ACCOUNT_CHANGED);// 1. 重点 广播的动作
             publishIntent.putExtra(SipProfile.FIELD_ID, accountId);
             getContext().sendBroadcast(publishIntent);
             BackupWrapper.getInstance(getContext()).dataChanged();
         }  
    }

    /**
     * 该内容观察者用来接收上面[1. 重点 广播的动作] 的广播 ,
     * 该内容观察者的注册是在SipService类中的registerBroadcasts()方法中进行的
     */
    public class DynamicReceiver4 extends BroadcastReceiver {
         /**
          * 处理账户信息发生改变的广播
          */
         private void onReceiveInternal(Context context, Intent intent, boolean isSticky) throws SameThreadException {
            ... // 省略 

            else if (action.equals(SipManager.ACTION_SIP_ACCOUNT_CHANGED)) { // 1. 处理用户信息发生改变的广播
                final long accountId = intent.getLongExtra(SipProfile.FIELD_ID, SipProfile.INVALID_ID);
                // Should that be threaded?
                if (accountId != SipProfile.INVALID_ID) {
                    // 2. 通过广播携带的账户ID , 获取到账户的实例
                    final SipProfile account = service.getAccount(accountId);
                    if (account != null) {
                        Log.d(THIS_FILE, "Enqueue set account registration");
                        // 3 . 重点 进行账户的注册 , 该service就是SipService
                        service.setAccountRegistration(account, account.active ? 1 : 0, true);
                    }
                }
            } 

            ... // 省略
         }
    }

    /**
     * 重点 所有的有关通话/信息的操作都是通过该服务来实现的
     */
    public class SipService extends Service {

        /**
         * 注册账户的方法
         */ 
        public boolean setAccountRegistration(SipProfile account, int renew,
            boolean forceReAdd) throws SameThreadException {
            boolean status = false;
            if (pjService != null) {
                //1. 重点 实际注册账户是通过PjsipService来实现的
                status = pjService.setAccountRegistration(account, renew,forceReAdd);
            }

            return status;
        }
    }

    /**
     * 重点 , 所有与pjsip库进行操作的方法 , 都是通过该类调用pjsua的native方法类实现的
     */
    public class PjSipService {

        /**
         * 进行账户向服务端注册的方法
         */
        public boolean setAccountRegistration(SipProfile account, int renew,
            boolean forceReAdd) throws SameThreadException {
            int status = -1;
            if (!created || account == null) {
                Log.e(THIS_FILE, "PJSIP is not started here, nothing can be done");
                return false;
            }
            if (account.id == SipProfile.INVALID_ID) {
                Log.w(THIS_FILE, "Trying to set registration on a deleted account");
                return false;
            }

            // 获取账户的状态信息
            SipProfileState profileState = getProfileState(account);

            // If local account -- Ensure we are not deleting, because this would be
            // invalid
            if (profileState.getWizard().equalsIgnoreCase(
                    WizardUtils.LOCAL_WIZARD_TAG)) {
                if (renew == 0) {
                    return false;
                }
            }

            // In case of already added, we have to act finely
            // If it's local we can just consider that we have to re-add account
            // since it will actually just touch the account with a modify
            if (profileState != null && profileState.isAddedToStack() && !profileState.getWizard().equalsIgnoreCase(WizardUtils.LOCAL_WIZARD_TAG)) {
                // The account is already there in accounts list
                service.getContentResolver().delete(ContentUris.withAppendedId(SipProfile.ACCOUNT_STATUS_URI,
                                account.id), null, null);
                Log.d(THIS_FILE,"Account already added to stack, remove and re-load or delete");
                if (renew == 1) {
                    if (forceReAdd) {// 先删除账户 , 然后重新进行添加账户
                        status = pjsua.acc_del(profileState.getPjsuaId());
                        addAccount(account); // 1. 重点 添加账户
                    } else {
                        // 设置账户的状态为在线 , 并重新进行注册
                        pjsua.acc_set_online_status(profileState.getPjsuaId(),getOnlineForStatus(service.getPresence()));
                        status = pjsua.acc_set_registration(profileState.getPjsuaId(), renew);
                    }
                } else {
                    // if(status == pjsuaConstants.PJ_SUCCESS && renew == 0) {
                    Log.d(THIS_FILE, "Delete account !!");
                    status = pjsua.acc_del(profileState.getPjsuaId());// 删除账户
                }
            } else {
                if (renew == 1) {
                    addAccount(account); // 2 .重点 添加账户
                } else {
                    Log.w(THIS_FILE, "Ask to unregister an unexisting account !!" + account.id);
                }

            }
            // PJ_SUCCESS = 0
            return status == 0;
        }

        /**
         * 重点 , 添加账户
         */
        public boolean addAccount(SipProfile profile) throws SameThreadException {
            int status = pjsuaConstants.PJ_FALSE;
            if (!created) {
                Log.e(THIS_FILE, "PJSIP is not started here, nothing can be done");
                return status == pjsuaConstants.PJ_SUCCESS;

            }
            PjSipAccount account = new PjSipAccount(profile);// 包装成PjSip账户
            account.applyExtraParams(service);// 设置额外的信息

            //账户状态实例 , 包含账户是否登录成功的状态
            SipProfileState currentAccountStatus = getProfileState(profile);
            account.cfg.setRegister_on_acc_add(pjsuaConstants.PJ_FALSE);

            if (currentAccountStatus.isAddedToStack()) {// 判断该账户是否曾经加载过
                pjsua.csipsimple_set_acc_user_data(// 曾经加载过
                        currentAccountStatus.getPjsuaId(), account.css_cfg);
                status = pjsua.acc_modify(currentAccountStatus.getPjsuaId(),
                        account.cfg);// 1. 重点 , 对账户的配置进行修改 ,并返回该账户的状态
                beforeAccountRegistration(currentAccountStatus.getPjsuaId(),
                        profile);// 先清除该账户在代理服务器的状态
                ContentValues cv = new ContentValues();
                cv.put(SipProfileState.ADDED_STATUS, status);// 2. 重点 , 设置该账户的状态
                //3 . 重点 , 讲该账户的状态更新到数据库中
                service.getContentResolver().update(ContentUris.withAppendedId(
                                SipProfile.ACCOUNT_STATUS_ID_URI_BASE, profile.id),cv, null, null);
                // 判断通用向导的类型 , 如果不是LOCAL ,则继续向下执行 , 我们选择的是BASIC
                if (!account.wizard.equalsIgnoreCase(WizardUtils.LOCAL_WIZARD_TAG)) {
                    // Re register //重新进行注册
                    if (status == pjsuaConstants.PJ_SUCCESS) {// 账户的配置修改成功
                        status = pjsua.acc_set_registration(
                                currentAccountStatus.getPjsuaId(), 1);// 则对账户设置注册
                        if (status == pjsuaConstants.PJ_SUCCESS) {// 账户设置注册成功
                            pjsua.acc_set_online_status(
                                    currentAccountStatus.getPjsuaId(), 1);// 则设置该账户为在线状态
                        }
                    }
                }
            } else {// 如果该账户没有注册过
                int[] accId = new int[1];
                if (account.wizard.equalsIgnoreCase(WizardUtils.LOCAL_WIZARD_TAG)) {// 设置向导选择的BASIC , 不是LOCAL , 不向下执行
                    // We already have local account by default
                    // For now consider we are talking about UDP one
                    // In the future local account should be set per transport
                    switch (account.transport) {
                    case SipProfile.TRANSPORT_UDP:
                        accId[0] = prefsWrapper.useIPv6() ? localUdp6AccPjId
                                : localUdpAccPjId;
                        break;
                    case SipProfile.TRANSPORT_TCP:
                        accId[0] = prefsWrapper.useIPv6() ? localTcp6AccPjId
                                : localTcpAccPjId;
                        break;
                    case SipProfile.TRANSPORT_TLS:
                        accId[0] = prefsWrapper.useIPv6() ? localTls6AccPjId
                                : localTlsAccPjId;
                        break;
                    default:
                        // By default use UDP
                        accId[0] = localUdpAccPjId;
                        break;
                    }

                    pjsua.csipsimple_set_acc_user_data(accId[0], account.css_cfg);
                    // TODO : use video cfg here
                    // nCfg.setVid_in_auto_show(pjsuaConstants.PJ_TRUE);
                    // nCfg.setVid_out_auto_transmit(pjsuaConstants.PJ_TRUE);
                    // status = pjsua.acc_modify(accId[0], nCfg);
                } else {
                    // TODO 执行此处的代码
                    // Cause of standard account different from local account :)
                    status = pjsua.acc_add(account.cfg, pjsuaConstants.PJ_FALSE,
                            accId);// 4. 进行账户的添加
                    pjsua.csipsimple_set_acc_user_data(accId[0], account.css_cfg);// 设置账户数据
                    beforeAccountRegistration(accId[0], profile);// 在对用户进行注册之前的操作,清除该账户的状态
                    pjsua.acc_set_registration(accId[0], 1);//重点 4. 对账户进行注册
                }

                if (status == pjsuaConstants.PJ_SUCCESS) {//5. 账户注册成功
                    SipProfileState ps = new SipProfileState(profile);// 获取该账户的Sip配置信息
                    ps.setAddedStatus(status);// 6. 设置注册的状态
                    ps.setPjsuaId(accId[0]);
                    service.getContentResolver().insert(
                            ContentUris.withAppendedId(
                                    SipProfile.ACCOUNT_STATUS_ID_URI_BASE,
                                    account.id), ps.getAsContentValue());// 7 .注册成功,并通知

                    pjsua.acc_set_online_status(accId[0], 1);// 设置该账户为在线状态
                }
            }

            return status == pjsuaConstants.PJ_SUCCESS;
        }
    }

    /**
     * UAStateReceiver , 该类重点 , 所有与pjsip交互的回调 , 都是通过此类完成的 ,
     * 相关的api可以参考文档中的pjsua_callback , 这里只关注注册的回调
     */
    public class UAStateReceiver{
            @Override
            public void on_reg_state(final int accountId) {
                lockCpu();
                pjService.service.getExecutor().execute(new SipRunnable() {
                    @Override
                    public void doRun() throws SameThreadException {
                        // Update java infos
                        // 1 . 更新账户的状态 , 又回到PjsipService中
                        pjService.updateProfileStateFromService(accountId);
                    }
                });
                unlockCpu();
            }
    }

    /**
     * 重点 , 所有与pjsip库进行操作的方法 , 都是通过该类调用pjsua的native方法类实现的
     */
    public class PjsipService{
            /**
             * Synchronize content provider backend from pjsip stack
             * 
             * @param pjsuaId the pjsua id of the account to synchronize
             * @throws SameThreadException
             */
            public void updateProfileStateFromService(int pjsuaId) throws SameThreadException {
                if (!created) {
                    return;
                }
                long accId = getAccountIdForPjsipId(service, pjsuaId);// 1. 获取注册的账号
                Log.d(THIS_FILE, "Update profile from service for " + pjsuaId + " aka in db " + accId);
                if (accId != SipProfile.INVALID_ID) { //2. 判断accId
                    int success = pjsuaConstants.PJ_FALSE;
                    pjsua_acc_info pjAccountInfo;
                    pjAccountInfo = new pjsua_acc_info();
                    success = pjsua.acc_get_info(pjsuaId, pjAccountInfo); // 3. 重点 , 从pjsip协议栈中获取账户信息
                    if (success == pjsuaConstants.PJ_SUCCESS && pjAccountInfo != null) {
                        ContentValues cv = new ContentValues();
                        // 4 . 设置用户的状态
                        try {
                            // Should be fine : status code are coherent with RFC
                            // status codes
                            cv.put(SipProfileState.STATUS_CODE, pjAccountInfo.getStatus().swigValue());
                        } catch (IllegalArgumentException e) {
                            cv.put(SipProfileState.STATUS_CODE,
                                    SipCallSession.StatusCode.INTERNAL_SERVER_ERROR);
                        }

                        cv.put(SipProfileState.STATUS_TEXT, pjStrToString(pjAccountInfo.getStatus_text()));
                        cv.put(SipProfileState.EXPIRES, pjAccountInfo.getExpires());

                        //5 . 更新用户的状态 
                        service.getContentResolver().update(
                                ContentUris.withAppendedId(SipProfile.ACCOUNT_STATUS_ID_URI_BASE, accId),
                                cv, null, null);

                        Log.d(THIS_FILE, "Profile state UP : " + cv);
                    }
                } else {
                    Log.e(THIS_FILE, "Trying to update not added account " + pjsuaId);
                }
            }

        /**
         * 1. 获取注册的账号
         */ 
        public static long getAccountIdForPjsipId(Context ctxt, int pjId) {
            long accId = SipProfile.INVALID_ID; //1 .设置accId
            // 2 . 查找账户
            Cursor c = ctxt.getContentResolver().query(SipProfile.ACCOUNT_STATUS_URI, null, null,
                    null, null);
            if (c != null) {
                try {
                    c.moveToFirst();
                    do {
                        int pjsuaId = c.getInt(c.getColumnIndex(SipProfileState.PJSUA_ID));
                        Log.d(THIS_FILE, "Found pjsua " + pjsuaId + " searching " + pjId);
                        if (pjsuaId == pjId) {
                            accId = c.getInt(c.getColumnIndex(SipProfileState.ACCOUNT_ID));
                            break;
                        }
                    } while (c.moveToNext());
                } catch (Exception e) {
                    Log.e(THIS_FILE, "Error on looping over sip profiles", e);
                } finally {
                    c.close();
                }
            }
            return accId;
        }
    }


    /**
     * 最后又回到DBProvider , 其实所有账户UI层面的状态 , 都是和DBProvider打交道 ,
     * MVC模式
     */ 
    public class DBProvider{
            @Override
            public int update(Uri uri, ContentValues values, String where, String[] whereArgs) {
                SQLiteDatabase db = mOpenHelper.getWritableDatabase();
                int count;
                String finalWhere;
                int matched = URI_MATCHER.match(uri);

                List<String> possibles = getPossibleFieldsForType(matched);
                checkSelection(possibles, where);

                // 1. 进行账户的更新 , 到此用户的注册创建注册流程已经走完 
                // 接下来 , 我们看UI层面是怎么更新用户的状态的
                switch (matched) {
                    case ACCOUNTS:
                        count = db.update(SipProfile.ACCOUNTS_TABLE_NAME, values, where, whereArgs);
                        break;
                    case ACCOUNTS_ID:
                        finalWhere = DatabaseUtilsCompat.concatenateWhere(SipProfile.FIELD_ID + " = " + ContentUris.parseId(uri), where);
                        count = db.update(SipProfile.ACCOUNTS_TABLE_NAME, values, finalWhere, whereArgs);
                        break;

                    // ... 只看和账户相关的更新                   

                    case ACCOUNTS_STATUS_ID:
                        long id = ContentUris.parseId(uri);
                        synchronized (profilesStatus){
                            SipProfileState ps = new SipProfileState();
                            if(profilesStatus.containsKey(id)) {
                                ContentValues currentValues = profilesStatus.get(id);
                                ps.createFromContentValue(currentValues);
                            }
                            ps.createFromContentValue(values);
                            ContentValues cv = ps.getAsContentValue();
                            cv.put(SipProfileState.ACCOUNT_ID, id);
                            profilesStatus.put(id, cv);
                            Log.d(THIS_FILE, "Updated "+cv);
                        }
                        count = 1;
                        break;
                    default:
                        throw new IllegalArgumentException(UNKNOWN_URI_LOG + uri);
                }

                //内容观察者 , 提示数据库发生改变
                getContext().getContentResolver().notifyChange(uri, null); 

                long rowId = -1;
                if (matched == ACCOUNTS_ID || matched == ACCOUNTS_STATUS_ID) {
                    rowId = ContentUris.parseId(uri); //获取更新账户的id
                }
                if (rowId >= 0) {
                    if (matched == ACCOUNTS_ID) {
                        // Don't broadcast if we only changed wizard or only changed priority
                        boolean doBroadcast = true;
                        if(values.size() == 1) {
                            if(values.containsKey(SipProfile.FIELD_WIZARD)) {
                                doBroadcast = false;
                            }else if(values.containsKey(SipProfile.FIELD_PRIORITY)) {
                                doBroadcast = false;
                            }
                        }
                        if(doBroadcast) {
                            broadcastAccountChange(rowId); // 发送广播 , 告知用户信息发生改变
                        }
                    } else if (matched == ACCOUNTS_STATUS_ID) {
                        broadcastRegistrationChange(rowId); // 发送广播 , 告知用户注册信息发生改变 , 主要更新AppWidget
                    }
                }

                return count;
            }
    }

    /**
     * 刚才说过 , UI账户状态的操作 , 都是和数据层面打交道 ,
     * 因此 , 这里用的是ContentResolver内容观察者
     */
    public class AccountsEditListFragment{
        @Override
        public void onResume() {
            super.onResume();
            if(statusObserver == null) {
                statusObserver = new AccountStatusContentObserver(mHandler);// 1 . 账户状态观察者
                getActivity().getContentResolver().registerContentObserver(SipProfile.ACCOUNT_STATUS_URI, true, statusObserver);
            }
            mAdapter.notifyDataSetChanged();
        }

        class AccountStatusContentObserver extends ContentObserver {

            public AccountStatusContentObserver(Handler h) {
                super(h);
            }

            public void onChange(boolean selfChange) {
                Log.d(THIS_FILE, "Accounts status.onChange( " + selfChange + ")");
                ((BaseAdapter) getListAdapter()).notifyDataSetChanged();// 2. 更新账户状态UI
            }
        }
    }

    /**
     * 更新UI
     */
    public class AccountsEditListAdapter{
        @Override
        public void bindView(View view, Context context, Cursor cursor) {
            super.bindView(view, context, cursor);

            AccountListItemViews tagView = (AccountListItemViews) view.getTag();
            if (tagView == null) {
                tagView = tagRowView(view);
            }

            // Get the view object and account object for the row
            final SipProfile account = new SipProfile(cursor);
            AccountRowTag tagIndicator = new AccountRowTag();
            tagIndicator.accountId = account.id;
            tagIndicator.activated = account.active;
            tagView.indicator.setTag(tagIndicator);

            tagView.indicator.setVisibility(draggable ? View.GONE : View.VISIBLE);
            tagView.grabber.setVisibility(draggable ? View.VISIBLE : View.GONE);

            // Get the status of this profile

            tagView.labelView.setText(account.display_name);
            // 1. 更新用户状态和颜色 , 到此用户的注册流程结束
            // Update status label and color
            if (account.active) {
                AccountStatusDisplay accountStatusDisplay = AccountListUtils.getAccountDisplay(context,
                        account.id);
                tagView.statusView.setText(accountStatusDisplay.statusLabel);
                tagView.labelView.setTextColor(accountStatusDisplay.statusColor);

                // Update checkbox selection
                tagView.activeCheckbox.setChecked(true);
                tagView.barOnOff.setImageResource(accountStatusDisplay.checkBoxIndicator);

            } else {
                tagView.statusView.setText(R.string.acct_inactive);
                tagView.labelView.setTextColor(mContext.getResources().getColor(
                        R.color.account_inactive));

                // Update checkbox selection
                tagView.activeCheckbox.setChecked(false);
                tagView.barOnOff.setImageResource(R.drawable.ic_indicator_off);

            }

            // Update account image
            final WizardInfo wizardInfos = WizardUtils.getWizardClass(account.wizard);
            if (wizardInfos != null) {
                tagView.activeCheckbox.setBackgroundResource(wizardInfos.icon);
            }
        }
    }



Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐