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