GroupedContactAdapter.java 21.9 KB
Newer Older
Alexander Ivanov's avatar
Alexander Ivanov committed
1 2
/**
 * Copyright (c) 2013, Redsolution LTD. All rights reserved.
3
 *
Alexander Ivanov's avatar
Alexander Ivanov committed
4 5
 * This file is part of Xabber project; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License, Version 3.
6
 *
Alexander Ivanov's avatar
Alexander Ivanov committed
7 8 9 10
 * Xabber is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
11
 *
Alexander Ivanov's avatar
Alexander Ivanov committed
12 13 14 15 16 17 18
 * You should have received a copy of the GNU General Public License,
 * along with this program. If not, see http://www.gnu.org/licenses/.
 */
package com.xabber.android.ui.adapter;

import android.app.Activity;
import android.content.Context;
19
import android.content.res.Resources;
20
import android.graphics.drawable.ColorDrawable;
21
import android.os.Build;
Alexander Ivanov's avatar
Alexander Ivanov committed
22 23 24
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
25
import android.widget.BaseAdapter;
Alexander Ivanov's avatar
Alexander Ivanov committed
26 27 28
import android.widget.ImageView;
import android.widget.TextView;

29
import com.xabber.android.R;
30
import com.xabber.android.data.SettingsManager;
31
import com.xabber.android.data.account.AccountItem;
Alexander Ivanov's avatar
Alexander Ivanov committed
32
import com.xabber.android.data.account.AccountManager;
33
import com.xabber.android.data.account.StatusMode;
34
import com.xabber.android.data.entity.BaseEntity;
35
import com.xabber.android.data.extension.avatar.AvatarManager;
Alexander Ivanov's avatar
Alexander Ivanov committed
36 37 38 39
import com.xabber.android.data.roster.AbstractContact;
import com.xabber.android.data.roster.Group;
import com.xabber.android.data.roster.GroupManager;
import com.xabber.android.data.roster.ShowOfflineMode;
40
import com.xabber.android.ui.ContactViewer;
Alexander Ivanov's avatar
Alexander Ivanov committed
41

42 43 44
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
45
import java.util.List;
46
import java.util.Locale;
47
import java.util.Map;
48

Alexander Ivanov's avatar
Alexander Ivanov committed
49 50
/**
 * Provide grouping implementation for the list of contacts.
51
 *
Alexander Ivanov's avatar
Alexander Ivanov committed
52 53
 * @author alexander.ivanov
 */
54
public abstract class GroupedContactAdapter extends BaseAdapter implements UpdatableAdapter {
Alexander Ivanov's avatar
Alexander Ivanov committed
55

56 57 58 59
    /**
     * List of groups used if contact has no groups.
     */
    static final Collection<Group> NO_GROUP_LIST;
Alexander Ivanov's avatar
Alexander Ivanov committed
60

61
    static final int TYPE_COUNT = 5;
62

63 64 65 66
    /**
     * View type used for contact items.
     */
    static final int TYPE_CONTACT = 0;
Alexander Ivanov's avatar
Alexander Ivanov committed
67

68 69 70 71
    /**
     * View type used for groups and accounts expanders.
     */
    static final int TYPE_GROUP = 1;
72
    static final int TYPE_ACCOUNT = 2;
73
    static final int TYPE_ACCOUNT_TOP_SEPARATOR = 3;
74
    static final int TYPE_ACCOUNT_BOTTOM_SEPARATOR = 4;
75

76
    static {
77
        Collection<Group> groups = new ArrayList<>(1);
78 79 80 81 82 83 84 85
        groups.add(new Group() {
            @Override
            public String getName() {
                return GroupManager.NO_GROUP;
            }
        });
        NO_GROUP_LIST = Collections.unmodifiableCollection(groups);
    }
Alexander Ivanov's avatar
Alexander Ivanov committed
86

87
    final ArrayList<BaseEntity> baseEntities = new ArrayList<>();
88 89 90 91
    /**
     * Layout inflater
     */
    private final LayoutInflater layoutInflater;
92
    private final Activity activity;
93 94
    private final int[] accountSubgroupColors;
    private final int activeChatsColor;
95
    private final OnClickListener onClickListener;
96
    private final ContactItemInflater contactItemInflater;
97
    private final int accountElevation;
98 99
    protected Locale locale = Locale.getDefault();
    private int[] accountGroupColors;
100

101
    public GroupedContactAdapter(Activity activity, OnClickListener onClickListener) {
102 103
        this.activity = activity;

104
        layoutInflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
105

106 107
        Resources resources = activity.getResources();

108

109
        accountGroupColors = resources.getIntArray(R.array.account_200);
110
        accountSubgroupColors = resources.getIntArray(R.array.account_50);
111
        activeChatsColor = resources.getColor(R.color.contact_list_active_chats_group_background);
112

113 114
        contactItemInflater = new ContactItemInflater(activity);

115 116
        accountElevation = activity.getResources().getDimensionPixelSize(R.dimen.account_group_elevation);

117
        this.onClickListener = onClickListener;
118
    }
Alexander Ivanov's avatar
Alexander Ivanov committed
119

120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
    @Override
    public void onChange() {
        notifyDataSetChanged();
    }

