1c12da4a53eee911765975f0d8afe62f749806b60Jin Cao/* 2c12da4a53eee911765975f0d8afe62f749806b60Jin Cao * Copyright (C) 2014 The Android Open Source Project 3c12da4a53eee911765975f0d8afe62f749806b60Jin Cao * 4c12da4a53eee911765975f0d8afe62f749806b60Jin Cao * Licensed under the Apache License, Version 2.0 (the "License"); 5c12da4a53eee911765975f0d8afe62f749806b60Jin Cao * you may not use this file except in compliance with the License. 6c12da4a53eee911765975f0d8afe62f749806b60Jin Cao * You may obtain a copy of the License at 7c12da4a53eee911765975f0d8afe62f749806b60Jin Cao * 8c12da4a53eee911765975f0d8afe62f749806b60Jin Cao * http://www.apache.org/licenses/LICENSE-2.0 9c12da4a53eee911765975f0d8afe62f749806b60Jin Cao * 10c12da4a53eee911765975f0d8afe62f749806b60Jin Cao * Unless required by applicable law or agreed to in writing, software 11c12da4a53eee911765975f0d8afe62f749806b60Jin Cao * distributed under the License is distributed on an "AS IS" BASIS, 12c12da4a53eee911765975f0d8afe62f749806b60Jin Cao * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13c12da4a53eee911765975f0d8afe62f749806b60Jin Cao * See the License for the specific language governing permissions and 14c12da4a53eee911765975f0d8afe62f749806b60Jin Cao * limitations under the License. 15c12da4a53eee911765975f0d8afe62f749806b60Jin Cao */ 16c12da4a53eee911765975f0d8afe62f749806b60Jin Caopackage com.android.mail.bitmap; 17c12da4a53eee911765975f0d8afe62f749806b60Jin Cao 18c12da4a53eee911765975f0d8afe62f749806b60Jin Caoimport android.content.res.Resources; 19c12da4a53eee911765975f0d8afe62f749806b60Jin Caoimport android.graphics.Bitmap; 20c12da4a53eee911765975f0d8afe62f749806b60Jin Caoimport android.graphics.BitmapShader; 21c12da4a53eee911765975f0d8afe62f749806b60Jin Caoimport android.graphics.Canvas; 22c12da4a53eee911765975f0d8afe62f749806b60Jin Caoimport android.graphics.Color; 23c12da4a53eee911765975f0d8afe62f749806b60Jin Caoimport android.graphics.ColorFilter; 24c12da4a53eee911765975f0d8afe62f749806b60Jin Caoimport android.graphics.Matrix; 25c12da4a53eee911765975f0d8afe62f749806b60Jin Caoimport android.graphics.Paint; 26c12da4a53eee911765975f0d8afe62f749806b60Jin Caoimport android.graphics.Rect; 27c12da4a53eee911765975f0d8afe62f749806b60Jin Caoimport android.graphics.Shader; 28c12da4a53eee911765975f0d8afe62f749806b60Jin Caoimport android.graphics.drawable.Drawable; 29c12da4a53eee911765975f0d8afe62f749806b60Jin Cao 30c12da4a53eee911765975f0d8afe62f749806b60Jin Caoimport com.android.bitmap.BitmapCache; 31c12da4a53eee911765975f0d8afe62f749806b60Jin Caoimport com.android.bitmap.RequestKey; 32c12da4a53eee911765975f0d8afe62f749806b60Jin Caoimport com.android.bitmap.ReusableBitmap; 33c12da4a53eee911765975f0d8afe62f749806b60Jin Cao 34c12da4a53eee911765975f0d8afe62f749806b60Jin Caoimport com.android.mail.R; 35c12da4a53eee911765975f0d8afe62f749806b60Jin Caoimport com.android.mail.bitmap.ContactResolver.ContactDrawableInterface; 36c12da4a53eee911765975f0d8afe62f749806b60Jin Cao 37c12da4a53eee911765975f0d8afe62f749806b60Jin Cao/** 38c12da4a53eee911765975f0d8afe62f749806b60Jin Cao * A drawable that encapsulates all the functionality needed to display a contact image, 39c12da4a53eee911765975f0d8afe62f749806b60Jin Cao * including request creation/cancelling and data unbinding/re-binding. 40c12da4a53eee911765975f0d8afe62f749806b60Jin Cao * <p> 41c12da4a53eee911765975f0d8afe62f749806b60Jin Cao * The actual contact resolving and decoding is handled by {@link ContactResolver}. 42c12da4a53eee911765975f0d8afe62f749806b60Jin Cao * <p> 43c12da4a53eee911765975f0d8afe62f749806b60Jin Cao * For better performance, you should define a cache with {@link #setBitmapCache(BitmapCache)}. 44c12da4a53eee911765975f0d8afe62f749806b60Jin Cao */ 45c12da4a53eee911765975f0d8afe62f749806b60Jin Caopublic abstract class AbstractAvatarDrawable extends Drawable implements ContactDrawableInterface { 46c12da4a53eee911765975f0d8afe62f749806b60Jin Cao protected final Resources mResources; 47c12da4a53eee911765975f0d8afe62f749806b60Jin Cao 48c12da4a53eee911765975f0d8afe62f749806b60Jin Cao private BitmapCache mCache; 49c12da4a53eee911765975f0d8afe62f749806b60Jin Cao private ContactResolver mContactResolver; 50c12da4a53eee911765975f0d8afe62f749806b60Jin Cao 51c12da4a53eee911765975f0d8afe62f749806b60Jin Cao protected ContactRequest mContactRequest; 52c12da4a53eee911765975f0d8afe62f749806b60Jin Cao protected ReusableBitmap mBitmap; 53c12da4a53eee911765975f0d8afe62f749806b60Jin Cao 54c12da4a53eee911765975f0d8afe62f749806b60Jin Cao protected final float mBorderWidth; 55c12da4a53eee911765975f0d8afe62f749806b60Jin Cao protected final Paint mBitmapPaint; 56c12da4a53eee911765975f0d8afe62f749806b60Jin Cao protected final Paint mBorderPaint; 57c12da4a53eee911765975f0d8afe62f749806b60Jin Cao protected final Matrix mMatrix; 58c12da4a53eee911765975f0d8afe62f749806b60Jin Cao 59c12da4a53eee911765975f0d8afe62f749806b60Jin Cao private int mDecodedWidth; 60c12da4a53eee911765975f0d8afe62f749806b60Jin Cao private int mDecodedHeight; 61c12da4a53eee911765975f0d8afe62f749806b60Jin Cao 62c12da4a53eee911765975f0d8afe62f749806b60Jin Cao public AbstractAvatarDrawable(final Resources res) { 63c12da4a53eee911765975f0d8afe62f749806b60Jin Cao mResources = res; 64c12da4a53eee911765975f0d8afe62f749806b60Jin Cao 65c12da4a53eee911765975f0d8afe62f749806b60Jin Cao mBitmapPaint = new Paint(); 66c12da4a53eee911765975f0d8afe62f749806b60Jin Cao mBitmapPaint.setAntiAlias(true); 67c12da4a53eee911765975f0d8afe62f749806b60Jin Cao mBitmapPaint.setFilterBitmap(true); 68c12da4a53eee911765975f0d8afe62f749806b60Jin Cao mBitmapPaint.setDither(true); 69c12da4a53eee911765975f0d8afe62f749806b60Jin Cao 70c12da4a53eee911765975f0d8afe62f749806b60Jin Cao mBorderWidth = res.getDimensionPixelSize(R.dimen.avatar_border_width); 71c12da4a53eee911765975f0d8afe62f749806b60Jin Cao 72c12da4a53eee911765975f0d8afe62f749806b60Jin Cao mBorderPaint = new Paint(); 73c12da4a53eee911765975f0d8afe62f749806b60Jin Cao mBorderPaint.setColor(Color.TRANSPARENT); 74c12da4a53eee911765975f0d8afe62f749806b60Jin Cao mBorderPaint.setStyle(Paint.Style.STROKE); 75c12da4a53eee911765975f0d8afe62f749806b60Jin Cao mBorderPaint.setStrokeWidth(mBorderWidth); 76c12da4a53eee911765975f0d8afe62f749806b60Jin Cao mBorderPaint.setAntiAlias(true); 77c12da4a53eee911765975f0d8afe62f749806b60Jin Cao 78c12da4a53eee911765975f0d8afe62f749806b60Jin Cao mMatrix = new Matrix(); 79c12da4a53eee911765975f0d8afe62f749806b60Jin Cao } 80c12da4a53eee911765975f0d8afe62f749806b60Jin Cao 81c12da4a53eee911765975f0d8afe62f749806b60Jin Cao public void setBitmapCache(final BitmapCache cache) { 82c12da4a53eee911765975f0d8afe62f749806b60Jin Cao mCache = cache; 83c12da4a53eee911765975f0d8afe62f749806b60Jin Cao } 84c12da4a53eee911765975f0d8afe62f749806b60Jin Cao 85c12da4a53eee911765975f0d8afe62f749806b60Jin Cao public void setContactResolver(final ContactResolver contactResolver) { 86c12da4a53eee911765975f0d8afe62f749806b60Jin Cao mContactResolver = contactResolver; 87c12da4a53eee911765975f0d8afe62f749806b60Jin Cao } 88c12da4a53eee911765975f0d8afe62f749806b60Jin Cao 89c12da4a53eee911765975f0d8afe62f749806b60Jin Cao @Override 90c12da4a53eee911765975f0d8afe62f749806b60Jin Cao public void draw(final Canvas canvas) { 91c12da4a53eee911765975f0d8afe62f749806b60Jin Cao final Rect bounds = getBounds(); 92c12da4a53eee911765975f0d8afe62f749806b60Jin Cao if (!isVisible() || bounds.isEmpty()) { 93c12da4a53eee911765975f0d8afe62f749806b60Jin Cao return; 94c12da4a53eee911765975f0d8afe62f749806b60Jin Cao } 95c12da4a53eee911765975f0d8afe62f749806b60Jin Cao 96c12da4a53eee911765975f0d8afe62f749806b60Jin Cao if (mBitmap != null && mBitmap.bmp != null) { 97c12da4a53eee911765975f0d8afe62f749806b60Jin Cao // Draw sender image. 98c12da4a53eee911765975f0d8afe62f749806b60Jin Cao drawBitmap(mBitmap.bmp, mBitmap.getLogicalWidth(), mBitmap.getLogicalHeight(), canvas); 99c12da4a53eee911765975f0d8afe62f749806b60Jin Cao } else { 100c12da4a53eee911765975f0d8afe62f749806b60Jin Cao // Draw the default image 101c12da4a53eee911765975f0d8afe62f749806b60Jin Cao drawDefaultAvatar(canvas); 102c12da4a53eee911765975f0d8afe62f749806b60Jin Cao } 103c12da4a53eee911765975f0d8afe62f749806b60Jin Cao } 104c12da4a53eee911765975f0d8afe62f749806b60Jin Cao 105c12da4a53eee911765975f0d8afe62f749806b60Jin Cao protected abstract void drawDefaultAvatar(Canvas canvas); 106c12da4a53eee911765975f0d8afe62f749806b60Jin Cao 107c12da4a53eee911765975f0d8afe62f749806b60Jin Cao /** 108c12da4a53eee911765975f0d8afe62f749806b60Jin Cao * Draw the bitmap onto the canvas at the current bounds taking into account the current scale. 109c12da4a53eee911765975f0d8afe62f749806b60Jin Cao */ 110c12da4a53eee911765975f0d8afe62f749806b60Jin Cao protected void drawBitmap(final Bitmap bitmap, final int width, final int height, 111c12da4a53eee911765975f0d8afe62f749806b60Jin Cao final Canvas canvas) { 112c12da4a53eee911765975f0d8afe62f749806b60Jin Cao final Rect bounds = getBounds(); 113c12da4a53eee911765975f0d8afe62f749806b60Jin Cao // Draw bitmap through shader first. 114c12da4a53eee911765975f0d8afe62f749806b60Jin Cao final BitmapShader shader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, 115c12da4a53eee911765975f0d8afe62f749806b60Jin Cao Shader.TileMode.CLAMP); 116c12da4a53eee911765975f0d8afe62f749806b60Jin Cao mMatrix.reset(); 117c12da4a53eee911765975f0d8afe62f749806b60Jin Cao 118c12da4a53eee911765975f0d8afe62f749806b60Jin Cao // Fit bitmap to bounds. 119c12da4a53eee911765975f0d8afe62f749806b60Jin Cao final float boundsWidth = (float) bounds.width(); 120c12da4a53eee911765975f0d8afe62f749806b60Jin Cao final float boundsHeight = (float) bounds.height(); 121c12da4a53eee911765975f0d8afe62f749806b60Jin Cao final float scale = Math.max(boundsWidth / width, boundsHeight / height); 122c12da4a53eee911765975f0d8afe62f749806b60Jin Cao mMatrix.postScale(scale, scale); 123c12da4a53eee911765975f0d8afe62f749806b60Jin Cao 124c12da4a53eee911765975f0d8afe62f749806b60Jin Cao // Translate bitmap to dst bounds. 125c12da4a53eee911765975f0d8afe62f749806b60Jin Cao mMatrix.postTranslate(bounds.left, bounds.top); 126c12da4a53eee911765975f0d8afe62f749806b60Jin Cao 127c12da4a53eee911765975f0d8afe62f749806b60Jin Cao shader.setLocalMatrix(mMatrix); 128c12da4a53eee911765975f0d8afe62f749806b60Jin Cao mBitmapPaint.setShader(shader); 129c12da4a53eee911765975f0d8afe62f749806b60Jin Cao drawCircle(canvas, bounds, mBitmapPaint); 130c12da4a53eee911765975f0d8afe62f749806b60Jin Cao 131c12da4a53eee911765975f0d8afe62f749806b60Jin Cao // Then draw the border. 132c12da4a53eee911765975f0d8afe62f749806b60Jin Cao final float radius = bounds.width() / 2f - mBorderWidth / 2; 133c12da4a53eee911765975f0d8afe62f749806b60Jin Cao canvas.drawCircle(bounds.centerX(), bounds.centerY(), radius, mBorderPaint); 134c12da4a53eee911765975f0d8afe62f749806b60Jin Cao } 135c12da4a53eee911765975f0d8afe62f749806b60Jin Cao 136c12da4a53eee911765975f0d8afe62f749806b60Jin Cao /** 137c12da4a53eee911765975f0d8afe62f749806b60Jin Cao * Draws the largest circle that fits within the given <code>bounds</code>. 138c12da4a53eee911765975f0d8afe62f749806b60Jin Cao * 139c12da4a53eee911765975f0d8afe62f749806b60Jin Cao * @param canvas the canvas on which to draw 140c12da4a53eee911765975f0d8afe62f749806b60Jin Cao * @param bounds the bounding box of the circle 141c12da4a53eee911765975f0d8afe62f749806b60Jin Cao * @param paint the paint with which to draw 142c12da4a53eee911765975f0d8afe62f749806b60Jin Cao */ 143c12da4a53eee911765975f0d8afe62f749806b60Jin Cao protected static void drawCircle(Canvas canvas, Rect bounds, Paint paint) { 144c12da4a53eee911765975f0d8afe62f749806b60Jin Cao canvas.drawCircle(bounds.centerX(), bounds.centerY(), bounds.width() / 2, paint); 145c12da4a53eee911765975f0d8afe62f749806b60Jin Cao } 146c12da4a53eee911765975f0d8afe62f749806b60Jin Cao 147c12da4a53eee911765975f0d8afe62f749806b60Jin Cao @Override 148c12da4a53eee911765975f0d8afe62f749806b60Jin Cao public int getDecodeWidth() { 149c12da4a53eee911765975f0d8afe62f749806b60Jin Cao return mDecodedWidth; 150c12da4a53eee911765975f0d8afe62f749806b60Jin Cao } 151c12da4a53eee911765975f0d8afe62f749806b60Jin Cao 152c12da4a53eee911765975f0d8afe62f749806b60Jin Cao @Override 153c12da4a53eee911765975f0d8afe62f749806b60Jin Cao public int getDecodeHeight() { 154c12da4a53eee911765975f0d8afe62f749806b60Jin Cao return mDecodedHeight; 155c12da4a53eee911765975f0d8afe62f749806b60Jin Cao } 156c12da4a53eee911765975f0d8afe62f749806b60Jin Cao 157c12da4a53eee911765975f0d8afe62f749806b60Jin Cao public void setDecodeDimensions(final int decodeWidth, final int decodeHeight) { 158c12da4a53eee911765975f0d8afe62f749806b60Jin Cao mDecodedWidth = decodeWidth; 159c12da4a53eee911765975f0d8afe62f749806b60Jin Cao mDecodedHeight = decodeHeight; 160c12da4a53eee911765975f0d8afe62f749806b60Jin Cao } 161c12da4a53eee911765975f0d8afe62f749806b60Jin Cao 162c12da4a53eee911765975f0d8afe62f749806b60Jin Cao public void unbind() { 163c12da4a53eee911765975f0d8afe62f749806b60Jin Cao setImage(null); 164c12da4a53eee911765975f0d8afe62f749806b60Jin Cao } 165c12da4a53eee911765975f0d8afe62f749806b60Jin Cao 166c12da4a53eee911765975f0d8afe62f749806b60Jin Cao public void bind(final String name, final String email) { 167c12da4a53eee911765975f0d8afe62f749806b60Jin Cao setImage(new ContactRequest(name, email)); 168c12da4a53eee911765975f0d8afe62f749806b60Jin Cao } 169c12da4a53eee911765975f0d8afe62f749806b60Jin Cao 170c12da4a53eee911765975f0d8afe62f749806b60Jin Cao private void setImage(final ContactRequest contactRequest) { 171c12da4a53eee911765975f0d8afe62f749806b60Jin Cao if (mContactRequest != null && mContactRequest.equals(contactRequest)) { 172c12da4a53eee911765975f0d8afe62f749806b60Jin Cao return; 173c12da4a53eee911765975f0d8afe62f749806b60Jin Cao } 174c12da4a53eee911765975f0d8afe62f749806b60Jin Cao 175c12da4a53eee911765975f0d8afe62f749806b60Jin Cao if (mBitmap != null) { 176c12da4a53eee911765975f0d8afe62f749806b60Jin Cao mBitmap.releaseReference(); 177c12da4a53eee911765975f0d8afe62f749806b60Jin Cao mBitmap = null; 178c12da4a53eee911765975f0d8afe62f749806b60Jin Cao } 179c12da4a53eee911765975f0d8afe62f749806b60Jin Cao 180c12da4a53eee911765975f0d8afe62f749806b60Jin Cao if (mContactResolver != null) { 181c12da4a53eee911765975f0d8afe62f749806b60Jin Cao mContactResolver.remove(mContactRequest, this); 182c12da4a53eee911765975f0d8afe62f749806b60Jin Cao } 183c12da4a53eee911765975f0d8afe62f749806b60Jin Cao 184c12da4a53eee911765975f0d8afe62f749806b60Jin Cao mContactRequest = contactRequest; 185c12da4a53eee911765975f0d8afe62f749806b60Jin Cao 186c12da4a53eee911765975f0d8afe62f749806b60Jin Cao if (contactRequest == null) { 187c12da4a53eee911765975f0d8afe62f749806b60Jin Cao invalidateSelf(); 188c12da4a53eee911765975f0d8afe62f749806b60Jin Cao return; 189c12da4a53eee911765975f0d8afe62f749806b60Jin Cao } 190c12da4a53eee911765975f0d8afe62f749806b60Jin Cao 191c12da4a53eee911765975f0d8afe62f749806b60Jin Cao ReusableBitmap cached = null; 192c12da4a53eee911765975f0d8afe62f749806b60Jin Cao if (mCache != null) { 193c12da4a53eee911765975f0d8afe62f749806b60Jin Cao cached = mCache.get(contactRequest, true /* incrementRefCount */); 194c12da4a53eee911765975f0d8afe62f749806b60Jin Cao } 195c12da4a53eee911765975f0d8afe62f749806b60Jin Cao 196c12da4a53eee911765975f0d8afe62f749806b60Jin Cao if (cached != null) { 197c12da4a53eee911765975f0d8afe62f749806b60Jin Cao setBitmap(cached); 198c12da4a53eee911765975f0d8afe62f749806b60Jin Cao } else { 199c12da4a53eee911765975f0d8afe62f749806b60Jin Cao decode(); 200c12da4a53eee911765975f0d8afe62f749806b60Jin Cao } 201c12da4a53eee911765975f0d8afe62f749806b60Jin Cao } 202c12da4a53eee911765975f0d8afe62f749806b60Jin Cao 203c12da4a53eee911765975f0d8afe62f749806b60Jin Cao private void decode() { 204c12da4a53eee911765975f0d8afe62f749806b60Jin Cao if (mContactRequest == null) { 205c12da4a53eee911765975f0d8afe62f749806b60Jin Cao return; 206c12da4a53eee911765975f0d8afe62f749806b60Jin Cao } 207c12da4a53eee911765975f0d8afe62f749806b60Jin Cao // Add to batch. 208c12da4a53eee911765975f0d8afe62f749806b60Jin Cao mContactResolver.add(mContactRequest, this); 209c12da4a53eee911765975f0d8afe62f749806b60Jin Cao } 210c12da4a53eee911765975f0d8afe62f749806b60Jin Cao 211c12da4a53eee911765975f0d8afe62f749806b60Jin Cao private void setBitmap(final ReusableBitmap bmp) { 212c12da4a53eee911765975f0d8afe62f749806b60Jin Cao if (mBitmap != null && mBitmap != bmp) { 213c12da4a53eee911765975f0d8afe62f749806b60Jin Cao mBitmap.releaseReference(); 214c12da4a53eee911765975f0d8afe62f749806b60Jin Cao } 215c12da4a53eee911765975f0d8afe62f749806b60Jin Cao mBitmap = bmp; 216c12da4a53eee911765975f0d8afe62f749806b60Jin Cao invalidateSelf(); 217c12da4a53eee911765975f0d8afe62f749806b60Jin Cao } 218c12da4a53eee911765975f0d8afe62f749806b60Jin Cao 219c12da4a53eee911765975f0d8afe62f749806b60Jin Cao @Override 220c12da4a53eee911765975f0d8afe62f749806b60Jin Cao public void onDecodeComplete(RequestKey key, ReusableBitmap result) { 221c12da4a53eee911765975f0d8afe62f749806b60Jin Cao final ContactRequest request = (ContactRequest) key; 222c12da4a53eee911765975f0d8afe62f749806b60Jin Cao // Remove from batch. 223c12da4a53eee911765975f0d8afe62f749806b60Jin Cao mContactResolver.remove(request, this); 224c12da4a53eee911765975f0d8afe62f749806b60Jin Cao if (request.equals(mContactRequest)) { 225c12da4a53eee911765975f0d8afe62f749806b60Jin Cao setBitmap(result); 226c12da4a53eee911765975f0d8afe62f749806b60Jin Cao } else { 227c12da4a53eee911765975f0d8afe62f749806b60Jin Cao // if the requests don't match (i.e. this request is stale), decrement the 228c12da4a53eee911765975f0d8afe62f749806b60Jin Cao // ref count to allow the bitmap to be pooled 229c12da4a53eee911765975f0d8afe62f749806b60Jin Cao if (result != null) { 230c12da4a53eee911765975f0d8afe62f749806b60Jin Cao result.releaseReference(); 231c12da4a53eee911765975f0d8afe62f749806b60Jin Cao } 232c12da4a53eee911765975f0d8afe62f749806b60Jin Cao } 233c12da4a53eee911765975f0d8afe62f749806b60Jin Cao } 234c12da4a53eee911765975f0d8afe62f749806b60Jin Cao 235c12da4a53eee911765975f0d8afe62f749806b60Jin Cao @Override 236c12da4a53eee911765975f0d8afe62f749806b60Jin Cao public void setAlpha(int alpha) { 237c12da4a53eee911765975f0d8afe62f749806b60Jin Cao mBitmapPaint.setAlpha(alpha); 238c12da4a53eee911765975f0d8afe62f749806b60Jin Cao } 239c12da4a53eee911765975f0d8afe62f749806b60Jin Cao 240c12da4a53eee911765975f0d8afe62f749806b60Jin Cao @Override 241c12da4a53eee911765975f0d8afe62f749806b60Jin Cao public void setColorFilter(ColorFilter cf) { 242c12da4a53eee911765975f0d8afe62f749806b60Jin Cao mBitmapPaint.setColorFilter(cf); 243c12da4a53eee911765975f0d8afe62f749806b60Jin Cao } 244c12da4a53eee911765975f0d8afe62f749806b60Jin Cao 245c12da4a53eee911765975f0d8afe62f749806b60Jin Cao @Override 246c12da4a53eee911765975f0d8afe62f749806b60Jin Cao public int getOpacity() { 247c12da4a53eee911765975f0d8afe62f749806b60Jin Cao return 0; 248c12da4a53eee911765975f0d8afe62f749806b60Jin Cao } 249c12da4a53eee911765975f0d8afe62f749806b60Jin Cao} 250