package com.moez.QKSMS.ui.view;
import android.content.AsyncQueryHandler;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.res.TypedArray;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.support.v4.content.ContextCompat;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.ImageView;
import com.moez.QKSMS.R;
import com.moez.QKSMS.common.LiveViewManager;
import com.moez.QKSMS.common.TypefaceManager;
import com.moez.QKSMS.enums.QKPreference;
import com.moez.QKSMS.common.utils.ImageUtils;
import com.moez.QKSMS.common.utils.Units;
import com.moez.QKSMS.ui.ThemeManager;
public class AvatarView extends ImageView implements View.OnClickListener {
private final String TAG = "AvatarView";
static final String[] PHONE_LOOKUP_PROJECTION = new String[]{
ContactsContract.PhoneLookup._ID,
ContactsContract.PhoneLookup.LOOKUP_KEY,
};
public static final String ME = "Me";
static final int PHONE_ID_COLUMN_INDEX = 0;
static final int PHONE_LOOKUP_STRING_COLUMN_INDEX = 1;
static final private int TOKEN_PHONE_LOOKUP = 1;
static final private int TOKEN_PHONE_LOOKUP_AND_TRIGGER = 3;
static final private String EXTRA_URI_CONTENT = "uri_content";
protected String[] mExcludeMimes = null;
private Uri mContactUri;
private String mContactPhone;
private QueryHandler mQueryHandler;
private Bundle mExtras = null;
private String mInitial = "#";
private Paint mPaint;
private Drawable mDefaultDrawable;
private int mYOffset;
/**
* When setImageDrawable is called with a drawable, we circle crop to size of this view and use
* that instead. Keep the original in case our size changes, or in case the size wasn't ready.
*/
private Drawable mOriginalDrawable;
public AvatarView(Context context) {
this(context, null);
}
public AvatarView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public AvatarView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
if (!isInEditMode()) {
mQueryHandler = new QueryHandler(context.getContentResolver());
mPaint = new Paint();
mPaint.setTextSize(Units.dpToPx(context, 32));
mPaint.setTextAlign(Paint.Align.CENTER);
mPaint.setAntiAlias(true);
mPaint.setTypeface(TypefaceManager.obtainTypeface(getContext(), TypefaceManager.Typefaces.ROBOTO_LIGHT));
mDefaultDrawable = ContextCompat.getDrawable(context, R.drawable.ic_person);
final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AvatarView, defStyle, 0);
for (int i = 0; i < a.getIndexCount(); i++) {
if (a.getIndex(i) == R.styleable.AvatarView_initialSize) {
mPaint.setTextSize(a.getDimensionPixelSize(a.getIndex(i), (int) mPaint.getTextSize()));
}
}
a.recycle();
mYOffset = (int) ((mPaint.descent() + mPaint.ascent()) / 2);
setOnClickListener(this);
LiveViewManager.registerView(QKPreference.THEME, this, key -> {
mPaint.setColor(ThemeManager.getTextOnColorPrimary());
mDefaultDrawable.setColorFilter(ThemeManager.getTextOnColorPrimary(), PorterDuff.Mode.SRC_ATOP);
if (getBackground() == null) {
setBackgroundResource(R.drawable.circle);
}
getBackground().setColorFilter(ThemeManager.getColor(), PorterDuff.Mode.SRC_ATOP);
});
}
}
/**
* True if a contact, an email address or a phone number has been assigned
*/
private boolean isAssigned() {
return mContactPhone != null;
}
/**
* Assign the contact uri that this QuickContactBadge should be associated
* with. Note that this is only used for displaying the QuickContact window and
* won't bind the contact's photo for you. Call {@link #setImageDrawable(Drawable)} to set the
* photo.
*
* @param contactUri Either a {@link android.provider.ContactsContract.Contacts#CONTENT_URI} or
* {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI} style URI.
*/
public void assignContactUri(Uri contactUri) {
mContactUri = contactUri;
mContactPhone = null;
}
public Uri getContactUri() {
return mContactUri;
}
/**
* Assign a contact based on a phone number. This should only be used when
* the contact's URI is not available, as an extra query will have to be
* performed to lookup the URI based on the phone number.
*
* @param phoneNumber The phone number of the contact.
* @param lazyLookup If this is true, the lookup query will not be performed
* until this view is clicked.
*/
public void assignContactFromPhone(String phoneNumber, boolean lazyLookup) {
assignContactFromPhone(phoneNumber, lazyLookup, new Bundle());
}
/**
* Assign a contact based on a phone number. This should only be used when
* the contact's URI is not available, as an extra query will have to be
* performed to lookup the URI based on the phone number.
*
* @param phoneNumber The phone number of the contact.
* @param lazyLookup If this is true, the lookup query will not be performed
* until this view is clicked.
* @param extras A bundle of extras to populate the contact edit page with if the contact
* is not found and the user chooses to add the phone number to an existing contact or
* create a new contact. Uses the same string constants as those found in
* {@link android.provider.ContactsContract.Intents.Insert}
*/
public void assignContactFromPhone(String phoneNumber, boolean lazyLookup, Bundle extras) {
mContactPhone = phoneNumber;
mExtras = extras;
if (!lazyLookup && mQueryHandler != null) {
mQueryHandler.startQuery(TOKEN_PHONE_LOOKUP, null,
Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, mContactPhone),
PHONE_LOOKUP_PROJECTION, null, null, null);
} else {
mContactUri = null;
}
}
public void setContactName(String name) {
if (TextUtils.isEmpty(name) || name.equals(ME)) {
mInitial = "";
super.setImageDrawable(mDefaultDrawable);
} else if (name.length() == 1) {
mInitial = "" + name.toUpperCase();
if (mOriginalDrawable == null) super.setImageDrawable(null);
} else if (isPhoneNumberFormat(name)) {
mInitial = "";
if (mOriginalDrawable == null) super.setImageDrawable(mDefaultDrawable);
} else {
mInitial = "" + name.toUpperCase().charAt(0);
if (mOriginalDrawable == null) super.setImageDrawable(null);
}
invalidate();
}
private boolean isPhoneNumberFormat(String name) {
if (TextUtils.isEmpty(name)) {
return false;
}
char c = name.charAt(0);
return !name.contains("@") && (c == '+' || c == '(' || Character.isDigit(c));
}
@Override
public void onClick(View v) {
// If contact has been assigned, mExtras should no longer be null, but do a null check
// anyway just in case assignContactFromPhone or Email was called with a null bundle or
// wasn't assigned previously.
final Bundle extras = (mExtras == null) ? new Bundle() : mExtras;
if (mQueryHandler != null) {
if (mContactPhone != null) {
extras.putString(EXTRA_URI_CONTENT, mContactPhone);
mQueryHandler.startQuery(TOKEN_PHONE_LOOKUP_AND_TRIGGER, extras,
Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, mContactPhone),
PHONE_LOOKUP_PROJECTION, null, null, null);
} else if (mContactUri != null) {
mQueryHandler.startQuery(TOKEN_PHONE_LOOKUP_AND_TRIGGER, extras, mContactUri,
PHONE_LOOKUP_PROJECTION, null, null, null);
}
}
}
@Override
public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
super.onInitializeAccessibilityEvent(event);
event.setClassName(AvatarView.class.getName());
}
@Override
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(info);
info.setClassName(AvatarView.class.getName());
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
// Set the image, in case we didn't previously have the actual measurements of the view
setImageWhenReady();
}
/**
* Circle crops the given drawable and sets it as the background. If the given drawable is null,
* just generate a colored drawable and set that.
*
* @param drawable
*/
@Override
public void setImageDrawable(Drawable drawable) {
mOriginalDrawable = drawable;
setImageWhenReady();
}
private void setImageWhenReady() {
if (isInEditMode()) {
super.setImageDrawable(mOriginalDrawable);
return;
}
if (getWidth() > 0 && getHeight() > 0) {
// If our size is initialized correctly, then set up the drawable here.
if (mOriginalDrawable != null) {
// Circle crop the given bitmap.
Bitmap orig = ((BitmapDrawable) mOriginalDrawable).getBitmap();
Bitmap bitmap = orig.copy(Bitmap.Config.ARGB_8888, true);
int w = getWidth();
Bitmap roundBitmap = ImageUtils.getCircleBitmap(bitmap, w);
super.setImageDrawable(new BitmapDrawable(getResources(), roundBitmap));
getBackground().setColorFilter(0x00000000, PorterDuff.Mode.SRC_ATOP);
} else {
super.setImageDrawable(TextUtils.isEmpty(mInitial) ? mDefaultDrawable : null);
getBackground().setColorFilter(ThemeManager.getColor(), PorterDuff.Mode.SRC_ATOP);
}
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (getDrawable() == null && !isInEditMode()) {
int xPos = (getWidth() / 2);
int yPos = (getHeight() / 2) - mYOffset;
canvas.drawText("" + mInitial, xPos, yPos, mPaint);
}
}
private class QueryHandler extends AsyncQueryHandler {
public QueryHandler(ContentResolver cr) {
super(cr);
}
@Override
protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
Uri lookupUri = null;
Uri createUri = null;
boolean trigger = false;
Bundle extras = (cookie != null) ? (Bundle) cookie : new Bundle();
try {
switch (token) {
case TOKEN_PHONE_LOOKUP_AND_TRIGGER:
trigger = true;
if (extras.getString(EXTRA_URI_CONTENT) != null) {
createUri = Uri.fromParts("tel", extras.getString(EXTRA_URI_CONTENT), null);
}
//$FALL-THROUGH$
case TOKEN_PHONE_LOOKUP: {
if (cursor != null && cursor.moveToFirst()) {
long contactId = cursor.getLong(PHONE_ID_COLUMN_INDEX);
String lookupKey = cursor.getString(PHONE_LOOKUP_STRING_COLUMN_INDEX);
lookupUri = ContactsContract.Contacts.getLookupUri(contactId, lookupKey);
}
break;
}
}
} finally {
if (cursor != null) {
cursor.close();
}
}
mContactUri = lookupUri;
if (trigger && lookupUri != null) {
// Found contact, so trigger QuickContact
ContactsContract.QuickContact.showQuickContact(getContext(), AvatarView.this, lookupUri,
ContactsContract.QuickContact.MODE_LARGE, mExcludeMimes);
} else if (createUri != null) {
// Prompt user to add this person to contacts
final Intent intent = new Intent(ContactsContract.Intents.SHOW_OR_CREATE_CONTACT, createUri);
if (extras != null) {
extras.remove(EXTRA_URI_CONTENT);
intent.putExtras(extras);
}
getContext().startActivity(intent);
}
}
}
}