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  */
16 package com.android.messaging.ui;
17 
18 import android.app.Fragment;
19 import android.content.ActivityNotFoundException;
20 import android.content.Intent;
21 import android.net.Uri;
22 import android.os.Bundle;
23 import android.view.LayoutInflater;
24 import android.view.Menu;
25 import android.view.MenuInflater;
26 import android.view.MenuItem;
27 import android.view.View;
28 import android.view.View.OnLayoutChangeListener;
29 import android.view.ViewGroup;
30 import android.widget.ExpandableListAdapter;
31 import android.widget.ExpandableListView;
32 import android.widget.ExpandableListView.OnChildClickListener;
33 
34 import com.android.messaging.R;
35 import com.android.messaging.datamodel.DataModel;
36 import com.android.messaging.datamodel.MediaScratchFileProvider;
37 import com.android.messaging.datamodel.binding.Binding;
38 import com.android.messaging.datamodel.binding.BindingBase;
39 import com.android.messaging.datamodel.data.PersonItemData;
40 import com.android.messaging.datamodel.data.VCardContactItemData;
41 import com.android.messaging.datamodel.data.PersonItemData.PersonItemDataListener;
42 import com.android.messaging.util.Assert;
43 import com.android.messaging.util.SafeAsyncTask;
44 import com.android.messaging.util.UiUtils;
45 import com.android.messaging.util.UriUtil;
46 
47 /**
48  * A fragment that shows the content of a VCard that contains one or more contacts.
49  */
50 public 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
onCreate(final Bundle savedInstanceState)65     public void onCreate(final Bundle savedInstanceState) {
66         super.onCreate(savedInstanceState);
67         setHasOptionsMenu(true);
68     }
69 
70     @Override
onCreateView(final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState)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
onDestroy()111     public void onDestroy() {
112         super.onDestroy();
113         if (mBinding.isBound()) {
114             mBinding.unbind();
115         }
116         mListView.setAdapter((ExpandableListAdapter) null);
117     }
118 
shouldShowAddToContactsItem()119     private boolean shouldShowAddToContactsItem() {
120         return mBinding.isBound() && mBinding.getData().hasValidVCard();
121     }
122 
123     @Override
onCreateOptionsMenu(final Menu menu, final MenuInflater inflater)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
onOptionsItemSelected(final MenuItem item)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 
setVCardUri(final Uri vCardUri)170     public void setVCardUri(final Uri vCardUri) {
171         Assert.isTrue(!mBinding.isBound());
172         mVCardUri = vCardUri;
173     }
174 
175     @Override
onPersonDataUpdated(final PersonItemData data)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
onPersonDataFailed(final PersonItemData data, final Exception exception)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