修改动态和部分我的界面

This commit is contained in:
被淹死的鱼
2026-03-17 20:24:06 +08:00
parent 48f01f0942
commit b03b04dad5
1166 changed files with 10063 additions and 6325 deletions

View File

@@ -0,0 +1,90 @@
package com.xuebiping.bolizhuzi.utils;
import android.text.TextUtils;
import android.util.Base64;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;
/**
* Created by zhangbin on 2019/1/23.
*/
public class AESUtils {
private static final String CBC_PKCS5_PADDING = "AES/ECB/PKCS7Padding";//AES是加密方式 CBC是工作模式 PKCS5Padding是填充模式
private static final String AES = "AES";//AES 加密
/**
* 加密
*
* @param content 需要加密的内容
* @param password 加密密码
* @return
*/
public static String encrypt(String password, String content) {
if (TextUtils.isEmpty(content)) {
return content;
}
try {
SecretKeySpec key = new SecretKeySpec(password.getBytes(), AES);
Cipher cipher = Cipher.getInstance(CBC_PKCS5_PADDING);
byte[] byteContent = content.getBytes("utf-8");
cipher.init(Cipher.ENCRYPT_MODE, key);// 初始化
byte[] result = cipher.doFinal(byteContent);
return new String(Base64.encode(result, Base64.DEFAULT)); // 加密
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
return null;
}
/**
* 解密
*
* @param content 待解密内容
* @param password 解密密钥
* @return
*/
public static String decrypt(String password, String content) {
if (TextUtils.isEmpty(content)) {
return content;
}
try {
byte[] enc = Base64.decode(content, Base64.DEFAULT);
SecretKeySpec key = new SecretKeySpec(password.getBytes(), AES);
Cipher cipher = Cipher.getInstance(AES);// 创建密码器
cipher.init(Cipher.DECRYPT_MODE, key);// 初始化
byte[] result = cipher.doFinal(enc);
return new String(result); // 加密
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
return null;
}
}

View File

@@ -0,0 +1,42 @@
package com.xuebiping.bolizhuzi.utils;
import android.content.Context;
import android.content.res.TypedArray;
import android.widget.ImageView;
import com.xuebiping.bolizhuzi.R;
public class AnimUtils {
public static FrameAnimation startGuardAnimation(Context context, ImageView imageView) {
return startGuardAnimation_(context,imageView,R.array.c);
}
public static FrameAnimation startGuardAnimation_(Context context,ImageView imageView,int id){
FrameAnimation frameAnimation = new FrameAnimation(imageView, getRes(context,id), 30, true);
frameAnimation.setAnimationListener(new FrameAnimation.AnimationListener() {
@Override
public void onAnimationStart() {
}
@Override
public void onAnimationEnd() {
}
@Override
public void onAnimationRepeat() {
}
});
return frameAnimation;
}
private static int[] getRes(Context context,int id) {
TypedArray typedArray = context.getResources().obtainTypedArray(id);
int len = typedArray.length();
int[] resId = new int[len];
for (int i = 0; i < len; i++) {
resId[i] = typedArray.getResourceId(i, -1);
}
typedArray.recycle();
return resId;
}
}

View File

@@ -0,0 +1,36 @@
package com.xuebiping.bolizhuzi.utils;
import com.google.android.material.appbar.AppBarLayout;
public abstract class AppBarStateChangeListener implements AppBarLayout.OnOffsetChangedListener {
public enum State {
EXPANDED,
COLLAPSED,
IDLE
}
private State mCurrentState = State.IDLE;
@Override
public final void onOffsetChanged(AppBarLayout appBarLayout, int i) {
if (i == 0) {
if (mCurrentState != State.EXPANDED) {
onStateChanged(appBarLayout, State.EXPANDED);
}
mCurrentState = State.EXPANDED;
} else if (Math.abs(i) >= appBarLayout.getTotalScrollRange()) {
if (mCurrentState != State.COLLAPSED) {
onStateChanged(appBarLayout, State.COLLAPSED);
}
mCurrentState = State.COLLAPSED;
} else {
if (mCurrentState != State.IDLE) {
onStateChanged(appBarLayout, State.IDLE);
}
mCurrentState = State.IDLE;
}
}
public abstract void onStateChanged(AppBarLayout appBarLayout, State state);
}

View File

@@ -0,0 +1,70 @@
package com.xuebiping.bolizhuzi.utils;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.MotionEvent;
import java.lang.ref.WeakReference;
public class AutoPollRecyclerView extends RecyclerView {
private static final long TIME_AUTO_POLL = 30;
AutoPollTask autoPollTask;
private boolean running;//标识是否正在自动轮询
private boolean canRun;//标识是否可以自动轮询可在不需要时置false
public AutoPollRecyclerView(@NonNull Context context, AttributeSet attrs) {
super(context, attrs);
autoPollTask = new AutoPollTask(this);
}
static class AutoPollTask implements Runnable {
private final WeakReference<AutoPollRecyclerView> mReference;
public AutoPollTask(AutoPollRecyclerView reference) {
this.mReference = new WeakReference<AutoPollRecyclerView>(reference);
}
@Override
public void run() {
AutoPollRecyclerView recyclerView = mReference.get();
if (recyclerView != null && recyclerView.running && recyclerView.canRun) {
recyclerView.scrollBy(2, 2);
recyclerView.postDelayed(recyclerView.autoPollTask, recyclerView.TIME_AUTO_POLL);
}
}
}
public void start() {
if (running)
stop();
canRun = true;
running = true;
postDelayed(autoPollTask, TIME_AUTO_POLL);
}
public void stop() {
running = false;
removeCallbacks(autoPollTask);
}
@Override
public boolean onTouchEvent(MotionEvent e) {
switch (e.getAction()) {
case MotionEvent.ACTION_DOWN:
if (running)
stop();
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_OUTSIDE:
if (canRun)
start();
break;
}
return super.onTouchEvent(e);
}
}

View File

@@ -0,0 +1,29 @@
package com.xuebiping.bolizhuzi.utils;
import android.widget.RelativeLayout;
import com.fengliyan.uikit.emoji.utils.ScreenUtil;
import com.youth.banner.Banner;
/**
* ying 2020/11/30
* Describe
*/
public class BaseUtils {
public static Banner setBannerLayoutParams(Banner banner) {
int displayWidth = ScreenUtil.getDisplayWidth() - ScreenUtil.dip2px(12);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(displayWidth, displayWidth / 3);
banner.setLayoutParams(params);
return banner;
}
public static String setTextMoney(double totalMoney) {
String value = "";
if (totalMoney >= 10000) {
double money = totalMoney / 10000;
value = String.format("%.1f", money - 0.05) + "";
} else {
value = String.format("%.0f", totalMoney);
}
return value;
}
}

View File

@@ -0,0 +1,74 @@
package com.xuebiping.bolizhuzi.utils;
import android.content.Context;
import android.content.SharedPreferences;
import com.xuebiping.bolizhuzi.view.base.BaseApplication;
/**
* Created by zhangbin on 2019/1/19.
*/
public class BeautySPUtils {
private final static String name = "beauty_config";
private final static int mode = Context.MODE_PRIVATE;
/**
* 保存首选项
* @param key
* @param value
*/
public static void saveBoolean(String key, boolean value){
SharedPreferences sp = BaseApplication.getInstance().getApplicationContext().getSharedPreferences(name, mode);
SharedPreferences.Editor edit = sp.edit();
edit.putBoolean(key, value);
edit.commit();
}
public static void saveFloat(String key, Float value) {
SharedPreferences sp = BaseApplication.getInstance().getApplicationContext().getSharedPreferences(name, mode);
SharedPreferences.Editor edit = sp.edit();
edit.putFloat(key, value);
edit.commit();
}
public static void saveString(String key, String value){
SharedPreferences sp = BaseApplication.getInstance().getApplicationContext().getSharedPreferences(name, mode);
SharedPreferences.Editor edit = sp.edit();
edit.putString(key, value);
edit.commit();
}
/**
* 获取首选项
* @param key
* @return
*/
public static boolean getBoolean(String key){
SharedPreferences sp = BaseApplication.getInstance().getApplicationContext().getSharedPreferences(name, mode);
return sp.getBoolean(key, false);
}
public static Float getFloat(String key) {
SharedPreferences sp = BaseApplication.getInstance().getApplicationContext().getSharedPreferences(name, mode);
return sp.getFloat(key, 0);
}
public static String getString(String key) {
SharedPreferences sp = BaseApplication.getInstance().getApplicationContext().getSharedPreferences(name, mode);
return sp.getString(key, "");
}
/**
* 清除保存
*/
public static void clear(){
SharedPreferences sp = BaseApplication.getInstance().getApplicationContext().getSharedPreferences(name, mode);
SharedPreferences.Editor editor = sp.edit();
editor.clear();
editor.commit();
}
}

View File

@@ -0,0 +1,46 @@
package com.xuebiping.bolizhuzi.utils;
import android.graphics.Bitmap;
import android.util.LruCache;
/**
* Created by dmw on 2023/10/30
* Desc: 内存缓存存储bitmap
* keybitmap对应的文件路径
*/
public class BitmapLruCache {
private static final String TAG = "BitmapLruCache";
private LruCache<String, Bitmap> cache;
public BitmapLruCache() {
// Calculate the maximum memory available to the cache
int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
// Set the cache size to be a fraction of the available memory
int cacheSize = maxMemory / 8;
// Initialize the LruCache with the calculated cache size
cache = new LruCache<String, Bitmap>(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap bitmap) {
// Return the size of the bitmap in kilobytes
return bitmap.getByteCount() / 1024;
}
};
}
public void putBitmap(String key, Bitmap bitmap) {
cache.put(key, bitmap);
}
public Bitmap getBitmap(String key) {
return cache.get(key);
}
public void clearCache() {
cache.evictAll();
}
}

View File

@@ -0,0 +1,64 @@
package com.xuebiping.bolizhuzi.utils;
import com.fengliyan.http.httprequest.HttpMethod;
import com.fengliyan.http.httprequest.TType;
import com.xuebiping.bolizhuzi.controller.constant.ConstUrl;
import com.xuebiping.bolizhuzi.view.base.BaseActivity;
import com.xuebiping.bolizhuzi.view.base.utils.HttpUiCallBack;
import com.xuebiping.bolizhuzi.view.base.utils.http.HttpRequest;
import com.xuebiping.bolizhuzi.view.base.utils.http.HttpResult;
import com.xuebiping.bolizhuzi.view.base.utils.task.HttpWithUiTask;
/**
* 埋点工具类
*/
public class BuriedPointUtils {
/**
* 1=礼包弹出
*/
public static final String BURIED1 = "1";
/**
* 2=点击了充值的人
*/
public static final String BURIED2 = "2";
/**
* 3=充值8元的人、3=充值18元的人、3=充值28元的人
*/
public static final String BURIED3 = "3";
//用户行为数据埋点
public static void buriedPoint(BaseActivity activity, String type_id) {
HttpWithUiTask<Object> task = new HttpWithUiTask<Object>(activity, new HttpUiCallBack<Object>() {
@Override
public void onSuccess(BaseActivity activity, Object result, String tips) {
}
@Override
public void onFailure(BaseActivity activity, String tip) {
}
@Override
public void onException(BaseActivity activity, Throwable e) {
}
}) {
@Override
protected HttpResult<Object> run() throws Exception {
final String url = ConstUrl.FIRST_RECHARGE_BURIED_POINT;
HttpRequest.Builder builder = new HttpRequest.Builder()
.setResultType(new TType<HttpResult<Object>>() {
}).setHttpMethod(HttpMethod.POST)
.addParam("type_id", type_id)
.setUrl(url);
HttpRequest request = builder.build();
return request.request();
}
};
task.start();
}
}

View File

@@ -0,0 +1,330 @@
package com.xuebiping.bolizhuzi.utils;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.os.Handler;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.widget.EditText;
import com.xuebiping.bolizhuzi.R;
import java.util.ArrayList;
import java.util.List;
import static android.graphics.Paint.ANTI_ALIAS_FLAG;
/**
* @author luchao
*/
public class CaptchaInputView extends EditText {
private int borderColor;
private float borderWidth;
private float borderRadius;
private int passwordLength = 4;
private int passwordColor;
private float passwordWidth;
private float passwordRadius;
private Paint passwordPaint = new Paint(ANTI_ALIAS_FLAG);
private Paint borderPaint = new Paint(ANTI_ALIAS_FLAG);
private Paint linePaint = new Paint(ANTI_ALIAS_FLAG);
private final int defaultContMargin = 5;
private final int defaultSplitLineWidth = 3;
private TextChangeListener mTextChangeListener;
private float mDefaultInputViewTextSize, mDefaultInputViewPadding, mDefaultInputTextSize;
private float mCursorWidth;
private int mCursorHeight;
private float mDefalutMargin = 10;
private boolean mPwdVisiable = true;
private String mInputText;
private List<RectF> rectList;
private Context mContext;
private int mSelectIndex = 0;
private Handler mCursorHandler;
private CursorRunnable mCursorRunnable;
static final int CURSOR_DELAY_TIME = 500;
public CaptchaInputView(Context context, AttributeSet attrs) {
super(context, attrs);
this.mContext = context;
final Resources res = getResources();
final int defaultBorderColor = res.getColor(R.color.default_ev_border_color);
final float defaultBorderWidth = res.getDimension(R.dimen.dp1);
final float defaultBorderRadius = res.getDimension(R.dimen.dp3);
final int defaultPasswordLength = 4;
final int defaultPasswordColor = res.getColor(R.color.default_ev_password_color);
final float defaultPasswordWidth = res.getDimension(R.dimen.dp10);
final float defaultPasswordRadius = res.getDimension(R.dimen.dp3);
final float defaultInputViewTextSize = res.getDimension(R.dimen.dp60);
final float defaultInputViewPadding = res.getDimension(R.dimen.dp12);
final float defaultInputTextSize = res.getDimension(R.dimen.dp22);
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.PasswordInputView, 0, 0);
try {
borderColor = a.getColor(R.styleable.PasswordInputView_captchaBorderColor, defaultBorderColor);
borderWidth = a.getDimension(R.styleable.PasswordInputView_captchaBorderWidth, defaultBorderWidth);
borderRadius = a.getDimension(R.styleable.PasswordInputView_captchaBorderRadius, defaultBorderRadius);
passwordLength = a.getInt(R.styleable.PasswordInputView_captchaLength, defaultPasswordLength);
passwordColor = a.getColor(R.styleable.PasswordInputView_captchaColor, defaultPasswordColor);
passwordWidth = a.getDimension(R.styleable.PasswordInputView_captchaWidth, defaultPasswordWidth);
passwordRadius = a.getDimension(R.styleable.PasswordInputView_captchaRadius, defaultPasswordRadius);
mDefaultInputViewTextSize = a.getDimension(R.styleable.PasswordInputView_captchaViewSize, defaultInputViewTextSize);
mDefaultInputViewPadding = a.getDimension(R.styleable.PasswordInputView_captchaViewSize, defaultInputViewPadding);
mDefaultInputTextSize = a.getDimension(R.styleable.PasswordInputView_captchaTextSize, defaultInputTextSize);
} finally {
a.recycle();
}
mCursorWidth = mContext.getResources().getDimension(R.dimen.dp1);
mCursorHeight = (int) mContext.getResources().getDimension(R.dimen.dp30);
borderPaint.setStrokeWidth(borderWidth);
borderPaint.setColor(borderColor);
linePaint.setColor(getResources().getColor(R.color.select_border_color));
linePaint.setStrokeWidth(mCursorWidth); //绘制直线
borderPaint.setStyle(Paint.Style.FILL);
borderPaint.setAntiAlias(true);
passwordPaint.setColor(passwordColor);
passwordPaint.setTextSize(mDefaultInputTextSize);
rectList = new ArrayList<>();
mCursorHandler = new Handler();
mCursorRunnable = new CursorRunnable();
mCursorHandler.post(mCursorRunnable);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = passwordLength * (int)mDefaultInputViewTextSize + (int)mDefaultInputViewPadding * 3 + (int)mDefalutMargin*2;
int height = (int)mDefaultInputViewTextSize + (int)mDefalutMargin*2;
setMeasuredDimension(width, height);
}
private class CursorRunnable implements Runnable {
private boolean mCancelled = false;
private boolean mCursorVisible = false;
@Override
public void run() {
if (mCancelled) {
return;
}
postInvalidate();
postDelayed(this, CURSOR_DELAY_TIME);
}
void cancel() {
if (!mCancelled) {
mCursorHandler.removeCallbacks(this);
mCancelled = true;
}
}
public boolean getCursorVisiable() {
return mCursorVisible = !mCursorVisible;
}
}
public void stopCursor() {
if(mCursorRunnable != null && mCursorHandler != null) {
mCursorRunnable.cancel();
}
}
// 点击事件的处理
// @Override
// public boolean onTouchEvent(MotionEvent event) {
// float x = event.getX();
// float y = event.getY();
// switch (event.getAction()) {
// case MotionEvent.ACTION_DOWN:
// for(int i = 0; i< rectList.size(); i++) {
// RectF rectF = rectList.get(i);
// if(rectF.contains(x, y)) {
// mSelectIndex = i;
// postInvalidate();
//
// Log.d("draw", "index="+i);
// break;
// }
// }
// break;
// }
// return super.onTouchEvent(event);
// }
@Override
protected void onDraw(Canvas canvas) {
rectList.clear();
//边框
int left = (int)mDefalutMargin;
int top = (int)mDefalutMargin;
for(int i = 0; i < passwordLength; i++) {
if(i < mSelectIndex) {
//彩色边框
borderPaint.setColor(getResources().getColor(R.color.select_border_color));
} else {
//灰色边框
borderPaint.setColor(borderColor);
}
RectF rectF = new RectF(left, top, mDefaultInputViewTextSize + left, mDefaultInputViewTextSize + top);
rectList.add(rectF);
canvas.drawRoundRect(rectF, 24, 24, borderPaint);
left+= mDefaultInputViewPadding + mDefaultInputViewTextSize;
}
//内容,密码可见
int textLeft = (int)mDefalutMargin + (int)mDefaultInputViewTextSize/2;
if(mPwdVisiable) {
for(int i = 0; i < mInputText.length(); i++) {
String text = mInputText.substring(i, i + 1);
int textWidth = !TextUtils.isEmpty(text) ? getTextWidth(passwordPaint, text)/2 : 0;
canvas.drawText(text, textLeft - textWidth, mDefaultInputViewTextSize/2 + mDefaultInputTextSize/2, passwordPaint);
textLeft+= mDefaultInputViewPadding + mDefaultInputViewTextSize;
}
} else {
for(int i = 0; i < mInputText.length(); i++) {
String text = mInputText.substring(i, i + 1);
int textWidth = !TextUtils.isEmpty(text) ? getTextWidth(passwordPaint, "*")/2 : 0;
canvas.drawText("*", textLeft - textWidth, mDefaultInputViewTextSize/2 + mDefaultInputTextSize/2 + 5, passwordPaint);
textLeft+= mDefaultInputViewPadding + mDefaultInputViewTextSize;
}
}
//光标
if(mSelectIndex < passwordLength && mCursorRunnable.getCursorVisiable()) {
int cursorLeft = (int) mDefalutMargin;
int cursorTop = (int) mDefalutMargin + ((int) mDefaultInputViewTextSize - mCursorHeight) / 2;
int startX = cursorLeft + (int) mDefaultInputViewTextSize / 2 + mSelectIndex * (int) mDefaultInputViewTextSize + mSelectIndex * (int) mDefaultInputViewPadding;
int stopX = startX;
int startY = cursorTop;
int stopY = startY + mCursorHeight;
canvas.drawLine(startX, startY, stopX, stopY, linePaint);
}
}
public int getTextWidth(Paint paint, String str) {
int iRet = 0;
if (str != null && str.length() > 0) {
int len = str.length();
float[] widths = new float[len];
paint.getTextWidths(str, widths);
for (int j = 0; j < len; j++) {
iRet += (int) Math.ceil(widths[j]);
}
}
return iRet;
}
@Override
protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
super.onTextChanged(text, start, lengthBefore, lengthAfter);
if (mTextChangeListener != null) {
mTextChangeListener.onTextChanged(text, start, lengthBefore, lengthAfter);
}
mInputText = text.toString();
if(mInputText.length() > 0) {
mSelectIndex = mInputText.length();
} else {
mSelectIndex = 0;
}
postInvalidate();
}
public void setPwdVisiable(boolean pwdVisiable) {
this.mPwdVisiable = pwdVisiable;
}
public void setTextLength(int length) {
this.passwordLength = length;
postInvalidate();
}
public int getBorderColor() {
return borderColor;
}
public void setBorderColor(int borderColor) {
this.borderColor = borderColor;
borderPaint.setColor(borderColor);
invalidate();
}
public float getBorderWidth() {
return borderWidth;
}
public void setBorderWidth(float borderWidth) {
this.borderWidth = borderWidth;
borderPaint.setStrokeWidth(borderWidth);
invalidate();
}
public float getBorderRadius() {
return borderRadius;
}
public void setBorderRadius(float borderRadius) {
this.borderRadius = borderRadius;
invalidate();
}
public int getPasswordLength() {
return passwordLength;
}
public void setPasswordLength(int passwordLength) {
this.passwordLength = passwordLength;
invalidate();
}
public int getPasswordColor() {
return passwordColor;
}
public void setPasswordColor(int passwordColor) {
this.passwordColor = passwordColor;
passwordPaint.setColor(passwordColor);
invalidate();
}
public float getPasswordWidth() {
return passwordWidth;
}
public void setPasswordWidth(float passwordWidth) {
this.passwordWidth = passwordWidth;
passwordPaint.setStrokeWidth(passwordWidth);
invalidate();
}
public float getPasswordRadius() {
return passwordRadius;
}
public void setPasswordRadius(float passwordRadius) {
this.passwordRadius = passwordRadius;
invalidate();
}
public void setTextChangeListener(TextChangeListener mTextChangeListener) {
this.mTextChangeListener = mTextChangeListener;
}
public interface TextChangeListener {
void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter);
}
}

View File

@@ -0,0 +1,83 @@
package com.xuebiping.bolizhuzi.utils;
import android.content.Context;
import android.content.SharedPreferences;
/**
* Created by zhangbin on 2019/1/10.
*/
public class CitySPUtils {
private final static String name = "city_config";
private final static int mode = Context.MODE_PRIVATE;
/**
* 保存首选项
* @param context
* @param key
* @param value
*/
public static void saveBoolean(Context context, String key, boolean value){
SharedPreferences sp = context.getSharedPreferences(name, mode);
SharedPreferences.Editor edit = sp.edit();
edit.putBoolean(key, value);
edit.commit();
}
public static void saveInt(Context context, String key, int value){
SharedPreferences sp = context.getSharedPreferences(name, mode);
SharedPreferences.Editor edit = sp.edit();
edit.putInt(key, value);
edit.commit();
}
public static void saveString(Context context, String key, String value){
SharedPreferences sp = context.getSharedPreferences(name, mode);
SharedPreferences.Editor edit = sp.edit();
edit.putString(key, value);
edit.commit();
}
/**
* 获取首选项
* @param context
* @param key
* @param defValue
* @return
*/
public static boolean getBoolean(Context context, String key, boolean defValue){
SharedPreferences sp = context.getSharedPreferences(name, mode);
return sp.getBoolean(key, defValue);
}
public static int getInt(Context context, String key, int defValue){
SharedPreferences sp = context.getSharedPreferences(name, mode);
return sp.getInt(key, defValue);
}
public static int getInt(Context context, String key){
SharedPreferences sp = context.getSharedPreferences(name, mode);
return sp.getInt(key, 0);
}
public static String getString(Context context, String key, String defValue){
SharedPreferences sp = context.getSharedPreferences(name, mode);
return sp.getString(key, defValue);
}
public static String getString(Context context, String key) {
SharedPreferences sp = context.getSharedPreferences(name, mode);
return sp.getString(key, "");
}
/**
* 清除保存
* @param context
*/
public static void clear(Context context){
SharedPreferences sp = context.getSharedPreferences(name, mode);
SharedPreferences.Editor editor = sp.edit();
editor.clear();
editor.commit();
}
}

View File