    @Override
    public int getCount() {
        return baseEntities.size();
    }

    @Override
    public Object getItem(int position) {
        return baseEntities.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

140 141
    @Override
    public int getViewTypeCount() {
142
        return TYPE_COUNT;
143
    }
Alexander Ivanov's avatar
Alexander Ivanov committed
144

145 146 147
    @Override
    public int getItemViewType(int position) {
        Object object = getItem(position);
148
        if (object instanceof AbstractContact) {
149
            return TYPE_CONTACT;
150 151
        } else if (object instanceof AccountConfiguration) {
            return TYPE_ACCOUNT;
152
        } else if (object instanceof GroupConfiguration) {
153
            return TYPE_GROUP;
154 155
        } else if (object instanceof ContactListAdapter.AccountTopSeparator) {
            return TYPE_ACCOUNT_TOP_SEPARATOR;
156 157
        } else if (object instanceof ContactListAdapter.AccountBottomSeparator) {
            return TYPE_ACCOUNT_BOTTOM_SEPARATOR;
158
        } else {
159
            throw new IllegalStateException();
160
        }
161
    }
Alexander Ivanov's avatar
Alexander Ivanov committed
162

163 164
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
165 166
        switch (getItemViewType(position)) {
            case TYPE_CONTACT:
167
                return getContactView(position, convertView, parent);
168 169

            case TYPE_GROUP:
170
                return getGroupView(position, convertView, parent);
171

172 173
            case TYPE_ACCOUNT:
                return getAccountView(position, convertView, parent);
174

175 176
            case TYPE_ACCOUNT_TOP_SEPARATOR:
                return getView(convertView, parent, R.layout.account_group_item_top_separator);
177

178 179 180
            case TYPE_ACCOUNT_BOTTOM_SEPARATOR:
                return getAccountBottomSeparatorView(
                        getView(convertView, parent, R.layout.account_group_item_bottom_separator), position);
181

182 183 184 185
            default:
                throw new IllegalStateException();
        }
    }
186

187 188 189 190 191 192 193 194 195
    private View getView(View convertView, ViewGroup parent, int layoutId) {
        final View view;
        if (convertView == null) {
            view = layoutInflater.inflate(layoutId, parent, false);
        } else {
            view = convertView;
        }
        return view;
    }
196

197 198 199 200
    private View getAccountBottomSeparatorView(View view, int position) {
        final ContactListAdapter.AccountBottomSeparator accountBottomSeparator
                = (ContactListAdapter.AccountBottomSeparator) getItem(position);
        final int level = AccountManager.getInstance().getColorLevel(accountBottomSeparator.getAccount());
201

202 203
        View bottomLayer = view.findViewById(R.id.bottom_layer);
        View topLayer = view.findViewById(R.id.top_layer);
204

205 206
        bottomLayer.setBackgroundDrawable(new ColorDrawable(accountSubgroupColors[level]));
        topLayer.setBackgroundDrawable(new ColorDrawable(accountSubgroupColors[level]));
207

208
        StatusMode statusMode = AccountManager.getInstance().getAccount(accountBottomSeparator.getAccount()).getDisplayStatusMode();
209

210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225
        View offlineShadowBottom = view.findViewById(R.id.offline_shadow_top);
        View offlineShadowTop = view.findViewById(R.id.offline_shadow_bottom);

        if (statusMode == StatusMode.unavailable || statusMode == StatusMode.connection) {
            offlineShadowBottom.setVisibility(View.VISIBLE);
            offlineShadowTop.setVisibility(View.VISIBLE);
        } else {
            offlineShadowBottom.setVisibility(View.GONE);
            offlineShadowTop.setVisibility(View.GONE);
        }

        return view;
    }

