From 197fe02be4cc967678b8fa97a3f1880349426885 Mon Sep 17 00:00:00 2001 From: Sam Bosley Date: Fri, 6 Apr 2012 16:40:36 -0700 Subject: [PATCH] Sectioned headers in the assignment picker. booyah --- astrid/.classpath | 1 + astrid/libs/CWAC-SackOfViewsAdapter.jar | Bin 0 -> 2343 bytes .../astrid/actfm/EditPeopleControlSet.java | 217 ++++++---- astrid/res/layout/list_header.xml | 10 + astrid/res/values/strings-actfm.xml | 7 + astrid/res/values/styles.xml | 7 + .../commonsware/cwac/merge/MergeAdapter.java | 379 ++++++++++++++++++ .../cwac/merge/MergeSpinnerAdapter.java | 102 +++++ 8 files changed, 637 insertions(+), 86 deletions(-) create mode 100644 astrid/libs/CWAC-SackOfViewsAdapter.jar create mode 100644 astrid/res/layout/list_header.xml create mode 100644 astrid/src/com/commonsware/cwac/merge/MergeAdapter.java create mode 100644 astrid/src/com/commonsware/cwac/merge/MergeSpinnerAdapter.java diff --git a/astrid/.classpath b/astrid/.classpath index cc10d291f..a20a47fca 100644 --- a/astrid/.classpath +++ b/astrid/.classpath @@ -30,5 +30,6 @@ + diff --git a/astrid/libs/CWAC-SackOfViewsAdapter.jar b/astrid/libs/CWAC-SackOfViewsAdapter.jar new file mode 100644 index 0000000000000000000000000000000000000000..0b43a064f21dd21b1a72eac91d4b3ffbe6a171b4 GIT binary patch literal 2343 zcmai#3pkW%8^>Sg!8o5nmaLHTF=a(l7MUSu-_DlOj4{U9jBy$^47Db;w3tssLkH3% zBs7vRB2tcxLt|}>oGLRmm8Nf0WUr6i`@Np`a9#iZec#XhUf1(;g9-?W0RRX9M7#)g zz=jY71Ob;Hy%Tpf(LodDqWRSE)xc~U3qU6y`4Ifl37d23zj0&zX$@S~@fk&6S{ zrRxz4YH0LS0&1xDxT~ky!=&GQd@`!*plqap6YQ~*H;}=%(~tv>t1lpV$U!|kafN!2 znQjhvBC`v39(+c@A_dBpCzzinEt2#}P!B9g!G@i^WD!>%Tfg29uQSiTw=T%*2N51> zyw>smE2O_Fc&gCwFl>x}RKP!|6*f^LV*C;RVEJw<3)UYI91?`Zt$RU$ti6|Az;7e1yc_Dq(D-ImxPb9>47C~7uo zZ89K$#hV?P_;#g|H?&_x0buvPHtxyW!C{n7P(Tb8g7lBT1w-jUe5LCZa;7ag{!1metm^_#K(m$7f>+Y^+5tuRe5*A#!E3CX@e6R^WY1x)E+E31X zSCJ|?i37d8?-DJGsMp>}k>cB2S?F7})zsh*IcX{IT1$P9-N7b|yn&~2>D#3TRMgd* z*A%+P0zq?caOu?yk(rD6)Zuv3!kv}M>29t}ORu|_AaIMsNHxRL&97*xz?|)cMfq6a z5-v6uJ=7X{8T`}$ZAL)*N*@qOHO@LBmAou37@>XIC><>k-(1x%aMrj)Zj5FxSt8O1 zDL_oe`lO`Ta^#BPx+bl%XYW%mb619H)zNVY+VySH9W&|pYWVPA#mEdk%lc(!e%$Rk zD%|?hyzs}r5({DEF7C0^vdR6=D^zw1JbL07i0)zhUR$1fbPi2<3J-%(OoxZvFAlRS zCQ=v6W;6;>^g(CZM_3gD2s`Ql5&Iq~H)&Rr)147Edj$d&GqCP_2{W^?x-rgA@5s#H znl=|*1{1p;?(geCM1w5+A4a_!-N+lzvLo%=fc?z^lA&PlT;;Ar-!WjEj{XUWy^~A6FWfA(0 zpyId}`3J#&(1PZIaz_GdgsjIdm@()*l(%u^y6wGs1Dcb>Bd^S<=k!n9L@qqt#nFO~ z>)fg1#>iJtibf)#kh90=VJ$UFg{jxeZ%m<87S4x6?xvZ6R2sSPAMT(5PIfKF-A|t_ zifKsz3tT`&S9;xemTDjNQ#?c!r{=143OU-W*wpgMYP6>y^d+524&58*bAyI=VX+C+ zcS$a<^}tyu^+pk8E$(3PD~*rYPlVG3IoZX5WKHIM<3()`M>mhTKzk3J>CrRlJDh`= zqcPL4k3`T94zLIGtFo%6?FjFO5aq9Txb`RsE&NP0J9N$bw7=v>a=S_5xjsROCkw?b zCzW(nPb|r{Ioa&BvDX)6?NUkR{O+6xF`=Kt?_dWEA556n958Jn!}G~aS>O}7RpA=5dW|n0XBu-EQWEpF6~RL+ znfEljZ)BfPJu9U(7FyE&pYzkT>C@B9C3P{SSsiXdgUKn5)VQPlo4T`_Q|_&3uRPKB zv};Iry%Htbqv*l|E7_eob=8d9k;HDM3a+UCXaGYXXI}}@|D64-l>Ow`FMMuLK7M(D zt>MQz2zen5%tKb6>wNoy!ZqvWFkCgPMrpD>@4jx`fX0o*H^bLw`{&|!d9Pn!wpMd< zoPBeQeRcdDZdlVgvlva&3;kwQQxy@w{AX jF#I;O&AIi>_4Z#o*otCqP$A*fJRshs$PWM?K7aiWL<(`! literal 0 HcmV?d00001 diff --git a/astrid/plugin-src/com/todoroo/astrid/actfm/EditPeopleControlSet.java b/astrid/plugin-src/com/todoroo/astrid/actfm/EditPeopleControlSet.java index 43b88da6b..5d081320c 100644 --- a/astrid/plugin-src/com/todoroo/astrid/actfm/EditPeopleControlSet.java +++ b/astrid/plugin-src/com/todoroo/astrid/actfm/EditPeopleControlSet.java @@ -40,6 +40,7 @@ import android.widget.ListView; import android.widget.TextView; import android.widget.Toast; +import com.commonsware.cwac.merge.MergeAdapter; import com.timsu.astrid.R; import com.todoroo.andlib.data.TodorooCursor; import com.todoroo.andlib.service.Autowired; @@ -114,8 +115,6 @@ public class EditPeopleControlSet extends PopupControlSet { private final View assignedClear; - private final ArrayList listValues = new ArrayList(); - private final int loginRequestCode; private boolean assignedToMe = false; @@ -351,16 +350,17 @@ public class EditPeopleControlSet extends PopupControlSet { HashSet emails = new HashSet(); HashMap names = new HashMap(); + ArrayList coreUsers = new ArrayList(); + ArrayList listUsers = new ArrayList(); + ArrayList astridUsers = new ArrayList(); + int assignedIndex = 0; try { - if(t.getValue(Task.USER_ID) > 0) { - JSONObject user = new JSONObject(t.getValue(Task.USER)); - sharedPeople.add(0, user); - } - + ArrayList coreUsersJson = new ArrayList(); JSONObject myself = new JSONObject(); myself.put("id", Task.USER_ID_SELF); - sharedPeople.add(0, myself); + myself.put("picture", ActFmPreferenceService.thisUser().optString("picture")); + coreUsersJson.add(myself); boolean hasTags = t.getTransitory("tags") != null && ((HashSet)t.getTransitory("tags")).size() > 0; @@ -368,106 +368,154 @@ public class EditPeopleControlSet extends PopupControlSet { if (addUnassigned) { JSONObject unassigned = new JSONObject(); unassigned.put("id", Task.USER_ID_UNASSIGNED); - sharedPeople.add(1, unassigned); + unassigned.put("default_picture", R.drawable.icn_anyone); + coreUsersJson.add(unassigned); } - addAstridFriends(sharedPeople); - - // de-duplicate by user id and/or email - listValues.clear(); - for(int i = 0; i < sharedPeople.size(); i++) { - JSONObject person = sharedPeople.get(i); - if(person == null) - continue; - long id = person.optLong("id", -2); - if(id == ActFmPreferenceService.userId() || (id >= -1 && userIds.contains(id))) - continue; - userIds.add(id); - - String email = person.optString("email"); - if(!TextUtils.isEmpty(email) && emails.contains(email)) - continue; - emails.add(email); - - String name = person.optString("name"); - if(id == 0) - name = activity.getString(R.string.actfm_EPA_assign_me); - if (id == -1) - name = activity.getString(R.string.actfm_EPA_unassigned); - - AssignedToUser atu = new AssignedToUser(name, person); - listValues.add(atu); - if(names.containsKey(name)) { - AssignedToUser user = names.get(name); - if(user != null && user.user.has("email")) { - user.label += " (" + user.user.optString("email") + ")"; - names.put(name, null); - } - if(!TextUtils.isEmpty("email")) - atu.label += " (" + email + ")"; - } else if(TextUtils.isEmpty(name)) { - if(!TextUtils.isEmpty("email")) - atu.label = email; - else - listValues.remove(atu); - } else - names.put(name, atu); + if(t.getValue(Task.USER_ID) > 0) { + JSONObject user = new JSONObject(t.getValue(Task.USER)); + coreUsersJson.add(0, user); } - String assignedStr = t.getValue(Task.USER); - if (!TextUtils.isEmpty(assignedStr)) { - JSONObject assigned = new JSONObject(assignedStr); - long assignedId = assigned.optLong("id", -2); - String assignedEmail = assigned.optString("email"); - for (int i = 0; i < listValues.size(); i++) { - JSONObject user = listValues.get(i).user; - if (user != null) { - if (user.optLong("id") == assignedId || - (user.optString("email").equals(assignedEmail) && - !(TextUtils.isEmpty(assignedEmail)))) - assignedIndex = i; - } - } - } + ArrayList astridFriends = getAstridFriends(); + + // de-duplicate by user id and/or email + coreUsers = convertJsonUsersToAssignedUsers(coreUsersJson, userIds, emails, names); + listUsers = convertJsonUsersToAssignedUsers(sharedPeople, userIds, emails, names); + astridUsers = convertJsonUsersToAssignedUsers(astridFriends, userIds, emails, names); contactPickerUser = new AssignedToUser(activity.getString(R.string.actfm_EPA_choose_contact), new JSONObject().put("default_picture", R.drawable.icn_friends) - .put(CONTACT_CHOOSER_USER, true)); + .put(CONTACT_CHOOSER_USER, true)); int contactsIndex = addUnassigned ? 2 : 1; - listValues.add(contactsIndex, contactPickerUser); - if (assignedIndex >= contactsIndex) - assignedIndex++; + coreUsers.add(contactsIndex, contactPickerUser); for (AssignedChangedListener l : listeners) { if (l.shouldShowTaskRabbit()) { taskRabbitUser = new AssignedToUser(activity.getString(R.string.actfm_EPA_task_rabbit), new JSONObject().put("default_picture", R.drawable.task_rabbit_image)); int taskRabbitIndex = addUnassigned ? 3 : 2; - listValues.add(taskRabbitIndex, taskRabbitUser); + coreUsers.add(taskRabbitIndex, taskRabbitUser); if(l.didPostToTaskRabbit()){ assignedIndex = taskRabbitIndex; - } else if (assignedIndex >= taskRabbitIndex) { - assignedIndex++; } } } + + if (assignedIndex == 0) { + assignedIndex = findAssignedIndex(t, coreUsers, listUsers, astridUsers); + } + } catch (JSONException e) { exceptionService.reportError("json-reading-data", e); } selected = assignedIndex; - final AssignedUserAdapter usersAdapter = new AssignedUserAdapter(activity, listValues); + final MergeAdapter mergeAdapter = new MergeAdapter(); + AssignedUserAdapter coreUserAdapter = new AssignedUserAdapter(activity, coreUsers, 0); + AssignedUserAdapter listUserAdapter = new AssignedUserAdapter(activity, listUsers, coreUserAdapter.getCount() + 1); + int offsetForAstridUsers = listUserAdapter.getCount() > 0 ? 2 : 1; + AssignedUserAdapter astridUserAdapter = new AssignedUserAdapter(activity, astridUsers, coreUserAdapter.getCount() + listUserAdapter.getCount() + offsetForAstridUsers); + + LayoutInflater inflater = activity.getLayoutInflater(); + TextView header1 = (TextView) inflater.inflate(R.layout.list_header, null); + header1.setText(R.string.actfm_EPA_assign_header_members); + TextView header2 = (TextView) inflater.inflate(R.layout.list_header, null); + header2.setText(R.string.actfm_EPA_assign_header_friends); + + mergeAdapter.addAdapter(coreUserAdapter); + if (listUserAdapter.getCount() > 0) { + mergeAdapter.addView(header1); + mergeAdapter.addAdapter(listUserAdapter); + } + if (astridUserAdapter.getCount() > 0) { + mergeAdapter.addView(header2); + mergeAdapter.addAdapter(astridUserAdapter); + } + activity.runOnUiThread(new Runnable() { @Override public void run() { - assignedList.setAdapter(usersAdapter); + assignedList.setAdapter(mergeAdapter); assignedList.setItemChecked(selected, true); refreshDisplayView(); } }); } - private void addAstridFriends(ArrayList sharedPeople) { + @SuppressWarnings("nls") + private ArrayList convertJsonUsersToAssignedUsers(ArrayList jsonUsers, + HashSet userIds, HashSet emails, HashMap names) { + ArrayList users = new ArrayList(); + for(int i = 0; i < jsonUsers.size(); i++) { + JSONObject person = jsonUsers.get(i); + if(person == null) + continue; + long id = person.optLong("id", -2); + if(id == ActFmPreferenceService.userId() || (id >= -1 && userIds.contains(id))) + continue; + userIds.add(id); + + String email = person.optString("email"); + if(!TextUtils.isEmpty(email) && emails.contains(email)) + continue; + emails.add(email); + + String name = person.optString("name"); + if(id == 0) + name = activity.getString(R.string.actfm_EPA_assign_me); + if (id == -1) + name = activity.getString(R.string.actfm_EPA_unassigned); + + AssignedToUser atu = new AssignedToUser(name, person); + users.add(atu); + if(names.containsKey(name)) { + AssignedToUser user = names.get(name); + if(user != null && user.user.has("email")) { + user.label += " (" + user.user.optString("email") + ")"; + names.put(name, null); + } + if(!TextUtils.isEmpty("email")) + atu.label += " (" + email + ")"; + } else if(TextUtils.isEmpty(name)) { + if(!TextUtils.isEmpty("email")) + atu.label = email; + else + users.remove(atu); + } else + names.put(name, atu); + } + return users; + } + + @SuppressWarnings("nls") + private int findAssignedIndex(Task t, ArrayList... userLists) throws JSONException { + String assignedStr = t.getValue(Task.USER); + if (!TextUtils.isEmpty(assignedStr)) { + JSONObject assigned = new JSONObject(assignedStr); + long assignedId = assigned.optLong("id", -2); + String assignedEmail = assigned.optString("email"); + + int index = 0; + for (ArrayList userList : userLists) { + for (int i = 0; i < userList.size(); i++) { + JSONObject user = userList.get(i).user; + if (user != null) { + if (user.optLong("id") == assignedId || + (user.optString("email").equals(assignedEmail) && + !(TextUtils.isEmpty(assignedEmail)))) + return index; + } + index++; + } + index++; // Add one for headers separating user lists + } + } + return 0; + } + + private ArrayList getAstridFriends() { + ArrayList astridFriends = new ArrayList(); TodorooCursor users = userDao.query(Query.select(User.PROPERTIES).orderBy(Order.asc(User.NAME))); try { User user = new User(); @@ -476,7 +524,7 @@ public class EditPeopleControlSet extends PopupControlSet { JSONObject userJson = new JSONObject(); try { ActFmSyncService.JsonHelper.jsonFromUser(userJson, user); - sharedPeople.add(userJson); + astridFriends.add(userJson); } catch (JSONException e) { // Ignored } @@ -484,14 +532,18 @@ public class EditPeopleControlSet extends PopupControlSet { } finally { users.close(); } + return astridFriends; } private class AssignedUserAdapter extends ArrayAdapter { - public AssignedUserAdapter(Context context, ArrayList people) { + private final int positionOffset; + + public AssignedUserAdapter(Context context, ArrayList people, int positionOffset) { super(context, R.layout.assigned_adapter_row, people); + this.positionOffset = positionOffset; } @SuppressWarnings("nls") @@ -501,21 +553,14 @@ public class EditPeopleControlSet extends PopupControlSet { convertView = activity.getLayoutInflater().inflate(R.layout.assigned_adapter_row, parent, false); CheckedTextView ctv = (CheckedTextView) convertView.findViewById(android.R.id.text1); super.getView(position, ctv, parent); - if (assignedList.getCheckedItemPosition() == position) { + if (assignedList.getCheckedItemPosition() == position + positionOffset) { ctv.setChecked(true); } else { ctv.setChecked(false); } AsyncImageView image = (AsyncImageView) convertView.findViewById(R.id.person_image); image.setDefaultImageResource(R.drawable.icn_default_person_image); - if (position == 0) { - image.setUrl(ActFmPreferenceService.thisUser().optString("picture")); - } else if (position == 1) { - image.setUrl(""); - image.setDefaultImageResource(R.drawable.icn_anyone); - } else { - image.setUrl(getItem(position).user.optString("picture")); - } + image.setUrl(getItem(position).user.optString("picture")); if (getItem(position).user.optInt("default_picture", 0) > 0) { image.setDefaultImageResource(getItem(position).user.optInt("default_picture")); } diff --git a/astrid/res/layout/list_header.xml b/astrid/res/layout/list_header.xml new file mode 100644 index 000000000..10f6ab8ca --- /dev/null +++ b/astrid/res/layout/list_header.xml @@ -0,0 +1,10 @@ + + \ No newline at end of file diff --git a/astrid/res/values/strings-actfm.xml b/astrid/res/values/strings-actfm.xml index c2256c781..0015d6ecd 100644 --- a/astrid/res/values/strings-actfm.xml +++ b/astrid/res/values/strings-actfm.xml @@ -169,6 +169,12 @@ Help me get this done! + + List Members + + + Astrid Friends + Create a shared tag? @@ -199,6 +205,7 @@ Log in Make private + diff --git a/astrid/res/values/styles.xml b/astrid/res/values/styles.xml index 5c99490fa..1881b52da 100644 --- a/astrid/res/values/styles.xml +++ b/astrid/res/values/styles.xml @@ -329,6 +329,13 @@ bold ?attr/asTextColor + + diff --git a/astrid/src/com/commonsware/cwac/merge/MergeAdapter.java b/astrid/src/com/commonsware/cwac/merge/MergeAdapter.java new file mode 100644 index 000000000..846299d17 --- /dev/null +++ b/astrid/src/com/commonsware/cwac/merge/MergeAdapter.java @@ -0,0 +1,379 @@ +/*** + Copyright (c) 2008-2009 CommonsWare, LLC + Portions (c) 2009 Google, Inc. + + 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.commonsware.cwac.merge; + +import android.database.DataSetObserver; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.ListAdapter; +import android.widget.SectionIndexer; +import java.util.ArrayList; +import java.util.List; +import com.commonsware.cwac.sacklist.SackOfViewsAdapter; + +/** + * Adapter that merges multiple child adapters and views + * into a single contiguous whole. + * + * Adapters used as pieces within MergeAdapter must + * have view type IDs monotonically increasing from 0. Ideally, + * adapters also have distinct ranges for their row ids, as + * returned by getItemId(). + * + */ +public class MergeAdapter extends BaseAdapter implements SectionIndexer { + protected ArrayList pieces=new ArrayList(); + + /** + * Stock constructor, simply chaining to the superclass. + */ + public MergeAdapter() { + super(); + } + + /** + * Adds a new adapter to the roster of things to appear + * in the aggregate list. + * @param adapter Source for row views for this section + */ + public void addAdapter(ListAdapter adapter) { + pieces.add(adapter); + adapter.registerDataSetObserver(new CascadeDataSetObserver()); + } + + /** + * Adds a new View to the roster of things to appear + * in the aggregate list. + * @param view Single view to add + */ + public void addView(View view) { + addView(view, false); + } + + /** + * Adds a new View to the roster of things to appear + * in the aggregate list. + * @param view Single view to add + * @param enabled false if views are disabled, true if enabled + */ + public void addView(View view, boolean enabled) { + ArrayList list=new ArrayList(1); + + list.add(view); + + addViews(list, enabled); + } + + /** + * Adds a list of views to the roster of things to appear + * in the aggregate list. + * @param views List of views to add + */ + public void addViews(List views) { + addViews(views, false); + } + + /** + * Adds a list of views to the roster of things to appear + * in the aggregate list. + * @param views List of views to add + * @param enabled false if views are disabled, true if enabled + */ + public void addViews(List views, boolean enabled) { + if (enabled) { + addAdapter(new EnabledSackAdapter(views)); + } + else { + addAdapter(new SackOfViewsAdapter(views)); + } + } + + /** + * Get the data item associated with the specified + * position in the data set. + * @param position Position of the item whose data we want + */ + @Override + public Object getItem(int position) { + for (ListAdapter piece : pieces) { + int size=piece.getCount(); + + if (position sections=new ArrayList(); + + for (ListAdapter piece : pieces) { + if (piece instanceof SectionIndexer) { + Object[] curSections=((SectionIndexer)piece).getSections(); + + if (curSections!=null) { + for (Object section : curSections) { + sections.add(section); + } + } + } + } + + if (sections.size()==0) { + return(null); + } + + return(sections.toArray(new Object[0])); + } + + private static class EnabledSackAdapter extends SackOfViewsAdapter { + public EnabledSackAdapter(List views) { + super(views); + } + + @Override + public boolean areAllItemsEnabled() { + return(true); + } + + @Override + public boolean isEnabled(int position) { + return(true); + } + } + + private class CascadeDataSetObserver extends DataSetObserver { + @Override + public void onChanged() { + notifyDataSetChanged(); + } + + @Override + public void onInvalidated() { + notifyDataSetInvalidated(); + } + } +} \ No newline at end of file diff --git a/astrid/src/com/commonsware/cwac/merge/MergeSpinnerAdapter.java b/astrid/src/com/commonsware/cwac/merge/MergeSpinnerAdapter.java new file mode 100644 index 000000000..b1f059add --- /dev/null +++ b/astrid/src/com/commonsware/cwac/merge/MergeSpinnerAdapter.java @@ -0,0 +1,102 @@ +package com.commonsware.cwac.merge; + +import java.util.List; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ListAdapter; +import android.widget.SpinnerAdapter; + +/** + * Adapter that merges multiple child adapters into a single + * contiguous whole to be consumed by a Spinner. + * + * Adapters used as pieces within MergeSpinnerAdapter must + * have view type IDs monotonically increasing from 0. + * Ideally, adapters also have distinct ranges for their row + * ids, as returned by getItemId(). + * + * All Adapters used as pieces within MergeSpinnerAdapter + * must be properly-configured implementations of + * SpinnerAdapter (e.g., ArrayAdapter, CursorAdapter). + */ +public class MergeSpinnerAdapter extends MergeAdapter { + /** + * Stock constructor, simply chaining to the superclass. + */ + public MergeSpinnerAdapter() { + super(); + } + + /* + * Returns the drop-down View for a given position, by + * iterating over the pieces. Assumes that all pieces are + * implementations of SpinnerAdapter. + * + * @see android.widget.BaseAdapter#getDropDownView(int, + * android.view.View, android.view.ViewGroup) + */ + public View getDropDownView(int position, View convertView, + ViewGroup parent) { + for (ListAdapter piece : pieces) { + int size=piece.getCount(); + + if (position views) { + throw new RuntimeException("Not supported with MergeSpinnerAdapter"); + } + + /** + * Adds a list of views to the roster of things to appear + * in the aggregate list. + * + * @param views + * List of views to add + * @param enabled + * false if views are disabled, true if enabled + */ + public void addViews(List views, boolean enabled) { + throw new RuntimeException("Not supported with MergeSpinnerAdapter"); + } +}