@@ -0,0 +1,156 @@
package com.xuebiping.bolizhuzi.utils;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import java.util.Random;
/**
* Created by ekikousei易皇星 on 16/11/21.
* E-mail:13764664731@163.com
* Signature:缘分是本书,翻的不经意会错过,读的太认真会流泪!!
* <p/>
* 类描述: 用于图形验证码的工具类
*/
public class CodeUtils {
// private static final char[] CHARS = {
// '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
// 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
// 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
// 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
// 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
// };
// private static final char[] CHARS = {
//
// 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
// 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
//
// };
private static CodeUtils mCodeUtils;
private int mPaddingLeft, mPaddingTop;
private StringBuilder mBuilder = new StringBuilder();
private Random mRandom = new Random();
//Default Settings
// private static final int DEFAULT_CODE_LENGTH = 6;//验证码的长度 这里是6位
private static final int DEFAULT_CODE_LENGTH = 4;//验证码的长度 这里是4位
private static final int DEFAULT_FONT_SIZE = 60;//字体大小
private static final int DEFAULT_LINE_NUMBER = 3;//多少条干扰线
private static final int BASE_PADDING_LEFT = 40; //左边距
private static final int RANGE_PADDING_LEFT = 30;//左边距范围值
private static final int BASE_PADDING_TOP = 70;//上边距
private static final int RANGE_PADDING_TOP = 15;//上边距范围值
private static final int DEFAULT_WIDTH = 300;//默认宽度.图片的总宽
private static final int DEFAULT_HEIGHT = 100;//默认高度.图片的总高
private static final int DEFAULT_COLOR = 0xDF;//默认背景颜色值
private String code;
public static CodeUtils getInstance() {
if (mCodeUtils == null) {
mCodeUtils = new CodeUtils();
}
return mCodeUtils;
}
//生成验证码图片 返回类型为bitmap 直接用imageview.setbitmap()即可
public Bitmap createBitmap(String mCode) {
mPaddingLeft = 0; //每次生成验证码图片时初始化
mPaddingTop = 0;
Bitmap bitmap = Bitmap.createBitmap(DEFAULT_WIDTH, DEFAULT_HEIGHT, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
code = mCode;
canvas.drawColor(Color.rgb(DEFAULT_COLOR, DEFAULT_COLOR, DEFAULT_COLOR));
Paint paint = new Paint();
paint.setTextSize(DEFAULT_FONT_SIZE);
for (int i = 0; i < mCode.length(); i++) {
randomTextStyle(paint);
randomPadding();
canvas.drawText(mCode.charAt(i) + "", mPaddingLeft, mPaddingTop, paint);
}
//干扰线
for (int i = 0; i < DEFAULT_LINE_NUMBER; i++) {
drawLine(canvas, paint);
}
canvas.save();//保存
canvas.restore();
return bitmap;
}
/**
* 得到图片中的验证码字符串
*
* @return
*/
public String getCode() {
return code;
}
//生成验证码
// public String createCode(String code) {
// mBuilder.delete(0, mBuilder.length()); //使用之前首先清空内容
//
// for (int i = 0; i < DEFAULT_CODE_LENGTH; i++) {
// mBuilder.append(CHARS[mRandom.nextInt(CHARS.length)]);
// }
//
// return mBuilder.toString();
// }
//生成干扰线
private void drawLine(Canvas canvas, Paint paint) {
int color = randomColor();
int startX = mRandom.nextInt(DEFAULT_WIDTH);
int startY = mRandom.nextInt(DEFAULT_HEIGHT);
int stopX = mRandom.nextInt(DEFAULT_WIDTH);
int stopY = mRandom.nextInt(DEFAULT_HEIGHT);
paint.setStrokeWidth(1);
paint.setColor(color);
canvas.drawLine(startX, startY, stopX, stopY, paint);
}
//随机颜色
private int randomColor() {
mBuilder.delete(0, mBuilder.length()); //使用之前首先清空内容
String haxString;
for (int i = 0; i < 3; i++) {
haxString = Integer.toHexString(mRandom.nextInt(0xFF));
if (haxString.length() == 1) {
haxString = "0" + haxString;
}
mBuilder.append(haxString);
}
return Color.parseColor("#" + mBuilder.toString());
}
//随机文本样式
private void randomTextStyle(Paint paint) {
int color = randomColor();
paint.setColor(color);
paint.setFakeBoldText(mRandom.nextBoolean()); //true为粗体false为非粗体
float skewX = mRandom.nextInt(11) / 10;
skewX = mRandom.nextBoolean() ? skewX : -skewX;
paint.setTextSkewX(skewX); //float类型参数负数表示右斜整数左斜
// paint.setUnderlineText(true); //true为下划线false为非下划线
// paint.setStrikeThruText(true); //true为删除线false为非删除线
}
//随机间距
private void randomPadding() {
mPaddingLeft += BASE_PADDING_LEFT + mRandom.nextInt(RANGE_PADDING_LEFT);
mPaddingTop = BASE_PADDING_TOP + mRandom.nextInt(RANGE_PADDING_TOP);
}
}

View File

@@ -0,0 +1,175 @@
package com.xuebiping.bolizhuzi.utils;
import android.content.Context;
import android.text.TextUtils;
import android.util.Log;
import com.bun.miitmdid.interfaces.IIdentifierListener;
import com.bun.miitmdid.interfaces.IdSupplier;
import com.github.gzuliyujiang.oaid.DeviceIdentifier;
import com.fengliyan.device.DeviceManager;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
/**
* Date: 16:27 2021/2/26 0026
* Version: 1.0.3
**/
public class DemoHelper implements IIdentifierListener {
public static final String TAG = "DemoHelper";
public static final int HELPER_VERSION_CODE = 20210928; // DemoHelper版本号
private final AppIdsUpdater appIdsUpdater;
private boolean isCertInit = false;
public boolean isSDKLogOn = true;
public static final String ASSET_FILE_NAME_CERT = "com.xuebiping.bolizhuzi.cert.pem"; // 2设置 asset证书文件名
public DemoHelper(AppIdsUpdater appIdsUpdater){
//System.loadLibrary("nllvm1632808251147706677"); // 3加固版本在调用前必须载入SDK安全库
// DemoHelper版本建议与SDK版本一致
this.appIdsUpdater = appIdsUpdater;
}
/**
* 获取OAID
* @param cxt
*/
public void getDeviceIds(Context cxt){
String result = DeviceManager.getInstance().getOaid();
if (!TextUtils.isEmpty(result)){
appIdsUpdater.onIdsValid(result);
}else{
String oaid = DeviceIdentifier.getOAID(cxt);
if (!TextUtils.isEmpty(oaid)){
appIdsUpdater.onIdsValid(oaid);
return;
}
String aid = DeviceIdentifier.getAndroidID(cxt);
if (!TextUtils.isEmpty(aid)){
appIdsUpdater.onIdsValid(aid);
return;
}
String ids = DeviceIdentifier.getPseudoID();
if (!TextUtils.isEmpty(ids)){
appIdsUpdater.onIdsValid(ids);
return;
}
}
// 4初始化SDK证书
// if(!isCertInit){ // 证书只需初始化一次
// // 证书为PEM文件中的所有文本内容包括首尾行、换行符
// isCertInit = MdidSdkHelper.InitCert(cxt, loadPemFromAssetFile(cxt, ASSET_FILE_NAME_CERT));
// if(!isCertInit){
// Log.w(TAG, "getDeviceIds: cert init failed");
// }
// }
//
// //可选设置InitSDK接口回调超时时间(仅适用于接口为异步)默认值为5000ms.
// // 注:请在调用前设置一次后就不再更改,否则可能导致回调丢失、重复等问题
// MdidSdkHelper.setGlobalTimeout(5000);
//
// // 5调用SDK获取ID
// int code = MdidSdkHelper.InitSdk(cxt, isSDKLogOn, this);
//
// // 6根据SDK返回的code进行不同处理
// IdSupplierImpl unsupportedIdSupplier = new IdSupplierImpl();
// if(code == InfoCode.INIT_ERROR_CERT_ERROR){ // 证书未初始化或证书无效SDK内部不会回调onSupport
// // APP自定义逻辑
// Log.w(TAG,"cert not init or check not pass");
// onSupport(unsupportedIdSupplier);
// }else if(code == InfoCode.INIT_ERROR_DEVICE_NOSUPPORT){ // 不支持的设备, SDK内部不会回调onSupport
// // APP自定义逻辑
// Log.w(TAG,"device not supported");
// onSupport(unsupportedIdSupplier);
// }else if( code == InfoCode.INIT_ERROR_LOAD_CONFIGFILE){ // 加载配置文件出错, SDK内部不会回调onSupport
// // APP自定义逻辑
// Log.w(TAG,"failed to load config file");
// onSupport(unsupportedIdSupplier);
// }else if(code == InfoCode.INIT_ERROR_MANUFACTURER_NOSUPPORT){ // 不支持的设备厂商, SDK内部不会回调onSupport
// // APP自定义逻辑
// Log.w(TAG,"manufacturer not supported");
// onSupport(unsupportedIdSupplier);
// }else if(code == InfoCode.INIT_ERROR_SDK_CALL_ERROR){ // sdk调用出错, SSDK内部不会回调onSupport
// // APP自定义逻辑
// Log.w(TAG,"sdk call error");
// onSupport(unsupportedIdSupplier);
// } else if(code == InfoCode.INIT_INFO_RESULT_DELAY) { // 获取接口是异步的SDK内部会回调onSupport
// Log.i(TAG, "result delay (async)");
// }else if(code == InfoCode.INIT_INFO_RESULT_OK){ // 获取接口是同步的SDK内部会回调onSupport
// Log.i(TAG, "result ok (sync)");
// }else {
// // sdk版本高于DemoHelper代码版本可能出现的情况无法确定是否调用onSupport
// // 不影响成功的OAID获取
// Log.w(TAG,"getDeviceIds: unknown code: " + code);
// }
}
/**
* APP自定义的getDeviceIds(Context cxt)的接口回调
* @param supplier
*/
@Override
public void onSupport(IdSupplier supplier) {
if(supplier==null) {
Log.w(TAG, "onSupport: supplier is null");
return;
}
if(appIdsUpdater ==null) {
Log.w(TAG, "onSupport: callbackListener is null");
return;
}
// 获取Id信息
// 注IdSupplier中的内容为本次调用MdidSdkHelper.InitSdk()的结果,不会实时更新。 如需更新需调用MdidSdkHelper.InitSdk()
// boolean isSupported = supplier.isSupported();
// boolean isLimited = supplier.isLimited();
String oaid=supplier.getOAID();
// String vaid=supplier.getVAID();
// String aaid=supplier.getAAID();
// String idsText= "support: " + (isSupported ? "true" : "false") +
// "\nlimit: " + (isLimited ? "true" : "false") +
// "\nOAID: " + oaid +
// "\nVAID: " + vaid +
// "\nAAID: " + aaid + "\n";
String idsText=oaid;
Log.d(TAG, "onSupport: ids: \n" + idsText);
appIdsUpdater.onIdsValid(idsText);
}
public interface AppIdsUpdater{
void onIdsValid(String ids);
}
/**
* 从asset文件读取证书内容
* @param context
* @param assetFileName
* @return 证书字符串
*/
public static String loadPemFromAssetFile(Context context, String assetFileName){
try {
InputStream is = context.getAssets().open(assetFileName);
BufferedReader in = new BufferedReader(new InputStreamReader(is));
StringBuilder builder = new StringBuilder();
String line;
while ((line = in.readLine()) != null){
builder.append(line);
builder.append('\n');
}
return builder.toString();
} catch (IOException e) {
Log.e(TAG, "loadPemFromAssetFile failed");
return "";
}
}
}

View File

@@ -0,0 +1,321 @@
package com.xuebiping.bolizhuzi.utils;
import android.widget.ImageView;
public class FrameAnimation {
private boolean mIsRepeat;
private AnimationListener mAnimationListener;
private ImageView mImageView;
private int[] mFrameRess;
/**
* 每帧动画的播放间隔数组
*/
private int[] mDurations;
/**
* 每帧动画的播放间隔
*/
private int mDuration;
/**
* 下一遍动画播放的延迟时间
*/
private int mDelay;
private int mLastFrame;
private boolean mNext;
private boolean mPause;
private int mCurrentSelect;
private int mCurrentFrame;
private static final int SELECTED_A = 1;
private static final int SELECTED_B = 2;
private static final int SELECTED_C = 3;
private static final int SELECTED_D = 4;
/**
* @param iv 播放动画的控件
* @param frameRes 播放的图片数组
* @param duration 每帧动画的播放间隔(毫秒)
* @param isRepeat 是否循环播放
*/
public FrameAnimation(ImageView iv, int[] frameRes, int duration, boolean isRepeat) {
this.mImageView = iv;
this.mFrameRess = frameRes;
this.mDuration = duration;
this.mLastFrame = frameRes.length - 1;
this.mIsRepeat = isRepeat;
play(0);
}
/**
* @param iv 播放动画的控件
* @param frameRess 播放的图片数组
* @param durations 每帧动画的播放间隔(毫秒)
* @param isRepeat 是否循环播放
*/
public FrameAnimation(ImageView iv, int[] frameRess, int[] durations, boolean isRepeat) {
this.mImageView = iv;
this.mFrameRess = frameRess;
this.mDurations = durations;
this.mLastFrame = frameRess.length - 1;
this.mIsRepeat = isRepeat;
playByDurations(0);
}
/**
* 循环播放动画
*
* @param iv 播放动画的控件
* @param frameRess 播放的图片数组
* @param duration 每帧动画的播放间隔(毫秒)
* @param delay 循环播放的时间间隔
*/
public FrameAnimation(ImageView iv, int[] frameRess, int duration, int delay) {
this.mImageView = iv;
this.mFrameRess = frameRess;
this.mDuration = duration;
this.mDelay = delay;
this.mLastFrame = frameRess.length - 1;
playAndDelay(0);
}
/**
* 循环播放动画
*
* @param iv 播放动画的控件
* @param frameRess 播放的图片数组
* @param durations 每帧动画的播放间隔(毫秒)
* @param delay 循环播放的时间间隔
*/
public FrameAnimation(ImageView iv, int[] frameRess, int[] durations, int delay) {
this.mImageView = iv;
this.mFrameRess = frameRess;
this.mDurations = durations;
this.mDelay = delay;
this.mLastFrame = frameRess.length - 1;
playByDurationsAndDelay(0);
}
private void playByDurationsAndDelay(final int i) {
mImageView.postDelayed(new Runnable() {
@Override
public void run() {
if (mPause) { // 暂停和播放需求
mCurrentSelect = SELECTED_A;
mCurrentFrame = i;
return;
}
if (0 == i) {
if (mAnimationListener != null) {
mAnimationListener.onAnimationStart();
}
}
mImageView.setBackgroundResource(mFrameRess[i]);
if (i == mLastFrame) {
if (mAnimationListener != null) {
mAnimationListener.onAnimationRepeat();
}
mNext = true;
playByDurationsAndDelay(0);
} else {
playByDurationsAndDelay(i + 1);
}
}
}, mNext && mDelay > 0 ? mDelay : mDurations[i]);
}
private void playAndDelay(final int i) {
mImageView.postDelayed(new Runnable() {
@Override
public void run() {
if (mPause) {
if (mPause) {
mCurrentSelect = SELECTED_B;
mCurrentFrame = i;
return;
}
return;
}
mNext = false;
if (0 == i) {
if (mAnimationListener != null) {
mAnimationListener.onAnimationStart();
}
}
mImageView.setBackgroundResource(mFrameRess[i]);
if (i == mLastFrame) {
if (mAnimationListener != null) {
mAnimationListener.onAnimationRepeat();
}
mNext = true;
playAndDelay(0);
} else {
playAndDelay(i + 1);
}
}
}, mNext && mDelay > 0 ? mDelay : mDuration);
}
private void playByDurations(final int i) {
mImageView.postDelayed(new Runnable() {
@Override
public void run() {
if (mPause) {
if (mPause) {
mCurrentSelect = SELECTED_C;
mCurrentFrame = i;
return;
}
return;
}
if (0 == i) {
if (mAnimationListener != null) {
mAnimationListener.onAnimationStart();
}
}
mImageView.setBackgroundResource(mFrameRess[i]);
if (i == mLastFrame) {
if (mIsRepeat) {
if (mAnimationListener != null) {
mAnimationListener.onAnimationRepeat();
}
playByDurations(0);
} else {
if (mAnimationListener != null) {
mAnimationListener.onAnimationEnd();
}
}
} else {
playByDurations(i + 1);
}
}
}, mDurations[i]);
}
private void play(final int i) {
mImageView.postDelayed(new Runnable() {
@Override
public void run() {
if (mPause) {
if (mPause) {
mCurrentSelect = SELECTED_D;
mCurrentFrame = i;
return;
}
return;
}
if (0 == i) {
if (mAnimationListener != null) {
mAnimationListener.onAnimationStart();
}
}
mImageView.setBackgroundResource(mFrameRess[i]);
if (i == mLastFrame) {
if (mIsRepeat) {
if (mAnimationListener != null) {
mAnimationListener.onAnimationRepeat();
}
play(0);
} else {
if (mAnimationListener != null) {
mAnimationListener.onAnimationEnd();
}
}
} else {
play(i + 1);
}
}
}, mDuration);
}
public static interface AnimationListener {
/**
* <p>Notifies the start of the animation.</p>
*/
void onAnimationStart();
/**
* <p>Notifies the end of the animation. This callback is not invoked
* for animations with repeat count set to INFINITE.</p>
*/
void onAnimationEnd();
/**
* <p>Notifies the repetition of the animation.</p>
*/
void onAnimationRepeat();
}
/**
* <p>Binds an animation listener to this animation. The animation listener
* is notified of animation events such as the end of the animation or the
* repetition of the animation.</p>
*
* @param listener the animation listener to be notified
*/
public void setAnimationListener(AnimationListener listener) {
this.mAnimationListener = listener;
}
public void release() {
pauseAnimation();
}
public void pauseAnimation() {
this.mPause = true;
}
public boolean isPause() {
return this.mPause;
}
public void restartAnimation() {
if (mPause) {
mPause = false;
switch (mCurrentSelect) {
case SELECTED_A:
playByDurationsAndDelay(mCurrentFrame);
break;
case SELECTED_B:
playAndDelay(mCurrentFrame);
break;
case SELECTED_C:
playByDurations(mCurrentFrame);
break;
case SELECTED_D:
play(mCurrentFrame);
break;
default:
break;
}
}
}
}

View File

@@ -0,0 +1,117 @@
package com.xuebiping.bolizhuzi.utils;
import android.content.Context;
import android.widget.ImageView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.resource.bitmap.CenterCrop;
import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
import com.xuebiping.bolizhuzi.R;
import com.luck.picture.lib.engine.ImageEngine;
import com.luck.picture.lib.utils.ActivityCompatHelper;
/**
* @authorluck
* @date2019-11-13 17:02
* @describeGlide加载引擎
*/
public class GlideEngine implements ImageEngine {
/**
* 加载图片
*
* @param context 上下文
* @param url 资源url
* @param imageView 图片承载控件
*/
@Override
public void loadImage(Context context, String url, ImageView imageView) {
if (!ActivityCompatHelper.assertValidRequest(context)) {
return;
}
Glide.with(context)
.load(url)
.into(imageView);
}
@Override
public void loadImage(Context context, ImageView imageView, String url, int maxWidth, int maxHeight) {
if (!ActivityCompatHelper.assertValidRequest(context)) {
return;
}
Glide.with(context)
.load(url)
.override(maxWidth, maxHeight)
.into(imageView);
}
/**
* 加载相册目录封面
*
* @param context 上下文
* @param url 图片路径
* @param imageView 承载图片ImageView
*/
@Override
public void loadAlbumCover(Context context, String url, ImageView imageView) {
if (!ActivityCompatHelper.assertValidRequest(context)) {
return;
}
Glide.with(context)
.asBitmap()
.load(url)
.override(180, 180)
.sizeMultiplier(0.5f)
.transform(new CenterCrop(), new RoundedCorners(8))
.placeholder(R.drawable.ps_image_placeholder)
.into(imageView);
}
/**
* 加载图片列表图片
*
* @param context 上下文
* @param url 图片路径
* @param imageView 承载图片ImageView
*/
@Override
public void loadGridImage(Context context, String url, ImageView imageView) {
if (!ActivityCompatHelper.assertValidRequest(context)) {
return;
}
Glide.with(context)
.load(url)
.override(200, 200)
.centerCrop()
.placeholder(R.drawable.ps_image_placeholder)
.into(imageView);
}
@Override
public void pauseRequests(Context context) {
if (!ActivityCompatHelper.assertValidRequest(context)) {
return;
}
Glide.with(context).pauseRequests();
}
@Override
public void resumeRequests(Context context) {
if (!ActivityCompatHelper.assertValidRequest(context)) {
return;
}
Glide.with(context).resumeRequests();
}
private GlideEngine() {
}
private static final class InstanceHolder {
static final GlideEngine instance = new GlideEngine();
}
public static GlideEngine createGlideEngine() {
return InstanceHolder.instance;
}
}

View File

@@ -0,0 +1,24 @@
package com.xuebiping.bolizhuzi.utils;
import android.content.Context;
import android.widget.ImageView;
import com.bumptech.glide.Glide;
import com.youth.banner.loader.ImageLoader;
public class GlideImageLoader extends ImageLoader {
@Override
public void displayImage(Context context, Object path, ImageView imageView) {
//具体方法内容自己去选择次方法是为了减少banner过多的依赖第三方包所以将这个权限开放给使用者去选择
Glide.with(context.getApplicationContext())
.load(path)
.into(imageView);
}
// @Override
// public ImageView createImageView(Context context) {
// //圆角
// return new RoundAngleImageView(context);
// }
}

View File

@@ -0,0 +1,29 @@
package com.xuebiping.bolizhuzi.utils;
import android.content.Context;
import android.widget.ImageView;
import com.bumptech.glide.Glide;
import com.xuebiping.bolizhuzi.model.main.BannerBean;
import com.fengliyan.uikit.emoji.utils.ScreenUtil;
import com.youth.banner.loader.ImageLoader;
public class GlideImageLoader1 extends ImageLoader {
@Override
public void displayImage(Context context, Object path, ImageView imageView) {
//具体方法内容自己去选择次方法是为了减少banner过多的依赖第三方包所以将这个权限开放给使用者去选择
Glide.with(context.getApplicationContext())
.load(((BannerBean) path).getImage())
.into(imageView);
}
@Override
public ImageView createImageView(Context context) {
//圆角
RoundAngleImageView imageView = new RoundAngleImageView(context);
imageView.setType(RoundAngleImageView.TYPE_ROUND);
imageView.setRoundRadius(ScreenUtil.dip2px(12));
return imageView;
}
}

View File

@@ -0,0 +1,81 @@
package com.xuebiping.bolizhuzi.utils;
/**
* Created by zhangbin on 2019/2/15.
*/
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool;
import com.bumptech.glide.load.resource.bitmap.BitmapTransformation;
import java.security.MessageDigest;
/**
* Glide 圆角 Transform
*/
public class GlideRoundTransform2 extends BitmapTransformation {
private static float radius = 0f;
/**
* 构造函数 默认圆角半径 4dp
*
* @param context Context
*/
public GlideRoundTransform2(Context context) {
this(context, 4);
}
/**
* 构造函数
*
* @param context Context
* @param dp 圆角半径
*/
public GlideRoundTransform2(Context context, int dp) {
super();
// radius = Resources.getSystem().getDisplayMetrics().density * dp;
radius = dp;
}
@Override
protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
return roundCrop(pool, toTransform);
}
private static Bitmap roundCrop(BitmapPool pool, Bitmap source) {
if (source == null) return null;
Bitmap result = pool.get(source.getWidth(), source.getHeight(), Bitmap.Config.ARGB_8888);
if (result == null) {
result = Bitmap.createBitmap(source.getWidth(), source.getHeight(), Bitmap.Config.ARGB_8888);
}
Canvas canvas = new Canvas(result);
Paint paint = new Paint();
paint.setShader(new BitmapShader(source, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP));
paint.setAntiAlias(true);
RectF rectF = new RectF(0f, 0f, source.getWidth(), source.getHeight());
canvas.drawRoundRect(rectF, radius, radius, paint);
return result;
}
// @Override
// public String getId() {
// return getClass().getName() + Math.round(radius);
// }
@Override
public void updateDiskCacheKey(MessageDigest messageDigest) {
}
}

View File