    private View getAccountView(int position, View convertView, ViewGroup parent) {
        final View view;
226
        final ContactListItemViewHolder viewHolder;
227
        if (convertView == null) {
228 229 230 231 232 233
            view = layoutInflater.inflate(R.layout.contact_list_item, parent, false);

            viewHolder = new ContactListItemViewHolder(view);
            viewHolder.outgoingMessageIndicator.setVisibility(View.GONE);
            viewHolder.color.setVisibility(View.INVISIBLE);
            viewHolder.largeClientIcon.setVisibility(View.GONE);
234

235 236 237
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                view.setElevation(accountElevation);
            }
238 239 240
            view.setTag(viewHolder);
        } else {
            view = convertView;
241
            viewHolder = (ContactListItemViewHolder) view.getTag();
242 243 244
        }

        final AccountConfiguration configuration = (AccountConfiguration) getItem(position);
245

246
        final String account = configuration.getAccount();
247

248 249 250
        viewHolder.statusIcon.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
251
                onClickListener.onAccountMenuClick(v, account);
252
            }
253
        });
254

255 256
        final int level = AccountManager.getInstance().getColorLevel(account);
        view.setBackgroundDrawable(new ColorDrawable(accountGroupColors[level]));
257

258 259
        viewHolder.name.setText(GroupManager.getInstance().getGroupName(account, configuration.getUser()));
        viewHolder.smallRightText.setText(configuration.getOnline() + "/" + configuration.getTotal());
260

261 262
        AccountItem accountItem = AccountManager.getInstance().getAccount(account);
        String statusText = accountItem.getStatusText().trim();
263

264 265 266
        if (statusText.isEmpty()) {
            statusText = activity.getString(accountItem.getDisplayStatusMode().getStringID());
        }
267

268
        viewHolder.secondLineMessage.setText(statusText);
269

270 271 272 273 274 275
        if (SettingsManager.contactsShowAvatars()) {
            viewHolder.avatar.setVisibility(View.VISIBLE);
            viewHolder.avatar.setImageDrawable(AvatarManager.getInstance().getAccountAvatar(account));
        } else {
            viewHolder.avatar.setVisibility(View.GONE);
        }
276 277 278 279

        viewHolder.avatar.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
280
                activity.startActivity(ContactViewer.createIntent(activity, account, GroupManager.IS_ACCOUNT));
281 282 283
            }
        });

284
        viewHolder.statusIcon.setImageLevel(accountItem.getDisplayStatusMode().getStatusLevel());
285

286 287 288 289 290 291 292 293
        ShowOfflineMode showOfflineMode = configuration.getShowOfflineMode();
        if (showOfflineMode == ShowOfflineMode.normal) {
            if (SettingsManager.contactsShowOffline()) {
                showOfflineMode = ShowOfflineMode.always;
            } else {
                showOfflineMode = ShowOfflineMode.never;
            }
        }
294

