修改动态和部分我的界面
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
* key:bitmap对应的文件路径
|
||||
*/
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
156
app/src/main/java/com/xuebiping/bolizhuzi/utils/CodeUtils.java
Normal file
156
app/src/main/java/com/xuebiping/bolizhuzi/utils/CodeUtils.java
Normal 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);
|
||||
}
|
||||
}
|
||||
175
app/src/main/java/com/xuebiping/bolizhuzi/utils/DemoHelper.java
Normal file
175
app/src/main/java/com/xuebiping/bolizhuzi/utils/DemoHelper.java
Normal 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 "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
117
app/src/main/java/com/xuebiping/bolizhuzi/utils/GlideEngine.java
Normal file
117
app/src/main/java/com/xuebiping/bolizhuzi/utils/GlideEngine.java
Normal 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;
|
||||
|
||||
/**
|
||||
* @author:luck
|
||||
* @date:2019-11-13 17:02
|
||||
* @describe:Glide加载引擎
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
// }
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
}
|
||||
}
|
||||
263
app/src/main/java/com/xuebiping/bolizhuzi/utils/GlideUtils.java
Normal file
263
app/src/main/java/com/xuebiping/bolizhuzi/utils/GlideUtils.java
Normal 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);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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,需要设置inDensity,inTargetDensity,才能正确的缩放
|
||||
*/
|
||||
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)
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 控制内容填充的区域
|
||||
* (注意:这里的left,top,right,bottom同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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
)
|
||||
121
app/src/main/java/com/xuebiping/bolizhuzi/utils/PayUtils.java
Normal file
121
app/src/main/java/com/xuebiping/bolizhuzi/utils/PayUtils.java
Normal 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"));
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
85
app/src/main/java/com/xuebiping/bolizhuzi/utils/SPUtils.java
Normal file
85
app/src/main/java/com/xuebiping/bolizhuzi/utils/SPUtils.java
Normal 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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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.bottom,r.top其实是状态栏的高度
|
||||
return (r.bottom - r.top);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
596
app/src/main/java/com/xuebiping/bolizhuzi/utils/StrU.java
Normal file
596
app/src/main/java/com/xuebiping/bolizhuzi/utils/StrU.java
Normal 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) : "");
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
307
app/src/main/java/com/xuebiping/bolizhuzi/utils/WaveView.java
Normal file
307
app/src/main/java/com/xuebiping/bolizhuzi/utils/WaveView.java
Normal 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);
|
||||
}
|
||||
}*/
|
||||
|
||||
}
|
||||
@@ -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}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user