@@ -0,0 +1,263 @@
package com.xuebiping.bolizhuzi.utils;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Point;
import android.graphics.drawable.Drawable;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import androidx.annotation.Nullable;
import com.bumptech.glide.Glide;
import com.bumptech.glide.RequestBuilder;
import com.bumptech.glide.load.DataSource;
import com.bumptech.glide.load.MultiTransformation;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.bumptech.glide.load.engine.GlideException;
import com.bumptech.glide.load.resource.gif.GifDrawable;
import com.bumptech.glide.request.RequestListener;
import com.bumptech.glide.request.RequestOptions;
import com.bumptech.glide.request.target.SimpleTarget;
import com.bumptech.glide.request.target.Target;
import com.bumptech.glide.request.transition.Transition;
import com.xuebiping.bolizhuzi.R;
import com.xuebiping.bolizhuzi.controller.constant.ConstUrl;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import jp.wasabeef.glide.transformations.BlurTransformation;
import jp.wasabeef.glide.transformations.RoundedCornersTransformation;
/**
* Created by ying on 2020/4/11.
* Describe:
*/
public class GlideUtils {
private static final String TAG = "GlideUtils";
public static void loadImage(ImageView imageView, Object imgRes, int radiusPx, int placeHolderRes) {
MultiTransformation<Bitmap> transformation = null;
if (radiusPx > 0) {
transformation = new MultiTransformation<>(new RoundedCornersTransformation(radiusPx, 0, RoundedCornersTransformation.CornerType.ALL));
}
if (placeHolderRes == 0) {
placeHolderRes = R.drawable.avatar_bg_white;
}
RequestBuilder<Drawable> req = Glide.with(imageView).load(imgRes).placeholder(placeHolderRes).error(R.drawable.nim_default_img_failed);
if (transformation != null) {
req.apply(RequestOptions.bitmapTransform(transformation).dontAnimate());
}
req.into(imageView);
}
public static void showVideo(final ImageView imageView, String url, SimpleTarget<Bitmap> bitmapSimpleTarget) {
Glide.with(imageView.getContext())
.setDefaultRequestOptions(
new RequestOptions()
.frame(1000000)//单位微秒
.centerCrop()
)
.asBitmap()
.load(StrU.getResourcePath(url, imageView.getContext()))
.into(bitmapSimpleTarget);
}
public static void showVideoLocal(final Activity activity, final ImageView imageView, String url) {
Glide.with(activity)
.setDefaultRequestOptions(
new RequestOptions()
.frame(1000000)//单位微秒
.centerCrop()
)
.asBitmap()
.load(url)
.into(new SimpleTarget<Bitmap>() {
@Override
public void onResourceReady(Bitmap resource, Transition<? super Bitmap> transition) {
imageView.setImageBitmap(resource);
}
});
}
public static void showVideoRvLocal(final Activity activity, final ImageView imageView, String url, final int mag) {
Glide.with(activity)
.setDefaultRequestOptions(
new RequestOptions()
.frame(1000000)//单位微秒
.centerCrop()
)
.asBitmap()
.load(url)
.into(new SimpleTarget<Bitmap>() {
@Override
public void onResourceReady(Bitmap resource, Transition<? super Bitmap> transition) {
Point outSize = new Point();
activity.getWindowManager().getDefaultDisplay().getSize(outSize);
outSize.x = (outSize.x) / 2 - mag * 2;
double i = outSize.x * 10d / resource.getWidth() / 10.00d;
if (ConstUrl.LOGDEBUG) Log.i(TAG, "onResourceReady: " + i);
ViewGroup.LayoutParams layoutParams = imageView.getLayoutParams();
layoutParams.width = (int) (resource.getWidth() * i);
layoutParams.height = (int) (resource.getHeight() * i);
if (ConstUrl.LOGDEBUG)
Log.i(TAG, "width: " + resource.getWidth() + " height " + resource.getHeight());
if (ConstUrl.LOGDEBUG)
Log.i(TAG, "layoutParams width: " + layoutParams.width + " height " + layoutParams.height);
imageView.setLayoutParams(layoutParams);
imageView.setImageBitmap(resource);
}
});
}
public static void showVideo(final Activity activity, final ImageView imageView, String url, final int mag) {
GlideUtils.showVideo(imageView, url, new SimpleTarget<Bitmap>() {
@Override
public void onResourceReady(Bitmap resource, Transition<? super Bitmap> transition) {
if (ConstUrl.LOGDEBUG)
Log.i(TAG, "width: " + resource.getWidth() + " height " + resource.getHeight());
Point outSize = new Point();
activity.getWindowManager().getDefaultDisplay().getSize(outSize);
outSize.x = (outSize.x) / 2 - mag * 2;
double i = outSize.x * 10d / resource.getWidth() / 10.00d;
ViewGroup.LayoutParams layoutParams = imageView.getLayoutParams();
layoutParams.width = (int) (resource.getWidth() * i);
layoutParams.height = (int) (resource.getHeight() * i);
imageView.setLayoutParams(layoutParams);
imageView.setImageBitmap(resource);
}
});
}
public static void showVideo(final ImageView imageView, String url, final int width) {
GlideUtils.showVideo(imageView, url, new SimpleTarget<Bitmap>() {
@Override
public void onResourceReady(Bitmap resource, Transition<? super Bitmap> transition) {
if (ConstUrl.LOGDEBUG)
Log.i(TAG, "width: " + resource.getWidth() + " height " + resource.getHeight());
ViewGroup.LayoutParams layoutParams = imageView.getLayoutParams();
layoutParams.width = width;
layoutParams.height = width * resource.getHeight() / resource.getWidth();
imageView.setLayoutParams(layoutParams);
imageView.setImageBitmap(resource);
}
});
}
public static void showVideo(Context context, ImageView imageView, String url) {
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
Glide.with(context)
.setDefaultRequestOptions(
new RequestOptions()
.frame(1000000)//单位微秒
.centerCrop()
).load(StrU.getResourcePath(url, context))
.into(imageView);
}
public static void showImageNone(Context context, ImageView imageView, String url) {
Glide.with(context)
.load(StrU.getResourcePath(url, context))
.apply(new RequestOptions()
.skipMemoryCache(true)
.diskCacheStrategy(DiskCacheStrategy.NONE)
)
.into(imageView);
}
public static void showImage(Context context, ImageView imageView, String url) {
Glide.with(context)
.load(StrU.getResourcePath(url, context))
.into(imageView);
}
/**
* 高斯模糊
*
* @param context
* @param imageView
* @param url
* @param radius
*/
public static void showImageformation(Context context, ImageView imageView, String url, int radius) {
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
Glide.with(context)
.load(StrU.getResourcePath(url, context))
.apply(RequestOptions.bitmapTransform(new BlurTransformation(radius))
.skipMemoryCache(true)
.diskCacheStrategy(DiskCacheStrategy.NONE))
.into(imageView);
}
public static void showGif(final Context context, final ImageView imageView, @Nullable Integer resourceId) {
imageView.setVisibility(View.VISIBLE);
imageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
imageView.setVisibility(View.GONE);
}
});
Glide.with(context).asGif().load(resourceId).listener(new RequestListener<GifDrawable>() {
@Override
public boolean onLoadFailed(@Nullable GlideException e, Object model, Target target, boolean isFirstResource) {
return false;
}
@Override
public boolean onResourceReady(final GifDrawable resource, Object model, Target target, DataSource dataSource, boolean isFirstResource) {
try {
Field gifStateField = GifDrawable.class.getDeclaredField("state");
gifStateField.setAccessible(true);
Class gifStateClass = Class.forName("com.bumptech.glide.load.resource.gif.GifDrawable$GifState");
Field gifFrameLoaderField = gifStateClass.getDeclaredField("frameLoader");
gifFrameLoaderField.setAccessible(true);
Class gifFrameLoaderClass = Class.forName("com.bumptech.glide.load.resource.gif.GifFrameLoader");
Field gifDecoderField = gifFrameLoaderClass.getDeclaredField("gifDecoder");
gifDecoderField.setAccessible(true);
Class gifDecoderClass = Class.forName("com.bumptech.glide.gifdecoder.GifDecoder");
Object gifDecoder = gifDecoderField.get(gifFrameLoaderField.get(gifStateField.get(resource)));
Method getDelayMethod = gifDecoderClass.getDeclaredMethod("getDelay", int.class);
getDelayMethod.setAccessible(true);
//设置只播放一次
// resource.setLoopCount(1);
resource.start();
//获得总帧数
int count = resource.getFrameCount();
if (ConstUrl.LOGDEBUG) Log.i(TAG, "onResourceReady: 总帧数 " + count);
int delay = 0;
for (int i = 0; i < count; i++) {
//计算每一帧所需要的时间进行累加
delay += (int) getDelayMethod.invoke(gifDecoder, i);
if (ConstUrl.LOGDEBUG) Log.i(TAG, "onResourceReady: 时间 " + delay);
}
delay *= 2;
final int finalDelay = delay;
if (ConstUrl.LOGDEBUG) Log.i(TAG, "onResourceReady: 总时间 " + delay);
imageView.postDelayed(new Runnable() {
@Override
public void run() {
resource.stop();
// Toast.makeText(context, "播放完成 " + finalDelay, Toast.LENGTH_SHORT).show();
// imageView.setVisibility(View.GONE);
}
}, delay);
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
}).into(imageView);
}
}

View File

@@ -0,0 +1,181 @@
package com.xuebiping.bolizhuzi.utils
import android.graphics.Canvas
import android.graphics.Rect
import android.graphics.RectF
import android.view.View
import androidx.annotation.ColorInt
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.blankj.utilcode.util.SizeUtils
import kotlin.math.roundToInt
class GridDecoration : LinearDecoration() {
private var horizontalSpace: Int = 0
private var verticalSpace: Int = 0
override fun setColor(@ColorInt color: Int): GridDecoration {
super.setColor(color)
return this
}
/**
* 左右Border只对横向滚动并且Item宽度不填充为屏幕宽度的RecyclerView使用
* (VERTICAL方向左右border设置无效)
* VERTICAL方向请自行设置recycleview左右padding为左右border的边距解决)
*/
override fun setBorder(borderLeft: Int, borderTop: Int, borderRight: Int, borderBottom: Int): GridDecoration {
super.setBorder(borderLeft, borderTop, borderRight, borderBottom)
return this
}
override fun setBorder(leftDp: Float, topDp: Float, rightDp: Float, bottomDp: Float): GridDecoration {
super.setBorder(leftDp, topDp, rightDp, bottomDp)
return this
}
fun setSizeDp(horizontalSpace: Float, verticalSpace: Float): GridDecoration {
this.horizontalSpace = SizeUtils.dp2px(horizontalSpace)
this.verticalSpace = SizeUtils.dp2px(verticalSpace)
super.setSize(this.verticalSpace)
return this
}
fun setSize(horizontalSpace: Int, verticalSpace: Int): GridDecoration {
this.horizontalSpace = horizontalSpace
this.verticalSpace = verticalSpace
super.setSize(verticalSpace)
return this
}
private fun getGridPosition(parent: RecyclerView, view: View): Int {
return parent.getChildAdapterPosition(view)
}
private fun getGridItemCount(parent: RecyclerView): Int {
return parent.adapter?.itemCount ?: 0
}
private fun isFirstRow(isVertical: Boolean, parent: RecyclerView, view: View): Boolean {
val childAdapterPosition = getGridPosition(parent, view)
val spanCount = (parent.layoutManager as GridLayoutManager).spanCount
return if (isVertical) {
childAdapterPosition < spanCount
} else {
childAdapterPosition % spanCount == 0
}
}
private fun isLastRow(isVertical: Boolean, parent: RecyclerView, view: View): Boolean {
val spanCount = (parent.layoutManager as GridLayoutManager).spanCount
val childAdapterPosition = getGridPosition(parent, view)
val itemCount = getGridItemCount(parent)
return if (isVertical) {
val remainder = itemCount % spanCount
childAdapterPosition >= itemCount - if (remainder == 0) spanCount else remainder
} else {
childAdapterPosition % spanCount == spanCount - 1
}
}
private fun isFirstColumn(isVertical: Boolean, parent: RecyclerView, view: View): Boolean {
val childAdapterPosition = getGridPosition(parent, view)
val spanCount = (parent.layoutManager as GridLayoutManager).spanCount
return if (isVertical) {
childAdapterPosition % spanCount == 0
} else {
childAdapterPosition < spanCount
}
}
private fun isLastColumn(isVertical: Boolean, parent: RecyclerView, view: View): Boolean {
val spanCount = (parent.layoutManager as GridLayoutManager).spanCount
val childAdapterPosition = getGridPosition(parent, view)
val itemCount = getGridItemCount(parent)
return if (isVertical) {
childAdapterPosition % spanCount == spanCount - 1
} else {
childAdapterPosition >= itemCount - itemCount % spanCount
}
}
override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
val layoutManager = parent.layoutManager
if (layoutManager is GridLayoutManager && layoutManager.spanCount == 1) {
super.onDraw(c, parent, state)
return
}
for (i in 0 until parent.childCount) {
val view = parent.getChildAt(i)
val rectf = RectF()
val rect = Rect()
getItemOffsetF(rectf, view, parent, state)
getItemOffsets(rect, view, parent, state)
val left = view.left - rect.left.toFloat()
val top = view.top - rect.top.toFloat()
val right = view.right + rect.right.toFloat()
val bottom = view.bottom + rect.bottom.toFloat()
//绘制左边分割线
c.drawRect(left, top, left + rectf.left, bottom, dividerPaint)
//绘制上分割线
c.drawRect(left, top, right, top + rectf.top, dividerPaint)
//绘制右边分割线
c.drawRect(right - rectf.right, top, right, bottom, dividerPaint)
//绘制下分割线
c.drawRect(left, bottom - rectf.bottom, right, bottom, dividerPaint)
}
}
override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
//super.getItemOffsets(outRect, view, parent, state)
//精确的GridItem需要偏移的矩阵数据
val rectF = RectF()
getItemOffsetF(rectF, view, parent, state)
if (parent.layoutDirection == View.LAYOUT_DIRECTION_LTR) {
outRect.left = rectF.left.roundToInt()
outRect.right = rectF.right.roundToInt()
} else {
outRect.left = rectF.right.roundToInt()
outRect.right = rectF.left.roundToInt()
}
outRect.top = rectF.top.roundToInt()
outRect.bottom = rectF.bottom.roundToInt()
}
private fun getItemOffsetF(outRect: RectF, view: View, parent: RecyclerView, state: RecyclerView.State){
val layoutManager = parent.layoutManager
if (layoutManager !is GridLayoutManager) {
outRect.set(horizontalSpace.toFloat()/2, 0f, horizontalSpace.toFloat()/2, verticalSpace.toFloat())
return
}
if(layoutManager.spanCount == 1){
val rect = Rect()
super.getItemOffsets(rect, view, parent, state)
outRect.set(rect.left.toFloat(), rect.top.toFloat(), rect.right.toFloat(), rect.bottom.toFloat())
return
}
val isVertical = layoutManager.orientation == LinearLayoutManager.VERTICAL
val spanCount = layoutManager.spanCount
if(isVertical){
val adapterPosition = parent.getChildAdapterPosition(view)
val eachWidth = (spanCount - 1) * horizontalSpace.toFloat() / spanCount
outRect.left = adapterPosition % spanCount * (horizontalSpace - eachWidth)
outRect.right = eachWidth - outRect.left
outRect.top = 0f
outRect.bottom = verticalSpace.toFloat()
}else{
outRect.set(0f, 0f, horizontalSpace.toFloat(), verticalSpace.toFloat())
}
//只接受2列并且左右border间距相等的边框分割线(否则左右边界线不做偏移)
if (spanCount == 2 && borderRect.left == borderRect.right) {
if (isFirstColumn(isVertical, parent, view)) outRect.left = outRect.left + borderRect.left
if (isLastColumn(isVertical, parent, view)) outRect.right = outRect.right + borderRect.right
}
if (isFirstRow(isVertical, parent, view)) outRect.top = borderRect.top.toFloat()
if (isLastRow(isVertical, parent, view)) outRect.bottom = borderRect.bottom.toFloat()
}
}

View File

@@ -0,0 +1,115 @@
package com.xuebiping.bolizhuzi.utils;
import android.content.ClipboardManager;
import android.content.Context;
import android.text.TextUtils;
import android.util.Log;
import com.fengliyan.device.DeviceManager;
import com.xuebiping.bolizhuzi.controller.user.manager.UserManager;
//该类用于绑定固定邀请人id
public class InviteCodeUpdateUtils {
private static final String TAG = "InviteCodeUpdateUtils";
public interface CodeCallback {
void callback(String code);
}
public static String inviteCode = "";
// public static String inviteCode = BuildConfig.InviteCode;
private static boolean isInit = false;
public static String getLocalInviteCode() {
if (!isInit) {
getInviteCode();
isInit = true;
}
String localInviteCode = UserManager.getInviteCode();
if (!TextUtils.isEmpty(localInviteCode)) {
Log.i(TAG, "getLocalInviteCode:localInviteCode ------->" + localInviteCode);
return localInviteCode;
} else {
Log.i(TAG, "getLocalInviteCode:inviteCode ------->" + inviteCode);
return inviteCode;
}
}
public static void getOpeninstallData(Context context, final CodeCallback callback) {
// OpenInstall.getInstall(new AppInstallListener() {
// @Override
// public void onInstallFinish(AppData appData, Error error) {
// if (appData != null) {
// String bindData = appData.getData();
// Gson gson = new Gson();
// InviteCodeBean inviteCodeBean = gson.fromJson(bindData, InviteCodeBean.class);
// Log.i(TAG, "onInstallFinish: bindData--------->" + bindData);
// if (inviteCodeBean != null) {
// UserManager.setInviteCode(inviteCodeBean.getInvite_code());
// }
// }
// callback.callback(getLocalInviteCode());
// }
// });
// 获取ClipboardManager实例
ClipboardManager manager = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
if (manager != null) {
if (manager.hasPrimaryClip() && manager.getPrimaryClip().getItemCount() > 0) {
CharSequence addedText = manager.getPrimaryClip().getItemAt(0).getText();
String addedTextString = String.valueOf(addedText);
if (!TextUtils.isEmpty(addedTextString)) {
if (addedTextString.startsWith("#siyu#")) {
// 使用textString变量进行操作例如显示或处理文本数据
String code = addedTextString.substring(6);
Log.d("Clipboard", "Text from Clipboard: " + code);
Log.i(TAG, "getOpeninstallData: ------->" + code);
UserManager.setInviteCode(code);
}
}
}
}
callback.callback(getLocalInviteCode());
}
public static void getInviteCode() {
String market = DeviceManager.getInstance().getApplicationMarket();
switch (market) {
case "oss":
inviteCode = "";
break;
case "vivo":
inviteCode = "1375043";
break;
case "oppo":
inviteCode = "1375044";
break;
case "xiaomi":
inviteCode = "1375045";
break;
case "yingyongbao":
inviteCode = "1375047";
break;
case "huawei":
inviteCode = "1423191";
break;
// case "meizu":
// inviteCode = "1426940";`
// break;
case "baiduss":
inviteCode = "1621937";
break;
case "baiduxxl":
inviteCode = "1450565";
break;
case "douyin":
inviteCode = "1595267";
break;
case "kuaishou":
inviteCode = "1595264";
break;
case "honor":
inviteCode = "1626328";
break;
}
}
}

View File

@@ -0,0 +1,165 @@
package com.xuebiping.bolizhuzi.utils
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Rect
import android.view.View
import androidx.annotation.ColorInt
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.blankj.utilcode.util.SizeUtils
open class LinearDecoration : RecyclerView.ItemDecoration() {
internal val dividerPaint: Paint = Paint(Paint.ANTI_ALIAS_FLAG)
internal val borderRect = Rect()
private var dividerSize: Int = 0
private val bgPaint: Paint = Paint(Paint.ANTI_ALIAS_FLAG)
private var dividerPaddingStart = 0
private var dividerPaddingEnd = 0
init {
dividerPaint.color = Color.TRANSPARENT
dividerPaint.style = Paint.Style.FILL
bgPaint.color = Color.TRANSPARENT
bgPaint.style = Paint.Style.FILL
}
open fun setColor(@ColorInt color: Int): LinearDecoration {
dividerPaint.color = color
return this
}
open fun setBorder(
borderLeft: Int,
borderTop: Int = borderLeft,
borderRight: Int = borderLeft,
borderBottom: Int = borderLeft
): LinearDecoration {
borderRect.set(borderLeft, borderTop, borderRight, borderBottom)
return this
}
open fun setBorder(
leftDp: Float,
topDp: Float = leftDp,
rightDp: Float = leftDp,
bottomDp: Float = leftDp
): LinearDecoration {
borderRect.set(
SizeUtils.dp2px(leftDp),
SizeUtils.dp2px(topDp),
SizeUtils.dp2px(rightDp),
SizeUtils.dp2px(bottomDp)
)
return this
}
fun setSizeDp(dividerSize: Float): LinearDecoration {
this.dividerSize = SizeUtils.dp2px(dividerSize)
return this
}
fun setSize(dividerSize: Int): LinearDecoration {
this.dividerSize = dividerSize
return this
}
fun setDivider(
@ColorInt paddingColor: Int,
@ColorInt dividerColor: Int,
dividerSize: Int,
start: Int,
end: Int = start
): LinearDecoration {
dividerPaddingStart = start
dividerPaddingEnd = end
setSize(dividerSize)
setColor(dividerColor)
bgPaint.color = paddingColor
return this
}
private fun isLastItem(parent: RecyclerView, item: View): Boolean {
val childAdapterPosition = parent.getChildAdapterPosition(item)
return childAdapterPosition == parent.adapter!!.itemCount - 1
}
private fun isFirstItem(parent: RecyclerView, item: View): Boolean {
val childAdapterPosition = parent.getChildAdapterPosition(item)
return childAdapterPosition == 0
}
override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
val layoutManager = parent.layoutManager
if (layoutManager == null || layoutManager !is LinearLayoutManager) {
return
}
for (i in 0 until parent.childCount) {
val view = parent.getChildAt(i)
val dividerLeft: Float
val dividerRight: Float
val dividerTop: Float
val dividerBottom: Float
val outRect = Rect()
getOffsets(outRect, view, parent)
if ((parent.layoutManager as LinearLayoutManager).orientation == LinearLayoutManager.VERTICAL) {
dividerTop = view.top.toFloat() - outRect.top
dividerBottom = view.top.toFloat()
dividerLeft = (view.left + dividerPaddingStart).toFloat()
dividerRight = (view.right - dividerPaddingEnd).toFloat()
c.drawRect(
view.left.toFloat(),
dividerTop,
view.right.toFloat(),
dividerBottom,
bgPaint
)
} else {
dividerLeft = view.left.toFloat() - outRect.left
dividerRight = view.left.toFloat()
dividerTop = (view.top + dividerPaddingStart).toFloat()
dividerBottom = (view.bottom - dividerPaddingEnd).toFloat()
c.drawRect(
dividerLeft,
view.top.toFloat(),
dividerRight,
view.bottom.toFloat(),
bgPaint
)
}
c.drawRect(dividerLeft, dividerTop, dividerRight, dividerBottom, dividerPaint)
}
}
override fun getItemOffsets(
outRect: Rect,
view: View,
parent: RecyclerView,
state: RecyclerView.State
) {
super.getItemOffsets(outRect, view, parent, state)
getOffsets(outRect, view, parent)
}
private fun getOffsets(outRect: Rect, view: View, parent: RecyclerView) {
val layoutManager = parent.layoutManager
if (layoutManager == null || layoutManager !is LinearLayoutManager) {
return
}
outRect.set(borderRect)
if (layoutManager.orientation == LinearLayoutManager.HORIZONTAL) {
outRect.left = if (isFirstItem(parent, view)) borderRect.left else dividerSize
outRect.right = if (isLastItem(parent, view)) borderRect.right else 0
} else {
outRect.top = if (isFirstItem(parent, view)) borderRect.top else dividerSize
outRect.bottom = if (isLastItem(parent, view)) borderRect.bottom else 0
}
}
}

View File

@@ -0,0 +1,54 @@
package com.xuebiping.bolizhuzi.utils;
import android.content.Context;
import android.content.SharedPreferences;
/**
* Created by zhangbin on 2019/1/19.
*/
public class ListStringSPUtils {
private final static String name = "listString";
private final static int mode = Context.MODE_PRIVATE;
/**
* 保存首选项
* @param context
* @param key
* @param value
*/
public static void saveString(Context context, String key, String value){
SharedPreferences sp = context.getSharedPreferences(name, mode);
SharedPreferences.Editor edit = sp.edit();
edit.putString(key, value);
edit.commit();
}
/**
* 获取首选项
* @param context
* @param key
* @param defValue
* @return
*/
public static String getString(Context context, String key, String defValue){
SharedPreferences sp = context.getSharedPreferences(name, mode);
return sp.getString(key, defValue);
}
public static String getString(Context context, String key) {
SharedPreferences sp = context.getSharedPreferences(name, mode);
return sp.getString(key, "");
}
/**
* 清除保存
* @param context
*/
public static void clear(Context context){
SharedPreferences sp = context.getSharedPreferences(name, mode);
SharedPreferences.Editor editor = sp.edit();
editor.clear();
editor.commit();
}
}

View File

@@ -0,0 +1,26 @@
package com.xuebiping.bolizhuzi.utils;
import android.content.Context;
import android.content.SharedPreferences;
/**
* Created by zhangbin on 2018/12/22.
*/
public class LongSpUtils {
private final static String name = "long";
private final static int mode = Context.MODE_PRIVATE;
public static void saveLong(Context context, String key, long value){
SharedPreferences sp = context.getSharedPreferences(name, mode);
SharedPreferences.Editor edit = sp.edit();
edit.putLong(key, value);
edit.commit();
}
public static long getLong(Context context, String key) {
SharedPreferences sp = context.getSharedPreferences(name, mode);
return sp.getLong(key, 0);
}
}

View File