295 296
        viewHolder.smallRightIcon.setImageLevel(showOfflineMode.ordinal());

297

298
        StatusMode statusMode = AccountManager.getInstance().getAccount(configuration.getAccount()).getDisplayStatusMode();
299

300 301 302 303 304
        if (statusMode == StatusMode.unavailable || statusMode == StatusMode.connection) {
            viewHolder.offlineShadow.setVisibility(View.VISIBLE);
        } else {
            viewHolder.offlineShadow.setVisibility(View.GONE);
        }
305

306 307
        return view;
    }
308

309 310 311 312 313 314 315 316 317 318 319
    private View getGroupView(int position, View convertView, ViewGroup parent) {
        final View view;
        final GroupViewHolder viewHolder;
        if (convertView == null) {
            view = layoutInflater.inflate(R.layout.base_group_item, parent, false);
            viewHolder = new GroupViewHolder(view);
            view.setTag(viewHolder);
        } else {
            view = convertView;
            viewHolder = (GroupViewHolder) view.getTag();
        }
320

321 322
        final GroupConfiguration configuration = (GroupConfiguration) getItem(position);
        final int level = AccountManager.getInstance().getColorLevel(configuration.getAccount());
323

324 325
        final String name = GroupManager.getInstance()
                .getGroupName(configuration.getAccount(), configuration.getUser());
326

327

328 329 330 331 332 333
        viewHolder.indicator.setImageLevel(configuration.isExpanded() ? 1 : 0);
        viewHolder.groupOfflineIndicator.setImageLevel(configuration.getShowOfflineMode().ordinal());

        int color;

        viewHolder.groupOfflineIndicator.setVisibility(View.GONE);
334
        viewHolder.offlineShadow.setVisibility(View.GONE);
335 336 337 338 339 340 341 342 343 344

        if (configuration.getUser().equals(GroupManager.ACTIVE_CHATS)) {
            color = activeChatsColor;
            viewHolder.name.setText(name);
        } else {
            viewHolder.name.setText(name + " (" + configuration.getOnline()
                    + "/" + configuration.getTotal() + ")");

            color = accountSubgroupColors[level];
            viewHolder.groupOfflineIndicator.setVisibility(View.VISIBLE);
345

346
            AccountItem accountItem = AccountManager.getInstance().getAccount(configuration.getAccount());
347

348 349
            if (accountItem != null) {
                StatusMode statusMode = accountItem.getDisplayStatusMode();
350 351 352
                if (statusMode == StatusMode.unavailable || statusMode == StatusMode.connection) {
                    viewHolder.offlineShadow.setVisibility(View.VISIBLE);
                }
353
            }
354
        }
355

356
        view.setBackgroundDrawable(new ColorDrawable(color));
357

358 359
        return view;
    }
360

361
    private View getContactView(int position, View convertView, ViewGroup parent) {
362
        final AbstractContact abstractContact = (AbstractContact) getItem(position);
363
        return contactItemInflater.setUpContactView(convertView, parent, abstractContact);
364
    }
Alexander Ivanov's avatar
Alexander Ivanov committed
365

366 367 368 369 370 371 372
    /**
     * Gets or creates roster group in roster account.
     *
     * @param accountConfiguration
     * @param name
     * @return
     */
373
    protected GroupConfiguration getGroupConfiguration(AccountConfiguration accountConfiguration, String name) {
374 375
        GroupConfiguration groupConfiguration = accountConfiguration.getGroupConfiguration(name);
        if (groupConfiguration != null) {
376
            return groupConfiguration;
377
        }
378
        groupConfiguration = new GroupConfiguration(
379
                accountConfiguration.getAccount(), name, GroupManager.getInstance());
380 381 382
        accountConfiguration.addGroupConfiguration(groupConfiguration);
        return groupConfiguration;
    }
Alexander Ivanov's avatar
Alexander Ivanov committed
383

384 385 386 387 388 389 390
    /**
     * Gets or creates roster group in tree map.
     *
     * @param groups
     * @param name
     * @return
     */
