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