@@ -0,0 +1,172 @@
package com.xuebiping.bolizhuzi.utils;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.os.Build;
import android.renderscript.Allocation;
import android.renderscript.Element;
import android.renderscript.RenderScript;
import android.renderscript.ScriptIntrinsicBlur;
import android.util.AttributeSet;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.AppCompatImageView;
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool;
import com.bumptech.glide.load.resource.bitmap.CenterCrop;
import com.bumptech.glide.request.RequestOptions;
import java.security.MessageDigest;
/**
* 自定义MaskImageView
*
* @author ThirdGoddess
* @email ofmyhub@gmail.com
* @Github https://github.com/ThirdGoddess
* @date :2020-05-23 21:54 毛玻璃
*/
public class MaskImageView extends AppCompatImageView {
private Context context;
//蒙版色值
private int color = Color.parseColor("#66000000");
//是否显示蒙版
private boolean isShowMask = true;
public MaskImageView(Context context) {
super(context);
this.context = context;
}
public MaskImageView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
this.context = context;
}
public MaskImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.context = context;
}
@SuppressLint("DrawAllocation")
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (isShowMask) {
canvas.drawColor(color);
}
}
/**
* 设置蒙版颜色,可以通过类似于(#81FFFFFF来控制透明度
*/
public MaskImageView setMaskColor(int color) {
this.color = color;
return this;
}
/**
* 显示蒙版
*/
public void showMask() {
isShowMask = true;
invalidate();
}
/**
* 关闭蒙版
*/
public void dismissMask() {
isShowMask = false;
invalidate();
}
/**
* 高斯模糊处理
*
* @param radius
* @return
*/
public Bitmap setGaussblur(int radius, Bitmap source) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
RenderScript renderScript = RenderScript.create(context);
final Allocation input = Allocation.createFromBitmap(renderScript, source);
final Allocation output = Allocation.createTyped(renderScript, input.getType());
ScriptIntrinsicBlur scriptIntrinsicBlur;
scriptIntrinsicBlur = ScriptIntrinsicBlur.create(renderScript, Element.U8_4(renderScript));
scriptIntrinsicBlur.setInput(input);
scriptIntrinsicBlur.setRadius(radius);
scriptIntrinsicBlur.forEach(output);
output.copyTo(source);
renderScript.destroy();
return source;
} else {
return source;
}
}
/**
* 开启高斯模糊
*
* @return
*/
public RequestOptions setGaussBlur() {
return RequestOptions.bitmapTransform(new GlideBlurTransformation(context));
}
/**
* 压缩Bitmap
*/
public class GlideBlurTransformation extends CenterCrop {
private Context context;
GlideBlurTransformation(Context context) {
this.context = context;
}
@Override
protected Bitmap transform(@NonNull BitmapPool pool, @NonNull Bitmap toTransform, int outWidth, int outHeight) {
Bitmap bitmap = super.transform(pool, toTransform, outWidth, outHeight);
return blurBitmap(context, bitmap, 25 , (int) (outWidth * 0.6), (int) (outHeight * 0.6));
}
@Override
public void updateDiskCacheKey(@NonNull MessageDigest messageDigest) {
}
}
/**
* 模糊Bitmap处理
*
* @param context
* @param source
* @param blurRadius
* @param outWidth
* @param outHeight
* @return
*/
public Bitmap blurBitmap(Context context, Bitmap source, float blurRadius, int outWidth, int outHeight) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
Bitmap inputBitmap = Bitmap.createScaledBitmap(source, outWidth, outHeight, false);
Bitmap outputBitmap = Bitmap.createBitmap(inputBitmap);
RenderScript rs = RenderScript.create(context);
ScriptIntrinsicBlur blurScript;
blurScript = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
Allocation tmpIn = Allocation.createFromBitmap(rs, inputBitmap);
Allocation tmpOut = Allocation.createFromBitmap(rs, outputBitmap);
blurScript.setRadius(blurRadius);
blurScript.setInput(tmpIn);
blurScript.forEach(tmpOut);
tmpOut.copyTo(outputBitmap);
return outputBitmap;
} else {
return source;
}
}
}

View File