391
    protected GroupConfiguration getGroupConfiguration(Map<String, GroupConfiguration> groups, String name) {
392
        GroupConfiguration groupConfiguration = groups.get(name);
393
        if (groupConfiguration != null) {
394
            return groupConfiguration;
395
        }
396
        groupConfiguration = new GroupConfiguration(GroupManager.NO_ACCOUNT, name, GroupManager.getInstance());
397 398 399
        groups.put(name, groupConfiguration);
        return groupConfiguration;
    }
Alexander Ivanov's avatar
Alexander Ivanov committed
400

401 402 403 404 405 406 407 408 409 410 411 412
    /**
     * Adds contact to specified group.
     *
     * @param abstractContact
     * @param group
     * @param online
     * @param accounts
     * @param groups
     * @param contacts
     * @param showAccounts
     * @param showGroups
     */
413
    protected void addContact(AbstractContact abstractContact, String group, boolean online,
414 415
        Map<String, AccountConfiguration> accounts, Map<String, GroupConfiguration> groups,
        List<AbstractContact> contacts, boolean showAccounts, boolean showGroups) {
416 417 418 419
        if (showAccounts) {
            final String account = abstractContact.getAccount();
            final AccountConfiguration accountConfiguration;
            accountConfiguration = accounts.get(account);
420
            if (accountConfiguration == null) {
421
                return;
422
            }
423
            if (showGroups) {
424 425
                GroupConfiguration groupConfiguration
                        = getGroupConfiguration(accountConfiguration, group);
426 427
                if (accountConfiguration.isExpanded()) {
                    groupConfiguration.setNotEmpty();
428
                    if (groupConfiguration.isExpanded()) {
429
                        groupConfiguration.addAbstractContact(abstractContact);
430
                    }
431 432 433
                }
                groupConfiguration.increment(online);
            } else {
434
                if (accountConfiguration.isExpanded()) {
435
                    accountConfiguration.addAbstractContact(abstractContact);
436
                }
437 438 439 440
            }
            accountConfiguration.increment(online);
        } else {
            if (showGroups) {
441
                GroupConfiguration groupConfiguration = getGroupConfiguration(groups, group);
442
                groupConfiguration.setNotEmpty();
443
                if (groupConfiguration.isExpanded()) {
444
                    groupConfiguration.addAbstractContact(abstractContact);
445
                }
446 447 448 449 450 451
                groupConfiguration.increment(online);
            } else {
                contacts.add(abstractContact);
            }
        }
    }
Alexander Ivanov's avatar
Alexander Ivanov committed
452

