1 /* 2 * Copyright (C) 2014 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 17 package com.android.contacts.common.widget; 18 19 import android.annotation.SuppressLint; 20 import android.app.AlertDialog; 21 import android.app.Dialog; 22 import android.app.DialogFragment; 23 import android.content.Context; 24 import android.content.DialogInterface; 25 import android.os.Bundle; 26 import android.os.Handler; 27 import android.os.ResultReceiver; 28 import android.support.annotation.Nullable; 29 import android.telecom.PhoneAccount; 30 import android.telecom.PhoneAccountHandle; 31 import android.telecom.TelecomManager; 32 import android.text.TextUtils; 33 import android.view.LayoutInflater; 34 import android.view.View; 35 import android.view.ViewGroup; 36 import android.widget.ArrayAdapter; 37 import android.widget.CheckBox; 38 import android.widget.CompoundButton; 39 import android.widget.ImageView; 40 import android.widget.LinearLayout; 41 import android.widget.ListAdapter; 42 import android.widget.TextView; 43 import com.android.contacts.common.R; 44 import com.android.contacts.common.compat.PhoneAccountCompat; 45 import com.android.contacts.common.compat.PhoneNumberUtilsCompat; 46 import java.util.ArrayList; 47 import java.util.List; 48 49 /** 50 * Dialog that allows the user to select a phone accounts for a given action. Optionally provides 51 * the choice to set the phone account as default. 52 */ 53 public class SelectPhoneAccountDialogFragment extends DialogFragment { 54 55 private static final String ARG_TITLE_RES_ID = "title_res_id"; 56 private static final String ARG_CAN_SET_DEFAULT = "can_set_default"; 57 private static final String ARG_ACCOUNT_HANDLES = "account_handles"; 58 private static final String ARG_IS_DEFAULT_CHECKED = "is_default_checked"; 59 private static final String ARG_LISTENER = "listener"; 60 private static final String ARG_CALL_ID = "call_id"; 61 62 private List<PhoneAccountHandle> mAccountHandles; 63 private boolean mIsSelected; 64 private boolean mIsDefaultChecked; 65 private SelectPhoneAccountListener mListener; 66 SelectPhoneAccountDialogFragment()67 public SelectPhoneAccountDialogFragment() {} 68 69 /** 70 * Create new fragment instance with default title and no option to set as default. 71 * 72 * @param accountHandles The {@code PhoneAccountHandle}s available to select from. 73 * @param listener The listener for the results of the account selection. 74 */ newInstance( List<PhoneAccountHandle> accountHandles, SelectPhoneAccountListener listener, @Nullable String callId)75 public static SelectPhoneAccountDialogFragment newInstance( 76 List<PhoneAccountHandle> accountHandles, 77 SelectPhoneAccountListener listener, 78 @Nullable String callId) { 79 return newInstance( 80 R.string.select_account_dialog_title, false, accountHandles, listener, callId); 81 } 82 83 /** 84 * Create new fragment instance. This method also allows specifying a custom title and "set 85 * default" checkbox. 86 * 87 * @param titleResId The resource ID for the string to use in the title of the dialog. 88 * @param canSetDefault {@code true} if the dialog should include an option to set the selection 89 * as the default. False otherwise. 90 * @param accountHandles The {@code PhoneAccountHandle}s available to select from. 91 * @param listener The listener for the results of the account selection. 92 */ newInstance( int titleResId, boolean canSetDefault, List<PhoneAccountHandle> accountHandles, SelectPhoneAccountListener listener, @Nullable String callId)93 public static SelectPhoneAccountDialogFragment newInstance( 94 int titleResId, 95 boolean canSetDefault, 96 List<PhoneAccountHandle> accountHandles, 97 SelectPhoneAccountListener listener, 98 @Nullable String callId) { 99 ArrayList<PhoneAccountHandle> accountHandlesCopy = new ArrayList<>(); 100 if (accountHandles != null) { 101 accountHandlesCopy.addAll(accountHandles); 102 } 103 SelectPhoneAccountDialogFragment fragment = new SelectPhoneAccountDialogFragment(); 104 final Bundle args = new Bundle(); 105 args.putInt(ARG_TITLE_RES_ID, titleResId); 106 args.putBoolean(ARG_CAN_SET_DEFAULT, canSetDefault); 107 args.putParcelableArrayList(ARG_ACCOUNT_HANDLES, accountHandlesCopy); 108 args.putParcelable(ARG_LISTENER, listener); 109 args.putString(ARG_CALL_ID, callId); 110 fragment.setArguments(args); 111 fragment.setListener(listener); 112 return fragment; 113 } 114 setListener(SelectPhoneAccountListener listener)115 public void setListener(SelectPhoneAccountListener listener) { 116 mListener = listener; 117 } 118 119 @Override onSaveInstanceState(Bundle outState)120 public void onSaveInstanceState(Bundle outState) { 121 super.onSaveInstanceState(outState); 122 outState.putBoolean(ARG_IS_DEFAULT_CHECKED, mIsDefaultChecked); 123 } 124 125 @Override onCreateDialog(Bundle savedInstanceState)126 public Dialog onCreateDialog(Bundle savedInstanceState) { 127 int titleResId = getArguments().getInt(ARG_TITLE_RES_ID); 128 boolean canSetDefault = getArguments().getBoolean(ARG_CAN_SET_DEFAULT); 129 mAccountHandles = getArguments().getParcelableArrayList(ARG_ACCOUNT_HANDLES); 130 mListener = getArguments().getParcelable(ARG_LISTENER); 131 if (savedInstanceState != null) { 132 mIsDefaultChecked = savedInstanceState.getBoolean(ARG_IS_DEFAULT_CHECKED); 133 } 134 mIsSelected = false; 135 136 final DialogInterface.OnClickListener selectionListener = 137 new DialogInterface.OnClickListener() { 138 @Override 139 public void onClick(DialogInterface dialog, int which) { 140 mIsSelected = true; 141 PhoneAccountHandle selectedAccountHandle = mAccountHandles.get(which); 142 Bundle result = new Bundle(); 143 result.putParcelable( 144 SelectPhoneAccountListener.EXTRA_SELECTED_ACCOUNT_HANDLE, selectedAccountHandle); 145 result.putBoolean(SelectPhoneAccountListener.EXTRA_SET_DEFAULT, mIsDefaultChecked); 146 result.putString(SelectPhoneAccountListener.EXTRA_CALL_ID, getCallId()); 147 if (mListener != null) { 148 mListener.onReceiveResult(SelectPhoneAccountListener.RESULT_SELECTED, result); 149 } 150 } 151 }; 152 153 final CompoundButton.OnCheckedChangeListener checkListener = 154 new CompoundButton.OnCheckedChangeListener() { 155 @Override 156 public void onCheckedChanged(CompoundButton check, boolean isChecked) { 157 mIsDefaultChecked = isChecked; 158 } 159 }; 160 161 AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); 162 ListAdapter selectAccountListAdapter = 163 new SelectAccountListAdapter( 164 builder.getContext(), R.layout.select_account_list_item, mAccountHandles); 165 166 AlertDialog dialog = 167 builder 168 .setTitle(titleResId) 169 .setAdapter(selectAccountListAdapter, selectionListener) 170 .create(); 171 172 if (canSetDefault) { 173 // Generate custom checkbox view, lint suppressed since no appropriate parent (is dialog) 174 @SuppressLint("InflateParams") 175 LinearLayout checkboxLayout = 176 (LinearLayout) 177 LayoutInflater.from(builder.getContext()) 178 .inflate(R.layout.default_account_checkbox, null); 179 180 CheckBox cb = (CheckBox) checkboxLayout.findViewById(R.id.default_account_checkbox_view); 181 cb.setOnCheckedChangeListener(checkListener); 182 cb.setChecked(mIsDefaultChecked); 183 184 dialog.getListView().addFooterView(checkboxLayout); 185 } 186 187 return dialog; 188 } 189 190 @Override onCancel(DialogInterface dialog)191 public void onCancel(DialogInterface dialog) { 192 if (!mIsSelected && mListener != null) { 193 Bundle result = new Bundle(); 194 result.putString(SelectPhoneAccountListener.EXTRA_CALL_ID, getCallId()); 195 mListener.onReceiveResult(SelectPhoneAccountListener.RESULT_DISMISSED, result); 196 } 197 super.onCancel(dialog); 198 } 199 200 @Nullable getCallId()201 private String getCallId() { 202 return getArguments().getString(ARG_CALL_ID); 203 } 204 205 public static class SelectPhoneAccountListener extends ResultReceiver { 206 207 static final int RESULT_SELECTED = 1; 208 static final int RESULT_DISMISSED = 2; 209 210 static final String EXTRA_SELECTED_ACCOUNT_HANDLE = "extra_selected_account_handle"; 211 static final String EXTRA_SET_DEFAULT = "extra_set_default"; 212 static final String EXTRA_CALL_ID = "extra_call_id"; 213 SelectPhoneAccountListener()214 protected SelectPhoneAccountListener() { 215 super(new Handler()); 216 } 217 218 @Override onReceiveResult(int resultCode, Bundle resultData)219 protected void onReceiveResult(int resultCode, Bundle resultData) { 220 if (resultCode == RESULT_SELECTED) { 221 onPhoneAccountSelected( 222 resultData.getParcelable(EXTRA_SELECTED_ACCOUNT_HANDLE), 223 resultData.getBoolean(EXTRA_SET_DEFAULT), 224 resultData.getString(EXTRA_CALL_ID)); 225 } else if (resultCode == RESULT_DISMISSED) { 226 onDialogDismissed(resultData.getString(EXTRA_CALL_ID)); 227 } 228 } 229 onPhoneAccountSelected( PhoneAccountHandle selectedAccountHandle, boolean setDefault, @Nullable String callId)230 public void onPhoneAccountSelected( 231 PhoneAccountHandle selectedAccountHandle, boolean setDefault, @Nullable String callId) {} 232 onDialogDismissed(@ullable String callId)233 public void onDialogDismissed(@Nullable String callId) {} 234 } 235 236 private static class SelectAccountListAdapter extends ArrayAdapter<PhoneAccountHandle> { 237 238 private int mResId; 239 SelectAccountListAdapter( Context context, int resource, List<PhoneAccountHandle> accountHandles)240 SelectAccountListAdapter( 241 Context context, int resource, List<PhoneAccountHandle> accountHandles) { 242 super(context, resource, accountHandles); 243 mResId = resource; 244 } 245 246 @Override getView(int position, View convertView, ViewGroup parent)247 public View getView(int position, View convertView, ViewGroup parent) { 248 LayoutInflater inflater = 249 (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); 250 251 View rowView; 252 final ViewHolder holder; 253 254 if (convertView == null) { 255 // Cache views for faster scrolling 256 rowView = inflater.inflate(mResId, null); 257 holder = new ViewHolder(); 258 holder.labelTextView = (TextView) rowView.findViewById(R.id.label); 259 holder.numberTextView = (TextView) rowView.findViewById(R.id.number); 260 holder.imageView = (ImageView) rowView.findViewById(R.id.icon); 261 rowView.setTag(holder); 262 } else { 263 rowView = convertView; 264 holder = (ViewHolder) rowView.getTag(); 265 } 266 267 PhoneAccountHandle accountHandle = getItem(position); 268 PhoneAccount account = 269 getContext().getSystemService(TelecomManager.class).getPhoneAccount(accountHandle); 270 if (account == null) { 271 return rowView; 272 } 273 holder.labelTextView.setText(account.getLabel()); 274 if (account.getAddress() == null 275 || TextUtils.isEmpty(account.getAddress().getSchemeSpecificPart())) { 276 holder.numberTextView.setVisibility(View.GONE); 277 } else { 278 holder.numberTextView.setVisibility(View.VISIBLE); 279 holder.numberTextView.setText( 280 PhoneNumberUtilsCompat.createTtsSpannable( 281 account.getAddress().getSchemeSpecificPart())); 282 } 283 holder.imageView.setImageDrawable( 284 PhoneAccountCompat.createIconDrawable(account, getContext())); 285 return rowView; 286 } 287 288 private static final class ViewHolder { 289 290 TextView labelTextView; 291 TextView numberTextView; 292 ImageView imageView; 293 } 294 } 295 } 296