@@ -0,0 +1,113 @@
package com.xuebiping.bolizhuzi.utils;
import android.Manifest;
import android.graphics.Color;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.text.style.AbsoluteSizeSpan;
import android.text.style.ForegroundColorSpan;
import android.view.View;
import android.view.ViewGroup;
import android.widget.RelativeLayout;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.constraintlayout.widget.ConstraintSet;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import com.xuebiping.bolizhuzi.R;
import com.luck.picture.lib.interfaces.OnPermissionDescriptionListener;
import com.luck.picture.lib.permissions.PermissionConfig;
import com.luck.picture.lib.utils.DensityUtil;
import com.luck.picture.lib.widget.MediumBoldTextView;
/**
* 添加权限说明
*/
public class MeOnPermissionDescriptionListener implements OnPermissionDescriptionListener {
private final static String TAG_EXPLAIN_VIEW = "TAG_EXPLAIN_VIEW";
@Override
public void onPermissionDescription(Fragment fragment, String[] permissionArray) {
View rootView = fragment.requireView();
if (rootView instanceof ViewGroup) {
addPermissionDescription(false, (ViewGroup) rootView, permissionArray);
}
}
@Override
public void onDismiss(Fragment fragment) {
removePermissionDescription((ViewGroup) fragment.requireView());
}
/**
* 添加权限说明
*
* @param viewGroup
* @param permissionArray
*/
private static void addPermissionDescription(boolean isHasSimpleXCamera, ViewGroup viewGroup, String[] permissionArray) {
int dp10 = DensityUtil.dip2px(viewGroup.getContext(), 10);
int dp15 = DensityUtil.dip2px(viewGroup.getContext(), 15);
MediumBoldTextView view = new MediumBoldTextView(viewGroup.getContext());
view.setTag(TAG_EXPLAIN_VIEW);
view.setTextSize(14);
view.setTextColor(Color.parseColor("#333333"));
view.setPadding(dp10, dp15, dp10, dp15);
String title;
String explain;
if (TextUtils.equals(permissionArray[0], PermissionConfig.CAMERA[0])) {
title = "相机权限使用说明";
explain = "相机权限使用说明\n用户app用于拍照/录视频";
} else if (TextUtils.equals(permissionArray[0], Manifest.permission.RECORD_AUDIO)) {
if (isHasSimpleXCamera) {
title = "麦克风权限使用说明";
explain = "麦克风权限使用说明\n用户app用于录视频时采集声音";
} else {
title = "录音权限使用说明";
explain = "录音权限使用说明\n用户app用于采集声音";
}
} else {
title = "存储权限使用说明";
explain = "存储权限使用说明\n用户app写入/下载/保存/读取/修改/删除图片、视频、文件等信息";
}
int startIndex = 0;
int endOf = startIndex + title.length();
SpannableStringBuilder builder = new SpannableStringBuilder(explain);
builder.setSpan(new AbsoluteSizeSpan(DensityUtil.dip2px(viewGroup.getContext(), 16)), startIndex, endOf, Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
builder.setSpan(new ForegroundColorSpan(0xFF333333), startIndex, endOf, Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
view.setText(builder);
view.setBackground(ContextCompat.getDrawable(viewGroup.getContext(), R.drawable.bg_vip));
if (isHasSimpleXCamera) {
RelativeLayout.LayoutParams layoutParams =
new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
layoutParams.topMargin = DensityUtil.getStatusBarHeight(viewGroup.getContext());
layoutParams.leftMargin = dp10;
layoutParams.rightMargin = dp10;
viewGroup.addView(view, layoutParams);
} else {
ConstraintLayout.LayoutParams layoutParams =
new ConstraintLayout.LayoutParams(ConstraintLayout.LayoutParams.MATCH_PARENT, ConstraintLayout.LayoutParams.WRAP_CONTENT);
layoutParams.topToBottom = R.id.title_bar;
layoutParams.leftToLeft = ConstraintSet.PARENT_ID;
layoutParams.leftMargin = dp10;
layoutParams.rightMargin = dp10;
viewGroup.addView(view, layoutParams);
}
}
/**
* 移除权限说明
*
* @param viewGroup
*/
private static void removePermissionDescription(ViewGroup viewGroup) {
View tagExplainView = viewGroup.findViewWithTag(TAG_EXPLAIN_VIEW);
viewGroup.removeView(tagExplainView);
}
}

View File

@@ -0,0 +1,245 @@
package com.xuebiping.bolizhuzi.utils;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Handler;
import android.os.Message;
import android.text.TextUtils;
import android.util.Base64;
import android.util.Log;
import com.google.gson.Gson;
import com.xuebiping.bolizhuzi.R;
import com.xuebiping.bolizhuzi.agora.utils.ToastUtil;
import com.xuebiping.bolizhuzi.controller.constant.ConsUser;
import com.xuebiping.bolizhuzi.controller.settings.manager.SettingManager;
import com.xuebiping.bolizhuzi.im.uikit.common.ToastHelper;
import com.xuebiping.bolizhuzi.im.uikit.common.util.file.AttachmentStore;
import com.xuebiping.bolizhuzi.im.uikit.common.util.file.FileUtil;
import com.xuebiping.bolizhuzi.im.uikit.common.util.media.ImageUtil;
import com.xuebiping.bolizhuzi.im.uikit.common.util.storage.StorageType;
import com.xuebiping.bolizhuzi.im.uikit.common.util.storage.StorageUtil;
import com.xuebiping.bolizhuzi.im.uikit.common.util.string.MD5;
import com.xuebiping.bolizhuzi.model.chat.ImageCheckBean;
import com.xuebiping.bolizhuzi.view.base.BaseActivity;
import com.xuebiping.bolizhuzi.view.base.utils.HttpUiCallBack;
import com.xuebiping.bolizhuzi.view.settings.video.ExtractFrameWorkThread;
import com.xuebiping.bolizhuzi.view.settings.video.ExtractVideoInfoUtil;
import com.xuebiping.bolizhuzi.view.settings.video.PictureUtils;
import com.xuebiping.bolizhuzi.view.settings.video.VideoEditInfo;
import com.fengliyan.uikit.toast.MaleToast;
import com.luck.picture.lib.config.PictureMimeType;
import com.luck.picture.lib.entity.LocalMedia;
import com.luck.picture.lib.entity.MediaExtraInfo;
import com.luck.picture.lib.interfaces.OnResultCallbackListener;
import com.luck.picture.lib.utils.MediaUtils;
import com.netease.nimlib.sdk.msg.MessageBuilder;
import com.netease.nimlib.sdk.msg.constant.SessionTypeEnum;
import com.netease.nimlib.sdk.msg.model.IMMessage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
public class MeOnResultCallbackListener implements OnResultCallbackListener<LocalMedia> {
private ExtractFrameWorkThread mExtractFrameWorkThread;
private long endPosition;
private final List<String> pornList = new ArrayList<>();
private int thumbnailsCount;
private String videoMd5;
private File videoFile;
private String OutPutFileDirPath;
private ExtractVideoInfoUtil mExtractVideoInfoUtil;
private Activity activity;
private String sessionId;
private SessionTypeEnum sessionType;
private SendMsgCallBack mSendMsgCallBack;
public interface SendMsgCallBack {
void sendMsgCallBack(IMMessage message);
}
public MeOnResultCallbackListener(Activity activity, String sessionId, SessionTypeEnum sessionType, SendMsgCallBack mSendMsgCallBack) {
this.activity = activity;
this.sessionId = sessionId;
this.sessionType = sessionType;
this.mSendMsgCallBack = mSendMsgCallBack;
}
@Override
public void onResult(ArrayList<LocalMedia> result) {
for (LocalMedia media : result) {
if (media.getWidth() == 0 || media.getHeight() == 0) {
if (PictureMimeType.isHasImage(media.getMimeType())) {
MediaExtraInfo imageExtraInfo = MediaUtils.getImageSize(activity, media.getPath());
media.setWidth(imageExtraInfo.getWidth());
media.setHeight(imageExtraInfo.getHeight());
} else if (PictureMimeType.isHasVideo(media.getMimeType())) {
MediaExtraInfo videoExtraInfo = MediaUtils.getVideoSize(activity, media.getPath());
media.setWidth(videoExtraInfo.getWidth());
media.setHeight(videoExtraInfo.getHeight());
}
}
Log.i("TAG", "onResult: media------>" + media.getRealPath());
if (PictureMimeType.isHasImage(media.getMimeType())) {
File imageFile = new File(media.getRealPath());
String extension = FileUtil.getExtensionName(media.getRealPath());
imageFile = ImageUtil.getScaledImageFileWithMD5(imageFile, extension);
if (imageFile != null) {
IMMessage message = MessageBuilder.createImageMessage(sessionId, sessionType, imageFile, imageFile.getName());
if (mSendMsgCallBack != null) {
mSendMsgCallBack.sendMsgCallBack(message);
}
} else {
ToastHelper.showToast(activity, R.string.image_show_error);
}
} else if (PictureMimeType.isHasVideo(media.getMimeType())) {
videoMd5 = MD5.getStreamMD5(media.getRealPath());
String filename = videoMd5 + "." + FileUtil.getExtensionName(media.getRealPath());
String md5Path = StorageUtil.getWritePath(filename, StorageType.TYPE_VIDEO);
if (AttachmentStore.copy(media.getRealPath(), md5Path) != -1) {
videoFile = new File(md5Path);
pornList.clear();
thumbnailsCount = NoClearSPUtils.getInt(activity, ConsUser.SEND_VIDEO_MAX_NUM);
int interval = NoClearSPUtils.getInt(activity, ConsUser.SEND_VIDEO_INTERVAL) * 1000;
OutPutFileDirPath = PictureUtils.getSaveEditThumbnailDir(activity);
mExtractVideoInfoUtil = new ExtractVideoInfoUtil(videoFile.getAbsolutePath());
long duration = Long.parseLong(mExtractVideoInfoUtil.getVideoLength());
int length = interval * thumbnailsCount;
if (length > duration) {
thumbnailsCount = (int) (duration / interval);
endPosition = (long) interval * thumbnailsCount;
if (thumbnailsCount < 1) {
thumbnailsCount = 1;
endPosition = duration;
}
} else {
endPosition = length;
}
int extractW = mExtractVideoInfoUtil.getVideoWidth();
int extractH = mExtractVideoInfoUtil.getVideoHeight();
mExtractFrameWorkThread = new ExtractFrameWorkThread(extractW, extractH, mUIHandler, videoFile.getAbsolutePath(), OutPutFileDirPath, 0, endPosition, thumbnailsCount);
mExtractFrameWorkThread.start();
} else {
ToastHelper.showToast(activity, R.string.video_exception);
}
}
}
}
@Override
public void onCancel() {
Log.i("TAG", "PictureSelector Cancel");
}
private Handler mUIHandler = new SafeHandler(this);
private static class SafeHandler extends Handler {
private final WeakReference<MeOnResultCallbackListener> outerClassReference;
public SafeHandler(MeOnResultCallbackListener context) {
this.outerClassReference = new WeakReference<>(context);
}
@Override
public void handleMessage(Message msg) {
MeOnResultCallbackListener outerClass = outerClassReference.get();
if (outerClass == null) {
return; // 外部类已经被回收
}
switch (msg.what) {
case ExtractFrameWorkThread.MSG_SAVE_SUCCESS:
outerClass.checkVideo(msg);
break;
default:
// 可选:记录未处理的消息类型以便调试
// Log.d("SafeHandler", "Unhandled message: " + msg.what);
break;
}
}
}
//视频鉴黄
private void checkVideo(Message msg) {
VideoEditInfo info = (VideoEditInfo) msg.obj;
final Bitmap bitmap = BitmapFactory.decodeFile(info.path);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
if (bitmap != null) {
bitmap.compress(Bitmap.CompressFormat.JPEG, 80, bos); //参数100表示不压缩
}
String img = Base64.encodeToString(bos.toByteArray(), Base64.DEFAULT);
pornList.add(img);
if (pornList.size() == thumbnailsCount) {
String s = new Gson().toJson(pornList);
SettingManager.videoCheck((BaseActivity) activity, s, new HttpUiCallBack<ImageCheckBean>() {
@Override
public void onSuccess(BaseActivity activity, ImageCheckBean result, String tips) {
destroyWork();
if (result.getIs_upload() == 1) {
MediaPlayer mediaPlayer = getVideoMediaPlayer(videoFile);
long duration = mediaPlayer == null ? 0 : mediaPlayer.getDuration();
int height = mediaPlayer == null ? 0 : mediaPlayer.getVideoHeight();
int width = mediaPlayer == null ? 0 : mediaPlayer.getVideoWidth();
IMMessage message = MessageBuilder.createVideoMessage(sessionId, sessionType, videoFile, duration, width, height, videoMd5);
if (mSendMsgCallBack != null) {
mSendMsgCallBack.sendMsgCallBack(message);
}
} else {
MaleToast.showMessage(activity, "视频违规");
}
}
@Override
public void onFailure(BaseActivity activity, String tip) {
destroyWork();
ToastUtil.showToast(activity, tip);
}
@Override
public void onException(BaseActivity activity, Throwable e) {
destroyWork();
ToastUtil.showToast(activity, "发送失败" + e.getMessage());
}
});
}
}
//释放相关资源
private void destroyWork() {
if (mExtractFrameWorkThread != null) {
mExtractFrameWorkThread.stopExtract();
}
if (mExtractVideoInfoUtil != null) {
mExtractVideoInfoUtil.release();
}
if (!TextUtils.isEmpty(OutPutFileDirPath)) {
PictureUtils.deleteFile(new File(OutPutFileDirPath));
}
if (mUIHandler != null) {
mUIHandler.removeCallbacksAndMessages(null);
}
}
/**
* 获取视频mediaPlayer
*
* @param file 视频文件
* @return mediaPlayer
*/
private MediaPlayer getVideoMediaPlayer(File file) {
try {
return MediaPlayer.create(activity, Uri.fromFile(file));
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}

View File

@@ -0,0 +1,408 @@
package com.xuebiping.bolizhuzi.utils
import android.content.Context
import android.content.res.Resources
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Matrix
import android.graphics.Rect
import android.graphics.drawable.Drawable
import android.graphics.drawable.NinePatchDrawable
import android.util.DisplayMetrics
import android.util.Log
import androidx.annotation.DrawableRes
import java.io.File
import java.nio.ByteBuffer
import java.nio.ByteOrder
/**
* Created by p_dmweidu on 2023/11/6
* Desc: 从文件或者资源中加载一张png然后转化成NinePatchDrawable返回。
*/
class NinePatchDrawableFactory(private val context: Context) {
companion object {
private const val TAG = "NinePatchDrawableFactor"
//Note: 把加载过的图片缓存在内存里
private val bitmapLruCache = BitmapLruCache()
val NO_COLOR = 0x00000001
val COLOR_SIZE = 9
/**
* 同一个路径的图片如果需要水平镜像保存到在LruCache的时候key就是这个前缀+图片路径
*/
val HORIZONTAL_MIRROR_PREFIX = "horizontal_mirror_prefix"
}
/**
* 文件中原始图片的密度这里默认是1如果是3倍图就是3
* 从文件中解析出来的bitmap需要设置inDensityinTargetDensity才能正确的缩放
*/
private var bitmapMapInDensity: Int = 1
/**
* 是否要水平镜像,如果左右气泡都用一张图的话,需要水平镜像一下
*/
private var horizontalMirror = false
/**
* 图片资源id
*/
@DrawableRes
private var drawableResId: Int = 0
/**
* 图片文件目前只测试了png格式
*/
private var drawableFile: File? = null
/**
* 原始图片宽高
*/
private var originWidth: Int = 0
private var originHeight: Int = 0
/**
* 最终使用的图片宽高
*/
private var finalWidth: Int = 0
private var finalHeight: Int = 0
private var horizontalStretchBean: PatchStretchBean? = null
private var verticalStretchBean: PatchStretchBean? = null
private var paddingRect: Rect? = null
private var paddingLeft: Int = 0
private var paddingRight: Int = 0
private var paddingTop: Int = 0
private var paddingBottom: Int = 0
private var chunk: ByteArray? = null
private var finalPaddingRect = Rect()
/**
* 从资源文件中加载图片
*/
fun buildFromResource(): Drawable? {
val currentTimeMillis = System.currentTimeMillis()
val ninePatchDrawable = get9PatchFromResource(context.resources, drawableResId)
Log.i(
TAG, "buildFromResource: end 耗时:${System.currentTimeMillis() - currentTimeMillis} ms"
)
return ninePatchDrawable
}
/**
* 从文件中加载图片
*/
fun buildFromFile(): Drawable? {
val currentTimeMillis = System.currentTimeMillis()
val ninePatchDrawable = get9PatchFromFile(context.resources, drawableFile)
Log.i(
TAG,
"buildFromFile: end 耗时:${System.currentTimeMillis() - currentTimeMillis} ms"
)
return ninePatchDrawable
}
/**
* 设置横向的拉伸线段,必须的
* @param horizontalStretchBean 横向的拉伸线段
*/
fun setHorizontalStretchBean(horizontalStretchBean: PatchStretchBean): NinePatchDrawableFactory {
this.horizontalStretchBean = horizontalStretchBean
return this
}
/**
* 设置竖向的拉伸线段,必须的
* @param verticalStretchBean 竖向的拉伸线段
*/
fun setVerticalStretchBean(verticalStretchBean: PatchStretchBean): NinePatchDrawableFactory {
this.verticalStretchBean = verticalStretchBean
return this
}
/**
* 设置原始图片的宽高必须的是不是可以从bitmap里获取不能因为第二次复用的时候无法获取原始图片的宽度和高度
* @param originWidth 原始图片的宽度
* @param originHeight 原始图片的高度
*/
fun setOriginSize(originWidth: Int, originHeight: Int): NinePatchDrawableFactory {
this.originWidth = originWidth
this.originHeight = originHeight
return this
}
fun setDrawableResId(drawableResId: Int): NinePatchDrawableFactory {
this.drawableResId = drawableResId
return this
}
fun setDrawableFile(drawableFile: File): NinePatchDrawableFactory {
this.drawableFile = drawableFile
return this
}
fun setBitmapMapInDensity(bitmapMapInDensity: Int): NinePatchDrawableFactory {
this.bitmapMapInDensity = bitmapMapInDensity
return this
}
/**
* 设置padding不是必须的。
* @param rect padding的区域
*/
fun setPadding(rect: Rect): NinePatchDrawableFactory {
paddingRect = rect
return this
}
/**
* 设置是否水平镜像,如果左右聊天气泡都用一张图的话,需要水平镜像一下,不是必须的
* @param horizontalMirror 是否水平镜像
*/
fun setHorizontalMirror(horizontalMirror: Boolean): NinePatchDrawableFactory {
this.horizontalMirror = horizontalMirror
return this
}
/**
* @param resources
* @param resId 图片资源id
*/
private fun get9PatchFromResource(resources: Resources, resId: Int): Drawable? {
val resIdString = resId.toString()
Log.i(TAG, "setResourceData: resId = $resId")
var bitmap = if (horizontalMirror) {
bitmapLruCache.getBitmap(HORIZONTAL_MIRROR_PREFIX + resIdString)
} else {
bitmapLruCache.getBitmap(resIdString)
}
if (bitmap == null) {
bitmap = try {
BitmapFactory.decodeResource(resources, resId)
} catch (e: Throwable) {
e.printStackTrace()
null
}
if (bitmap != null) {
if (horizontalMirror) {
val matrix = Matrix()
matrix.postScale(-1f, 1f)
val mirrorBitmap = Bitmap.createBitmap(
bitmap, 0, 0, bitmap.width, bitmap.height, matrix,
false
)
bitmapLruCache.putBitmap(
HORIZONTAL_MIRROR_PREFIX + resIdString,
mirrorBitmap
)
bitmap = mirrorBitmap
} else {
bitmapLruCache.putBitmap(resIdString, bitmap)
}
Log.i(TAG, "setResourceData: width = ${bitmap.width}, height = ${bitmap.height}")
}
} else {
Log.i(TAG, "setResourceData: 从缓存中获取bitmap != null")
}
setFinalUsedBitmapSize(bitmap)
return get9PatchDrawable(bitmap, resources)
}
/**
* 设置本地文件夹中的图片
* 从文件解析的话需要处理缩放density
* @param file 本地png文件路径
*/
private fun get9PatchFromFile(
resources: Resources,
file: File?
): Drawable? {
if (file == null || !file.exists()) {
return null
}
val absolutePath = file.absolutePath
var bitmap = if (horizontalMirror) {
bitmapLruCache.getBitmap(HORIZONTAL_MIRROR_PREFIX + absolutePath)
} else {
bitmapLruCache.getBitmap(absolutePath)
}
if (bitmap == null) {
bitmap = try {
BitmapFactory.decodeFile(absolutePath, BitmapFactory.Options().apply {
//bitmap 原本的密度
inDensity = bitmapMapInDensity * DisplayMetrics.DENSITY_DEFAULT
//当前设备的密度
inTargetDensity = resources.displayMetrics.densityDpi
})
} catch (e: Throwable) {
e.printStackTrace()
null
}
if (bitmap != null) {
Log.i(
TAG,
"setFileData: not scale width = ${bitmap.width}, height = ${bitmap.height}"
)
if (horizontalMirror) {
val matrix = Matrix()
matrix.postScale(-1f, 1f)
val mirrorBitmap =
Bitmap.createBitmap(
bitmap,
0,
0,
bitmap.width,
bitmap.height,
matrix,
false
)
bitmapLruCache.putBitmap(
HORIZONTAL_MIRROR_PREFIX + absolutePath,
mirrorBitmap
)
bitmap = mirrorBitmap
} else {
bitmapLruCache.putBitmap(absolutePath, bitmap)
}
}
}
setFinalUsedBitmapSize(bitmap)
return get9PatchDrawable(bitmap, resources)
}
/**
* 控制内容填充的区域
* 注意这里的lefttoprightbottom同xml文件中的padding意思一致只不过这里是百分比形式
*/
private fun buildPadding() {
if (horizontalMirror) {
finalPaddingRect.left = ((originWidth - paddingRight) * finalWidth / originWidth)
finalPaddingRect.right = ((paddingLeft * finalWidth) / originWidth)
} else {
finalPaddingRect.left = ((paddingLeft * finalWidth) / originWidth)
finalPaddingRect.right = ((originWidth - paddingRight) * finalWidth / originWidth)
}
finalPaddingRect.top = (paddingTop * finalHeight / originHeight)
finalPaddingRect.bottom = ((originHeight - paddingBottom) * finalHeight / originHeight)
Log.i(TAG, "buildPadding: rect = $finalPaddingRect")
}
/**
* 直接处理bitmap数据
*/
private fun setFinalUsedBitmapSize(bitmap: Bitmap?) {
Log.i(TAG, "setBitmapData: width = ${bitmap?.width}, height = ${bitmap?.height}")
this.finalWidth = bitmap?.width ?: 0
this.finalHeight = bitmap?.height ?: 0
}
private fun buildChunk(): ByteArray {
// 横向和竖向端点的数量 = 线段数量 * 2这里只有一个线段所以都是2
val horizontalEndpointsSize = 2
val verticalEndpointsSize = 2
//这里计算的 arraySize 是 int 值,最终占用的字节数是 arraySize * 4
val arraySize = 1 + 2 + 4 + 1 + horizontalEndpointsSize + verticalEndpointsSize + COLOR_SIZE
//这里乘以4是因为一个int占用4个字节
val byteBuffer = ByteBuffer.allocate(arraySize * 4).order(ByteOrder.nativeOrder())
byteBuffer.put(1.toByte()) //第一个字节无意义不等于0就行
byteBuffer.put(horizontalEndpointsSize.toByte()) //mDivX x数组的长度
byteBuffer.put(verticalEndpointsSize.toByte()) //mDivY y数组的长度
byteBuffer.put(COLOR_SIZE.toByte()) //mColor数组的长度
// skip 8 bytes
byteBuffer.putInt(0)
byteBuffer.putInt(0)
//Note: 等待进一步研究这四个int值好像都写0也可以。
//左右padding
byteBuffer.putInt(finalPaddingRect.left)
byteBuffer.putInt(finalPaddingRect.right)
//上下padding
byteBuffer.putInt(finalPaddingRect.top)
byteBuffer.putInt(finalPaddingRect.bottom)
// byteBuffer.putInt(0)
// byteBuffer.putInt(0)
// //上下padding
// byteBuffer.putInt(0)
// byteBuffer.putInt(0)
// // skip 4 bytes
byteBuffer.putInt(0)
// regions 控制横向拉伸的线段数据
//mDivX数组
horizontalStretchBean?.let {
if (horizontalMirror) {
byteBuffer.putInt((originWidth - it.end) * finalWidth / originWidth)
byteBuffer.putInt((originWidth - it.start) * finalWidth / originWidth)
} else {
byteBuffer.putInt(it.start * finalWidth / originWidth)
byteBuffer.putInt(it.end * finalWidth / originWidth)
}
}
//mDivY数组
// regions 控制竖向拉伸的线段数据
verticalStretchBean?.let {
byteBuffer.putInt(it.start * finalHeight / originHeight)
byteBuffer.putInt(it.end * finalHeight / originHeight)
}
//mColor数组
for (i in 0 until COLOR_SIZE) {
byteBuffer.putInt(NO_COLOR)
}
return byteBuffer.array()
}
private fun get9PatchDrawable(bitmap: Bitmap?, resources: Resources): Drawable? {
return try {
paddingRect?.let {
paddingLeft = it.left
paddingRight = it.right
paddingTop = it.top
paddingBottom = it.bottom
buildPadding()
}
if (chunk == null) {
chunk = buildChunk()
}
val ninePatchDrawable =
NinePatchDrawable(resources, bitmap, chunk, finalPaddingRect, null)
ninePatchDrawable
} catch (e: Exception) {
null
}
}
}

View File

@@ -0,0 +1,88 @@
package com.xuebiping.bolizhuzi.utils;
import android.content.Context;
import android.content.SharedPreferences;
/**
* Created by zhangbin on 2019/2/21.
*/
public class NoClearSPUtils {
private final static String name = "no_clear_sp";
private final static int mode = Context.MODE_PRIVATE;
/**
* 保存首选项
* @param context
* @param key
* @param value
*/
public static void saveBoolean(Context context, String key, boolean value){
SharedPreferences sp = context.getSharedPreferences(name, mode);
SharedPreferences.Editor edit = sp.edit();
edit.putBoolean(key, value);
edit.commit();
}
public static void saveInt(Context context, String key, int value){
SharedPreferences sp = context.getSharedPreferences(name, mode);
SharedPreferences.Editor edit = sp.edit();
edit.putInt(key, value);
edit.commit();
}
public static void saveString(Context context, String key, String value){
SharedPreferences sp = context.getSharedPreferences(name, mode);
SharedPreferences.Editor edit = sp.edit();
edit.putString(key, value);
edit.commit();
}
/**
* 获取首选项
* @param context
* @param key
* @param defValue
* @return
*/
public static boolean getBoolean(Context context, String key, boolean defValue){
SharedPreferences sp = context.getSharedPreferences(name, mode);
return sp.getBoolean(key, defValue);
}
public static boolean getBoolean(Context context, String key){
SharedPreferences sp = context.getSharedPreferences(name, mode);
return sp.getBoolean(key, false);
}
public static int getInt(Context context, String key, int defValue){
SharedPreferences sp = context.getSharedPreferences(name, mode);
return sp.getInt(key, defValue);
}
public static int getInt(Context context, String key){
SharedPreferences sp = context.getSharedPreferences(name, mode);
return sp.getInt(key, 0);
}
public static String getString(Context context, String key, String defValue){
SharedPreferences sp = context.getSharedPreferences(name, mode);
return sp.getString(key, defValue);
}
public static String getString(Context context, String key) {
SharedPreferences sp = context.getSharedPreferences(name, mode);
return sp.getString(key, "");
}
/**
* 清除保存
* @param context
*/
public static void clear(Context context){
SharedPreferences sp = context.getSharedPreferences(name, mode);
SharedPreferences.Editor editor = sp.edit();
editor.clear();
editor.commit();
}
}

View File

@@ -0,0 +1,34 @@
package com.xuebiping.bolizhuzi.utils;
import android.app.NotificationManager;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import java.lang.reflect.Method;
/**
* Created by zhangbin on 2018/12/22.
*/
public class NotificationsUpUtils {
public static boolean isEnableV26(Context context) {
ApplicationInfo appInfo = context.getApplicationInfo();
String pkg = context.getApplicationContext().getPackageName();
int uid = appInfo.uid;
try {
NotificationManager notificationManager = (NotificationManager)
context.getSystemService(Context.NOTIFICATION_SERVICE);
Method sServiceField = notificationManager.getClass().getDeclaredMethod("getService");
sServiceField.setAccessible(true);
Object sService = sServiceField.invoke(notificationManager);
Method method = sService.getClass().getDeclaredMethod("areNotificationsEnabledForPackage"
, String.class, Integer.TYPE);
method.setAccessible(true);
return (boolean) method.invoke(sService, pkg, uid);
} catch (Exception e) {
return true;
}
}
}

View File

@@ -0,0 +1,139 @@
package com.xuebiping.bolizhuzi.utils;
import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.os.Build;
import androidx.core.app.NotificationCompat;
import com.xuebiping.bolizhuzi.R;
import com.xuebiping.bolizhuzi.model.main.VideoRequestBean;
import com.xuebiping.bolizhuzi.receiver.NotificationReceiver;
import com.xuebiping.bolizhuzi.view.main.MainActivity;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;
/**
* Created by zhangbin on 2018/12/22.
*/
public class NotificationsUtils {
private static final String CHECK_OP_NO_THROW = "checkOpNoThrow";
private static final String OP_POST_NOTIFICATION = "OP_POST_NOTIFICATION";
@SuppressLint("NewApi")
public static boolean isNotificationEnabled(Context context) {
AppOpsManager mAppOps =
(AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
ApplicationInfo appInfo = context.getApplicationInfo();
String pkg = context.getApplicationContext().getPackageName();
int uid = appInfo.uid;
Class appOpsClass = null;
/* Context.APP_OPS_MANAGER */
try {
appOpsClass = Class.forName(AppOpsManager.class.getName());
Method checkOpNoThrowMethod =
appOpsClass.getMethod(CHECK_OP_NO_THROW,
Integer.TYPE, Integer.TYPE, String.class);
Field opPostNotificationValue = appOpsClass.getDeclaredField(OP_POST_NOTIFICATION);
int value = (Integer) opPostNotificationValue.get(Integer.class);
return ((Integer) checkOpNoThrowMethod.invoke(mAppOps, value, uid, pkg) ==
AppOpsManager.MODE_ALLOWED);
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
public static void sendNotification(Context context, VideoRequestBean videoRequestBaseBean) {
if (videoRequestBaseBean == null) return;
Intent intentCancel = new Intent(context, NotificationReceiver.class);
intentCancel.setAction("notification_cancelled");
String channel_id = "CHANNEL_ID12";
intentCancel.putExtra(NotificationReceiver.TYPE, channel_id);
PendingIntent pendingIntentCancel = PendingIntent.getBroadcast(context, 0, intentCancel, PendingIntent.FLAG_ONE_SHOT);
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
Intent intent = new Intent(context, MainActivity.class);
intent.putExtra("videoRequestBaseBean", videoRequestBaseBean);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
Notification notification;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel notificationChannel = createNotificationChannel();
if (notificationChannel != null) {
notificationManager.createNotificationChannel(notificationChannel);
notification = new NotificationCompat.Builder(context, "芊颜")
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle("您收到一条视频通话")
.setSmallIcon(R.mipmap.ic_launcher)
.setContentText("您收到一条视频通话")
.setContentIntent(pendingIntent)
.setDeleteIntent(pendingIntentCancel)
.setAutoCancel(true)
.build();
notificationManager.notify(MainActivity.mCallID, notification);
}
} else {
notification = new NotificationCompat.Builder(context, "芊颜")
.setContentTitle("您收到一条视频通话")
.setContentText("您收到一条视频通话")
.setContentIntent(pendingIntent)
.setSmallIcon(R.mipmap.ic_launcher)
.setAutoCancel(true)
.setDeleteIntent(pendingIntentCancel)
.build();
notificationManager.notify(MainActivity.mCallID, notification);
}
}
private static NotificationChannel createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel notificationChannel = new NotificationChannel("芊颜",
"芊颜通知", NotificationManager.IMPORTANCE_HIGH);
return notificationChannel;
}
return null;
}
public static boolean isAppOnForeground(Context context) {
ActivityManager activityManager = (ActivityManager) context.getApplicationContext().getSystemService(
Context.ACTIVITY_SERVICE);
String packageName = context.getApplicationContext().getPackageName();
List<ActivityManager.RunningAppProcessInfo> appProcesses = activityManager.getRunningAppProcesses();
if (appProcesses == null)
return false;
for (ActivityManager.RunningAppProcessInfo appProcess : appProcesses) {
if (appProcess.processName.equals(packageName)
&& appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
return true;
}
}
return false;
}
public static void bring2Front(Context context) {
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.AppTask> list = activityManager.getAppTasks();
for (ActivityManager.AppTask appTask : list) {
appTask.moveToFront();
break;
}
}
}

View File

@@ -0,0 +1,10 @@
package com.xuebiping.bolizhuzi.utils
/**
* Created by dmw on 2023/10/23
* Desc: 定义拉伸区域的起点和终点
*/
data class PatchStretchBean(
val start: Int,
val end: Int
)

View File

@@ -0,0 +1,121 @@
package com.xuebiping.bolizhuzi.utils;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import com.fengliyan.device.DeviceManager;
import com.xuebiping.bolizhuzi.BuildConfig;
import com.xuebiping.bolizhuzi.controller.constant.Constant;
import com.xuebiping.bolizhuzi.view.base.PayWebViewActivity;
import com.fengliyan.uikit.toast.MaleToast;
import com.tencent.mm.opensdk.modelbiz.WXLaunchMiniProgram;
import com.tencent.mm.opensdk.openapi.IWXAPI;
import com.tencent.mm.opensdk.openapi.WXAPIFactory;
import java.util.List;
public class PayUtils {
/**
* 汇付微信小程序支付
*
* @param context
* @param goodsID
* @param type
* @param mini_program_type 0-正式版 1-开发版 2-体验版
*/
public static void wxminiPay(Activity context, int goodsID, int type, String ghOriId, int mini_program_type) {
String appId = Constant.WECHAT_APP_ID; // 填应用AppId
IWXAPI api = WXAPIFactory.createWXAPI(context, appId);
boolean isWXAppInstalledAndSupported = api.isWXAppInstalled();
if (isWXAppInstalledAndSupported) {
api.openWXApp();
api.registerApp(appId);
String path = String.format("pages/info/index?token=%s&source_app=4&channel=wechat&goodsId=%d&type=%d&version=%s&source_id=%d", DeviceManager.getInstance().getToken(), goodsID, type, BuildConfig.VERSION_NAME, 1);
WXLaunchMiniProgram.Req req = new WXLaunchMiniProgram.Req();
// req.userName = "gh_302cef1cd1e8"; // 填小程序原始id
req.userName = ghOriId; // 填小程序原始id
req.path = path; //拉起小程序页面的可带参路径,不填默认拉起小程序首页,对于小游戏,可以只传入 query 部分,来实现传参效果,如:传入 "?foo=bar"。
if (mini_program_type == 2) {
req.miniprogramType = WXLaunchMiniProgram.Req.MINIPROGRAM_TYPE_PREVIEW;// 可选打开 开发版,体验版和正式版
} else if (mini_program_type == 1) {
req.miniprogramType = WXLaunchMiniProgram.Req.MINIPROGRAM_TYPE_TEST;// 可选打开 开发版,体验版和正式版
} else {
req.miniprogramType = WXLaunchMiniProgram.Req.MINIPTOGRAM_TYPE_RELEASE;// 可选打开 开发版,体验版和正式版
}
api.sendReq(req);
} else {
MaleToast.showMessage(context, "未安装微信,不能支付");
}
}
//汇付支付宝唤醒
public static void ailWeb(String qrcodeUrl, Activity context) {
// 固定前缀
String topic = "alipays://platformapi/startapp?saId=10000007&qrcode=";
// 从汇付正扫接口获取的参数(以下值仅为示例)
String jumpUrl = topic + qrcodeUrl;
// 按以上示例拼接得出结果
// jumpUrl 为 alipays://platformapi/startapp?saId=10000007&qrcode=https://qr.alipay.com/bax02911brluc2xieoph6001
Intent intent = new Intent();
intent.setAction("android.intent.action.VIEW");
// jumpUrl 为先前示例中拼装的 url
Uri contentUrl = Uri.parse(jumpUrl);
intent.setData(contentUrl);
context.startActivity(intent);
}
/**
* 商福通支付
*
* @param context
* @param url
*/
public static void SFTPay(Activity context, String url) {
if (url.startsWith("weixin://")) {
IWXAPI api = WXAPIFactory.createWXAPI(context, null);
api.registerApp(Constant.WECHAT_APP_ID);
if (isWeChatAppInstalled(context, api)) {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(Uri.parse(url));
context.startActivity(intent);
} else {
MaleToast.showMessage(context, "您还未安装微信客户端");
}
} else if (url.startsWith("https://")) {
Intent intent = new Intent(context, PayWebViewActivity.class);
intent.putExtra("ClickUrl", url);
intent.putExtra("type", 5);
context.startActivity(intent);
}
}
public static boolean isWeChatAppInstalled(Context context, IWXAPI api) {
if (api.isWXAppInstalled()) {
return true;
} else {
final PackageManager packageManager = context.getPackageManager();// 获取packagemanager
List<PackageInfo> pinfo = packageManager.getInstalledPackages(0);// 获取所有已安装程序的包信息
if (pinfo != null) {
for (int i = 0; i < pinfo.size(); i++) {
String pn = pinfo.get(i).packageName;
if (pn.equalsIgnoreCase("com.tencent.mm")) {
return true;
}
}
}
return false;
}
}
public static boolean parseScheme(String url) {
return url.contains("platformapi/startapp") || (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) && (url.contains("platformapi") && url.contains("startapp"));
}
}

View File

@@ -0,0 +1,244 @@
package com.xuebiping.bolizhuzi.utils;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
import com.fengliyan.uikit.toast.MaleToast;
import com.xuebiping.bolizhuzi.BuildConfig;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class PermissionUtil {
public static void jumpPermissionPage(Context context) {
String manufacturer = Build.MANUFACTURER.toLowerCase();
switch (manufacturer) {
case "xiaomi":
goXiaoMiManager(context);
break;
case "huawei":
goHuaWeiManager(context);
break;
case "oppo":
goOppoManager(context);
break;
case "meizu":
goMeizuManager(context);
break;
case "samsung":
goSamsungManager(context);
break;
case "sony":
goSonyManager(context);
break;
case "lg":
goLGManager(context);
break;
case "letv":
goLetvManager(context);
break;
case "qiku":
case "360":
go360Manager(context);
break;
case "vivo":
goVivoManager(context);
break;
default:
goAppDetailSetting(context);
break;
}
}
private static void goXiaoMiManager(Context context) {
String rom = checkMIUI();
try{
Intent intent =new Intent();
if ("V5".equals(rom)) {
Uri packageURI = Uri.parse("package:" + BuildConfig.APPLICATION_ID);
intent =new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, packageURI);
} else if ("V6".equals(rom) || "V7".equals(rom)) {
intent.setAction("miui.intent.action.APP_PERM_EDITOR");
intent.setClassName("com.miui.securitycenter", "com.miui.permcenter.permissions.AppPermissionsEditorActivity");
intent.putExtra("extra_pkgname", BuildConfig.APPLICATION_ID);
} else if ("V8".equals(rom) || "V9".equals(rom)||"V10".equals(rom) || "V11".equals(rom)|| "V12".equals(rom)) {
intent.setAction("miui.intent.action.APP_PERM_EDITOR");
intent.setClassName("com.miui.securitycenter", "com.miui.permcenter.permissions.PermissionsEditorActivity");
intent.putExtra("extra_pkgname", BuildConfig.APPLICATION_ID);
} else {
goAppDetailSetting(context);
}
context.startActivity(intent);
}catch (Exception e) {
goAppDetailSetting(context);
}
}
private static void goHuaWeiManager(Context context) {
try {
Intent intent =new Intent(BuildConfig.APPLICATION_ID);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
ComponentName comp = new ComponentName("com.huawei.systemmanager", "com.huawei.permissionmanager.ui.MainActivity");
intent.setComponent(comp);
context.startActivity(intent);
} catch (Exception e) {
goAppDetailSetting(context);
}
}
private static void goOppoManager(Context context) {
try {
Intent intent =new Intent("android.settings.APPLICATION_DETAILS_SETTINGS");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
ComponentName comp = new ComponentName("com.coloros.securitypermission", "com.coloros.securitypermission.permission.PermissionAppAllPermissionActivity");
intent.setComponent(comp);
context.startActivity(intent);
} catch (Exception e) {
goAppDetailSetting(context);
}
}
private static void goMeizuManager(Context context) {
try {
Intent intent = new Intent("com.meizu.safe.security.SHOW_APPSEC");
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.putExtra("packageName", BuildConfig.APPLICATION_ID);
context.startActivity(intent);
} catch (Exception e) {
goAppDetailSetting(context);
}
}
private static void goSamsungManager(Context context) {
goAppDetailSetting(context);
}
private static void goSonyManager(Context context) {
try {
Intent intent =new Intent(BuildConfig.APPLICATION_ID);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
ComponentName comp = new ComponentName("com.sonymobile.cta", "com.sonymobile.cta.SomcCTAMainActivity");
intent.setComponent(comp);
context.startActivity(intent);
} catch (Exception e) {
goAppDetailSetting(context);
}
}
private static void goLGManager(Context context) {
try {
Intent intent = new Intent("android.intent.action.MAIN");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra("packageName", BuildConfig.APPLICATION_ID);
ComponentName comp =new ComponentName("com.android.settings", "com.android.settings.Settings.AccessLockSummaryActivity");
intent.setComponent(comp);
context.startActivity(intent);
} catch (Exception e) {
goAppDetailSetting(context);
}
}
private static void goLetvManager(Context context) {
try {
Intent intent =new Intent();
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra("packageName", BuildConfig.APPLICATION_ID);
ComponentName comp =new ComponentName("com.letv.android.letvsafe", "com.letv.android.letvsafe.PermissionAndApps");
intent.setComponent(comp);
context.startActivity(intent);
} catch (Exception e) {
goAppDetailSetting(context);
}
}
private static void go360Manager(Context context) {
try {
Intent intent = new Intent("android.intent.action.MAIN");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra("packageName", BuildConfig.APPLICATION_ID);
ComponentName comp =new ComponentName("com.android.settings", "com.android.settings.Settings.AccessLockSummaryActivity");
intent.setComponent(comp);
context.startActivity(intent);
} catch (Exception e) {
goAppDetailSetting(context);
}
}
private static void goVivoManager(Context context) {
try {
/* Intent intent = new Intent();
intent.setClassName("com.vivo.permissionmanager","com.vivo.permissionmanager.activity.SoftPermissionDetailActivity");
intent.setAction("secure.intent.action.softPermissionDetail");
intent.putExtra("packageName","BuildConfig.APPLICATION_ID");
context.startActivity(intent);*/
goAppDetailSetting(context);
} catch (Exception e) {
goAppDetailSetting(context);
}
}
public static void goAppDetailSetting(Context context) {
Intent intent = new Intent();
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if(Build.VERSION.SDK_INT >= 9){
intent.setAction("android.settings.APPLICATION_DETAILS_SETTINGS");
intent.setData(Uri.fromParts("package", context.getPackageName(), null));
} else if(Build.VERSION.SDK_INT <= 8){
intent.setAction(Intent.ACTION_VIEW);
intent.setClassName("com.android.settings","com.android.settings.InstalledAppDetails");
intent.putExtra("com.android.settings.ApplicationPkgName", context.getPackageName());
}
try{
context.startActivity(intent);
}catch (Exception e) {
MaleToast.showMessage(context,"页面调转失败");
}
}
public static void goLocationSetting(Context context) {
Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
context.startActivity(intent);
}
public static String checkMIUI() {
String versionCode = "";
String manufacturer = Build.MANUFACTURER;
String model = Build.MODEL;
Log.i("PermissionUtil","Build.MANUFACTURER = " + manufacturer + " ,Build.MODEL = " + Build.MODEL);
if (!TextUtils.isEmpty(manufacturer) && manufacturer.equals("Xiaomi")) {
versionCode = getSystemProperty("ro.miui.ui.version.name");
}
return versionCode;
}
public static String getSystemProperty(String propName) {
String line;
BufferedReader input = null;
try {
Process p = Runtime.getRuntime().exec("getprop " + propName);
input = new BufferedReader(new InputStreamReader(p.getInputStream()), 1024);
line = input.readLine();
input.close();
} catch (IOException ex) {
Log.i("PermissionUtil","Unable to read sysprop " + propName, ex);
return null;
} finally {
if (input != null) {
try {
input.close();
} catch (IOException e) {
Log.i("PermissionUtil","Exception while closing InputStream", e);
}
}
}
return line;
}
}

View File

@@ -0,0 +1,327 @@
package com.xuebiping.bolizhuzi.utils;
import android.content.Context;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.xuebiping.bolizhuzi.R;
import java.util.ArrayList;
import java.util.List;
/**
* 类PhoneCode
* 日期2018/3/14.
*/
public class PhoneCodeView extends RelativeLayout {
private final Context context;
private View view;
private TextView tv_code1;
private TextView tv_code2;
private TextView tv_code3;
private TextView tv_code4;
private TextView tv_code5;
private TextView tv_code6;
private View v1;
private View v2;
private View v3;
private View v4;
private View v5;
private View v6;
private EditText et_code;
private final List<String> codes = new ArrayList<>();
private InputMethodManager imm;
public PhoneCodeView(Context context) {
super(context);
this.context = context;
loadView();
}
public PhoneCodeView(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
loadView();
}
private void loadView() {
imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
View view = LayoutInflater.from(context).inflate(R.layout.phone_code_view, this);
initView(view);
initEvent();
showSoftInput();
}
private void initView(View view) {
tv_code1 = view.findViewById(R.id.tv_code1);
tv_code2 = view.findViewById(R.id.tv_code2);
tv_code3 = view.findViewById(R.id.tv_code3);
tv_code4 = view.findViewById(R.id.tv_code4);
tv_code5 = view.findViewById(R.id.tv_code5);
tv_code6 = view.findViewById(R.id.tv_code6);
et_code = view.findViewById(R.id.et_code);
v1 = view.findViewById(R.id.v1);
v2 = view.findViewById(R.id.v2);
v3 = view.findViewById(R.id.v3);
v4 = view.findViewById(R.id.v4);
v5 = view.findViewById(R.id.v5);
v6 = view.findViewById(R.id.v6);
}
private void initEvent() {
et_code.requestFocus();
et_code.setFocusable(true);
et_code.setFocusableInTouchMode(true);
//验证码输入
et_code.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void afterTextChanged(Editable editable) {
if (editable != null && editable.length() > 0) {
et_code.setText("");
if (codes.size() < 6) {
codes.add(editable.toString());
showCode();
}
if (codes.size() == 6 && !isFristRequest && mSetRequestListener != null) {
mSetRequestListener.setRequest();
isFristRequest = true;
}
}
}
});
// 监听验证码删除按键
et_code.setOnKeyListener(new OnKeyListener() {
@Override
public boolean onKey(View view, int keyCode, KeyEvent keyEvent) {
if (keyCode == KeyEvent.KEYCODE_DEL && keyEvent.getAction() == KeyEvent.ACTION_DOWN && codes.size() > 0) {
codes.remove(codes.size() - 1);
showCode();
isFristRequest = false;
return true;
}
return false;
}
});
}
/**
* 显示输入的验证码
*/
private void showCode() {
String code1 = "";
String code2 = "";
String code3 = "";
String code4 = "";
String code5 = "";
String code6 = "";
if (view != null) {
view.setEnabled(codes.size() >= 6);
}
if (codes.size() >= 1) {
code1 = codes.get(0);
}
if (codes.size() >= 2) {
code2 = codes.get(1);
}
if (codes.size() >= 3) {
code3 = codes.get(2);
}
if (codes.size() >= 4) {
code4 = codes.get(3);
}
if (codes.size() >= 5) {
code5 = codes.get(4);
}
if (codes.size() >= 6) {
code6 = codes.get(5);
}
tv_code1.setText(code1);
tv_code2.setText(code2);
tv_code3.setText(code3);
tv_code4.setText(code4);
tv_code5.setText(code5);
tv_code6.setText(code6);
setColor();
}
/**
* 设置高亮颜色
*/
private void setColor() {
if (codes.size() == 0) {
tv_code1.setBackgroundResource(R.drawable.bg_password_kuang1);
tv_code2.setBackgroundResource(R.drawable.bg_password_kuang);
tv_code3.setBackgroundResource(R.drawable.bg_password_kuang);
tv_code4.setBackgroundResource(R.drawable.bg_password_kuang);
tv_code5.setBackgroundResource(R.drawable.bg_password_kuang);
tv_code6.setBackgroundResource(R.drawable.bg_password_kuang);
v1.setVisibility(VISIBLE);
v2.setVisibility(GONE);
v3.setVisibility(GONE);
v4.setVisibility(GONE);
v5.setVisibility(GONE);
v6.setVisibility(GONE);
}
if (codes.size() == 1) {
tv_code1.setBackgroundResource(R.drawable.bg_password_kuang1);
tv_code2.setBackgroundResource(R.drawable.bg_password_kuang1);
tv_code3.setBackgroundResource(R.drawable.bg_password_kuang);
tv_code4.setBackgroundResource(R.drawable.bg_password_kuang);
tv_code5.setBackgroundResource(R.drawable.bg_password_kuang);
tv_code6.setBackgroundResource(R.drawable.bg_password_kuang);
v1.setVisibility(GONE);
v2.setVisibility(VISIBLE);
v3.setVisibility(GONE);
v4.setVisibility(GONE);
v5.setVisibility(GONE);
v6.setVisibility(GONE);
}
if (codes.size() == 2) {
tv_code1.setBackgroundResource(R.drawable.bg_password_kuang1);
tv_code2.setBackgroundResource(R.drawable.bg_password_kuang1);
tv_code3.setBackgroundResource(R.drawable.bg_password_kuang1);
tv_code4.setBackgroundResource(R.drawable.bg_password_kuang);
tv_code5.setBackgroundResource(R.drawable.bg_password_kuang);
tv_code6.setBackgroundResource(R.drawable.bg_password_kuang);
v1.setVisibility(GONE);
v2.setVisibility(GONE);
v3.setVisibility(VISIBLE);
v4.setVisibility(GONE);
v5.setVisibility(GONE);
v6.setVisibility(GONE);
}
if (codes.size() == 3) {
tv_code1.setBackgroundResource(R.drawable.bg_password_kuang1);
tv_code2.setBackgroundResource(R.drawable.bg_password_kuang1);
tv_code3.setBackgroundResource(R.drawable.bg_password_kuang1);
tv_code4.setBackgroundResource(R.drawable.bg_password_kuang1);
tv_code5.setBackgroundResource(R.drawable.bg_password_kuang1);
tv_code6.setBackgroundResource(R.drawable.bg_password_kuang);
v1.setVisibility(GONE);
v2.setVisibility(GONE);
v3.setVisibility(GONE);
v4.setVisibility(VISIBLE);
v5.setVisibility(GONE);
v6.setVisibility(GONE);
}
if (codes.size() == 4) {
tv_code1.setBackgroundResource(R.drawable.bg_password_kuang1);
tv_code2.setBackgroundResource(R.drawable.bg_password_kuang1);
tv_code3.setBackgroundResource(R.drawable.bg_password_kuang1);
tv_code4.setBackgroundResource(R.drawable.bg_password_kuang1);
tv_code5.setBackgroundResource(R.drawable.bg_password_kuang1);
tv_code6.setBackgroundResource(R.drawable.bg_password_kuang);
v1.setVisibility(GONE);
v2.setVisibility(GONE);
v3.setVisibility(GONE);
v4.setVisibility(GONE);
v5.setVisibility(VISIBLE);
v6.setVisibility(GONE);
}
if (codes.size() == 5) {
tv_code1.setBackgroundResource(R.drawable.bg_password_kuang1);
tv_code2.setBackgroundResource(R.drawable.bg_password_kuang1);
tv_code3.setBackgroundResource(R.drawable.bg_password_kuang1);
tv_code4.setBackgroundResource(R.drawable.bg_password_kuang1);
tv_code5.setBackgroundResource(R.drawable.bg_password_kuang1);
tv_code6.setBackgroundResource(R.drawable.bg_password_kuang1);
v1.setVisibility(GONE);
v2.setVisibility(GONE);
v3.setVisibility(GONE);
v4.setVisibility(GONE);
v5.setVisibility(GONE);
v6.setVisibility(VISIBLE);
}
if (codes.size() > 5) {
tv_code1.setBackgroundResource(R.drawable.bg_password_kuang1);
tv_code2.setBackgroundResource(R.drawable.bg_password_kuang1);
tv_code3.setBackgroundResource(R.drawable.bg_password_kuang1);
tv_code4.setBackgroundResource(R.drawable.bg_password_kuang1);
tv_code5.setBackgroundResource(R.drawable.bg_password_kuang1);
tv_code6.setBackgroundResource(R.drawable.bg_password_kuang1);
v1.setVisibility(GONE);
v2.setVisibility(GONE);
v3.setVisibility(GONE);
v4.setVisibility(GONE);
v5.setVisibility(GONE);
v6.setVisibility(GONE);
}
if (mSetSubmitListener != null) {
mSetSubmitListener.setSubmitVisibility(codes.size() > 5);
}
}
/**
* 显示键盘
*/
public void showSoftInput() {
//显示软键盘
if (imm != null && et_code != null) {
et_code.postDelayed(new Runnable() {
@Override
public void run() {
imm.showSoftInput(et_code, 0);
}
}, 200);
}
}
/**
* 获得手机号验证码
*
* @return 验证码
*/
public String getPhoneCode() {
StringBuilder sb = new StringBuilder();
for (String code : codes) {
sb.append(code);
}
return sb.toString();
}
public void setVCode(String code) {
et_code.setText(code);
}
public void setView(View view) {
this.view = view;
}
public interface SetSubmitListener {
void setSubmitVisibility(boolean b);
}
public void setSetSubmitListener(SetSubmitListener mSetSubmitListener) {
this.mSetSubmitListener = mSetSubmitListener;
}
private SetSubmitListener mSetSubmitListener;
private SetRequestListener mSetRequestListener;
private boolean isFristRequest;
public interface SetRequestListener {
void setRequest();
}
public void setSetRequestListener(SetRequestListener setRequestListener) {
mSetRequestListener = setRequestListener;
}
}

View File

@@ -0,0 +1,167 @@
package com.xuebiping.bolizhuzi.utils;
import android.animation.ObjectAnimator;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.Shader;
import android.util.AttributeSet;
import android.view.View;
import androidx.annotation.Nullable;
public class ProgressView extends View {
float progress = 0;
float radius = 0;
long duration = 1000;
int repeatCount = 0;
int color = 0xff000000;
private Paint paint;
Context context;
private ObjectAnimator animator;
public ProgressView(Context context) {
super(context);
this.context = context;
initView();
}
public ProgressView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
this.context = context;
initView();
}
public ProgressView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.context = context;
initView();
}
private void initView() {
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
animator = new ObjectAnimator();
//设置动画属性
animator.setPropertyName("progress");
//设置执行动画的View
animator.setTarget(this);
// animator.setRepeatCount(ValueAnimator.INFINITE);
}
@SuppressLint({"NewApi", "DrawAllocation"})
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
paint.setAntiAlias(true); //设置画笔为无锯齿
paint.setColor(color); //设置画笔颜色
paint.setStyle(Paint.Style.FILL);
paint.setStrokeWidth(1);
int[] color = new int[]{Color.parseColor("#F34EF6"), Color.parseColor("#416AF7")};
Shader textShader = new LinearGradient(0, 0, progress, getHeight(), color, null, Shader.TileMode.CLAMP);
paint.setShader(textShader);
RectF rectF = new RectF(0, 0, progress, getHeight());
canvas.drawRoundRect(rectF, radius, radius, paint);
}
/**
* set view background color
*
* @param color
*/
public void setColor(int color) {
this.color = color;
}
/**
* set view round radius
*
* @param radius
*/
public void setRadius(float radius) {
this.radius = radius;
}
/**
* set anim duration
*
* @param duration
*/
public void setDuration(long duration) {
this.duration = duration;
}
public void setRepeatCount(int repeatCount) {
this.repeatCount = repeatCount;
}
public float getProgress() {
return progress;
}
/**
* set progress
*
* @param progress
*/
public void setProgress(float progress) {
this.progress = progress;
invalidate();
}
/**
* 返回属性动画实例,可用于动画进度监听做后续操作
*
* @return
*/
public ObjectAnimator getAnimator() {
return animator;
}
public long currentPlayTime() {
return animator.getCurrentPlayTime();
}
public void setCurrentPlayTime(long time) {
animator.setCurrentPlayTime(time);
}
//
public void startAnim() {
if (animator != null) {
if (animator.isRunning()) {
animator.cancel();
}
animator = null;
}
animator = new ObjectAnimator();
//设置动画属性
animator.setPropertyName("progress");
//设置执行动画的View
animator.setTarget(this);
//设置进度数组, 0 - max
animator.setFloatValues(0, progress);
//设置动画时间
animator.setDuration(duration);
animator.setRepeatCount(repeatCount);
//动画开启
animator.start();
}
public void stopAnim() {
if (animator != null && animator.isRunning()) {
animator.end();
}
}
}

