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;
17
18import android.app.Fragment;
19import android.content.ActivityNotFoundException;
20import android.content.Intent;
21import android.net.Uri;
22import android.os.Bundle;
23import android.view.LayoutInflater;
24import android.view.Menu;
25import android.view.MenuInflater;
26import android.view.MenuItem;
27import android.view.View;
28import android.view.View.OnLayoutChangeListener;
29import android.view.ViewGroup;
30import android.widget.ExpandableListAdapter;
31import android.widget.ExpandableListView;
32import android.widget.ExpandableListView.OnChildClickListener;
33
34import com.android.messaging.R;
35import com.android.messaging.datamodel.DataModel;
36import com.android.messaging.datamodel.MediaScratchFileProvider;
37import com.android.messaging.datamodel.binding.Binding;
38import com.android.messaging.datamodel.binding.BindingBase;
39import com.android.messaging.datamodel.data.PersonItemData;
40import com.android.messaging.datamodel.data.VCardContactItemData;
41import com.android.messaging.datamodel.data.PersonItemData.PersonItemDataListener;
42import com.android.messaging.util.Assert;
43import com.android.messaging.util.SafeAsyncTask;
44import com.android.messaging.util.UiUtils;
45import com.android.messaging.util.UriUtil;
46
47/**
48 * A fragment that shows the content of a VCard that contains one or more contacts.
49 */
50public class VCardDetailFragment extends Fragment implements PersonItemDataListener {
51    private final Binding<VCardContactItemData> mBinding =
52            BindingBase.createBinding(this);
53    private ExpandableListView mListView;
54    private VCardDetailAdapter mAdapter;
55    private Uri mVCardUri;
56
57    /**
58     * We need to persist the VCard in the scratch directory before letting the user view it.
59     * We save this Uri locally, so that if the user cancels the action and re-perform the add
60     * to contacts action we don't have to persist it again.
61     */
62    private Uri mScratchSpaceUri;
63
64    @Override
65    public void onCreate(final Bundle savedInstanceState) {
66        super.onCreate(savedInstanceState);
67        setHasOptionsMenu(true);
68    }
69
70    @Override
71    public View onCreateView(final LayoutInflater inflater, final ViewGroup container,
72            final Bundle savedInstanceState) {
73        Assert.notNull(mVCardUri);
74        final View view = inflater.inflate(R.layout.vcard_detail_fragment, container, false);
75        mListView = (ExpandableListView) view.findViewById(R.id.list);
76        mListView.addOnLayoutChangeListener(new OnLayoutChangeListener() {
77            @Override
78            public void onLayoutChange(final View v, final int left, final int top, final int right,
79                    final int bottom, final int oldLeft, final int oldTop, final int oldRight,
80                    final int oldBottom) {
81                mListView.setIndicatorBounds(mListView.getWidth() - getResources()
82                        .getDimensionPixelSize(R.dimen.vcard_detail_group_indicator_width),
83                        mListView.getWidth());
84            }
85        });
86        mListView.setOnChildClickListener(new OnChildClickListener() {
87            @Override
88            public boolean onChildClick(ExpandableListView expandableListView, View clickedView,
89                    int groupPosition, int childPosition, long childId) {
90                if (!(clickedView instanceof PersonItemView)) {
91                    return false;
92                }
93                final Intent intent = ((PersonItemView) clickedView).getClickIntent();
94                if (intent != null) {
95                    try {
96                        startActivity(intent);
97                    } catch (ActivityNotFoundException e) {
98                        return false;
99                    }
100                    return true;
101                }
102                return false;
103            }
104        });
105        mBinding.bind(DataModel.get().createVCardContactItemData(getActivity(), mVCardUri));
106        mBinding.getData().setListener(this);
107        return view;
108    }
109
110    @Override
111    public void onDestroy() {
112        super.onDestroy();
113        if (mBinding.isBound()) {
114            mBinding.unbind();
115        }
116        mListView.setAdapter((ExpandableListAdapter) null);
117    }
118
119    private boolean shouldShowAddToContactsItem() {
120        return mBinding.isBound() && mBinding.getData().hasValidVCard();
121    }
122
123    @Override
124    public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) {
125        super.onCreateOptionsMenu(menu, inflater);
126        inflater.inflate(R.menu.vcard_detail_fragment_menu, menu);
127        final MenuItem addToContactsItem = menu.findItem(R.id.action_add_contact);
128        addToContactsItem.setVisible(shouldShowAddToContactsItem());
129    }
130
131    @Override
132    public boolean onOptionsItemSelected(final MenuItem item) {
133        switch (item.getItemId()) {
134            case R.id.action_add_contact:
135                mBinding.ensureBound();
136                final Uri vCardUri = mBinding.getData().getVCardUri();
137
138                // We have to do things in the background in case we need to copy the vcard data.
139                new SafeAsyncTask<Void, Void, Uri>() {
140                    @Override
141                    protected Uri doInBackgroundTimed(final Void... params) {
142                        // We can't delete the persisted vCard file because we don't know when to
143                        // delete it, since the app that uses it (contacts, dialer) may start or
144                        // shut down at any point. Therefore, we rely on the system to clean up
145                        // the cache directory for us.
146                        return mScratchSpaceUri != null ? mScratchSpaceUri :
147                            UriUtil.persistContentToScratchSpace(vCardUri);
148                    }
149
150                    @Override
151                    protected void onPostExecute(final Uri result) {
152                        if (result != null) {
153                            mScratchSpaceUri = result;
154                            if (getActivity() != null) {
155                                MediaScratchFileProvider.addUriToDisplayNameEntry(
156                                        result, mBinding.getData().getDisplayName());
157                                UIIntents.get().launchSaveVCardToContactsActivity(getActivity(),
158                                        result);
159                            }
160                        }
161                    }
162                }.executeOnThreadPool();
163                return true;
164
165            default:
166                return super.onOptionsItemSelected(item);
167        }
168    }
169
170    public void setVCardUri(final Uri vCardUri) {
171        Assert.isTrue(!mBinding.isBound());
172        mVCardUri = vCardUri;
173    }
174
175    @Override
176    public void onPersonDataUpdated(final PersonItemData data) {
177        Assert.isTrue(data instanceof VCardContactItemData);
178        mBinding.ensureBound();
179        final VCardContactItemData vCardData = (VCardContactItemData) data;
180        Assert.isTrue(vCardData.hasValidVCard());
181        mAdapter = new VCardDetailAdapter(getActivity(), vCardData.getVCardResource().getVCards());
182        mListView.setAdapter(mAdapter);
183
184        // Expand the contact card if there's only one contact.
185        if (mAdapter.getGroupCount() == 1) {
186            mListView.expandGroup(0);
187        }
188        getActivity().invalidateOptionsMenu();
189    }
190
191    @Override
192    public void onPersonDataFailed(final PersonItemData data, final Exception exception) {
193        mBinding.ensureBound();
194        UiUtils.showToastAtBottom(R.string.failed_loading_vcard);
195        getActivity().finish();
196    }
197}
198