453 454 455 456 457 458 459 460 461 462 463 464 465 466
    /**
     * Adds contact to there groups.
     *
     * @param abstractContact
     * @param online
     * @param accounts
     * @param groups
     * @param contacts
     * @param showAccounts
     * @param showGroups
     * @param showOffline
     * @return whether contact is visible.
     */
    protected boolean addContact(AbstractContact abstractContact,
467 468 469
                                 boolean online, Map<String, AccountConfiguration> accounts,
                                 Map<String, GroupConfiguration> groups,
                                 List<AbstractContact> contacts, boolean showAccounts,
470 471 472 473 474
                                 boolean showGroups, boolean showOffline) {
        boolean hasVisible = false;
        if (showAccounts) {
            final AccountConfiguration accountConfiguration;
            accountConfiguration = accounts.get(abstractContact.getAccount());
475 476 477
            if (accountConfiguration == null) {
                return false;
            }
478
            if (showGroups) {
479 480
                Collection<? extends Group> abstractGroups = abstractContact.getGroups();
                if (abstractGroups.size() == 0) {
481
                    abstractGroups = NO_GROUP_LIST;
482
                }
483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499
                for (Group abstractGroup : abstractGroups) {
                    GroupConfiguration groupConfiguration = getGroupConfiguration(
                            accountConfiguration, abstractGroup.getName());
                    if (online
                            || (groupConfiguration.getShowOfflineMode() == ShowOfflineMode.always)
                            || (accountConfiguration.getShowOfflineMode() == ShowOfflineMode.always && groupConfiguration
                            .getShowOfflineMode() == ShowOfflineMode.normal)
                            || (accountConfiguration.getShowOfflineMode() == ShowOfflineMode.normal
                            && groupConfiguration.getShowOfflineMode() == ShowOfflineMode.normal && showOffline)) {
                        // ............. group
                        // ......... | A | N | E
                        // ....... A | + | + | -
                        // account N | + | ? | -
                        // ....... E | + | - | -
                        hasVisible = true;
                        if (accountConfiguration.isExpanded()) {
                            groupConfiguration.setNotEmpty();
500 501 502
                            if (groupConfiguration.isExpanded()) {
                                groupConfiguration.addAbstractContact(abstractContact);
                            }
503 504 505 506 507
                        }
                    }
                    groupConfiguration.increment(online);
                }
            } else {
508
                if (online || (accountConfiguration.getShowOfflineMode() == ShowOfflineMode.always)
509 510
                        || (accountConfiguration.getShowOfflineMode() == ShowOfflineMode.normal && showOffline)) {
                    hasVisible = true;
511 512 513
                    if (accountConfiguration.isExpanded()) {
                        accountConfiguration.addAbstractContact(abstractContact);
                    }
514 515 516 517 518
                }
            }
            accountConfiguration.increment(online);
        } else {
            if (showGroups) {
519 520
                Collection<? extends Group> abstractGroups = abstractContact.getGroups();
                if (abstractGroups.size() == 0) {
521
                    abstractGroups = NO_GROUP_LIST;
522
                }
523
                for (Group abstractGroup : abstractGroups) {
524 525 526
                    GroupConfiguration groupConfiguration
                            = getGroupConfiguration(groups, abstractGroup.getName());
                    if (online || (groupConfiguration.getShowOfflineMode() == ShowOfflineMode.always)
527 528 529
                            || (groupConfiguration.getShowOfflineMode() == ShowOfflineMode.normal && showOffline)) {
                        groupConfiguration.setNotEmpty();
                        hasVisible = true;
530 531 532
                        if (groupConfiguration.isExpanded()) {
                            groupConfiguration.addAbstractContact(abstractContact);
                        }
533 534 535 536 537 538 539 540 541 542 543 544
                    }
                    groupConfiguration.increment(online);
                }
            } else {
                if (online || showOffline) {
                    hasVisible = true;
                    contacts.add(abstractContact);
                }
            }
        }
        return hasVisible;
    }
Alexander Ivanov's avatar
Alexander Ivanov committed
545

546 547 548 549
    /**
     * Sets whether group in specified account is expanded.
     */
    public void setExpanded(String account, String group, boolean expanded) {
550
        GroupManager.getInstance().setExpanded(account, group, expanded);
551 552
        onChange();
    }
Alexander Ivanov's avatar
Alexander Ivanov committed
553

554 555 556 557
    public interface OnClickListener {
        void onAccountMenuClick(View view, String account);
    }

558 559 560 561 562 563
    /**
     * Holder for views in contact list group.
     */
    private static class GroupViewHolder {
        final ImageView indicator;
        final TextView name;
564
        final ImageView groupOfflineIndicator;
565
        final ImageView offlineShadow;
Alexander Ivanov's avatar
Alexander Ivanov committed
566

567 568 569
        public GroupViewHolder(View view) {
            indicator = (ImageView) view.findViewById(R.id.indicator);
            name = (TextView) view.findViewById(R.id.name);
570
            groupOfflineIndicator = (ImageView) view.findViewById(R.id.group_offline_indicator);
571
            offlineShadow = (ImageView) view.findViewById(R.id.offline_shadow);
572 573
        }
    }
Alexander Ivanov's avatar
Alexander Ivanov committed
574 575

}