View File

@@ -0,0 +1,71 @@
package com.xuebiping.bolizhuzi.utils;
import android.content.Context;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.net.Uri;
import com.xuebiping.bolizhuzi.R;
import java.io.IOException;
public class RTCMediaUtils {
private static MediaPlayer mMediaPlayer;
private static int loopCount = 0;
public static void playNotificationMusic(Context context, boolean isLooping) {
if (mMediaPlayer == null) {
mMediaPlayer = new MediaPlayer();
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
}
Uri uri = Uri.parse("android.resource://" + context.getPackageName() + "/" + R.raw.call_bg);
try {
mMediaPlayer.reset();
mMediaPlayer.setDataSource(context, uri);
if (mMediaPlayer != null) {
mMediaPlayer.prepareAsync();
mMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
mp.start();
}
});
mMediaPlayer.setLooping(isLooping);
mMediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
@Override
public boolean onError(MediaPlayer mp, int what, int extra) {
return false;
}
});
mMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
if (!mp.isLooping()) {
loopCount++;
if (loopCount < 5) {
mp.start();
} else {
loopCount = 0;
}
}
}
});
}
} catch (IOException | IllegalStateException e) {
e.printStackTrace();
}
}
public static void stopNotificationMusic() {
try {
if (mMediaPlayer != null) {
mMediaPlayer.stop();
mMediaPlayer.reset();
mMediaPlayer = null;
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

View File

@@ -0,0 +1,194 @@
package com.xuebiping.bolizhuzi.utils;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.Shader.TileMode;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.TypedValue;
import androidx.appcompat.widget.AppCompatImageView;
/**
* Created by 1 on 2017/9/20.
*/
public class RoundAngleImageView extends AppCompatImageView {
private Paint mPaint;
private int mWidth;
private int mHeight;
private int mRadius;//圆半径
private RectF mRect;//矩形凹行大小
private int mRoundRadius;// 圆角大小
private BitmapShader mBitmapShader;//图形渲染
private Matrix mMatrix;
private int mType;// 记录是圆形还是圆角矩形
public static final int TYPE_CIRCLE = 0;// 圆形
public static final int TYPE_ROUND = 1;// 圆角矩形
public static final int TYPE_OVAL = 2;//椭圆形
public static final int DEFAUT_ROUND_RADIUS = 10;//默认圆角大小
public RoundAngleImageView(Context context) {
this(context, null);
}
public RoundAngleImageView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public RoundAngleImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initView();
}
private void initView() {
mPaint = new Paint();
mPaint.setAntiAlias(true);
mMatrix = new Matrix();
mRoundRadius = DEFAUT_ROUND_RADIUS;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// 如果是绘制圆形,则强制宽高大小一致
if (mType == TYPE_CIRCLE) {
mWidth = Math.min(getMeasuredWidth(), getMeasuredHeight());
mRadius = mWidth / 2;
setMeasuredDimension(mWidth, mWidth);
}
}
@Override
protected void onDraw(Canvas canvas) {
if (null == getDrawable()) {
return;
}
setBitmapShader();
if (mType == TYPE_CIRCLE) {
canvas.drawCircle(mRadius, mRadius, mRadius, mPaint);
} else if (mType == TYPE_ROUND) {
mPaint.setColor(Color.RED);
canvas.drawRoundRect(mRect, mRoundRadius, mRoundRadius, mPaint);
} else if (mType == TYPE_OVAL) {
canvas.drawOval(mRect, mPaint);
}
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mRect = new RectF(0, 0, getWidth(), getHeight());
}
/**
* 设置BitmapShader
*/
private void setBitmapShader() {
Drawable drawable = getDrawable();
if (null == drawable) {
return;
}
Bitmap bitmap = drawableToBitmap(drawable);
// 将bitmap作为着色器来创建一个BitmapShader
mBitmapShader = new BitmapShader(bitmap, TileMode.CLAMP, TileMode.CLAMP);
float scale = 1.0f;
if (mType == TYPE_CIRCLE) {
// 拿到bitmap宽或高的小值
int bSize = Math.min(bitmap.getWidth(), bitmap.getHeight());
scale = mWidth * 1.0f / bSize;
} else if (mType == TYPE_ROUND || mType == TYPE_OVAL) {
// 如果图片的宽或者高与view的宽高不匹配计算出需要缩放的比例缩放后的图片的宽高一定要大于我们view的宽高所以我们这里取大值
scale = Math.max(getWidth() * 1.0f / bitmap.getWidth(), getHeight() * 1.0f / bitmap.getHeight());
}
// shader的变换矩阵我们这里主要用于放大或者缩小
mMatrix.setScale(scale, scale);
// 设置变换矩阵
mBitmapShader.setLocalMatrix(mMatrix);
mPaint.setShader(mBitmapShader);
}
/**
* drawable转bitmap
*
* @param drawable
* @return
*/
private Bitmap drawableToBitmap(Drawable drawable) {
if (drawable instanceof BitmapDrawable) {
BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
return bitmapDrawable.getBitmap();
}
int w = drawable.getIntrinsicWidth();
int h = drawable.getIntrinsicHeight();
Bitmap bitmap = Bitmap.createBitmap(w, h, Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, w, h);
drawable.draw(canvas);
return bitmap;
}
/**
* 单位dp转单位px
*/
public int dpTodx(int dp) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
dp, getResources().getDisplayMetrics());
}
public int getType() {
return mType;
}
/**
* 设置图片类型:圆形、圆角矩形、椭圆形
*
* @param mType
*/
public void setType(int mType) {
if (this.mType != mType) {
this.mType = mType;
invalidate();
}
}
public int getRoundRadius() {
return mRoundRadius;
}
/**
* 设置圆角大小
*
* @param mRoundRadius
*/
public void setRoundRadius(int mRoundRadius) {
if (this.mRoundRadius != mRoundRadius) {
this.mRoundRadius = mRoundRadius;
invalidate();
}
}
}

View File

@@ -0,0 +1,85 @@
package com.xuebiping.bolizhuzi.utils;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
public final class SPUtils {
private final static String name = "config";
private final static int mode = Context.MODE_PRIVATE;
/**
* 保存首选项
*
* @param context
* @param key
* @param value
*/
public static void saveBoolean(Context context, String key, boolean value) {
SharedPreferences sp = context.getSharedPreferences(name, mode);
Editor edit = sp.edit();
edit.putBoolean(key, value);
edit.apply();
}
public static void saveInt(Context context, String key, int value) {
SharedPreferences sp = context.getSharedPreferences(name, mode);
Editor edit = sp.edit();
edit.putInt(key, value);
edit.apply();
}
public static void saveString(Context context, String key, String value) {
SharedPreferences sp = context.getSharedPreferences(name, mode);
Editor edit = sp.edit();
edit.putString(key, value);
edit.apply();
}
/**
* 获取首选项
*
* @param context
* @param key
* @param defValue
* @return
*/
public static boolean getBoolean(Context context, String key, boolean defValue) {
SharedPreferences sp = context.getSharedPreferences(name, mode);
return sp.getBoolean(key, defValue);
}
public static int getInt(Context context, String key, int defValue) {
SharedPreferences sp = context.getSharedPreferences(name, mode);
return sp.getInt(key, defValue);
}
public static int getInt(Context context, String key) {
SharedPreferences sp = context.getSharedPreferences(name, mode);
return sp.getInt(key, 0);
}
public static String getString(Context context, String key, String defValue) {
SharedPreferences sp = context.getSharedPreferences(name, mode);
return sp.getString(key, defValue);
}
public static String getString(Context context, String key) {
SharedPreferences sp = context.getSharedPreferences(name, mode);
return sp.getString(key, "");
}
/**
* 清除保存
*
* @param context
*/
public static void clear(Context context) {
SharedPreferences sp = context.getSharedPreferences(name, mode);
SharedPreferences.Editor editor = sp.edit();
editor.clear();
editor.apply();
}
}

View File

@@ -0,0 +1,99 @@
package com.xuebiping.bolizhuzi.utils;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.AsyncTask;
import android.provider.MediaStore;
import android.view.View;
import com.xuebiping.bolizhuzi.controller.constant.ConstUrl;
import com.fengliyan.uikit.toast.MaleToast;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
public class SaveImageUtil {
private View group;
private Context context;
public SaveImageUtil(View group, Context context) {
this.group = group;
this.context = context;
}
public void start(){
new SaveImage().execute();
}
class SaveImage extends AsyncTask<Boolean, Void, Boolean> {
@Override
protected Boolean doInBackground(Boolean... params) {
boolean result = false;
Bitmap bitmap = getCacheBitmapFromView(group);
if (bitmap != null) {
result = saveImageToGallery(getCacheBitmapFromView(group));
}
return result;
}
@Override
protected void onPostExecute(Boolean result) {
if (result) {
MaleToast.showMessage(context, "二维码保存成功");
} else {
MaleToast.showMessage(context, "二维码保存失败");
}
}
}
/**
* 获取一个 View 的缓存视图 * (前提是这个View已经渲染完成显示在页面上) * @param view * @return
*/
public static Bitmap getCacheBitmapFromView(View view) {
final boolean drawingCacheEnabled = true;
view.setDrawingCacheEnabled(drawingCacheEnabled);
view.buildDrawingCache(drawingCacheEnabled);
final Bitmap drawingCache = view.getDrawingCache();
Bitmap bitmap;
if (drawingCache != null) {
bitmap = Bitmap.createBitmap(drawingCache);
view.setDrawingCacheEnabled(false);
} else {
bitmap = null;
}
return bitmap;
}
public boolean saveImageToGallery(Bitmap bmp) { // 首先保存图片
String storePath = ConstUrl.DEFAULT_PATH + File.separator + System.currentTimeMillis() + "";
File appDir = new File(storePath);
if (!appDir.exists()) {
appDir.mkdir();
}
String fileName = System.currentTimeMillis() + ".jpg";
File file = new File(appDir, fileName);
try {
FileOutputStream fos = new FileOutputStream(file);
//通过io流的方式来压缩保存图片
boolean isSuccess = bmp.compress(Bitmap.CompressFormat.JPEG, 60, fos);
fos.flush();
fos.close();
//把文件插入到系统图库
MediaStore.Images.Media.insertImage(context.getContentResolver(), file.getAbsolutePath(), fileName, null);
//保存图片后发送广播通知更新数据库
Uri uri = Uri.fromFile(file);
context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, uri));
if (isSuccess) {
return true;
} else {
return false;
}
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
}

View File

@@ -0,0 +1,93 @@
package com.xuebiping.bolizhuzi.utils;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.RecyclerView;
public class SimpleItemTouchCallBack extends ItemTouchHelper.Callback{
private TouchCallBack mCallBack;
private int mAdapterPosition;
public SimpleItemTouchCallBack(TouchCallBack mCallBack) {
this.mCallBack = mCallBack;
}
/**
* 返回可以滑动的方向,一般使用makeMovementFlags(int,int)
* 或makeFlag(int, int)来构造我们的返回值
* @param recyclerView
* @param viewHolder
* @return
*/
@Override
public int getMovementFlags(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
//允许上下拖拽
int drag = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
//设置
return makeMovementFlags(drag,0);
}
/**
* 上下拖动item时回调,可以调用Adapter的notifyItemMoved方法来交换两个ViewHolder的位置
* 最后返回true
* 表示被拖动的ViewHolder已经移动到了目的位置
* @param recyclerView
* @param viewHolder
* @param target
* @return
*/
@Override
public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) {
//通知适配器,两个子条目位置发生改变
mCallBack.onItemMove(viewHolder.getAdapterPosition(),target.getAdapterPosition());
return true;
}
/**
* 当用户左右滑动item时达到删除条件就会调用,一般为一半,条目继续滑动删除,否则弹回
* @param viewHolder
* @param direction
*/
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
}
/**
* 支持长按拖动,默认是true
* @return
*/
@Override
public boolean isLongPressDragEnabled() {
return super.isLongPressDragEnabled();
}
/**
* 支持滑动,即可以调用到onSwiped()方法,默认是true
* @return
*/
@Override
public boolean isItemViewSwipeEnabled() {
return true;
}
@Override
public long getAnimationDuration(RecyclerView recyclerView, int animationType, float animateDx, float animateDy) {
long animationDuration = super.getAnimationDuration(recyclerView, animationType, animateDx, animateDy);
if (mCallBack != null) {
mCallBack.animationDuration(animationDuration);
}
return animationDuration;
}
public interface TouchCallBack {
//交换条目位置
void onItemMove(int fromPosition, int toPosition);
//删除条目
void onItemDelete(int position);
void animationDuration(long time);
}
}

View File

@@ -0,0 +1,75 @@
package com.xuebiping.bolizhuzi.utils;
import android.app.Activity;
import android.graphics.Rect;
import android.os.Build;
import android.view.View;
import android.view.ViewTreeObserver;
import android.widget.FrameLayout;
public class SoftHideKeyBoardUtil {
public static void assistActivity (Activity activity) {
new SoftHideKeyBoardUtil(activity);
}
private View mChildOfContent;
private int usableHeightPrevious;
private FrameLayout.LayoutParams frameLayoutParams;
//为适应华为小米等手机键盘上方出现黑条或不适配
private int contentHeight;//获取setContentView本来view的高度
private boolean isfirst = true;//只用获取一次
private int statusBarHeight;//状态栏高度
private SoftHideKeyBoardUtil(Activity activity) {
statusBarHeight= StatusBarUtil.getStatusBarHeight(activity);
//1、找到Activity的最外层布局控件它其实是一个DecorView,它所用的控件就是FrameLayout
FrameLayout content = (FrameLayout) activity.findViewById(android.R.id.content);
//2、获取到setContentView放进去的View
mChildOfContent = content.getChildAt(0);
//3、给Activity的xml布局设置View树监听当布局有变化如键盘弹出或收起时都会回调此监听
mChildOfContent.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
//4、软键盘弹起会使GlobalLayout发生变化
public void onGlobalLayout() {
if (isfirst) {
contentHeight = mChildOfContent.getHeight();//兼容华为等机型
isfirst = false;
}
//5、当前布局发生变化时对Activity的xml布局进行重绘
possiblyResizeChildOfContent();
}
});
//6、获取到Activity的xml布局的放置参数
frameLayoutParams = (FrameLayout.LayoutParams) mChildOfContent.getLayoutParams();
}
// 获取界面可用高度如果软键盘弹起后Activity的xml布局可用高度需要减去键盘高度
private void possiblyResizeChildOfContent() {
//1、获取当前界面可用高度键盘弹起后当前界面可用布局会减少键盘的高度
int usableHeightNow = computeUsableHeight();
//2、如果当前可用高度和原始值不一样
if (usableHeightNow != usableHeightPrevious) {
//3、获取Activity中xml中布局在当前界面显示的高度
int usableHeightSansKeyboard = mChildOfContent.getRootView().getHeight();
//4、Activity中xml布局的高度-当前可用高度
int heightDifference = usableHeightSansKeyboard - usableHeightNow;
//5、高度差大于屏幕1/4时说明键盘弹出
if (heightDifference > (usableHeightSansKeyboard/4)) {
// 6、键盘弹出了Activity的xml布局高度应当减去键盘高度
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){
frameLayoutParams.height = usableHeightSansKeyboard - heightDifference + statusBarHeight;
} else {
frameLayoutParams.height = usableHeightSansKeyboard - heightDifference;
}
} else {
frameLayoutParams.height = contentHeight;
}
//7、 重绘Activity的xml布局
mChildOfContent.requestLayout();
usableHeightPrevious = usableHeightNow;
}
}
private int computeUsableHeight() {
Rect r = new Rect();
mChildOfContent.getWindowVisibleDisplayFrame(r);
// 全屏模式下直接返回r.bottomr.top其实是状态栏的高度
return (r.bottom - r.top);
}
}

