Home | History | Annotate | Download | only in dialog
      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 
     17 package com.android.contacts.dialog;
     18 
     19 import android.animation.Animator;
     20 import android.animation.AnimatorListenerAdapter;
     21 import android.app.Activity;
     22 import android.content.Context;
     23 import android.content.Intent;
     24 import android.content.SharedPreferences;
     25 import android.net.Uri;
     26 import android.os.Bundle;
     27 import android.preference.PreferenceManager;
     28 import android.telecom.PhoneAccount;
     29 import android.telecom.PhoneAccountHandle;
     30 import android.telecom.TelecomManager;
     31 import android.text.Editable;
     32 import android.text.InputFilter;
     33 import android.text.TextUtils;
     34 import android.text.TextWatcher;
     35 import android.util.Log;
     36 import android.view.View;
     37 import android.view.ViewGroup;
     38 import android.view.ViewTreeObserver;
     39 import android.view.inputmethod.InputMethodManager;
     40 import android.widget.AdapterView;
     41 import android.widget.ArrayAdapter;
     42 import android.widget.EditText;
     43 import android.widget.ListView;
     44 import android.widget.QuickContactBadge;
     45 import android.widget.TextView;
     46 
     47 import com.android.contacts.CallUtil;
     48 import com.android.contacts.ContactPhotoManager;
     49 import com.android.contacts.R;
     50 import com.android.contacts.compat.CompatUtils;
     51 import com.android.contacts.compat.PhoneAccountSdkCompat;
     52 import com.android.contacts.compat.telecom.TelecomManagerCompat;
     53 import com.android.contacts.util.UriUtils;
     54 import com.android.phone.common.animation.AnimUtils;
     55 
     56 import java.nio.charset.Charset;
     57 import java.util.ArrayList;
     58 import java.util.List;
     59 
     60 /**
     61  * Implements a dialog which prompts for a call subject for an outgoing call.  The dialog includes
     62  * a pop up list of historical call subjects.
     63  */
     64 public class CallSubjectDialog extends Activity {
     65     private static final String TAG = "CallSubjectDialog";
     66     private static final int CALL_SUBJECT_LIMIT = 16;
     67     private static final int CALL_SUBJECT_HISTORY_SIZE = 5;
     68 
     69     private static final int REQUEST_SUBJECT = 1001;
     70 
     71     public static final String PREF_KEY_SUBJECT_HISTORY_COUNT = "subject_history_count";
     72     public static final String PREF_KEY_SUBJECT_HISTORY_ITEM = "subject_history_item";
     73 
     74     /**
     75      * Activity intent argument bundle keys:
     76      */
     77     public static final String ARG_PHOTO_ID = "PHOTO_ID";
     78     public static final String ARG_PHOTO_URI = "PHOTO_URI";
     79     public static final String ARG_CONTACT_URI = "CONTACT_URI";
     80     public static final String ARG_NAME_OR_NUMBER = "NAME_OR_NUMBER";
     81     public static final String ARG_IS_BUSINESS = "IS_BUSINESS";
     82     public static final String ARG_NUMBER = "NUMBER";
     83     public static final String ARG_DISPLAY_NUMBER = "DISPLAY_NUMBER";
     84     public static final String ARG_NUMBER_LABEL = "NUMBER_LABEL";
     85     public static final String ARG_PHONE_ACCOUNT_HANDLE = "PHONE_ACCOUNT_HANDLE";
     86 
     87     private int mAnimationDuration;
     88     private Charset mMessageEncoding;
     89     private View mBackgroundView;
     90     private View mDialogView;
     91     private QuickContactBadge mContactPhoto;
     92     private TextView mNameView;
     93     private TextView mNumberView;
     94     private EditText mCallSubjectView;
     95     private TextView mCharacterLimitView;
     96     private View mHistoryButton;
     97     private View mSendAndCallButton;
     98     private ListView mSubjectList;
     99 
    100     private int mLimit = CALL_SUBJECT_LIMIT;
    101     private int mPhotoSize;
    102     private SharedPreferences mPrefs;
    103     private List<String> mSubjectHistory;
    104 
    105     private long mPhotoID;
    106     private Uri mPhotoUri;
    107     private Uri mContactUri;
    108     private String mNameOrNumber;
    109     private boolean mIsBusiness;
    110     private String mNumber;
    111     private String mDisplayNumber;
    112     private String mNumberLabel;
    113     private PhoneAccountHandle mPhoneAccountHandle;
    114 
    115     /**
    116      * Handles changes to the text in the subject box.  Ensures the character limit is updated.
    117      */
    118     private final TextWatcher mTextWatcher = new TextWatcher() {
    119         @Override
    120         public void beforeTextChanged(CharSequence s, int start, int count, int after) {
    121             // no-op
    122         }
    123 
    124         @Override
    125         public void onTextChanged(CharSequence s, int start, int before, int count) {
    126             updateCharacterLimit();
    127         }
    128 
    129         @Override
    130         public void afterTextChanged(Editable s) {
    131             // no-op
    132         }
    133     };
    134 
    135     /**
    136      * Click listener which handles user clicks outside of the dialog.
    137      */
    138     private View.OnClickListener mBackgroundListener = new View.OnClickListener() {
    139         @Override
    140         public void onClick(View v) {
    141             finish();
    142         }
    143     };
    144 
    145     /**
    146      * Handles displaying the list of past call subjects.
    147      */
    148     private final View.OnClickListener mHistoryOnClickListener = new View.OnClickListener() {
    149         @Override
    150         public void onClick(View v) {
    151             hideSoftKeyboard(CallSubjectDialog.this, mCallSubjectView);
    152             showCallHistory(mSubjectList.getVisibility() == View.GONE);
    153         }
    154     };
    155 
    156     /**
    157      * Handles starting a call with a call subject specified.
    158      */
    159     private final View.OnClickListener mSendAndCallOnClickListener = new View.OnClickListener() {
    160         @Override
    161         public void onClick(View v) {
    162             String subject = mCallSubjectView.getText().toString();
    163             Intent intent = CallUtil.getCallWithSubjectIntent(mNumber, mPhoneAccountHandle,
    164                     subject);
    165 
    166             TelecomManagerCompat.placeCall(
    167                     CallSubjectDialog.this,
    168                     (TelecomManager) getSystemService(Context.TELECOM_SERVICE),
    169                     intent);
    170 
    171             mSubjectHistory.add(subject);
    172             saveSubjectHistory(mSubjectHistory);
    173             finish();
    174         }
    175     };
    176 
    177     /**
    178      * Handles auto-hiding the call history when user clicks in the call subject field to give it
    179      * focus.
    180      */
    181     private final View.OnClickListener mCallSubjectClickListener = new View.OnClickListener() {
    182         @Override
    183         public void onClick(View v) {
    184             if (mSubjectList.getVisibility() == View.VISIBLE) {
    185                 showCallHistory(false);
    186             }
    187         }
    188     };
    189 
    190     /**
    191      * Item click listener which handles user clicks on the items in the list view.  Dismisses
    192      * the activity, returning the subject to the caller and closing the activity with the
    193      * {@link Activity#RESULT_OK} result code.
    194      */
    195     private AdapterView.OnItemClickListener mItemClickListener =
    196             new AdapterView.OnItemClickListener() {
    197                 @Override
    198                 public void onItemClick(AdapterView<?> arg0, View view, int position, long arg3) {
    199                     mCallSubjectView.setText(mSubjectHistory.get(position));
    200                     showCallHistory(false);
    201                 }
    202             };
    203 
    204     /**
    205      * Show the call subject dialog given a phone number to dial (e.g. from the dialpad).
    206      *
    207      * @param activity The activity.
    208      * @param number The number to dial.
    209      */
    210     public static void start(Activity activity, String number) {
    211         start(activity,
    212                 -1 /* photoId */,
    213                 null /* photoUri */,
    214                 null /* contactUri */,
    215                 number /* nameOrNumber */,
    216                 false /* isBusiness */,
    217                 number /* number */,
    218                 null /* displayNumber */,
    219                 null /* numberLabel */,
    220                 null /* phoneAccountHandle */);
    221     }
    222 
    223     /**
    224      * Creates a call subject dialog.
    225      *
    226      * @param activity The current activity.
    227      * @param photoId The photo ID (used to populate contact photo).
    228      * @param photoUri The photo Uri (used to populate contact photo).
    229      * @param contactUri The Contact URI (used so quick contact can be invoked from contact photo).
    230      * @param nameOrNumber The name or number of the callee.
    231      * @param isBusiness {@code true} if a business is being called (used for contact photo).
    232      * @param number The raw number to dial.
    233      * @param displayNumber The number to dial, formatted for display.
    234      * @param numberLabel The label for the number (if from a contact).
    235      * @param phoneAccountHandle The phone account handle.
    236      */
    237     public static void start(Activity activity, long photoId, Uri photoUri, Uri contactUri,
    238             String nameOrNumber, boolean isBusiness, String number, String displayNumber,
    239             String numberLabel, PhoneAccountHandle phoneAccountHandle) {
    240         Bundle arguments = new Bundle();
    241         arguments.putLong(ARG_PHOTO_ID, photoId);
    242         arguments.putParcelable(ARG_PHOTO_URI, photoUri);
    243         arguments.putParcelable(ARG_CONTACT_URI, contactUri);
    244         arguments.putString(ARG_NAME_OR_NUMBER, nameOrNumber);
    245         arguments.putBoolean(ARG_IS_BUSINESS, isBusiness);
    246         arguments.putString(ARG_NUMBER, number);
    247         arguments.putString(ARG_DISPLAY_NUMBER, displayNumber);
    248         arguments.putString(ARG_NUMBER_LABEL, numberLabel);
    249         arguments.putParcelable(ARG_PHONE_ACCOUNT_HANDLE, phoneAccountHandle);
    250         start(activity, arguments);
    251     }
    252 
    253     /**
    254      * Shows the call subject dialog given a Bundle containing all the arguments required to
    255      * display the dialog (e.g. from Quick Contacts).
    256      *
    257      * @param activity The activity.
    258      * @param arguments The arguments bundle.
    259      */
    260     public static void start(Activity activity, Bundle arguments) {
    261         Intent intent = new Intent(activity, CallSubjectDialog.class);
    262         intent.putExtras(arguments);
    263         activity.startActivity(intent);
    264     }
    265 
    266     /**
    267      * Creates the dialog, inflating the layout and populating it with the name and phone number.
    268      *
    269      * @param savedInstanceState The last saved instance state of the Fragment,
    270      * or null if this is a freshly created Fragment.
    271      *
    272      * @return Dialog instance.
    273      */
    274     @Override
    275     public void onCreate(Bundle savedInstanceState) {
    276         super.onCreate(savedInstanceState);
    277         mAnimationDuration = getResources().getInteger(R.integer.call_subject_animation_duration);
    278         mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
    279         mPhotoSize = getResources().getDimensionPixelSize(
    280                 R.dimen.call_subject_dialog_contact_photo_size);
    281         readArguments();
    282         loadConfiguration();
    283         mSubjectHistory = loadSubjectHistory(mPrefs);
    284 
    285         setContentView(R.layout.dialog_call_subject);
    286         getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT,
    287                 ViewGroup.LayoutParams.MATCH_PARENT);
    288         mBackgroundView = findViewById(R.id.call_subject_dialog);
    289         mBackgroundView.setOnClickListener(mBackgroundListener);
    290         mDialogView = findViewById(R.id.dialog_view);
    291         mContactPhoto = (QuickContactBadge) findViewById(R.id.contact_photo);
    292         mNameView = (TextView) findViewById(R.id.name);
    293         mNumberView = (TextView) findViewById(R.id.number);
    294         mCallSubjectView = (EditText) findViewById(R.id.call_subject);
    295         mCallSubjectView.addTextChangedListener(mTextWatcher);
    296         mCallSubjectView.setOnClickListener(mCallSubjectClickListener);
    297         InputFilter[] filters = new InputFilter[1];
    298         filters[0] = new InputFilter.LengthFilter(mLimit);
    299         mCallSubjectView.setFilters(filters);
    300         mCharacterLimitView = (TextView) findViewById(R.id.character_limit);
    301         mHistoryButton = findViewById(R.id.history_button);
    302         mHistoryButton.setOnClickListener(mHistoryOnClickListener);
    303         mHistoryButton.setVisibility(mSubjectHistory.isEmpty() ? View.GONE : View.VISIBLE);
    304         mSendAndCallButton = findViewById(R.id.send_and_call_button);
    305         mSendAndCallButton.setOnClickListener(mSendAndCallOnClickListener);
    306         mSubjectList = (ListView) findViewById(R.id.subject_list);
    307         mSubjectList.setOnItemClickListener(mItemClickListener);
    308         mSubjectList.setVisibility(View.GONE);
    309 
    310         updateContactInfo();
    311         updateCharacterLimit();
    312     }
    313 
    314     /**
    315      * Populates the contact info fields based on the current contact information.
    316      */
    317     private void updateContactInfo() {
    318         if (mContactUri != null) {
    319             setPhoto(mPhotoID, mPhotoUri, mContactUri, mNameOrNumber, mIsBusiness);
    320         } else {
    321             mContactPhoto.setVisibility(View.GONE);
    322         }
    323         mNameView.setText(mNameOrNumber);
    324         if (!TextUtils.isEmpty(mNumberLabel) && !TextUtils.isEmpty(mDisplayNumber)) {
    325             mNumberView.setVisibility(View.VISIBLE);
    326             mNumberView.setText(getString(R.string.call_subject_type_and_number,
    327                     mNumberLabel, mDisplayNumber));
    328         } else {
    329             mNumberView.setVisibility(View.GONE);
    330             mNumberView.setText(null);
    331         }
    332     }
    333 
    334     /**
    335      * Reads arguments from the fragment arguments and populates the necessary instance variables.
    336      */
    337     private void readArguments() {
    338         Bundle arguments = getIntent().getExtras();
    339         if (arguments == null) {
    340             Log.e(TAG, "Arguments cannot be null.");
    341             return;
    342         }
    343         mPhotoID = arguments.getLong(ARG_PHOTO_ID);
    344         mPhotoUri = arguments.getParcelable(ARG_PHOTO_URI);
    345         mContactUri = arguments.getParcelable(ARG_CONTACT_URI);
    346         mNameOrNumber = arguments.getString(ARG_NAME_OR_NUMBER);
    347         mIsBusiness = arguments.getBoolean(ARG_IS_BUSINESS);
    348         mNumber = arguments.getString(ARG_NUMBER);
    349         mDisplayNumber = arguments.getString(ARG_DISPLAY_NUMBER);
    350         mNumberLabel = arguments.getString(ARG_NUMBER_LABEL);
    351         mPhoneAccountHandle = arguments.getParcelable(ARG_PHONE_ACCOUNT_HANDLE);
    352     }
    353 
    354     /**
    355      * Updates the character limit display, coloring the text RED when the limit is reached or
    356      * exceeded.
    357      */
    358     private void updateCharacterLimit() {
    359         String subjectText = mCallSubjectView.getText().toString();
    360         final int length;
    361 
    362         // If a message encoding is specified, use that to count bytes in the message.
    363         if (mMessageEncoding != null) {
    364             length = subjectText.getBytes(mMessageEncoding).length;
    365         } else {
    366             // No message encoding specified, so just count characters entered.
    367             length = subjectText.length();
    368         }
    369 
    370         mCharacterLimitView.setText(
    371                 getString(R.string.call_subject_limit, length, mLimit));
    372         if (length >= mLimit) {
    373             mCharacterLimitView.setTextColor(getResources().getColor(
    374                     R.color.call_subject_limit_exceeded));
    375         } else {
    376             mCharacterLimitView.setTextColor(getResources().getColor(
    377                     R.color.dialtacts_secondary_text_color));
    378         }
    379     }
    380 
    381     /**
    382      * Sets the photo on the quick contact photo.
    383      *
    384      * @param photoId
    385      * @param photoUri
    386      * @param contactUri
    387      * @param displayName
    388      * @param isBusiness
    389      */
    390     private void setPhoto(long photoId, Uri photoUri, Uri contactUri, String displayName,
    391             boolean isBusiness) {
    392         mContactPhoto.assignContactUri(contactUri);
    393         if (CompatUtils.isLollipopCompatible()) {
    394             mContactPhoto.setOverlay(null);
    395         }
    396 
    397         int contactType;
    398         if (isBusiness) {
    399             contactType = ContactPhotoManager.TYPE_BUSINESS;
    400         } else {
    401             contactType = ContactPhotoManager.TYPE_DEFAULT;
    402         }
    403 
    404         String lookupKey = null;
    405         if (contactUri != null) {
    406             lookupKey = UriUtils.getLookupKeyFromUri(contactUri);
    407         }
    408 
    409         ContactPhotoManager.DefaultImageRequest
    410                 request = new ContactPhotoManager.DefaultImageRequest(
    411                 displayName, lookupKey, contactType, true /* isCircular */);
    412 
    413         if (photoId == 0 && photoUri != null) {
    414             ContactPhotoManager.getInstance(this).loadPhoto(mContactPhoto, photoUri,
    415                     mPhotoSize, false /* darkTheme */, true /* isCircular */, request);
    416         } else {
    417             ContactPhotoManager.getInstance(this).loadThumbnail(mContactPhoto, photoId,
    418                     false /* darkTheme */, true /* isCircular */, request);
    419         }
    420     }
    421 
    422     /**
    423      * Loads the subject history from shared preferences.
    424      *
    425      * @param prefs Shared preferences.
    426      * @return List of subject history strings.
    427      */
    428     public static List<String> loadSubjectHistory(SharedPreferences prefs) {
    429         int historySize = prefs.getInt(PREF_KEY_SUBJECT_HISTORY_COUNT, 0);
    430         List<String> subjects = new ArrayList(historySize);
    431 
    432         for (int ix = 0 ; ix < historySize; ix++) {
    433             String historyItem = prefs.getString(PREF_KEY_SUBJECT_HISTORY_ITEM + ix, null);
    434             if (!TextUtils.isEmpty(historyItem)) {
    435                 subjects.add(historyItem);
    436             }
    437         }
    438 
    439         return subjects;
    440     }
    441 
    442     /**
    443      * Saves the subject history list to shared prefs, removing older items so that there are only
    444      * {@link #CALL_SUBJECT_HISTORY_SIZE} items at most.
    445      *
    446      * @param history The history.
    447      */
    448     private void saveSubjectHistory(List<String> history) {
    449         // Remove oldest subject(s).
    450         while (history.size() > CALL_SUBJECT_HISTORY_SIZE) {
    451             history.remove(0);
    452         }
    453 
    454         SharedPreferences.Editor editor = mPrefs.edit();
    455         int historyCount = 0;
    456         for (String subject : history) {
    457             if (!TextUtils.isEmpty(subject)) {
    458                 editor.putString(PREF_KEY_SUBJECT_HISTORY_ITEM + historyCount,
    459                         subject);
    460                 historyCount++;
    461             }
    462         }
    463         editor.putInt(PREF_KEY_SUBJECT_HISTORY_COUNT, historyCount);
    464         editor.apply();
    465     }
    466 
    467     /**
    468      * Hide software keyboard for the given {@link View}.
    469      */
    470     public void hideSoftKeyboard(Context context, View view) {
    471         InputMethodManager imm = (InputMethodManager) context.getSystemService(
    472                 Context.INPUT_METHOD_SERVICE);
    473         if (imm != null) {
    474             imm.hideSoftInputFromWindow(view.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
    475         }
    476     }
    477 
    478     /**
    479      * Hides or shows the call history list.
    480      *
    481      * @param show {@code true} if the call history should be shown, {@code false} otherwise.
    482      */
    483     private void showCallHistory(final boolean show) {
    484         // Bail early if the visibility has not changed.
    485         if ((show && mSubjectList.getVisibility() == View.VISIBLE) ||
    486                 (!show && mSubjectList.getVisibility() == View.GONE)) {
    487             return;
    488         }
    489 
    490         final int dialogStartingBottom = mDialogView.getBottom();
    491         if (show) {
    492             // Showing the subject list; bind the list of history items to the list and show it.
    493             ArrayAdapter<String> adapter = new ArrayAdapter<String>(CallSubjectDialog.this,
    494                     R.layout.call_subject_history_list_item, mSubjectHistory);
    495             mSubjectList.setAdapter(adapter);
    496             mSubjectList.setVisibility(View.VISIBLE);
    497         } else {
    498             // Hiding the subject list.
    499             mSubjectList.setVisibility(View.GONE);
    500         }
    501 
    502         // Use a ViewTreeObserver so that we can animate between the pre-layout and post-layout
    503         // states.
    504         final ViewTreeObserver observer = mBackgroundView.getViewTreeObserver();
    505         observer.addOnPreDrawListener(
    506                 new ViewTreeObserver.OnPreDrawListener() {
    507                     @Override
    508                     public boolean onPreDraw() {
    509                         // We don't want to continue getting called.
    510                         if (observer.isAlive()) {
    511                             observer.removeOnPreDrawListener(this);
    512                         }
    513 
    514                         // Determine the amount the dialog has shifted due to the relayout.
    515                         int shiftAmount = dialogStartingBottom - mDialogView.getBottom();
    516 
    517                         // If the dialog needs to be shifted, do that now.
    518                         if (shiftAmount != 0) {
    519                             // Start animation in translated state and animate to translationY 0.
    520                             mDialogView.setTranslationY(shiftAmount);
    521                             mDialogView.animate()
    522                                     .translationY(0)
    523                                     .setInterpolator(AnimUtils.EASE_OUT_EASE_IN)
    524                                     .setDuration(mAnimationDuration)
    525                                     .start();
    526                         }
    527 
    528                         if (show) {
    529                             // Show the subhect list.
    530                             mSubjectList.setTranslationY(mSubjectList.getHeight());
    531 
    532                             mSubjectList.animate()
    533                                     .translationY(0)
    534                                     .setInterpolator(AnimUtils.EASE_OUT_EASE_IN)
    535                                     .setDuration(mAnimationDuration)
    536                                     .setListener(new AnimatorListenerAdapter() {
    537                                         @Override
    538                                         public void onAnimationEnd(Animator animation) {
    539                                             super.onAnimationEnd(animation);
    540                                         }
    541 
    542                                         @Override
    543                                         public void onAnimationStart(Animator animation) {
    544                                             super.onAnimationStart(animation);
    545                                             mSubjectList.setVisibility(View.VISIBLE);
    546                                         }
    547                                     })
    548                                     .start();
    549                         } else {
    550                             // Hide the subject list.
    551                             mSubjectList.setTranslationY(0);
    552 
    553                             mSubjectList.animate()
    554                                     .translationY(mSubjectList.getHeight())
    555                                     .setInterpolator(AnimUtils.EASE_OUT_EASE_IN)
    556                                     .setDuration(mAnimationDuration)
    557                                     .setListener(new AnimatorListenerAdapter() {
    558                                         @Override
    559                                         public void onAnimationEnd(Animator animation) {
    560                                             super.onAnimationEnd(animation);
    561                                             mSubjectList.setVisibility(View.GONE);
    562                                         }
    563 
    564                                         @Override
    565                                         public void onAnimationStart(Animator animation) {
    566                                             super.onAnimationStart(animation);
    567                                         }
    568                                     })
    569                                     .start();
    570                         }
    571                         return true;
    572                     }
    573                 }
    574         );
    575     }
    576 
    577     /**
    578      * Loads the message encoding and maximum message length from the phone account extras for the
    579      * current phone account.
    580      */
    581     private void loadConfiguration() {
    582         // Only attempt to load configuration from the phone account extras if the SDK is N or
    583         // later.  If we've got a prior SDK the default encoding and message length will suffice.
    584         int sdk = android.os.Build.VERSION.SDK_INT;
    585         if(sdk <= android.os.Build.VERSION_CODES.M) {
    586             return;
    587         }
    588 
    589         if (mPhoneAccountHandle == null) {
    590             return;
    591         }
    592 
    593         TelecomManager telecomManager =
    594                 (TelecomManager) getSystemService(Context.TELECOM_SERVICE);
    595         final PhoneAccount account = telecomManager.getPhoneAccount(mPhoneAccountHandle);
    596 
    597         Bundle phoneAccountExtras = PhoneAccountSdkCompat.getExtras(account);
    598         if (phoneAccountExtras == null) {
    599             return;
    600         }
    601 
    602         // Get limit, if provided; otherwise default to existing value.
    603         mLimit = phoneAccountExtras
    604                 .getInt(PhoneAccountSdkCompat.EXTRA_CALL_SUBJECT_MAX_LENGTH, mLimit);
    605 
    606         // Get charset; default to none (e.g. count characters 1:1).
    607         String charsetName = phoneAccountExtras.getString(
    608                 PhoneAccountSdkCompat.EXTRA_CALL_SUBJECT_CHARACTER_ENCODING);
    609 
    610         if (!TextUtils.isEmpty(charsetName)) {
    611             try {
    612                 mMessageEncoding = Charset.forName(charsetName);
    613             } catch (java.nio.charset.UnsupportedCharsetException uce) {
    614                 // Character set was invalid; log warning and fallback to none.
    615                 Log.w(TAG, "Invalid charset: " + charsetName);
    616                 mMessageEncoding = null;
    617             }
    618         } else {
    619             // No character set specified, so count characters 1:1.
    620             mMessageEncoding = null;
    621         }
    622     }
    623 }
    624