1/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package com.android.messaging.ui.conversationsettings;
17
18import android.app.Activity;
19import android.app.AlertDialog;
20import android.app.Fragment;
21import android.content.Context;
22import android.content.DialogInterface;
23import android.content.Intent;
24import android.content.res.Resources;
25import android.database.Cursor;
26import android.media.RingtoneManager;
27import android.os.Bundle;
28import android.os.Parcelable;
29import android.provider.Settings;
30import android.view.LayoutInflater;
31import android.view.View;
32import android.view.ViewGroup;
33import android.widget.ArrayAdapter;
34import android.widget.BaseAdapter;
35import android.widget.ListView;
36import android.widget.TextView;
37
38import com.android.messaging.R;
39import com.android.messaging.datamodel.DataModel;
40import com.android.messaging.datamodel.binding.Binding;
41import com.android.messaging.datamodel.binding.BindingBase;
42import com.android.messaging.datamodel.data.ParticipantData;
43import com.android.messaging.datamodel.data.ParticipantListItemData;
44import com.android.messaging.datamodel.data.PeopleAndOptionsData;
45import com.android.messaging.datamodel.data.PeopleAndOptionsData.PeopleAndOptionsDataListener;
46import com.android.messaging.datamodel.data.PeopleOptionsItemData;
47import com.android.messaging.datamodel.data.PersonItemData;
48import com.android.messaging.ui.CompositeAdapter;
49import com.android.messaging.ui.PersonItemView;
50import com.android.messaging.ui.UIIntents;
51import com.android.messaging.ui.conversation.ConversationActivity;
52import com.android.messaging.util.Assert;
53
54import java.util.ArrayList;
55import java.util.List;
56
57/**
58 * Shows a list of participants of a conversation and displays options.
59 */
60public class PeopleAndOptionsFragment extends Fragment
61        implements PeopleAndOptionsDataListener, PeopleOptionsItemView.HostInterface {
62    private ListView mListView;
63    private OptionsListAdapter mOptionsListAdapter;
64    private PeopleListAdapter mPeopleListAdapter;
65    private final Binding<PeopleAndOptionsData> mBinding =
66            BindingBase.createBinding(this);
67
68    private static final int REQUEST_CODE_RINGTONE_PICKER = 1000;
69
70    @Override
71    public void onCreate(final Bundle savedInstanceState) {
72        super.onCreate(savedInstanceState);
73        mBinding.getData().init(getLoaderManager(), mBinding);
74    }
75
76    @Override
77    public View onCreateView(final LayoutInflater inflater, final ViewGroup container,
78            final Bundle savedInstanceState) {
79        final View view = inflater.inflate(R.layout.people_and_options_fragment, container, false);
80        mListView = (ListView) view.findViewById(android.R.id.list);
81        mPeopleListAdapter = new PeopleListAdapter(getActivity());
82        mOptionsListAdapter = new OptionsListAdapter();
83        final CompositeAdapter compositeAdapter = new CompositeAdapter(getActivity());
84        compositeAdapter.addPartition(new PeopleAndOptionsPartition(mOptionsListAdapter,
85                R.string.general_settings_title, false));
86        compositeAdapter.addPartition(new PeopleAndOptionsPartition(mPeopleListAdapter,
87                R.string.participant_list_title, true));
88        mListView.setAdapter(compositeAdapter);
89        return view;
90    }
91
92    @Override
93    public void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
94        super.onActivityResult(requestCode, resultCode, data);
95        if (resultCode == Activity.RESULT_OK && requestCode == REQUEST_CODE_RINGTONE_PICKER) {
96            final Parcelable pick = data.getParcelableExtra(
97                    RingtoneManager.EXTRA_RINGTONE_PICKED_URI);
98            final String pickedUri = pick == null ? "" : pick.toString();
99            mBinding.getData().setConversationNotificationSound(mBinding, pickedUri);
100        }
101    }
102
103    @Override
104    public void onDestroy() {
105        super.onDestroy();
106        mBinding.unbind();
107    }
108
109    public void setConversationId(final String conversationId) {
110        Assert.isTrue(getView() == null);
111        Assert.notNull(conversationId);
112        mBinding.bind(DataModel.get().createPeopleAndOptionsData(conversationId, getActivity(),
113                this));
114    }
115
116    @Override
117    public void onOptionsCursorUpdated(final PeopleAndOptionsData data, final Cursor cursor) {
118        Assert.isTrue(cursor == null || cursor.getCount() == 1);
119        mBinding.ensureBound(data);
120        mOptionsListAdapter.swapCursor(cursor);
121    }
122
123    @Override
124    public void onParticipantsListLoaded(final PeopleAndOptionsData data,
125            final List<ParticipantData> participants) {
126        mBinding.ensureBound(data);
127        mPeopleListAdapter.updateParticipants(participants);
128        final ParticipantData otherParticipant = participants.size() == 1 ?
129                participants.get(0) : null;
130        mOptionsListAdapter.setOtherParticipant(otherParticipant);
131    }
132
133    @Override
134    public void onOptionsItemViewClicked(final PeopleOptionsItemData item,
135            final boolean isChecked) {
136        switch (item.getItemId()) {
137            case PeopleOptionsItemData.SETTING_NOTIFICATION_ENABLED:
138                mBinding.getData().enableConversationNotifications(mBinding, isChecked);
139                break;
140
141            case PeopleOptionsItemData.SETTING_NOTIFICATION_SOUND_URI:
142                final Intent ringtonePickerIntent = UIIntents.get().getRingtonePickerIntent(
143                        getString(R.string.notification_sound_pref_title),
144                        item.getRingtoneUri(), Settings.System.DEFAULT_NOTIFICATION_URI,
145                        RingtoneManager.TYPE_NOTIFICATION);
146                startActivityForResult(ringtonePickerIntent, REQUEST_CODE_RINGTONE_PICKER);
147                break;
148
149            case PeopleOptionsItemData.SETTING_NOTIFICATION_VIBRATION:
150                mBinding.getData().enableConversationNotificationVibration(mBinding,
151                        isChecked);
152                break;
153
154            case PeopleOptionsItemData.SETTING_BLOCKED:
155                if (item.getOtherParticipant().isBlocked()) {
156                    mBinding.getData().setDestinationBlocked(mBinding, false);
157                    break;
158                }
159                final Resources res = getResources();
160                final Activity activity = getActivity();
161                new AlertDialog.Builder(activity)
162                        .setTitle(res.getString(R.string.block_confirmation_title,
163                                item.getOtherParticipant().getDisplayDestination()))
164                        .setMessage(res.getString(R.string.block_confirmation_message))
165                        .setNegativeButton(android.R.string.cancel, null)
166                        .setPositiveButton(android.R.string.ok,
167                                new DialogInterface.OnClickListener() {
168                            @Override
169                            public void onClick(DialogInterface arg0, int arg1) {
170                                mBinding.getData().setDestinationBlocked(mBinding, true);
171                                activity.setResult(ConversationActivity.FINISH_RESULT_CODE);
172                                activity.finish();
173                            }
174                        })
175                        .create()
176                        .show();
177                break;
178        }
179    }
180
181    /**
182     * A simple adapter that takes a conversation metadata cursor and binds
183     * PeopleAndOptionsItemViews to individual COLUMNS of the first cursor record. (Note
184     * that this is not a CursorAdapter because it treats individual columns of the cursor as
185     * separate options to display for the conversation, e.g. notification settings).
186     */
187    private class OptionsListAdapter extends BaseAdapter {
188        private Cursor mOptionsCursor;
189        private ParticipantData mOtherParticipantData;
190
191        public Cursor swapCursor(final Cursor newCursor) {
192            final Cursor oldCursor = mOptionsCursor;
193            if (newCursor != oldCursor) {
194                mOptionsCursor = newCursor;
195                notifyDataSetChanged();
196            }
197            return oldCursor;
198        }
199
200        public void setOtherParticipant(final ParticipantData participantData) {
201            if (mOtherParticipantData != participantData) {
202                mOtherParticipantData = participantData;
203                notifyDataSetChanged();
204            }
205        }
206
207        @Override
208        public int getCount() {
209            int count = PeopleOptionsItemData.SETTINGS_COUNT;
210            if (mOtherParticipantData == null) {
211                count--;
212            }
213            return mOptionsCursor == null ? 0 : count;
214        }
215
216        @Override
217        public Object getItem(final int position) {
218            return null;
219        }
220
221        @Override
222        public long getItemId(final int position) {
223            return 0;
224        }
225
226        @Override
227        public View getView(final int position, final View convertView, final ViewGroup parent) {
228            final PeopleOptionsItemView itemView;
229            if (convertView != null && convertView instanceof PeopleOptionsItemView) {
230                itemView = (PeopleOptionsItemView) convertView;
231            } else {
232                final LayoutInflater inflater = (LayoutInflater) getActivity()
233                        .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
234                itemView = (PeopleOptionsItemView)
235                        inflater.inflate(R.layout.people_options_item_view, parent, false);
236            }
237            mOptionsCursor.moveToFirst();
238            itemView.bind(mOptionsCursor, position, mOtherParticipantData,
239                    PeopleAndOptionsFragment.this);
240            return itemView;
241        }
242    }
243
244    /**
245     * An adapter that takes a list of ParticipantData and displays them as a list of
246     * ParticipantListItemViews.
247     */
248    private class PeopleListAdapter extends ArrayAdapter<ParticipantData> {
249        public PeopleListAdapter(final Context context) {
250            super(context, R.layout.people_list_item_view, new ArrayList<ParticipantData>());
251        }
252
253        public void updateParticipants(final List<ParticipantData> newList) {
254            clear();
255            addAll(newList);
256            notifyDataSetChanged();
257        }
258
259        @Override
260        public View getView(final int position, final View convertView, final ViewGroup parent) {
261            PersonItemView itemView;
262            final ParticipantData item = getItem(position);
263            if (convertView != null && convertView instanceof PersonItemView) {
264                itemView = (PersonItemView) convertView;
265            } else {
266                final LayoutInflater inflater = (LayoutInflater) getContext()
267                        .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
268                itemView = (PersonItemView) inflater.inflate(R.layout.people_list_item_view, parent,
269                        false);
270            }
271            final ParticipantListItemData itemData =
272                    DataModel.get().createParticipantListItemData(item);
273            itemView.bind(itemData);
274
275            // Any click on the row should have the same effect as clicking the avatar icon
276            final PersonItemView itemViewClosure = itemView;
277            itemView.setListener(new PersonItemView.PersonItemViewListener() {
278                @Override
279                public void onPersonClicked(final PersonItemData data) {
280                    itemViewClosure.performClickOnAvatar();
281                }
282
283                @Override
284                public boolean onPersonLongClicked(final PersonItemData data) {
285                    if (mBinding.isBound()) {
286                        final CopyContactDetailDialog dialog = new CopyContactDetailDialog(
287                                getContext(), data.getDetails());
288                        dialog.show();
289                        return true;
290                    }
291                    return false;
292                }
293            });
294            return itemView;
295        }
296    }
297
298    /**
299     * Represents a partition/section in the People & Options list (e.g. "general options" and
300     * "people in this conversation" sections).
301     */
302    private class PeopleAndOptionsPartition extends CompositeAdapter.Partition {
303        private final int mHeaderResId;
304        private final boolean mNeedDivider;
305
306        public PeopleAndOptionsPartition(final BaseAdapter adapter, final int headerResId,
307                final boolean needDivider) {
308            super(true /* showIfEmpty */, true /* hasHeader */, adapter);
309            mHeaderResId = headerResId;
310            mNeedDivider = needDivider;
311        }
312
313        @Override
314        public View getHeaderView(final View convertView, final ViewGroup parentView) {
315            View view = null;
316            if (convertView != null && convertView.getId() == R.id.people_and_options_header) {
317                view = convertView;
318            } else {
319                view = LayoutInflater.from(getActivity()).inflate(
320                        R.layout.people_and_options_section_header, parentView, false);
321            }
322            final TextView text = (TextView) view.findViewById(R.id.header_text);
323            final View divider = view.findViewById(R.id.divider);
324            text.setText(mHeaderResId);
325            divider.setVisibility(mNeedDivider ? View.VISIBLE : View.GONE);
326            return view;
327        }
328    }
329}
330