View File

@@ -0,0 +1,80 @@
package com.xuebiping.bolizhuzi.utils;
import android.app.Activity;
import android.graphics.Rect;
import android.view.View;
import android.view.ViewTreeObserver;
/**
* Created by 张斌 on 2018/5/2 0002.
*/
public class SoftKeyBoardListener {
private View rootView;//activity的根视图
int rootViewVisibleHeight;//纪录根视图的显示高度
private OnSoftKeyBoardChangeListener onSoftKeyBoardChangeListener;
public SoftKeyBoardListener(Activity activity) {
//获取activity的根视图
rootView = activity.getWindow().getDecorView();
//监听视图树中全局布局发生改变或者视图树中的某个视图的可视状态发生改变
rootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
//获取当前根视图在屏幕上显示的大小
Rect r = new Rect();
rootView.getWindowVisibleDisplayFrame(r);
int visibleHeight = r.height();
if (rootViewVisibleHeight == 0) {
rootViewVisibleHeight = visibleHeight;
return;
}
//根视图显示高度没有变化,可以看作软键盘显示/隐藏状态没有改变
if (rootViewVisibleHeight == visibleHeight) {
return;
}
//根视图显示高度变小超过200可以看作软键盘显示了
if (rootViewVisibleHeight - visibleHeight > 200) {
if (onSoftKeyBoardChangeListener != null) {
// onSoftKeyBoardChangeListener.keyBoardShow(rootViewVisibleHeight - visibleHeight);
onSoftKeyBoardChangeListener.keyBoardShow(rootViewVisibleHeight - visibleHeight);
}
rootViewVisibleHeight = visibleHeight;
return;
}
//根视图显示高度变大超过200可以看作软键盘隐藏了
if (visibleHeight - rootViewVisibleHeight > 200) {
if (onSoftKeyBoardChangeListener != null) {
// onSoftKeyBoardChangeListener.keyBoardHide(visibleHeight - rootViewVisibleHeight);
onSoftKeyBoardChangeListener.keyBoardHide(rootViewVisibleHeight - visibleHeight);
}
rootViewVisibleHeight = visibleHeight;
return;
}
}
});
}
private void setOnSoftKeyBoardChangeListener(OnSoftKeyBoardChangeListener onSoftKeyBoardChangeListener) {
this.onSoftKeyBoardChangeListener = onSoftKeyBoardChangeListener;
}
public interface OnSoftKeyBoardChangeListener {
// void keyBoardShow(int height);
//
// void keyBoardHide(int height);
void keyBoardShow(int height);
void keyBoardHide(int height);
}
public static void setListener(Activity activity, OnSoftKeyBoardChangeListener onSoftKeyBoardChangeListener) {
SoftKeyBoardListener softKeyBoardListener = new SoftKeyBoardListener(activity);
softKeyBoardListener.setOnSoftKeyBoardChangeListener(onSoftKeyBoardChangeListener);
}
}

View File

@@ -0,0 +1,24 @@
package com.xuebiping.bolizhuzi.utils;
import android.app.Activity;
import android.content.Context;
import android.view.View;
public class StatusBarUtil {
public static void setAndroidNativeLightStatusBar(Activity activity, Boolean dark) {
View decorView= activity.getWindow().getDecorView();
if (dark) {
decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN|View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
}else {
decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN|View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
}
}
public static int getStatusBarHeight(Context context) {
int result = 0;
int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
result = context.getResources().getDimensionPixelSize(resourceId);
}
return result;
}
}

View File

@@ -0,0 +1,596 @@
package com.xuebiping.bolizhuzi.utils;
import android.content.Context;
import android.graphics.Typeface;
import androidx.core.content.ContextCompat;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.style.AbsoluteSizeSpan;
import android.text.style.ForegroundColorSpan;
import android.text.style.StyleSpan;
import android.widget.TextView;
import com.xuebiping.bolizhuzi.controller.constant.ConsUser;
import com.xuebiping.bolizhuzi.controller.constant.ConstUrl;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class StrU {
/**
* @param str
* @return String
*/
public static String doEmpty(String str) {
return doEmpty(str, "");
}
/**
* @param str
* @param defaultValue
* @return String
*/
public static String doEmpty(String str, String defaultValue) {
if (str == null || str.equalsIgnoreCase("null") || str.trim().equals("") || str.trim().equals("")) {
str = defaultValue;
} else if (str.startsWith("null")) {
str = str.substring(4, str.length());
}
return str.trim();
}
/**
*
*/
private final static String PLEASE_SELECT = "";
public static boolean notEmpty(Object o) {
return o != null && !"".equals(o.toString().trim()) && !"null".equalsIgnoreCase(o.toString().trim()) && !"undefined".equalsIgnoreCase(o.toString().trim())
&& !PLEASE_SELECT.equals(o.toString().trim());
}
public static boolean empty(Object o) {
return o == null || "".equals(o.toString().trim()) || "null".equalsIgnoreCase(o.toString().trim()) || "undefined".equalsIgnoreCase(o.toString().trim())
|| PLEASE_SELECT.equals(o.toString().trim());
}
public static boolean num(Object o) {
int n = 0;
try {
n = Integer.parseInt(o.toString().trim());
} catch (NumberFormatException e) {
e.printStackTrace();
}
if (n > 0) {
return true;
} else {
return false;
}
}
public static boolean decimal(Object o) {
double n = 0;
try {
n = Double.parseDouble(o.toString().trim());
} catch (NumberFormatException e) {
e.printStackTrace();
}
if (n > 0.0) {
return true;
} else {
return false;
}
}
/**
* @param Jid
* @return
*/
public static String getUserNameByJid(String Jid) {
if (empty(Jid)) {
return null;
}
if (!Jid.contains("@")) {
return Jid;
}
return Jid.split("@")[0];
}
/**
* @param jidFor
* @param userName
* @return
*/
public static String getJidByName(String userName, String jidFor) {
if (empty(jidFor) || empty(jidFor)) {
return null;
}
return userName + "@" + jidFor;
}
/**
* @param userName
* @return
*/
public static String getJidByName(String userName) {
String jidFor = "ahic.com.cn";
return getJidByName(userName, jidFor);
}
/**
* 判断是不是有汉字
*
* @param name
* @return
*/
public static boolean checkNameChese(String name) {
boolean res = true;
char[] cTemp = name.toCharArray();
for (int i = 0; i < name.length(); i++) {
if (!isChinese(cTemp[i])) {
res = false;
break;
}
}
return res;
}
/**
* @param c
* @return
*/
public static boolean isChinese(char c) {
Character.UnicodeBlock ub = Character.UnicodeBlock.of(c);
if (ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS || ub == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A
|| ub == Character.UnicodeBlock.GENERAL_PUNCTUATION || ub == Character.UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION || ub == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS) {
return true;
}
return false;
}
/**
* 判断字符串是否为空
*
* @param str
* @return
*/
public static boolean isEmpty(String str) {
if ("".equals(str) || null == str || "null".equals(str) || str.length() == 0) {
return true;
}
return false;
}
/**
* 判断时间六个月内
*
* @return
*/
public static boolean isDate(int staYear, int endYear, int staMonth, int endMonth) {
int sub = endMonth - staMonth;
if (staYear == endYear && sub > -1 && 6 > sub) {
return true;
} else {
if (sub < -6) {
return true;
}
return false;
}
}
/**
* 判断是否是身份证号
*
* @param str
* @return
*/
public static boolean isIdCard(String str) {
String regExpress = "([0-9]{17}([0-9]|X))|([0-9]{15})";
Pattern pattern = Pattern.compile(regExpress);
boolean result = pattern.matcher(str).matches();
return result;
}
/**
* unicode转String
*/
public static String UnicodeToString(String str) {
Pattern pattern = Pattern.compile("(\\\\u(\\p{XDigit}{4}))");
Matcher matcher = pattern.matcher(str);
char ch;
while (matcher.find()) {
ch = (char) Integer.parseInt(matcher.group(2), 16);
str = str.replace(matcher.group(1), ch + "");
}
return str;
}
/**
* 从字符串中取数字
*
* @param content
* @return
*/
public static String getNumbers(String content) {
Pattern pattern = Pattern.compile("\\d+");
Matcher matcher = pattern.matcher(content);
while (matcher.find()) {
return matcher.group(0);
}
return "";
}
/**
* 描述:获取字符串的长度.
*
* @param str 指定的字符串
* @return 字符串的长度中文字符计2个
*/
public static int strLength(String str) {
int valueLength = 0;
String chinese = "[\u0391-\uFFE5]";
if (!isEmpty(str)) {
for (int i = 0; i < str.length(); i++) {
String temp = str.substring(i, i + 1);
if (temp.matches(chinese)) {
valueLength += 2;
} else {
valueLength += 1;
}
}
}
return valueLength;
}
/**
* 判断字符串首字母是否为英文
*
* @param s
* @return
*/
public static boolean isEnglish(String s) {
if (!(s.charAt(0) >= 'A' && s.charAt(0) <= 'Z') && !(s.charAt(0) >= 'a' && s.charAt(0) <= 'z')) {
return false;
}
return true;
}
/**
* 10 * 中国移动China Mobile 11 *
* 134[0-8],135,136,137,138,139,147,150,151,152,157
* ,158,159,178,182,183,184,187,188 12
*/
public static final String CM = "^1(34[0-8]|(3[5-9]|47|5[017-9]|78|8[23478])\\d)\\d{7}$";
/**
* 15 * 中国联通China Unicom 16 * 130,131,132,145,152,155,156,176,185,186 17
*/
public static final String CU = "^1(3[0-2]|45|5[256]|76|8[56])\\d{8}$";
/**
* 20 * 中国电信China Telecom 21 * 133,1349,153,177,180,181,189 22
*/
public static final String CT = "^1((33|53|77|8[019])[0-9]|349)\\d{7}$";
/**
* 29 * 虚拟运营商 30 * 170 31
*/
public static final String VT = "^1(|70)\\d{8}$";
/**
* 验证手机号码
*/
public static boolean isMobileNO(String mobilePhone) {
String str = "^1[3|4|5|6|7|8|9]\\d{9}$";
Pattern p = Pattern.compile(str);
Matcher m = p.matcher(mobilePhone);
return m.matches();
}
/**
* 判断邮箱
*
* @param email
* @return
*/
public static boolean isEmail(String email) {
String str = "^([a-zA-Z0-9_\\-\\.]+)@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.)|(([a-zA-Z0-9\\-]+\\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\\]?)$";
Pattern p = Pattern.compile(str);
Matcher m = p.matcher(email);
return m.matches();
}
/**
* 判断车牌号
*
* @return
*/
public static boolean isPlatenumber(String platenumber) {
String str = "[a-zA-Z]{1}[0-9a-zA-Z]{5,6}$";
Pattern p = Pattern.compile(str);
Matcher m = p.matcher(platenumber);
return m.matches();
}
/**
* 用户名判断
*
* @param str
* @return
*/
public static boolean isRight(String str) {
String sp = "/^1[34578][0-9]{9}$/";
Pattern p = Pattern.compile(sp);
Matcher m = p.matcher(str);
if (m.find()) {
return true;
}
return false;
}
/**
* 判断两个字符串是否相同
*
* @param str
* @param oStr
* @return
*/
public static boolean equals(String str, String oStr) {
if (str != null && oStr != null) {
if (str.equals(oStr)) {
return true;
} else {
return false;
}
} else {
return false;
}
}
/**
* 判断是否是网址
*
* @return
*/
public static boolean isHttp(String url) {
String regex0 = "((http[s]{0,1}|ftp)://[a-zA-Z0-9\\.\\-]+\\.([a-zA-Z]{2,3})(:\\d+)?(/[a-zA-Z0-9\\.\\-~!@#$%^&*+?:_/=<>]*)?)|([a-zA-Z0-9\\.\\-]+\\.([a-zA-Z]{2,3})(:\\d+)?(/[a-zA-Z0-9\\.\\-~!@#$%^&*+?:_/=<>]*)?)";
Pattern p = Pattern.compile(regex0);
Matcher m = p.matcher(url);
if (m.find()) {
return true;
}
return false;
}
/**
* 判断是不是数字
*
* @param str
* @return
*/
public static boolean isNumeric(String str) {
Pattern pattern = Pattern.compile("[0-9]*");
return pattern.matcher(str).matches();
}
public static byte[] intToByteArray(int value) {
byte[] b = new byte[4];
for (int i = 0; i < 4; i++) {
int offset = (b.length - 1 - i) * 8;
b[i] = (byte) (value >>> offset & 0xFF);
}
return b;
}
public static byte[] byteMerger(byte[] byte_1, byte[] byte_2) {
byte[] byte_3 = new byte[byte_1.length + byte_2.length];
System.arraycopy(byte_1, 0, byte_3, 0, byte_1.length);
System.arraycopy(byte_2, 0, byte_3, byte_1.length, byte_2.length);
return byte_3;
}
public static int byteArrayToInt(byte[] b) throws Exception {
if (b.length > 4) {
throw new Exception("byte array to int ,byte array length must <=4 ");
}
return (int) byteArrayToLong(b);
}
public static long byteArrayToLong(byte[] b) throws Exception {
if (b.length > 8) {
throw new Exception("byte array to long ,byte array length must <=8 ");
}
long value = 0L;
for (int i = 0; i < b.length; i++) {
value += ((b[i] & 0xFF) << ((b.length - i - 1) * 8));
}
return value;
}
public static String byteToHexString(byte[] data) {
StringBuffer sb = new StringBuffer();
if (data != null) {
sb.append("\n[");
for (int i = 0; i < data.length; i++) {
int value = data[i] & 0xFF;
String str = Integer.toHexString(value);
if (str.length() == 1) {
sb.append("0");
}
sb.append(str.toUpperCase()).append(" ,");
if ((i > 0) && (i % 16 == 0)) {
sb.append('\n');
}
}
if (sb.indexOf(",") > 0) {
sb.setLength(sb.length() - 1);
}
sb.append("]");
}
return sb.toString();
}
/**
* 分割String转List
*
* @param listText
* @return
*/
public static List<String> StringToList(String listText) {
String SEP1 = ",";
if (!StrU.isEmpty(listText)) {
List<String> list = new ArrayList<String>();
String[] text = listText.split(SEP1);
for (String str : text) {
list.add(str);
}
return list;
}
return null;
}
/**
* List<Sting>转逗号分隔string
*
* @param list
* @return
*/
public static String listToString(List list, String separator) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < list.size(); i++) {
if (i == list.size() - 1) {
sb.append(list.get(i));
} else {
sb.append(list.get(i));
sb.append(separator);
}
}
return sb.toString();
}
/**
* 设置Span字体大小
*/
public static void setSpanTextSize(String text, int start, int end, TextView tv, int textS) {
SpannableString spannableString = new SpannableString(text);
spannableString.setSpan(new AbsoluteSizeSpan(textS, true), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
tv.setText(spannableString);
}
/**
* 设置Span字体大小粗细
*/
public static void setSpanTextSizeStyle(String text, int start, int end, TextView tv, int textS) {
SpannableString spannableString = new SpannableString(text);
spannableString.setSpan(new AbsoluteSizeSpan(textS, true), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
spannableString.setSpan(new StyleSpan(Typeface.BOLD), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); //粗体
tv.setText(spannableString);
}
/**
* 设置Span字体颜色
*/
public static void setSpanTextColor(String text, int start, int end, TextView tv, int color, Context context) {
SpannableString spannableString = new SpannableString(text);
spannableString.setSpan(new ForegroundColorSpan(ContextCompat.getColor(context, color)), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
tv.setText(spannableString);
}
/**
* 设置Span字体颜色大小
*
* @param text 文本
* @param start 开始位置
* @param end 结束位置
* @param tv textview
* @param color 颜色
* @param dp 字体大小
* @param context c
*/
public static void setSpanTextColorSize(String text, int start, int end, TextView tv, int color, int dp, Context context) {
SpannableString spannableString = new SpannableString(text);
spannableString.setSpan(new AbsoluteSizeSpan(dp, true), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
spannableString.setSpan(new ForegroundColorSpan(ContextCompat.getColor(context, color)), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
tv.setText(spannableString);
}
/**
* 设置Span字体颜色大小粗细
*
* @param text 文本
* @param start 开始位置
* @param end 结束位置
* @param tv textview
* @param color 颜色
* @param dp 字体大小
* @param isBold 是否加粗
* @param context c
*/
public static void setSpanTextColorSizeBold(String text, int start, int end, TextView tv, int color, int dp, boolean isBold, Context context) {
SpannableString spannableString = new SpannableString(text);
spannableString.setSpan(new AbsoluteSizeSpan(dp, true), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
spannableString.setSpan(new ForegroundColorSpan(ContextCompat.getColor(context, color)), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
if (isBold) {
spannableString.setSpan(new StyleSpan(Typeface.BOLD), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); //粗体
} else {
spannableString.setSpan(new StyleSpan(Typeface.NORMAL), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
tv.setText(spannableString);
}
// 两次点击按钮之间的点击间隔不能少于1000毫秒
private static final int MIN_CLICK_DELAY_TIME = 1000;
private static long lastClickTime;
/**
* 防止重复点击
*
* @return
*/
public static boolean isFastClick() {
boolean flag = false;
long curClickTime = System.currentTimeMillis();
if ((curClickTime - lastClickTime) >= MIN_CLICK_DELAY_TIME) {
flag = true;
}
lastClickTime = curClickTime;
return flag;
}
public static int StringToInt(String number, int defaultNum) {
try {
if (!StrU.isEmpty(number)) {
return Integer.parseInt(number.trim());
} else {
return defaultNum;
}
} catch (NumberFormatException ignore) {
return defaultNum;
}
}
/**
* @param str 需要分割的字符串
* @param key 分割的值
* @param index 分割后返回值下标
* @return 指定文本分割字符串
*/
public static String StringSplit(String str, String key, int index) {
if (!StrU.isEmpty(str)) {
String[] array = str.split(key);
return array[index];
} else {
return "";
}
}
//获取图片地址
public static String getResourcePath(String path, Context context) {
return ConstUrl.IMAGE_URL + path + (!isEmpty(NoClearSPUtils.getString(context, ConsUser.RESOURCE_VERSION)) ?
NoClearSPUtils.getString(context, ConsUser.RESOURCE_VERSION) : "");
}
}

View File

@@ -0,0 +1,30 @@
package com.xuebiping.bolizhuzi.utils;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.TimeZone;
/**
* Created by zhangbin on 2019/1/21.
*/
public class TimeZoneUtils {
public static long getTime() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
sdf.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai")); //指定时区
String format = sdf.format(Calendar.getInstance().getTime());
// DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
java.util.Date date = null;
try {
date = sdf.parse(format);
} catch (ParseException e) {
e.printStackTrace();
}
Calendar cal = Calendar.getInstance();
cal.setTime(date);
long timestamp = cal.getTimeInMillis();
return timestamp;
}
}

View File

@@ -0,0 +1,307 @@
package com.xuebiping.bolizhuzi.utils;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.Interpolator;
import android.view.animation.LinearInterpolator;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* 水波纹扩散效果
* https://github.com/hackware1993/WaveView
*/
public class WaveView extends View {
private float mInitialRadius; // 初始波纹半径
private float mMaxRadius; // 最大波纹半径
private long mDuration = 3000; // 一个波纹从创建到消失的持续时间
private int mSpeed = 1000; // 波纹的创建速度每500ms创建一个
private float mMaxRadiusRate = 1f;//0.85f;
private boolean mMaxRadiusSet;
private boolean mIsRunning;
private long mLastCreateTime;
private List<Circle> mCircleList = new ArrayList<Circle>();
private Runnable mCreateCircle = new Runnable() {
@Override
public void run() {
if (mIsRunning) {
newCircle();
postDelayed(mCreateCircle, mSpeed);
}
}
};
private Interpolator mInterpolator = new LinearInterpolator();
private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
public WaveView(Context context) {
super(context);
}
public WaveView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void setStyle(Paint.Style style) {
mPaint.setStyle(style);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
if (!mMaxRadiusSet) {
mMaxRadius = Math.min(w, h) * mMaxRadiusRate / 2.0f;
}
}
public void setMaxRadiusRate(float maxRadiusRate) {
mMaxRadiusRate = maxRadiusRate;
}
public void setColor(int color) {
mPaint.setColor(color);
}
/**
* 开始
*/
public void start() {
if (!mIsRunning) {
mIsRunning = true;
mCreateCircle.run();
}
}
/**
* 缓慢停止
*/
public void stop() {
mIsRunning = false;
}
/**
* 立即停止
*/
public void stopImmediately() {
mIsRunning = false;
mCircleList.clear();
invalidate();
}
protected void onDraw(Canvas canvas) {
Iterator<Circle> iterator = mCircleList.iterator();
while (iterator.hasNext()) {
Circle circle = iterator.next();
float radius = circle.getCurrentRadius();
if (System.currentTimeMillis() - circle.mCreateTime < mDuration) {
mPaint.setAlpha(circle.getAlpha());
canvas.drawCircle(getWidth() / 2, getHeight() / 2, radius, mPaint);
} else {
iterator.remove();
}
}
if (mCircleList.size() > 0) {
postInvalidateDelayed(10);
}
}
public void setInitialRadius(float radius) {
mInitialRadius = radius;
}
public void setDuration(long duration) {
mDuration = duration;
}
public void setMaxRadius(float maxRadius) {
mMaxRadius = maxRadius;
mMaxRadiusSet = true;
}
public void setSpeed(int speed) {
mSpeed = speed;
}
private void newCircle() {
long currentTime = System.currentTimeMillis();
if (currentTime - mLastCreateTime < mSpeed) {
return;
}
Circle circle = new Circle();
mCircleList.add(circle);
invalidate();
mLastCreateTime = currentTime;
}
private class Circle {
private long mCreateTime;
Circle() {
mCreateTime = System.currentTimeMillis();
}
int getAlpha() {
float percent = (getCurrentRadius() - mInitialRadius) / (mMaxRadius - mInitialRadius);
return (int) ((55 - mInterpolator.getInterpolation(percent) * 46));
//Log.i("透明度",""+mInterpolator.getInterpolation(percent));
//return (int) ((255*0.08)*mInterpolator.getInterpolation(percent));
}
float getCurrentRadius() {
float percent = (System.currentTimeMillis() - mCreateTime) * 1.0f / mDuration;
return mInitialRadius + mInterpolator.getInterpolation(percent) * (mMaxRadius - mInitialRadius);
}
}
public void setInterpolator(Interpolator interpolator) {
mInterpolator = interpolator;
if (mInterpolator == null) {
mInterpolator = new LinearInterpolator();
}
}
/*private Context mContext;
private int centerRadius;
private int maxRadius;
private int waveWidth;
private long waveIntervalTime = 500;
private long waveDuration = 1500;
private boolean running = false;
private List<Wave> waveList = new ArrayList<>();
private Paint paint = new Paint();
private int centerColor;
public WaveView(Context context) {
this(context,null);
}
public WaveView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs,0);
}
public WaveView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mContext = context;
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.WaveView,defStyleAttr,0);
centerColor = typedArray.getColor(
R.styleable.WaveView_center_color,
ContextCompat.getColor(context, R.color.mainTextColor)
);
centerRadius = (int) typedArray.getDimension(R.styleable.WaveView_center_radius, 4);
maxRadius = (int) typedArray.getDimension(R.styleable.WaveView_max_radius, 14f);
waveWidth = (int) typedArray.getDimension(R.styleable.WaveView_wave_width, 1.0f);
waveIntervalTime = typedArray.getInt(R.styleable.WaveView_wave_interval_time, 500);
waveDuration = typedArray.getInt(R.styleable.WaveView_wave_duration, 1500);
paint.setColor(centerColor);
typedArray.recycle();
}
public void setWaveStart(boolean waveStart) {
if (waveStart) {
if (!running) {
running = true;
waveList.add(new Wave());
}
} else {
running = false;
for (Wave wave: waveList) {
wave.cancelAnimation();
}
}
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
int radius = (Math.min(w,h)/2);
if (radius < maxRadius) {
maxRadius = radius;
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
for (Wave wave: waveList) {
paint.setAlpha(wave.getAlpha());
paint.setStrokeWidth(waveWidth);
paint.setStyle(Paint.Style.STROKE);
canvas.drawCircle(getWidth() >> 1, (getHeight() >> 1), wave.getCurrentRadius(), paint);
}
if (waveList.size() > 0) {
paint.setAlpha(255);
paint.setStyle(Paint.Style.FILL);
canvas.drawCircle(getWidth() >> 1, getHeight() >> 1, centerRadius, paint);
}
}
public class Wave {
private boolean hasCreateNewWave = false;
private ValueAnimator createWaveAnimation = ObjectAnimator.ofFloat(this, "percent", 0f, 1.0f);
private Float percent =0f;
@SuppressLint("ObjectAnimatorBinding")
public void setPercent(Float percent) {
this.percent = percent;
if (running && percent >= waveIntervalTime / waveDuration && !hasCreateNewWave) {
Wave wave = new Wave();
wave.createWaveAnimation.setInterpolator(new LinearInterpolator());
wave.createWaveAnimation.setDuration(waveDuration);
wave.createWaveAnimation.start();
wave.createWaveAnimation.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
if (running) {
waveList.remove(Wave.this);
}
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
waveList.add(wave);
hasCreateNewWave = true;
}
invalidate();
}
public Float getPercent() {
return percent;
}
public void cancelAnimation() {
createWaveAnimation.cancel();
}
public int getAlpha() {
return (int) (255 * (1 - percent));
}
public float getCurrentRadius() {
return centerRadius + percent * (maxRadius - centerRadius);
}
}*/
}

View File

@@ -0,0 +1,254 @@
package com.xuebiping.bolizhuzi.utils.camera2
import android.annotation.SuppressLint
import android.content.ContentValues
import android.content.Context
import android.content.res.Resources
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Matrix
import android.media.ExifInterface
import android.provider.MediaStore
import android.util.Base64
import java.io.ByteArrayOutputStream
import java.io.FileOutputStream
import java.text.SimpleDateFormat
import java.util.*
import kotlin.concurrent.thread
/**
*
* author : ChenSen
* data : 2018/3/16
* desc:
*/
object BitmapUtils {
@JvmStatic
fun Bitmap2StrByBase64(bit: Bitmap): String? {
val bos = ByteArrayOutputStream()
var start: Int
var end: Int
var options = 50
bit.compress(Bitmap.CompressFormat.JPEG, 100, bos) //参数100表示不压缩
while (bos.toByteArray().size / 1024 > 30) { //循环判断如果压缩后图片是否大于100kb,大于继续压缩
//循环判断如果压缩后图片是否大于100kb,大于继续压缩
start = bos.toByteArray().size / 1024
bos.reset() //重置baos即清空baos
bit.compress(Bitmap.CompressFormat.JPEG, options, bos) //这里压缩options%把压缩后的数据存放到baos中
if (options > 5) {
options -= 5 //每次都减少10
}
end = bos.toByteArray().size / 1024
if (start == end) {
break
}
}
return Base64.encodeToString(bos.toByteArray(), Base64.DEFAULT)
}
@JvmStatic
fun toByteArray(bitmap: Bitmap): ByteArray {
val os = ByteArrayOutputStream()
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, os)
return os.toByteArray()
}
@JvmStatic
fun mirror(rawBitmap: Bitmap): Bitmap {
val matrix = Matrix()
matrix.postScale(-1f, 1f)
return Bitmap.createBitmap(rawBitmap, 0, 0, rawBitmap.width, rawBitmap.height, matrix, true)
}
@JvmStatic
fun rotate(rawBitmap: Bitmap, degree: Float): Bitmap {
val matrix = Matrix()
matrix.postRotate(degree)
return Bitmap.createBitmap(rawBitmap, 0, 0, rawBitmap.width, rawBitmap.height, matrix, true)
}
@JvmStatic
fun decodeBitmap(bitmap: Bitmap, reqWidth: Int, reqHeight: Int): Bitmap {
val options = BitmapFactory.Options()
options.inJustDecodeBounds = true
val bos = ByteArrayOutputStream()
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, bos)
BitmapFactory.decodeByteArray(bos.toByteArray(), 0, bos.size(), options)
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight)
options.inJustDecodeBounds = false
return BitmapFactory.decodeByteArray(bos.toByteArray(), 0, bos.size(), options)
}
@JvmStatic
fun decodeBitmapFromFile(path: String, reqWidth: Int, reqHeight: Int): Bitmap {
val options = BitmapFactory.Options()
options.inJustDecodeBounds = true
BitmapFactory.decodeFile(path, options)
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight)
options.inJustDecodeBounds = false
return BitmapFactory.decodeFile(path, options)
}
@JvmStatic
fun decodeBitmapFromResource(
res: Resources,
resId: Int,
reqWidth: Int,
reqHeight: Int
): Bitmap {
val options = BitmapFactory.Options()
options.inJustDecodeBounds = true
BitmapFactory.decodeResource(res, resId, options)
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight)
options.inJustDecodeBounds = false
return BitmapFactory.decodeResource(res, resId, options)
}
@JvmStatic
private fun calculateInSampleSize(
options: BitmapFactory.Options,
reqWidth: Int,
reqHeight: Int
): Int {
val rawWidth = options.outWidth
val rawHeight = options.outHeight
var inSampleSize = 1
if (rawWidth > reqWidth || rawHeight > reqHeight) {
val halfWidth = rawWidth / 2
val halfHeight = rawHeight / 2
while ((halfWidth / inSampleSize) > reqWidth && (halfHeight / inSampleSize) > reqHeight) {
inSampleSize *= 2 //设置inSampleSize为2的幂是因为解码器最终还是会对非2的幂的数进行向下处理获取到最靠近2的幂的数
}
}
return inSampleSize
}
@JvmStatic
fun savePicNoRotate(
data: ByteArray?,
folderName: String = "camera1",
onSuccess: (savedPath: String, time: String) -> Unit,
onFailed: (msg: String) -> Unit
) {
thread {
try {
val temp = System.currentTimeMillis()
val picFile = FileUtil.createCameraFile(folderName)
if (picFile != null && data != null) {
val rawBitmap = BitmapFactory.decodeByteArray(data, 0, data.size)
// val resultBitmap = if (isMirror) mirror(rawBitmap) else rawBitmap
val outputStream = FileOutputStream(picFile)
outputStream.write(toByteArray(rawBitmap))
outputStream.close()
onSuccess(picFile.absolutePath, "${System.currentTimeMillis() - temp}")
}
} catch (e: Exception) {
e.printStackTrace()
onFailed("${e.message}")
}
}
}
fun getBitmapDegree(path: String): Int {
var degree = 0;
// 从指定路径下读取图片并获取其EXIF信息
var exifInterface = ExifInterface(path);
// 获取图片的旋转信息
var orientation = exifInterface.getAttributeInt(
ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_NORMAL
);
when (orientation) {
ExifInterface.ORIENTATION_ROTATE_90 -> degree = 90
ExifInterface.ORIENTATION_ROTATE_180 -> degree = 180
ExifInterface.ORIENTATION_ROTATE_270 -> degree = 270
}
return degree;
}
@JvmStatic
fun savePic(
data: ByteArray?,
folderName: String = "camera1",
degree: Float,
onSuccess: (savedPath: String, time: String) -> Unit,
onFailed: (msg: String) -> Unit
) {
thread {
try {
val temp = System.currentTimeMillis()
val picFile = FileUtil.createCameraFile(folderName)
if (picFile != null && data != null) {
val rawBitmap = BitmapFactory.decodeByteArray(data, 0, data.size)
val resultBitmap1 = mirror(rawBitmap)
val resultBitmap = rotate(resultBitmap1, degree)
val outputStream = FileOutputStream(picFile)
outputStream.write(toByteArray(resultBitmap))
outputStream.close()
// var degree = getBitmapDegree(picFile.absolutePath)
onSuccess(picFile.absolutePath, "${System.currentTimeMillis() - temp}")
}
} catch (e: Exception) {
e.printStackTrace()
onFailed("${e.message}")
}
}
}
@SuppressLint("SimpleDateFormat")
fun savePicToPublicStorage(
context: Context,
data: ByteArray?,
isMirror: Boolean = false,
onSuccess: (savedPath: String, time: String) -> Unit,
onFailed: (msg: String) -> Unit
) {
thread {
try {
val temp = System.currentTimeMillis()
val timeStamp = SimpleDateFormat("yyyyMMdd_HHmmss").format(Date())
val fileName = "IMG_$timeStamp.jpg"
// 创建图片索引
val value = ContentValues().apply {
put(MediaStore.Images.Media.DISPLAY_NAME, fileName)
put(MediaStore.Images.Media.MIME_TYPE, "image/jpg")
put(MediaStore.Images.Media.DATE_ADDED, temp)
}
// 将该索引信息插入数据表获得图片的Uri
// 保存的路径在 /sdcard/Pictures
val imageUri = context.contentResolver.insert(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
value
)
if (imageUri != null && data != null) {
//通过图片uri获得输出流
val outputStream = context.contentResolver.openOutputStream(imageUri)
val rawBitmap = BitmapFactory.decodeByteArray(data, 0, data.size)
val resultBitmap = if (isMirror) mirror(rawBitmap) else rawBitmap
resultBitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream)
onSuccess(imageUri.path ?: "", "${System.currentTimeMillis() - temp}")
}
} catch (e: Exception) {
e.printStackTrace()
onFailed("${e.message}")
}
}
}
}

View File

@@ -0,0 +1,273 @@
package com.xuebiping.bolizhuzi.utils.camera2
import android.app.Activity
import android.graphics.ImageFormat
import android.graphics.Matrix
import android.graphics.RectF
import android.hardware.Camera
import android.view.Surface
import android.view.SurfaceHolder
import android.view.SurfaceView
import android.widget.Toast
/**
* author : chensen
* data : 2018/3/17
* desc :
*/
class CameraHelper(activity: Activity, surfaceView: SurfaceView) : Camera.PreviewCallback {
private var mCamera: Camera? = null //Camera对象
private lateinit var mParameters: Camera.Parameters //Camera对象的参数
private var mSurfaceView: SurfaceView = surfaceView //用于预览的SurfaceView对象
var mSurfaceHolder: SurfaceHolder //SurfaceHolder对象
private var mActivity: Activity = activity
private var mCallBack: CallBack? = null //自定义的回调
var mCameraFacing = Camera.CameraInfo.CAMERA_FACING_FRONT //摄像头方向
var mDisplayOrientation: Int = 0 //预览旋转的角度
private var picWidth = 2160 //保存图片的宽
private var picHeight = 3840 //保存图片的高
override fun onPreviewFrame(data: ByteArray?, camera: Camera?) {
mCallBack?.onPreviewFrame(data)
}
fun takePic() {
mCamera?.let {
it.takePicture({}, null, { data, _ ->
it.startPreview()
mCallBack?.onTakePic(data)
})
}
}
private fun init() {
mSurfaceHolder.addCallback(object : SurfaceHolder.Callback {
override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
}
override fun surfaceDestroyed(holder: SurfaceHolder) {
releaseCamera()
}
override fun surfaceCreated(holder: SurfaceHolder) {
if (mCamera == null) {
openCamera(mCameraFacing)
}
startPreview()
}
})
}
//打开相机
private fun openCamera(cameraFacing: Int = Camera.CameraInfo.CAMERA_FACING_BACK): Boolean {
val supportCameraFacing = supportCameraFacing(cameraFacing)
if (supportCameraFacing) {
try {
mCamera = Camera.open(cameraFacing)
initParameters(mCamera!!)
mCamera?.setPreviewCallback(this)
} catch (e: Exception) {
e.printStackTrace()
toast("打开相机失败!")
return false
}
}
return supportCameraFacing
}
//配置相机参数
private fun initParameters(camera: Camera) {
try {
mParameters = camera.parameters
mParameters.previewFormat = ImageFormat.NV21
//获取与指定宽高相等或最接近的尺寸
//设置预览尺寸
val bestPreviewSize = getBestSize(mSurfaceView.width, mSurfaceView.height, mParameters.supportedPreviewSizes)
bestPreviewSize?.let {
mParameters.setPreviewSize(it.width, it.height)
}
//设置保存图片尺寸
val bestPicSize = getBestSize(picWidth, picHeight, mParameters.supportedPictureSizes)
bestPicSize?.let {
mParameters.setPictureSize(it.width, it.height)
}
//对焦模式
if (isSupportFocus(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE))
mParameters.focusMode = Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE
camera.parameters = mParameters
} catch (e: Exception) {
e.printStackTrace()
toast("相机初始化失败!")
}
}
//开始预览
fun startPreview() {
mCamera?.let {
it.setPreviewDisplay(mSurfaceHolder)
setCameraDisplayOrientation(mActivity)
it.startPreview()
// startFaceDetect()
}
}
private fun startFaceDetect() {
mCamera?.let {
it.startFaceDetection()
it.setFaceDetectionListener { faces, _ ->
mCallBack?.onFaceDetect(transForm(faces))
// log("检测到 ${faces.size} 张人脸")
}
}
}
//判断是否支持某一对焦模式
private fun isSupportFocus(focusMode: String): Boolean {
var autoFocus = false
val listFocusMode = mParameters.supportedFocusModes
for (mode in listFocusMode) {
if (mode == focusMode)
autoFocus = true
// log("相机支持的对焦模式: $mode")
}
return autoFocus
}
//切换摄像头
fun exchangeCamera() {
releaseCamera()
mCameraFacing = if (mCameraFacing == Camera.CameraInfo.CAMERA_FACING_BACK)
Camera.CameraInfo.CAMERA_FACING_FRONT
else
Camera.CameraInfo.CAMERA_FACING_BACK
openCamera(mCameraFacing)
startPreview()
}
//释放相机
fun releaseCamera() {
if (mCamera != null) {
// mCamera?.stopFaceDetection()
mCamera?.stopPreview()
mCamera?.setPreviewCallback(null)
mCamera?.release()
mCamera = null
}
}
//获取与指定宽高相等或最接近的尺寸
private fun getBestSize(targetWidth: Int, targetHeight: Int, sizeList: List<Camera.Size>): Camera.Size? {
var bestSize: Camera.Size? = null
val targetRatio = (targetHeight.toDouble() / targetWidth) //目标大小的宽高比
var minDiff = targetRatio
for (size in sizeList) {
val supportedRatio = (size.width.toDouble() / size.height)
// log("系统支持的尺寸 : ${size.width} * ${size.height} , 比例$supportedRatio")
}
for (size in sizeList) {
if (size.width == targetHeight && size.height == targetWidth) {
bestSize = size
break
}
val supportedRatio = (size.width.toDouble() / size.height)
if (Math.abs(supportedRatio - targetRatio) < minDiff) {
minDiff = Math.abs(supportedRatio - targetRatio)
bestSize = size
}
}
// log("目标尺寸 $targetWidth * $targetHeight 比例 $targetRatio")
// log("最优尺寸 ${bestSize?.height} * ${bestSize?.width}")
return bestSize
}
//设置预览旋转的角度
private fun setCameraDisplayOrientation(activity: Activity) {
val info = Camera.CameraInfo()
Camera.getCameraInfo(mCameraFacing, info)
val rotation = activity.windowManager.defaultDisplay.rotation
var screenDegree = 0
when (rotation) {
Surface.ROTATION_0 -> screenDegree = 0
Surface.ROTATION_90 -> screenDegree = 90
Surface.ROTATION_180 -> screenDegree = 180
Surface.ROTATION_270 -> screenDegree = 270
}
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
mDisplayOrientation = (info.orientation + screenDegree) % 360
mDisplayOrientation = (360 - mDisplayOrientation) % 360 // compensate the mirror
} else {
mDisplayOrientation = (info.orientation - screenDegree + 360) % 360
}
mCamera?.setDisplayOrientation(mDisplayOrientation)
// log("屏幕的旋转角度 : $rotation")
// log("setDisplayOrientation(result) : $mDisplayOrientation")
}
//判断是否支持某个相机
private fun supportCameraFacing(cameraFacing: Int): Boolean {
val info = Camera.CameraInfo()
for (i in 0 until Camera.getNumberOfCameras()) {
Camera.getCameraInfo(i, info)
if (info.facing == cameraFacing) return true
}
return false
}
//将相机中用于表示人脸矩形的坐标转换成UI页面的坐标
private fun transForm(faces: Array<Camera.Face>): ArrayList<RectF> {
val matrix = Matrix()
// Need mirror for front camera.
val mirror = (mCameraFacing == Camera.CameraInfo.CAMERA_FACING_FRONT)
matrix.setScale(if (mirror) -1f else 1f, 1f)
// This is the value for android.hardware.Camera.setDisplayOrientation.
matrix.postRotate(mDisplayOrientation.toFloat())
// Camera driver coordinates range from (-1000, -1000) to (1000, 1000).
// UI coordinates range from (0, 0) to (width, height).
matrix.postScale(mSurfaceView.width / 2000f, mSurfaceView.height / 2000f)
matrix.postTranslate(mSurfaceView.width / 2f, mSurfaceView.height / 2f)
val rectList = ArrayList<RectF>()
for (face in faces) {
val srcRect = RectF(face.rect)
val dstRect = RectF(0f, 0f, 0f, 0f)
matrix.mapRect(dstRect, srcRect)
rectList.add(dstRect)
}
return rectList
}
private fun toast(msg: String) {
Toast.makeText(mActivity, msg, Toast.LENGTH_SHORT).show()
}
fun getCamera(): Camera? = mCamera
fun addCallBack(callBack: CallBack) {
this.mCallBack = callBack
}
interface CallBack {
fun onPreviewFrame(data: ByteArray?)
fun onTakePic(data: ByteArray?)
fun onFaceDetect(faces: ArrayList<RectF>)
}
init {
mSurfaceHolder = mSurfaceView.holder
init()
}
}

