@VisibleForTesting
  static boolean isNumberAddress(String number) {
    if (number.contains("@")) return false;
    if (GroupUtil.isEncodedGroup(number)) return false;

    final String networkNumber = PhoneNumberUtils.extractNetworkPortion(number);
    if (TextUtils.isEmpty(networkNumber)) return false;
    if (networkNumber.length() < 3) return false;

    return PhoneNumberUtils.isWellFormedSmsAddress(number);
  }
Exemplo n.º 2
0
 // Some received sms's have addresses such as "OakfieldCPS" or "T-Mobile". This
 // function will attempt to identify these and return true. If the number contains
 // 3 or more digits, such as "jello123", this function will return false.
 // Some countries have 3 digits shortcodes and we have to identify them as numbers.
 //    http://en.wikipedia.org/wiki/Short_code
 // Examples of input/output for this function:
 //    "Jello123" -> false  [3 digits, it is considered to be the phone number "123"]
 //    "T-Mobile" -> true   [it is considered to be the address "T-Mobile"]
 //    "Mobile1"  -> true   [1 digit, it is considered to be the address "Mobile1"]
 //    "Dogs77"   -> true   [2 digits, it is considered to be the address "Dogs77"]
 //    "****1"    -> true   [1 digits, it is considered to be the address "****1"]
 //    "#4#5#6#"  -> true   [it is considered to be the address "#4#5#6#"]
 //    "AB12"     -> true   [2 digits, it is considered to be the address "AB12"]
 //    "12"       -> true   [2 digits, it is considered to be the address "12"]
 private boolean isAlphaNumber(String number) {
   // TODO: PhoneNumberUtils.isWellFormedSmsAddress() only check if the number is a valid
   // GSM SMS address. If the address contains a dialable char, it considers it a well
   // formed SMS addr. CDMA doesn't work that way and has a different parser for SMS
   // address (see CdmaSmsAddress.parse(String address)). We should definitely fix this!!!
   if (!PhoneNumberUtils.isWellFormedSmsAddress(number)) {
     // The example "T-Mobile" will exit here because there are no numbers.
     return true; // we're not an sms address, consider it an alpha number
   }
   if (MessageUtils.isAlias(number)) {
     return true;
   }
   number = PhoneNumberUtils.extractNetworkPortion(number);
   if (TextUtils.isEmpty(number)) {
     return true; // there are no digits whatsoever in the number
   }
   // At this point, anything like "Mobile1" or "Dogs77" will be stripped down to
   // "1" and "77". "#4#5#6#" remains as "#4#5#6#" at this point.
   return number.length() < 3;
 }
  @Override
  public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfoIn) {
    AdapterView.AdapterContextMenuInfo menuInfo;
    try {
      menuInfo = (AdapterView.AdapterContextMenuInfo) menuInfoIn;
    } catch (ClassCastException e) {
      Log.e(TAG, "bad menuInfoIn", e);
      return;
    }

    Cursor cursor = (Cursor) mAdapter.getItem(menuInfo.position);

    String number = cursor.getString(NUMBER_COLUMN_INDEX);
    Uri numberUri = null;
    boolean isVoicemail = false;
    boolean isSipNumber = false;
    if (number.equals(CallerInfo.UNKNOWN_NUMBER)) {
      number = getString(R.string.unknown);
    } else if (number.equals(CallerInfo.PRIVATE_NUMBER)) {
      number = getString(R.string.private_num);
    } else if (number.equals(CallerInfo.PAYPHONE_NUMBER)) {
      number = getString(R.string.payphone);
    } else if (PhoneNumberUtils.extractNetworkPortion(number).equals(mVoiceMailNumber)) {
      number = getString(R.string.voicemail);
      numberUri = Uri.parse("voicemail:x");
      isVoicemail = true;
    } else if (PhoneNumberUtils.isUriNumber(number)) {
      numberUri = Uri.fromParts("sip", number, null);
      isSipNumber = true;
    } else {
      numberUri = Uri.fromParts("tel", number, null);
    }

    ContactInfo info = mAdapter.getContactInfo(number);
    boolean contactInfoPresent = (info != null && info != ContactInfo.EMPTY);
    if (contactInfoPresent) {
      menu.setHeaderTitle(info.name);
    } else {
      menu.setHeaderTitle(number);
    }

    if (numberUri != null) {
      Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED, numberUri);
      menu.add(
              0,
              CONTEXT_MENU_CALL_CONTACT,
              0,
              getResources().getString(R.string.recentCalls_callNumber, number))
          .setIntent(intent);
    }

    if (contactInfoPresent) {
      Intent intent =
          new Intent(
              Intent.ACTION_VIEW, ContentUris.withAppendedId(Contacts.CONTENT_URI, info.personId));
      StickyTabs.setTab(intent, getIntent());
      menu.add(0, 0, 0, R.string.menu_viewContact).setIntent(intent);
    }

    if (numberUri != null && !isVoicemail && !isSipNumber) {
      menu.add(0, 0, 0, R.string.recentCalls_editNumberBeforeCall)
          .setIntent(new Intent(Intent.ACTION_DIAL, numberUri));
      menu.add(0, 0, 0, R.string.menu_sendTextMessage)
          .setIntent(new Intent(Intent.ACTION_SENDTO, Uri.fromParts("sms", number, null)));
    }

    // "Add to contacts" item, if this entry isn't already associated with a contact
    if (!contactInfoPresent && numberUri != null && !isVoicemail && !isSipNumber) {
      // TODO: This item is currently disabled for SIP addresses, because
      // the Insert.PHONE extra only works correctly for PSTN numbers.
      //
      // To fix this for SIP addresses, we need to:
      // - define ContactsContract.Intents.Insert.SIP_ADDRESS, and use it here if
      //   the current number is a SIP address
      // - update the contacts UI code to handle Insert.SIP_ADDRESS by
      //   updating the SipAddress field
      // and then we can remove the "!isSipNumber" check above.

      Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
      intent.setType(Contacts.CONTENT_ITEM_TYPE);
      intent.putExtra(Insert.PHONE, number);
      menu.add(0, 0, 0, R.string.recentCalls_addToContact).setIntent(intent);
    }
    menu.add(0, CONTEXT_MENU_ITEM_DELETE, 0, R.string.recentCalls_removeFromRecentList);
  }
    public void bindView(Context context, View view, Cursor c) {
      final RecentCallsListItemViews views = (RecentCallsListItemViews) view.getTag();

      String number = c.getString(NUMBER_COLUMN_INDEX);
      String formattedNumber = null;
      String callerName = c.getString(CALLER_NAME_COLUMN_INDEX);
      int callerNumberType = c.getInt(CALLER_NUMBERTYPE_COLUMN_INDEX);
      String callerNumberLabel = c.getString(CALLER_NUMBERLABEL_COLUMN_INDEX);

      // Store away the number so we can call it directly if you click on the call icon
      views.callView.setTag(number);

      // Lookup contacts with this number
      ContactInfo info = mContactInfo.get(number);
      if (info == null) {
        // Mark it as empty and queue up a request to find the name
        // The db request should happen on a non-UI thread
        info = ContactInfo.EMPTY;
        mContactInfo.put(number, info);
        enqueueRequest(number, c.getPosition(), callerName, callerNumberType, callerNumberLabel);
      } else if (info != ContactInfo.EMPTY) { // Has been queried
        // Check if any data is different from the data cached in the
        // calls db. If so, queue the request so that we can update
        // the calls db.
        if (!TextUtils.equals(info.name, callerName)
            || info.type != callerNumberType
            || !TextUtils.equals(info.label, callerNumberLabel)) {
          // Something is amiss, so sync up.
          enqueueRequest(number, c.getPosition(), callerName, callerNumberType, callerNumberLabel);
        }

        // Format and cache phone number for found contact
        if (info.formattedNumber == null) {
          info.formattedNumber = formatPhoneNumber(info.number);
        }
        formattedNumber = info.formattedNumber;
      }

      String name = info.name;
      int ntype = info.type;
      String label = info.label;
      // If there's no name cached in our hashmap, but there's one in the
      // calls db, use the one in the calls db. Otherwise the name in our
      // hashmap is more recent, so it has precedence.
      if (TextUtils.isEmpty(name) && !TextUtils.isEmpty(callerName)) {
        name = callerName;
        ntype = callerNumberType;
        label = callerNumberLabel;

        // Format the cached call_log phone number
        formattedNumber = formatPhoneNumber(number);
      }
      // Set the text lines and call icon.
      // Assumes the call back feature is on most of the
      // time. For private and unknown numbers: hide it.
      views.callView.setVisibility(View.VISIBLE);

      if (!TextUtils.isEmpty(name)) {
        views.line1View.setText(name);
        views.labelView.setVisibility(View.VISIBLE);

        // "type" and "label" are currently unused for SIP addresses.
        CharSequence numberLabel = null;
        if (!PhoneNumberUtils.isUriNumber(number)) {
          numberLabel = Phone.getDisplayLabel(context, ntype, label, mLabelArray);
        }
        views.numberView.setVisibility(View.VISIBLE);
        views.numberView.setText(formattedNumber);
        if (!TextUtils.isEmpty(numberLabel)) {
          views.labelView.setText(numberLabel);
          views.labelView.setVisibility(View.VISIBLE);

          // Zero out the numberView's left margin (see below)
          ViewGroup.MarginLayoutParams numberLP =
              (ViewGroup.MarginLayoutParams) views.numberView.getLayoutParams();
          numberLP.leftMargin = 0;
          views.numberView.setLayoutParams(numberLP);
        } else {
          // There's nothing to display in views.labelView, so hide it.
          // We can't set it to View.GONE, since it's the anchor for
          // numberView in the RelativeLayout, so make it INVISIBLE.
          //   Also, we need to manually *subtract* some left margin from
          // numberView to compensate for the right margin built in to
          // labelView (otherwise the number will be indented by a very
          // slight amount).
          //   TODO: a cleaner fix would be to contain both the label and
          // number inside a LinearLayout, and then set labelView *and*
          // its padding to GONE when there's no label to display.
          views.labelView.setText(null);
          views.labelView.setVisibility(View.INVISIBLE);

          ViewGroup.MarginLayoutParams labelLP =
              (ViewGroup.MarginLayoutParams) views.labelView.getLayoutParams();
          ViewGroup.MarginLayoutParams numberLP =
              (ViewGroup.MarginLayoutParams) views.numberView.getLayoutParams();
          // Equivalent to setting android:layout_marginLeft in XML
          numberLP.leftMargin = -labelLP.rightMargin;
          views.numberView.setLayoutParams(numberLP);
        }
      } else {
        if (number.equals(CallerInfo.UNKNOWN_NUMBER)) {
          number = getString(R.string.unknown);
          views.callView.setVisibility(View.INVISIBLE);
        } else if (number.equals(CallerInfo.PRIVATE_NUMBER)) {
          number = getString(R.string.private_num);
          views.callView.setVisibility(View.INVISIBLE);
        } else if (number.equals(CallerInfo.PAYPHONE_NUMBER)) {
          number = getString(R.string.payphone);
        } else if (PhoneNumberUtils.extractNetworkPortion(number).equals(mVoiceMailNumber)) {
          number = getString(R.string.voicemail);
        } else {
          // Just a raw number, and no cache, so format it nicely
          number = formatPhoneNumber(number);
        }

        views.line1View.setText(number);
        views.numberView.setVisibility(View.GONE);
        views.labelView.setVisibility(View.GONE);
      }

      long date = c.getLong(DATE_COLUMN_INDEX);

      // Set the date/time field by mixing relative and absolute times.
      int flags = DateUtils.FORMAT_ABBREV_RELATIVE;

      views.dateView.setText(
          DateUtils.getRelativeTimeSpanString(
              date, System.currentTimeMillis(), DateUtils.MINUTE_IN_MILLIS, flags));

      if (views.iconView != null) {
        int type = c.getInt(CALL_TYPE_COLUMN_INDEX);
        // Set the icon
        switch (type) {
          case Calls.INCOMING_TYPE:
            views.iconView.setImageDrawable(mDrawableIncoming);
            break;

          case Calls.OUTGOING_TYPE:
            views.iconView.setImageDrawable(mDrawableOutgoing);
            break;

          case Calls.MISSED_TYPE:
            views.iconView.setImageDrawable(mDrawableMissed);
            break;
        }
      }

      // Listen for the first draw
      if (mPreDrawListener == null) {
        mFirst = true;
        mPreDrawListener = this;
        view.getViewTreeObserver().addOnPreDrawListener(this);
      }
    }