View File

@@ -0,0 +1,53 @@
package com.xuebiping.bolizhuzi.utils.camera2
import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import android.util.TypedValue
import android.view.View
/**
* author : chensen
* data : 2018/3/19
* desc :
*/
class FaceView : View {
lateinit var mPaint: Paint
private var mCorlor = "#42ed45"
private var mFaces: ArrayList<RectF>? = null
constructor(context: Context) : super(context) {
init()
}
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
init()
}
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
init()
}
private fun init() {
mPaint = Paint()
mPaint.color = Color.parseColor(mCorlor)
mPaint.style = Paint.Style.STROKE
mPaint.strokeWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1f, context.resources.displayMetrics)
mPaint.isAntiAlias = true
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
mFaces?.let {
for (face in it) {
canvas.drawRect(face, mPaint)
}
}
}
fun setFaces(faces: ArrayList<RectF>) {
this.mFaces = faces
invalidate()
}
}

View File

@@ -0,0 +1,76 @@
package com.xuebiping.bolizhuzi.utils.camera2
import android.annotation.SuppressLint
import com.xuebiping.bolizhuzi.controller.constant.ConstUrl
import java.io.File
import java.text.SimpleDateFormat
import java.util.*
/**
* author : ChenSen
* data : 2018/3/15
* desc :
*/
object FileUtil {
private val rootFolderPath = ConstUrl.DEFAULT_PATH + File.separator + "CameraDemo"
// private val rootFolderPath = App.getRootPath() + File.separator + "CameraDemo"
@SuppressLint("SimpleDateFormat")
fun createImageFile(isCrop: Boolean = false): File? {
return try {
val rootFile = File(rootFolderPath + File.separator + "capture")
if (!rootFile.exists())
rootFile.mkdirs()
val timeStamp = SimpleDateFormat("yyyyMMdd_HHmmss").format(Date())
val fileName = if (isCrop) "IMG_${timeStamp}_CROP.jpg" else "IMG_$timeStamp.jpg"
File(rootFile.absolutePath + File.separator + fileName).apply {
if (!exists())
createNewFile()
}
} catch (e: Exception) {
e.printStackTrace()
null
}
}
@SuppressLint("SimpleDateFormat")
fun createCameraFile(folderName: String = "camera1"): File? {
return try {
val rootFile = File(rootFolderPath + File.separator + folderName)
if (!rootFile.exists())
rootFile.mkdirs()
val timeStamp = SimpleDateFormat("yyyyMMdd_HHmmss").format(Date())
val fileName = "IMG_$timeStamp.jpg"
File(rootFile.absolutePath + File.separator + fileName).apply {
if (!exists())
createNewFile()
}
} catch (e: Exception) {
e.printStackTrace()
null
}
}
@SuppressLint("SimpleDateFormat")
fun createVideoFile(): File? {
return try {
val rootFile = File(rootFolderPath + File.separator + "video")
if (!rootFile.exists())
rootFile.mkdirs()
val timeStamp = SimpleDateFormat("yyyyMMdd_HHmmss").format(Date())
val fileName = "VIDEO_$timeStamp.mp4"
File(rootFile.absolutePath + File.separator + fileName).apply {
if (!exists())
createNewFile()
}
} catch (e: Exception) {
e.printStackTrace()
null
}
}
}

View File

@@ -0,0 +1,140 @@
package com.xuebiping.bolizhuzi.utils.camera2
import android.Manifest
import android.app.Activity
import android.content.Intent
import android.graphics.RectF
import android.os.Bundle
import android.view.WindowManager
import com.fengliyan.base.base.permission.AbsPermissionResultCallBack
import com.fengliyan.base.base.permission.PermissionHelper
import com.fengliyan.device.DeviceManager
import com.xuebiping.bolizhuzi.databinding.ActivityCamera2Binding
import com.xuebiping.bolizhuzi.im.uikit.common.media.picker.activity.CropImageActivity
import com.xuebiping.bolizhuzi.view.base.BaseActivity
import com.xuebiping.bolizhuzi.view.main.dialog.PermissionDialog
import com.fengliyan.uikit.toast.MaleToast
class GirlCameraTakePicActivity : BaseActivity() {
private var mCameraHelper: CameraHelper? = null
lateinit var binding: ActivityCamera2Binding
private var _savedPath: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityCamera2Binding.inflate(layoutInflater)
setContentView(binding.root)
//权限允许
val permissionDialog = PermissionDialog(
this, arrayOf(
Manifest.permission.CAMERA,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE
)
)
permissionDialog.show()
PermissionHelper.request(arrayOf(
Manifest.permission.CAMERA,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE
),
object : AbsPermissionResultCallBack() {
override fun onPermissionGranted() {
permissionDialog.dismiss()
setContentView(binding.root)
setDefaultTitle()
hideTitleBar()
window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)
mCameraHelper =
CameraHelper(this@GirlCameraTakePicActivity, binding.surfaceView)
mCameraHelper!!.addCallBack(object : CameraHelper.CallBack {
override fun onPreviewFrame(data: ByteArray?) {
}
override fun onTakePic(data: ByteArray?) {
save(data!!)
}
override fun onFaceDetect(faces: ArrayList<RectF>) {
}
})
binding.btnTakePic.setOnClickListener { mCameraHelper?.takePic() }
binding.cancelBtn.setOnClickListener { finish() }
}
override fun onPermissionDenied(vararg permissions: String) {
val market = DeviceManager.getInstance().applicationMarket
if (market != null && market == "huawei") {
permissionDialog.dismiss()
finish()
return
}
super.onPermissionDenied(*permissions)
permissionDialog.dismiss()
}
})
}
private fun save(byteArray: ByteArray) {
mCameraHelper?.mDisplayOrientation?.toFloat()?.let {
BitmapUtils.savePic(
byteArray,
"camera2",
it,
{ savedPath, time ->
runOnUiThread {
//剪裁
_savedPath = savedPath;
resources.displayMetrics.widthPixels*1.5
CropImageActivity.startForFile(
this@GirlCameraTakePicActivity,
savedPath,
resources.displayMetrics.widthPixels,
(resources.displayMetrics.widthPixels * 1.5).toInt(),
savedPath,
100
);
// finish()
// mManager?.whoStartCropWithUi(Uri.parse("file://$savedPath"))
// val intent = Intent()
// intent.putExtra("path",savedPath)
// setResult(Activity.RESULT_OK,intent)
// finish()
}
},
{ msg ->
runOnUiThread {
MaleToast.showMessage(this@GirlCameraTakePicActivity, "拍照失败")
}
})
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == 100 && resultCode == Activity.RESULT_OK) {
// val result = Intent()
// result.putExtra(Extras.EXTRA_FILE_PATH, outPath)
// setResult(Activity.RESULT_OK, result)
// finish()
val intent = Intent()
intent.putExtra("path", _savedPath)
setResult(Activity.RESULT_OK, intent)
finish()
}
}
override fun onDestroy() {
super.onDestroy()
mCameraHelper?.releaseCamera()
}
}