1、修改登录功能并验证,除了支付宝登录其他都已验证
2、完成个人中心的功能,个人主页完成、钱包完成、背包完成
This commit is contained in:
@@ -10,6 +10,9 @@ android {
|
||||
defaultConfig {
|
||||
minSdk 24
|
||||
|
||||
versionCode Integer.parseInt(project.findProperty("APP_VERSION_CODE"))
|
||||
versionName project.findProperty("APP_VERSION_NAME")
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
consumerProguardFiles "consumer-rules.pro"
|
||||
}
|
||||
@@ -38,6 +41,7 @@ dependencies {
|
||||
implementation libs.appcompat
|
||||
implementation libs.material
|
||||
implementation libs.core.ktx
|
||||
implementation project(':moduletablayout')
|
||||
testImplementation libs.junit
|
||||
androidTestImplementation libs.ext.junit
|
||||
androidTestImplementation libs.espresso.core
|
||||
@@ -95,6 +99,23 @@ dependencies {
|
||||
|
||||
api(libs.github.shadowlayout)
|
||||
|
||||
compileOnly 'org.projectlombok:lombok:1.18.32'
|
||||
//判断是否是模拟器
|
||||
api(libs.easy.protector.release)
|
||||
//第三方Popup
|
||||
api(libs.custompopwindow)
|
||||
// cookie
|
||||
api(libs.persistentcookiejar)
|
||||
|
||||
//noinspection GradleDynamicVersion
|
||||
api(libs.dpa.oss.android.sdk)
|
||||
//微信登录
|
||||
api(libs.wechat.sdk.android)
|
||||
//支付宝
|
||||
api(libs.alipay.alipaysdk.android)
|
||||
|
||||
api(libs.easypermissions)
|
||||
api(libs.xbanner)
|
||||
|
||||
api(libs.lombok)
|
||||
annotationProcessor 'org.projectlombok:lombok:1.18.32'
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<application
|
||||
android:allowBackup="true">
|
||||
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
package com.qxcm.moduleutil.adapter;
|
||||
|
||||
import android.text.TextUtils;
|
||||
import android.view.View;
|
||||
import android.view.ViewTreeObserver;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.chad.library.adapter.base.BaseQuickAdapter;
|
||||
import com.chad.library.adapter.base.BaseViewHolder;
|
||||
import com.qxcm.moduleutil.R;
|
||||
import com.qxcm.moduleutil.bean.CircleListBean;
|
||||
import com.qxcm.moduleutil.utils.ImageUtils;
|
||||
import com.qxcm.moduleutil.widget.MyGridView;
|
||||
import com.qxcm.moduleutil.widget.img.FullScreenUtil;
|
||||
|
||||
public class CirleListAdapter extends BaseQuickAdapter<CircleListBean, BaseViewHolder> {
|
||||
public CirleListAdapter() {
|
||||
super(R.layout.item_cirle_list);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void convert(BaseViewHolder helper, CircleListBean item) {
|
||||
helper.addOnClickListener(R.id.dianzan)
|
||||
.addOnClickListener(R.id.dy_lookmore_tv)
|
||||
.addOnClickListener(R.id.dy_head_image)
|
||||
.addOnClickListener(R.id.dy_more_image)
|
||||
.addOnClickListener(R.id.dy_oneimage_iv);
|
||||
|
||||
//先让单图,多图,音频的布局显示
|
||||
helper.getView(R.id.dy_oneimage_iv).setVisibility(View.VISIBLE);
|
||||
helper.getView(R.id.dy_image_recyc).setVisibility(View.VISIBLE);
|
||||
helper.getView(R.id.iv_jubao).setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
// ARouter.getInstance().build(AroutUtil.COMMUNITY_JUBAO).withString("uid", item.getUid() + "").navigation();
|
||||
}
|
||||
});
|
||||
//昵称
|
||||
helper.setText(R.id.dy_name_text, item.getUserNickName());
|
||||
|
||||
//头像
|
||||
ImageUtils.loadHeadCC(item.getUserAvatar(), (ImageView) helper.getView(R.id.dy_head_image));
|
||||
|
||||
//动态内容以富文本展示
|
||||
String content = item.getContent();
|
||||
if (content == null || content.length() == 0) {
|
||||
helper.getView(R.id.dy_lookmore_tv).setVisibility(View.GONE);
|
||||
helper.getView(R.id.dy_content_tv).setVisibility(View.GONE);
|
||||
} else {
|
||||
helper.getView(R.id.dy_lookmore_tv).setVisibility(View.VISIBLE);
|
||||
helper.getView(R.id.dy_content_tv).setVisibility(View.VISIBLE);
|
||||
}
|
||||
helper.getView(R.id.dy_lookmore_tv).getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
|
||||
@Override
|
||||
public boolean onPreDraw() {
|
||||
//这个回调会调用多次,获取完行数记得注销监听
|
||||
TextView view = helper.getView(R.id.dy_content_tv);
|
||||
int lineCount = view.getLineCount();
|
||||
if (lineCount >= 7) {
|
||||
helper.getView(R.id.dy_lookmore_tv).setVisibility(View.VISIBLE);
|
||||
helper.getView(R.id.dy_content_tv).getViewTreeObserver().removeOnPreDrawListener(this);//销毁
|
||||
} else {
|
||||
helper.getView(R.id.dy_lookmore_tv).setVisibility(View.GONE);
|
||||
helper.getView(R.id.dy_content_tv).getViewTreeObserver().removeOnPreDrawListener(this);//销毁
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
helper.setText(R.id.dy_content_tv, content);
|
||||
|
||||
if (TextUtils.isEmpty(content)) {
|
||||
TextView view = helper.getView(R.id.dy_content_tv);
|
||||
view.setVisibility(View.GONE);
|
||||
} else {
|
||||
TextView view = helper.getView(R.id.dy_content_tv);
|
||||
view.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
|
||||
//点赞
|
||||
helper.setText(R.id.dy_fabulous, item.getLike());
|
||||
// if (item.is_praise() == 1) {
|
||||
// TextView dy_fabulous = helper.getView(R.id.dy_fabulous);
|
||||
// dy_fabulous.setTextColor(mContext.getResources().getColor(R.color.dianzan2));
|
||||
// helper.setImageResource(R.id.dianzan_image, R.drawable.dongtai_hudong_yidianzan);
|
||||
// } else {
|
||||
// TextView dy_fabulous = helper.getView(R.id.dy_fabulous);
|
||||
// dy_fabulous.setTextColor(mContext.getResources().getColor(R.color.dianzan1));
|
||||
// helper.setImageResource(R.id.dianzan_image, R.drawable.dongtai_hudong_dianzan);
|
||||
// }
|
||||
|
||||
//分享数
|
||||
helper.setText(R.id.dy_zs, item.getIsShare());
|
||||
//评论数
|
||||
helper.setText(R.id.dy_comment, item.getComment() + "");
|
||||
|
||||
|
||||
//时间
|
||||
|
||||
if (!item.getTime().isEmpty()) {
|
||||
try {
|
||||
helper.setText(R.id.dy_time_text, item.getTime());
|
||||
} catch (NumberFormatException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (item.getImages().size() != 0) {
|
||||
String[] arrIv = item.getImages().toArray(new String[item.getImages().size()]);
|
||||
int length = arrIv.length;
|
||||
|
||||
|
||||
OneImageYuanJiaoAdapter oneImageYuanJiaoAdapter = new OneImageYuanJiaoAdapter(mContext);
|
||||
MyGridView recyclerView = helper.getView(R.id.dy_image_recyc);
|
||||
recyclerView.setNumColumns(3);
|
||||
recyclerView.setAdapter(oneImageYuanJiaoAdapter);
|
||||
oneImageYuanJiaoAdapter.getList_adapter().clear();
|
||||
|
||||
for (int j = 0; j < arrIv.length; j++) {
|
||||
oneImageYuanJiaoAdapter.getList_adapter().add(arrIv[j]);
|
||||
}
|
||||
helper.getView(R.id.dy_oneimage_iv).setVisibility(View.GONE);
|
||||
|
||||
oneImageYuanJiaoAdapter.notifyDataSetChanged();
|
||||
|
||||
recyclerView.setOnItemClickListener((parent, view, position, id) -> {
|
||||
FullScreenUtil.showFullScreenDialog(mContext, position, oneImageYuanJiaoAdapter.getList_adapter());
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.qxcm.moduleutil.adapter;
|
||||
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentStatePagerAdapter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class MyPagerAdapter extends FragmentStatePagerAdapter {
|
||||
|
||||
private List<Fragment> fragments;
|
||||
private List<String> typeOnes;
|
||||
|
||||
public MyPagerAdapter(FragmentManager fm, List<Fragment> mFragments, List<String> ones) {
|
||||
super(fm);
|
||||
this.fragments = mFragments;
|
||||
this.typeOnes = ones;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return fragments.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getPageTitle(int position) {
|
||||
return typeOnes.get(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Fragment getItem(int position) {
|
||||
return fragments.get(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroyItem(ViewGroup container, int position, Object object) {
|
||||
super.destroyItem(container, position, object);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
package com.qxcm.moduleutil.base;
|
||||
|
||||
import android.app.Application;
|
||||
|
||||
public class BaseApplication extends Application {
|
||||
|
||||
}
|
||||
@@ -1,28 +1,52 @@
|
||||
package com.qxcm.moduleutil.widget;
|
||||
package com.qxcm.moduleutil.base;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.Application;
|
||||
import android.app.LauncherActivity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.Signature;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Base64;
|
||||
import android.webkit.WebView;
|
||||
|
||||
import androidx.multidex.MultiDex;
|
||||
import androidx.multidex.MultiDexApplication;
|
||||
|
||||
import com.blankj.utilcode.util.AppUtils;
|
||||
import com.blankj.utilcode.util.LogUtils;
|
||||
import com.blankj.utilcode.util.ProcessUtils;
|
||||
import com.blankj.utilcode.util.ServiceUtils;
|
||||
import com.lahm.library.EasyProtectorLib;
|
||||
import com.lahm.library.EmulatorCheckCallback;
|
||||
import com.qxcm.moduleutil.bean.UserBean;
|
||||
import com.qxcm.moduleutil.event.AppLifecycleEvent;
|
||||
import com.qxcm.moduleutil.interfaces.AppLifecycleUtil;
|
||||
import com.qxcm.moduleutil.utils.FloatWindowHelper;
|
||||
import com.qxcm.moduleutil.utils.SPConstants;
|
||||
import com.qxcm.moduleutil.utils.SpUtil;
|
||||
import com.qxcm.moduleutil.utils.UtilConfig;
|
||||
import com.qxcm.moduleutil.utils.config.EnvironmentEnum;
|
||||
import com.qxcm.moduleutil.utils.config.EnvironmentPrefs;
|
||||
import com.qxcm.moduleutil.widget.CommonAppConfig;
|
||||
import com.qxcm.moduleutil.widget.Constants;
|
||||
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import lombok.Getter;
|
||||
|
||||
|
||||
/**
|
||||
@@ -35,14 +59,47 @@ public class CommonAppContext extends MultiDexApplication {
|
||||
private static Handler sMainThreadHandler;
|
||||
private int mCount;
|
||||
private boolean mFront;//是否前台
|
||||
|
||||
|
||||
public String emulator = "0";
|
||||
@Getter
|
||||
private EnvironmentEnum currentEnvironment;
|
||||
public UserBean mUserBean;
|
||||
private final ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
|
||||
private ScheduledExecutorService scheduledExecutorServiceRoom = null;
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
sInstance = this;
|
||||
sMainThreadHandler=new Handler();
|
||||
UtilConfig.init(this);
|
||||
registerActivityLifecycleCallbacks();
|
||||
initWebView();
|
||||
if (ProcessUtils.isMainProcess()) {
|
||||
if (SpUtil.isAgreePolicy()) {
|
||||
checkInEmulator();
|
||||
UtilConfig.checkInEmulator();
|
||||
}
|
||||
}
|
||||
EnvironmentPrefs prefs = new EnvironmentPrefs(this);
|
||||
currentEnvironment = prefs.getSelectedEnvironment();
|
||||
}
|
||||
private void initWebView() {
|
||||
//Android P 以及之后版本不支持同时从多个进程使用具有相同数据目录的WebView
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
String processName = getProcessName(this);
|
||||
if (!AppUtils.getAppPackageName().equals(processName)) {//判断不等于默认进程名称
|
||||
WebView.setDataDirectorySuffix(processName);
|
||||
}
|
||||
}
|
||||
}
|
||||
public String getProcessName(Context context) {
|
||||
if (context == null) return null;
|
||||
ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
|
||||
for (ActivityManager.RunningAppProcessInfo processInfo : manager.getRunningAppProcesses()) {
|
||||
if (processInfo.pid == android.os.Process.myPid()) {
|
||||
return processInfo.processName;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
protected void attachBaseContext(Context base) {
|
||||
@@ -66,7 +123,14 @@ public class CommonAppContext extends MultiDexApplication {
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
|
||||
public void checkInEmulator() {
|
||||
emulator = EasyProtectorLib.checkIsRunningInEmulator(this, new EmulatorCheckCallback() {
|
||||
@Override
|
||||
public void findEmulator(String emulatorInfo) {
|
||||
// Logger.e(emulatorInfo);
|
||||
}
|
||||
}) ? "1" : "0";
|
||||
}
|
||||
|
||||
public static void postDelayed(Runnable runnable, long delayMillis) {
|
||||
if (sMainThreadHandler != null) {
|
||||
@@ -195,4 +259,44 @@ public class CommonAppContext extends MultiDexApplication {
|
||||
public void startInitSdk(){
|
||||
|
||||
}
|
||||
|
||||
public void setUser(UserBean userBean) {
|
||||
mUserBean = userBean;
|
||||
SpUtil.saveUserId(userBean.getUser_id());
|
||||
SpUtil.saveUserInfo(userBean);
|
||||
SpUtil.putToken(userBean.getToken());
|
||||
}
|
||||
|
||||
public UserBean getUser() {
|
||||
if (mUserBean == null) {
|
||||
mUserBean = SpUtil.getUserInfo();
|
||||
}
|
||||
return mUserBean;
|
||||
}
|
||||
public void setUserBean(UserBean bean) {
|
||||
mUserBean = bean;
|
||||
}
|
||||
public void clearLoginInfo() throws ClassNotFoundException {
|
||||
// mUid = null;
|
||||
// mToken = null;
|
||||
// SpUtil.getInstance().removeValue(
|
||||
// SPConstants.USER_ID, SPConstants.TOKEN, SPConstants.USER_INFO
|
||||
// );
|
||||
mUserBean = null;
|
||||
SpUtil.saveUserId(-1);
|
||||
SpUtil.saveUserInfo(new UserBean());
|
||||
SpUtil.putToken("");
|
||||
Intent intent = new Intent("com.example.action.LAUNCH_PAGE");
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
getApplicationContext().startActivity(intent);
|
||||
}
|
||||
|
||||
public static boolean isAlipayInstalled(Context context){
|
||||
try {
|
||||
context.getPackageManager().getPackageInfo("com.eg.android.AlipayGphone", 0);
|
||||
return true;//安装了支付宝
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
return false; //未安装支付宝
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
package com.qxcm.moduleutil.bean;
|
||||
|
||||
import android.text.TextUtils;
|
||||
|
||||
public class AttentionResp {
|
||||
|
||||
private String room_id;
|
||||
private String room_name;
|
||||
private String room_code;
|
||||
private String popularity;
|
||||
private String label_name;
|
||||
private String label_icon;
|
||||
private String owner_picture;
|
||||
private String owner_nickname;
|
||||
private int locked;
|
||||
|
||||
private String label_id;
|
||||
|
||||
public String getLabel_id() {
|
||||
return label_id;
|
||||
}
|
||||
|
||||
public void setLabel_id(String label_id) {
|
||||
this.label_id = label_id;
|
||||
}
|
||||
private String cover_picture;
|
||||
|
||||
public String getRoomPicture() {
|
||||
if (!TextUtils.isEmpty(cover_picture)) {
|
||||
return cover_picture;
|
||||
}
|
||||
return owner_picture;
|
||||
}
|
||||
|
||||
public String getCover_picture() {
|
||||
return cover_picture;
|
||||
}
|
||||
|
||||
public void setCover_picture(String cover_picture) {
|
||||
this.cover_picture = cover_picture;
|
||||
}
|
||||
|
||||
public int getLocked() {
|
||||
return locked;
|
||||
}
|
||||
|
||||
public void setLocked(int locked) {
|
||||
this.locked = locked;
|
||||
}
|
||||
|
||||
public String getRoom_id() {
|
||||
return room_id;
|
||||
}
|
||||
|
||||
public void setRoom_id(String room_id) {
|
||||
this.room_id = room_id;
|
||||
}
|
||||
|
||||
public String getRoom_name() {
|
||||
return room_name;
|
||||
}
|
||||
|
||||
public void setRoom_name(String room_name) {
|
||||
this.room_name = room_name;
|
||||
}
|
||||
|
||||
public String getRoom_code() {
|
||||
return room_code;
|
||||
}
|
||||
|
||||
public void setRoom_code(String room_code) {
|
||||
this.room_code = room_code;
|
||||
}
|
||||
|
||||
public String getPopularity() {
|
||||
return popularity;
|
||||
}
|
||||
|
||||
public void setPopularity(String popularity) {
|
||||
this.popularity = popularity;
|
||||
}
|
||||
|
||||
public String getLabel_name() {
|
||||
return label_name;
|
||||
}
|
||||
|
||||
public void setLabel_name(String label_name) {
|
||||
this.label_name = label_name;
|
||||
}
|
||||
|
||||
public String getLabel_icon() {
|
||||
return label_icon;
|
||||
}
|
||||
|
||||
public void setLabel_icon(String label_icon) {
|
||||
this.label_icon = label_icon;
|
||||
}
|
||||
|
||||
public String getOwner_picture() {
|
||||
return owner_picture;
|
||||
}
|
||||
|
||||
public void setOwner_picture(String owner_picture) {
|
||||
this.owner_picture = owner_picture;
|
||||
}
|
||||
|
||||
public String getOwner_nickname() {
|
||||
return owner_nickname;
|
||||
}
|
||||
|
||||
public void setOwner_nickname(String owner_nickname) {
|
||||
this.owner_nickname = owner_nickname;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.qxcm.moduleutil.bean;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class BlackUserBean {
|
||||
private String userName;
|
||||
private String userId;
|
||||
private String userAvatar;
|
||||
private int type;//0:关注;1:黑名单;2:粉丝
|
||||
private int status;//0:未关注 1:已关注
|
||||
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.qxcm.moduleutil.bean;
|
||||
|
||||
public class CheckTxtResp {
|
||||
|
||||
|
||||
private int result;
|
||||
|
||||
public int getResult() {
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setResult(int result) {
|
||||
this.result = result;
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,9 @@ package com.qxcm.moduleutil.bean;
|
||||
|
||||
import com.qxcm.moduleutil.widget.picker.PickerView;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class DateBean implements PickerView.PickerItem {
|
||||
|
||||
private String text;
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.qxcm.moduleutil.bean;
|
||||
|
||||
public class GiftBean {
|
||||
|
||||
private String number;
|
||||
private String gift_id;
|
||||
private String name;
|
||||
private String picture;
|
||||
private String price;
|
||||
|
||||
|
||||
public String getNumber() {
|
||||
return number;
|
||||
}
|
||||
|
||||
public void setNumber(String number) {
|
||||
this.number = number;
|
||||
}
|
||||
|
||||
public String getGift_id() {
|
||||
return gift_id;
|
||||
}
|
||||
|
||||
public void setGift_id(String gift_id) {
|
||||
this.gift_id = gift_id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getPicture() {
|
||||
return picture;
|
||||
}
|
||||
|
||||
public void setPicture(String picture) {
|
||||
this.picture = picture;
|
||||
}
|
||||
|
||||
public String getPrice() {
|
||||
return price;
|
||||
}
|
||||
|
||||
public void setPrice(String price) {
|
||||
this.price = price;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.qxcm.moduleutil.bean;
|
||||
|
||||
|
||||
import com.example.moduletablayout.listener.CustomTabEntity;
|
||||
|
||||
/**
|
||||
* 项目名称 qipao-android
|
||||
* 包名:com.qpyy.module.me.bean
|
||||
* 创建人 黄强
|
||||
* 创建时间 2020/12/1 17:40
|
||||
* 描述 describe
|
||||
*/
|
||||
public class HomePageTabBean implements CustomTabEntity {
|
||||
public HomePageTabBean(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
private String title;
|
||||
|
||||
@Override
|
||||
public String getTabTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTabSelectedIcon() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTabUnselectedIcon() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.qxcm.moduleutil.bean;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class HomeRoomInfo {
|
||||
private String room_name;
|
||||
private String cover_picture;
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
package com.qxcm.moduleutil.bean;
|
||||
|
||||
import android.text.TextUtils;
|
||||
|
||||
public class ManageRoomResp {
|
||||
|
||||
private String id;
|
||||
private String room_id;
|
||||
private String room_name;
|
||||
private String room_code;
|
||||
private String popularity;
|
||||
private String label_name;
|
||||
private String label_icon;
|
||||
private String owner_picture;
|
||||
private String owner_nickname;
|
||||
private int sys_type_id;
|
||||
private int locked;
|
||||
private String label_id;
|
||||
|
||||
public String getLabel_id() {
|
||||
return label_id;
|
||||
}
|
||||
|
||||
public void setLabel_id(String label_id) {
|
||||
this.label_id = label_id;
|
||||
}
|
||||
|
||||
private String cover_picture;
|
||||
|
||||
public String getRoomPicture() {
|
||||
if (!TextUtils.isEmpty(cover_picture)) {
|
||||
return cover_picture;
|
||||
}
|
||||
return owner_picture;
|
||||
}
|
||||
|
||||
public String getCover_picture() {
|
||||
return cover_picture;
|
||||
}
|
||||
|
||||
public void setCover_picture(String cover_picture) {
|
||||
this.cover_picture = cover_picture;
|
||||
}
|
||||
|
||||
public int getLocked() {
|
||||
return locked;
|
||||
}
|
||||
|
||||
public void setLocked(int locked) {
|
||||
this.locked = locked;
|
||||
}
|
||||
|
||||
public int getSys_type_id() {
|
||||
return sys_type_id;
|
||||
}
|
||||
|
||||
public void setSys_type_id(int sys_type_id) {
|
||||
this.sys_type_id = sys_type_id;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getRoom_id() {
|
||||
return room_id;
|
||||
}
|
||||
|
||||
public void setRoom_id(String room_id) {
|
||||
this.room_id = room_id;
|
||||
}
|
||||
|
||||
public String getRoom_name() {
|
||||
return room_name;
|
||||
}
|
||||
|
||||
public void setRoom_name(String room_name) {
|
||||
this.room_name = room_name;
|
||||
}
|
||||
|
||||
public String getRoom_code() {
|
||||
return room_code;
|
||||
}
|
||||
|
||||
public void setRoom_code(String room_code) {
|
||||
this.room_code = room_code;
|
||||
}
|
||||
|
||||
public String getPopularity() {
|
||||
return popularity;
|
||||
}
|
||||
|
||||
public void setPopularity(String popularity) {
|
||||
this.popularity = popularity;
|
||||
}
|
||||
|
||||
public String getLabel_name() {
|
||||
return label_name;
|
||||
}
|
||||
|
||||
public void setLabel_name(String label_name) {
|
||||
this.label_name = label_name;
|
||||
}
|
||||
|
||||
public String getLabel_icon() {
|
||||
return label_icon;
|
||||
}
|
||||
|
||||
public void setLabel_icon(String label_icon) {
|
||||
this.label_icon = label_icon;
|
||||
}
|
||||
|
||||
public String getOwner_picture() {
|
||||
return owner_picture;
|
||||
}
|
||||
|
||||
public void setOwner_picture(String owner_picture) {
|
||||
this.owner_picture = owner_picture;
|
||||
}
|
||||
|
||||
public String getOwner_nickname() {
|
||||
return owner_nickname;
|
||||
}
|
||||
|
||||
public void setOwner_nickname(String owner_nickname) {
|
||||
this.owner_nickname = owner_nickname;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.qxcm.moduleutil.bean;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class MyBagBean {
|
||||
private String myBagTitle;
|
||||
private String myBagType;
|
||||
|
||||
public MyBagBean(String myBagTitle, String myBagType) {
|
||||
this.myBagTitle = myBagTitle;
|
||||
this.myBagType = myBagType;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.qxcm.moduleutil.bean;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class MyBagDataBean {
|
||||
private String title;
|
||||
private String giftName;
|
||||
private String time;
|
||||
private String base_image;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
package com.qxcm.moduleutil.bean;
|
||||
|
||||
import android.text.TextUtils;
|
||||
|
||||
public class MyFootResp {
|
||||
|
||||
private String room_name;
|
||||
private String room_code;
|
||||
private String popularity;
|
||||
private String label_name;
|
||||
private String label_icon;
|
||||
private String owner_picture;
|
||||
private String owner_nickname;
|
||||
private String room_id;
|
||||
private int locked;
|
||||
|
||||
private String cover_picture;
|
||||
private String label_id;
|
||||
|
||||
public String getLabel_id() {
|
||||
return label_id;
|
||||
}
|
||||
|
||||
public void setLabel_id(String label_id) {
|
||||
this.label_id = label_id;
|
||||
}
|
||||
|
||||
public String getRoomPicture() {
|
||||
if (!TextUtils.isEmpty(cover_picture)) {
|
||||
return cover_picture;
|
||||
}
|
||||
return owner_picture;
|
||||
}
|
||||
|
||||
public String getCover_picture() {
|
||||
return cover_picture;
|
||||
}
|
||||
|
||||
public void setCover_picture(String cover_picture) {
|
||||
this.cover_picture = cover_picture;
|
||||
}
|
||||
|
||||
public int getLocked() {
|
||||
return locked;
|
||||
}
|
||||
|
||||
public void setLocked(int locked) {
|
||||
this.locked = locked;
|
||||
}
|
||||
|
||||
public String getRoom_id() {
|
||||
return room_id;
|
||||
}
|
||||
|
||||
public void setRoom_id(String room_id) {
|
||||
this.room_id = room_id;
|
||||
}
|
||||
|
||||
public String getRoom_name() {
|
||||
return room_name;
|
||||
}
|
||||
|
||||
public void setRoom_name(String room_name) {
|
||||
this.room_name = room_name;
|
||||
}
|
||||
|
||||
public String getRoom_code() {
|
||||
return room_code;
|
||||
}
|
||||
|
||||
public void setRoom_code(String room_code) {
|
||||
this.room_code = room_code;
|
||||
}
|
||||
|
||||
public String getPopularity() {
|
||||
return popularity;
|
||||
}
|
||||
|
||||
public void setPopularity(String popularity) {
|
||||
this.popularity = popularity;
|
||||
}
|
||||
|
||||
public String getLabel_name() {
|
||||
return label_name;
|
||||
}
|
||||
|
||||
public void setLabel_name(String label_name) {
|
||||
this.label_name = label_name;
|
||||
}
|
||||
|
||||
public String getLabel_icon() {
|
||||
return label_icon;
|
||||
}
|
||||
|
||||
public void setLabel_icon(String label_icon) {
|
||||
this.label_icon = label_icon;
|
||||
}
|
||||
|
||||
public String getOwner_picture() {
|
||||
return owner_picture;
|
||||
}
|
||||
|
||||
public void setOwner_picture(String owner_picture) {
|
||||
this.owner_picture = owner_picture;
|
||||
}
|
||||
|
||||
public String getOwner_nickname() {
|
||||
return owner_nickname;
|
||||
}
|
||||
|
||||
public void setOwner_nickname(String owner_nickname) {
|
||||
this.owner_nickname = owner_nickname;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
package com.qxcm.moduleutil.bean;
|
||||
|
||||
public class MyRoomBean {
|
||||
|
||||
/*
|
||||
"room_id": "10184180",
|
||||
"cover_picture": "httpss://osschumeng.oss-cn-beijing.aliyuncs.com/img/e86e2efe3f4412561e9c30326162d946.jpg",
|
||||
"room_name": "钊的房间",
|
||||
"online_num": 0,
|
||||
"label_id": "23",
|
||||
"label_name": "聊天",
|
||||
"label_icon": "",
|
||||
"favorite_count": "0",
|
||||
"come_count": "161",
|
||||
"today_income": 0
|
||||
*/
|
||||
|
||||
private String room_name; //房间名称
|
||||
private int room_id; //房间ID
|
||||
private String label_id; //房间类型
|
||||
private String label_name; //房间类型名称
|
||||
private String cover_picture; //房间图片
|
||||
private String label_icon; //房间类型图标
|
||||
private String online_num; //房间在线人数
|
||||
private String favorite_count; //房间收藏数
|
||||
private String come_count; //房间进入数
|
||||
private Double today_income; //今日收益;
|
||||
private int earnings_ratio;//房间收益比例
|
||||
|
||||
public int getEarnings_ratio() {
|
||||
return earnings_ratio;
|
||||
}
|
||||
|
||||
public void setEarnings_ratio(int earnings_ratio) {
|
||||
this.earnings_ratio = earnings_ratio;
|
||||
}
|
||||
|
||||
public String getRoom_name() {
|
||||
return room_name;
|
||||
}
|
||||
|
||||
public void setRoom_name(String room_name) {
|
||||
this.room_name = room_name;
|
||||
}
|
||||
|
||||
public int getRoom_id() {
|
||||
return room_id;
|
||||
}
|
||||
|
||||
public void setRoom_id(int room_id) {
|
||||
this.room_id = room_id;
|
||||
}
|
||||
|
||||
public String getLabel_id() {
|
||||
return label_id;
|
||||
}
|
||||
|
||||
public void setLabel_id(String label_id) {
|
||||
this.label_id = label_id;
|
||||
}
|
||||
|
||||
public String getLabel_name() {
|
||||
return label_name;
|
||||
}
|
||||
|
||||
public void setLabel_name(String label_name) {
|
||||
this.label_name = label_name;
|
||||
}
|
||||
|
||||
public String getCover_picture() {
|
||||
return cover_picture;
|
||||
}
|
||||
|
||||
public void setCover_picture(String cover_picture) {
|
||||
this.cover_picture = cover_picture;
|
||||
}
|
||||
|
||||
public String getLabel_icon() {
|
||||
return label_icon;
|
||||
}
|
||||
|
||||
public void setLabel_icon(String label_icon) {
|
||||
this.label_icon = label_icon;
|
||||
}
|
||||
|
||||
public String getOnline_num() {
|
||||
return online_num;
|
||||
}
|
||||
|
||||
public void setOnline_num(String online_num) {
|
||||
this.online_num = online_num;
|
||||
}
|
||||
|
||||
public String getFavorite_count() {
|
||||
return favorite_count;
|
||||
}
|
||||
|
||||
public void setFavorite_count(String favorite_count) {
|
||||
this.favorite_count = favorite_count;
|
||||
}
|
||||
|
||||
public String getCome_count() {
|
||||
return come_count;
|
||||
}
|
||||
|
||||
public void setCome_count(String come_count) {
|
||||
this.come_count = come_count;
|
||||
}
|
||||
|
||||
public Double getToday_income() {
|
||||
return today_income;
|
||||
}
|
||||
|
||||
public void setToday_income(Double today_income) {
|
||||
this.today_income = today_income;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
package com.qxcm.moduleutil.bean;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
public class PhotoWallResp implements Serializable {
|
||||
|
||||
public String getVedio() {
|
||||
return vedio;
|
||||
}
|
||||
|
||||
public void setVedio(String vedio) {
|
||||
this.vedio = vedio;
|
||||
}
|
||||
|
||||
public String getAvatar() {
|
||||
return avatar;
|
||||
}
|
||||
|
||||
public void setAvatar(String avatar) {
|
||||
this.avatar = avatar;
|
||||
}
|
||||
|
||||
public String getVedio_cover() {
|
||||
return vedio_cover;
|
||||
}
|
||||
|
||||
public void setVedio_cover(String vedio_cover) {
|
||||
this.vedio_cover = vedio_cover;
|
||||
}
|
||||
|
||||
public List<GiftResp> getList() {
|
||||
return list;
|
||||
}
|
||||
|
||||
public void setList(List<GiftResp> list) {
|
||||
this.list = list;
|
||||
}
|
||||
|
||||
private String vedio;
|
||||
private String avatar;
|
||||
private String vedio_cover;
|
||||
private List<GiftResp> list;
|
||||
|
||||
public static class GiftResp implements Serializable {
|
||||
public GiftResp(String id, String url, int width, int height) {
|
||||
this.id = id;
|
||||
this.url = url;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public int getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
public void setWidth(int width) {
|
||||
this.width = width;
|
||||
}
|
||||
|
||||
public int getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
public void setHeight(int height) {
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
private String id;
|
||||
private String url;
|
||||
private int width;
|
||||
private int height;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.qxcm.moduleutil.bean;
|
||||
|
||||
import com.chad.library.adapter.base.entity.MultiItemEntity;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* ProjectName: isolated-island
|
||||
* Package: com.yutang.xqipao.data
|
||||
* Description: java类作用描述
|
||||
* Author: 姚闻达
|
||||
* CreateDate: 2020/11/1 10:14
|
||||
* UpdateUser: 更新者
|
||||
* UpdateDate: 2020/11/1 10:14
|
||||
* UpdateRemark: 更新说明
|
||||
* Version: 1.0
|
||||
*/
|
||||
@Data
|
||||
public class RechargeBean implements MultiItemEntity {
|
||||
private int itemViewType;
|
||||
private String goldNum;
|
||||
private String money;
|
||||
private boolean isCustom; // 是否为自定义项
|
||||
|
||||
@Override
|
||||
public int getItemType() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,212 @@
|
||||
package com.qxcm.moduleutil.bean;
|
||||
|
||||
import com.chad.library.adapter.base.entity.MultiItemEntity;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class RoomDetails {
|
||||
/**
|
||||
* list : [{"total_price":416,"total_earning":83.2,"time":"2025-03-07","list":[{"user_head_picture":"https://osschumeng.oss-cn-beijing.aliyuncs.com/admin_images/67932634b1555.png","user_nickname":"用户10137552","get_user_nickname":"用户10137840","gift_name":"退烧药","gift_number":"3","total_price":"297.00","time":"2025-03-07","earning":59.4},{"user_head_picture":"https://osschumeng.oss-cn-beijing.aliyuncs.com/admin_images/67932634b1555.png","user_nickname":"用户10137552","get_user_nickname":"用户10137840","gift_name":"征婚帖","gift_number":"1","total_price":"19.00","time":"2025-03-07","earning":3.8},{"user_head_picture":"https://osschumeng.oss-cn-beijing.aliyuncs.com/admin_images/67932634b1555.png","user_nickname":"用户10137552","get_user_nickname":"用户10137840","gift_name":"香槟","gift_number":"1","total_price":"99.00","time":"2025-03-07","earning":19.8},{"user_head_picture":"https://osschumeng.oss-cn-beijing.aliyuncs.com/admin_images/67932634b1555.png","user_nickname":"钊","get_user_nickname":"用户10137840","gift_name":"小心心","gift_number":"1","total_price":"1.00","time":"2025-03-07","earning":0.2}]}]
|
||||
* total_amount : 416
|
||||
* total_earning : 83.2
|
||||
* room_name : 钊的房间
|
||||
* page : <div> </div>
|
||||
*/
|
||||
|
||||
private double total_amount;
|
||||
private double total_earning;
|
||||
private String room_name;
|
||||
private String page;
|
||||
private List<RoomDetailsList> list;
|
||||
|
||||
public double getTotal_amount() {
|
||||
return total_amount;
|
||||
}
|
||||
|
||||
public void setTotal_amount(double total_amount) {
|
||||
this.total_amount = total_amount;
|
||||
}
|
||||
|
||||
public double getTotal_earning() {
|
||||
return total_earning;
|
||||
}
|
||||
|
||||
public void setTotal_earning(double total_earning) {
|
||||
this.total_earning = total_earning;
|
||||
}
|
||||
|
||||
public String getRoom_name() {
|
||||
return room_name;
|
||||
}
|
||||
|
||||
public void setRoom_name(String room_name) {
|
||||
this.room_name = room_name;
|
||||
}
|
||||
|
||||
public String getPage() {
|
||||
return page;
|
||||
}
|
||||
|
||||
public void setPage(String page) {
|
||||
this.page = page;
|
||||
}
|
||||
|
||||
public List<RoomDetailsList> getList() {
|
||||
return list;
|
||||
}
|
||||
|
||||
public void setList(List<RoomDetailsList> list) {
|
||||
this.list = list;
|
||||
}
|
||||
|
||||
public static class RoomDetailsList implements MultiItemEntity {
|
||||
/**
|
||||
* total_price : 416
|
||||
* total_earning : 83.2
|
||||
* time : 2025-03-07
|
||||
* list : [{"user_head_picture":"https://osschumeng.oss-cn-beijing.aliyuncs.com/admin_images/67932634b1555.png","user_nickname":"用户10137552","get_user_nickname":"用户10137840","gift_name":"退烧药","gift_number":"3","total_price":"297.00","time":"2025-03-07","earning":59.4},{"user_head_picture":"https://osschumeng.oss-cn-beijing.aliyuncs.com/admin_images/67932634b1555.png","user_nickname":"用户10137552","get_user_nickname":"用户10137840","gift_name":"征婚帖","gift_number":"1","total_price":"19.00","time":"2025-03-07","earning":3.8},{"user_head_picture":"https://osschumeng.oss-cn-beijing.aliyuncs.com/admin_images/67932634b1555.png","user_nickname":"用户10137552","get_user_nickname":"用户10137840","gift_name":"香槟","gift_number":"1","total_price":"99.00","time":"2025-03-07","earning":19.8},{"user_head_picture":"https://osschumeng.oss-cn-beijing.aliyuncs.com/admin_images/67932634b1555.png","user_nickname":"钊","get_user_nickname":"用户10137840","gift_name":"小心心","gift_number":"1","total_price":"1.00","time":"2025-03-07","earning":0.2}]
|
||||
*/
|
||||
private int itemViewType = 1;
|
||||
private double total_price;
|
||||
private double total_earning;
|
||||
private String time;
|
||||
private List<RoomDetail> list;
|
||||
|
||||
public double getTotal_price() {
|
||||
return total_price;
|
||||
}
|
||||
|
||||
public void setTotal_price(double total_price) {
|
||||
this.total_price = total_price;
|
||||
}
|
||||
|
||||
public double getTotal_earning() {
|
||||
return total_earning;
|
||||
}
|
||||
|
||||
public void setTotal_earning(double total_earning) {
|
||||
this.total_earning = total_earning;
|
||||
}
|
||||
|
||||
public String getTime() {
|
||||
return time;
|
||||
}
|
||||
|
||||
public void setTime(String time) {
|
||||
this.time = time;
|
||||
}
|
||||
|
||||
public List<RoomDetail> getList() {
|
||||
return list;
|
||||
}
|
||||
|
||||
public void setList(List<RoomDetail> list) {
|
||||
this.list = list;
|
||||
}
|
||||
|
||||
public void setItemViewType(int itemViewType) {
|
||||
this.itemViewType = itemViewType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemType() {
|
||||
return itemViewType;
|
||||
}
|
||||
|
||||
public static class RoomDetail implements MultiItemEntity{
|
||||
/**
|
||||
* user_head_picture : https://osschumeng.oss-cn-beijing.aliyuncs.com/admin_images/67932634b1555.png
|
||||
* user_nickname : 用户10137552
|
||||
* get_user_nickname : 用户10137840
|
||||
* gift_name : 退烧药
|
||||
* gift_number : 3
|
||||
* total_price : 297.00
|
||||
* time : 2025-03-07
|
||||
* earning : 59.4
|
||||
*/
|
||||
private int itemViewType = 2;
|
||||
private String user_head_picture;
|
||||
private String user_nickname;
|
||||
private String get_user_nickname;
|
||||
private String gift_name;
|
||||
private int gift_number;
|
||||
private double total_price;
|
||||
private String time;
|
||||
private double earning;
|
||||
|
||||
|
||||
public String getUser_head_picture() {
|
||||
return user_head_picture;
|
||||
}
|
||||
|
||||
public void setUser_head_picture(String user_head_picture) {
|
||||
this.user_head_picture = user_head_picture;
|
||||
}
|
||||
|
||||
public String getUser_nickname() {
|
||||
return user_nickname;
|
||||
}
|
||||
|
||||
public void setUser_nickname(String user_nickname) {
|
||||
this.user_nickname = user_nickname;
|
||||
}
|
||||
|
||||
public String getGet_user_nickname() {
|
||||
return get_user_nickname;
|
||||
}
|
||||
|
||||
public void setGet_user_nickname(String get_user_nickname) {
|
||||
this.get_user_nickname = get_user_nickname;
|
||||
}
|
||||
|
||||
public String getGift_name() {
|
||||
return gift_name;
|
||||
}
|
||||
|
||||
public void setGift_name(String gift_name) {
|
||||
this.gift_name = gift_name;
|
||||
}
|
||||
|
||||
public int getGift_number() {
|
||||
return gift_number;
|
||||
}
|
||||
|
||||
public void setGift_number(int gift_number) {
|
||||
this.gift_number = gift_number;
|
||||
}
|
||||
|
||||
public double getTotal_price() {
|
||||
return total_price;
|
||||
}
|
||||
|
||||
public void setTotal_price(double total_price) {
|
||||
this.total_price = total_price;
|
||||
}
|
||||
|
||||
public String getTime() {
|
||||
return time;
|
||||
}
|
||||
|
||||
public void setTime(String time) {
|
||||
this.time = time;
|
||||
}
|
||||
|
||||
public double getEarning() {
|
||||
return earning;
|
||||
}
|
||||
|
||||
public void setEarning(double earning) {
|
||||
this.earning = earning;
|
||||
}
|
||||
|
||||
public void setItemViewType(int itemViewType) {
|
||||
this.itemViewType = itemViewType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemType() {
|
||||
return itemViewType;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.qxcm.moduleutil.bean;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class RoomSubsidy {
|
||||
private String explain;//api/about/showAbout&id=39",//补贴说明 (这是个页面,需要前端拼个域名)
|
||||
|
||||
private LastWeek lastweek;
|
||||
private ThisWeek thisweek;
|
||||
|
||||
|
||||
@Data
|
||||
public static class LastWeek {
|
||||
private double total_transaction;//累计流水
|
||||
private double subsidy_amount; //获得补贴
|
||||
private int status;//发放状态 0未发放,1已发放
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class ThisWeek {
|
||||
private double total_transaction;
|
||||
private double subsidy_amount;
|
||||
private int status;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.qxcm.moduleutil.bean;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class RoomSubsidyDetails {
|
||||
private String room_id;//房间id:214,
|
||||
private String start_time;//开始时间:"2025-04-07",
|
||||
private String end_time;//结束时间:"2025-04-13",
|
||||
private String total_transaction;//: 0, //累计流水
|
||||
private String subsidy_amount;// 0, //获得补贴
|
||||
private String status;//: 0 //0未发放,1已发放
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.qxcm.moduleutil.bean;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class SocietyBean {
|
||||
private int id;
|
||||
private String name;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.qxcm.moduleutil.bean;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class UserBean implements Serializable {
|
||||
|
||||
private int user_id;
|
||||
private int user_code;
|
||||
private String avatar;
|
||||
private String nickname;
|
||||
private String token;
|
||||
private String tencent_im;
|
||||
private String mobile;
|
||||
private int sex;
|
||||
|
||||
private List<MultiUserBean> multi_user;
|
||||
|
||||
@Data
|
||||
private static class MultiUserBean{
|
||||
private String id;
|
||||
private String user_code;
|
||||
private String avatar;
|
||||
private String nickname;
|
||||
private String sex;
|
||||
private String mobile;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.qxcm.moduleutil.bean;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class UserHomeResp implements Serializable {
|
||||
|
||||
private String user_id;
|
||||
private String user_code;
|
||||
private String head_picture;
|
||||
private String nickname;
|
||||
private String sex;
|
||||
private String signature;
|
||||
private String birthday;
|
||||
private String constellation;
|
||||
private String profession;
|
||||
private String emchat_username;
|
||||
private String nobility_icon;
|
||||
private String nobility_image;
|
||||
private String rank_icon;
|
||||
private String intro_voice;
|
||||
private String intro_voice_time;
|
||||
private String follow;
|
||||
private String follow_count;
|
||||
private String fans_count;
|
||||
private String age;
|
||||
private String city;
|
||||
private String is_online;
|
||||
private String only_friend;
|
||||
private String good_number;
|
||||
private String id_color;
|
||||
private String vedio;
|
||||
private PhotoWallResp user_photo;
|
||||
private String picture;
|
||||
private String charm_icon;
|
||||
private String charm_exp;
|
||||
private String user_title;
|
||||
|
||||
private String room_id_current;//用户当前所在房间ID
|
||||
private HomeRoomInfo room_info;
|
||||
|
||||
private String room_id_owner;//用户自己的房间id 0表示没有创建房间": 0,
|
||||
private String room_name_owner;//用户自己的房间名称
|
||||
|
||||
private int auth_status; // 1:已实名认证 0:未实名认证
|
||||
private SocietyBean society; // 公会信息
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.qxcm.moduleutil.bean;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class UserImgList {
|
||||
private String id;
|
||||
private String url;
|
||||
|
||||
public UserImgList(String id, String url) {
|
||||
this.id = id;
|
||||
this.url = url;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.qxcm.moduleutil.bean;
|
||||
|
||||
import com.stx.xhb.xbanner.entity.SimpleBannerInfo;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public class XBannerData extends SimpleBannerInfo implements Serializable {
|
||||
|
||||
|
||||
private int type;
|
||||
private String url;
|
||||
private String vedioCover;
|
||||
|
||||
public XBannerData(int type, String url, String vedioCover) {
|
||||
this.type = type;
|
||||
this.url = url;
|
||||
this.vedioCover = vedioCover;
|
||||
}
|
||||
|
||||
public String getVedioCover() {
|
||||
return vedioCover;
|
||||
}
|
||||
|
||||
public void setVedioCover(String vedioCover) {
|
||||
this.vedioCover = vedioCover;
|
||||
}
|
||||
|
||||
public int getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(int type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getXBannerUrl() {
|
||||
return url;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.qxcm.moduleutil.bean.details;
|
||||
|
||||
import com.chad.library.adapter.base.entity.MultiItemEntity;
|
||||
|
||||
public class BaseMultiItemEntity implements MultiItemEntity {
|
||||
public static final int TYPE_A = 1;
|
||||
public static final int TYPE_B = 2;
|
||||
|
||||
private int itemViewType;
|
||||
|
||||
public BaseMultiItemEntity(int itemViewType) {
|
||||
this.itemViewType = itemViewType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemType() {
|
||||
return itemViewType;
|
||||
}
|
||||
}
|
||||
|
||||
// 定义 TypeA 数据模型类
|
||||
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.qxcm.moduleutil.bean.details;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class DataModel {
|
||||
private RoomDeatailList typeA;
|
||||
private List<RoomDeatil> list;
|
||||
|
||||
// 添加相应的 getter 和 setter 方法
|
||||
public RoomDeatailList getTypeA() {
|
||||
return typeA;
|
||||
}
|
||||
|
||||
public void setTypeA(RoomDeatailList typeA) {
|
||||
this.typeA = typeA;
|
||||
}
|
||||
|
||||
public List<RoomDeatil> getList() {
|
||||
return list;
|
||||
}
|
||||
|
||||
public void setList(List<RoomDeatil> list) {
|
||||
this.list = list;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.qxcm.moduleutil.bean.details;
|
||||
|
||||
public class RoomDeatailList extends BaseMultiItemEntity {
|
||||
private double total_price;
|
||||
private double total_earning;
|
||||
private String time;
|
||||
public RoomDeatailList() {
|
||||
super(BaseMultiItemEntity.TYPE_A);
|
||||
}
|
||||
|
||||
public double getTotal_price() {
|
||||
return total_price;
|
||||
}
|
||||
|
||||
public void setTotal_price(double total_price) {
|
||||
this.total_price = total_price;
|
||||
}
|
||||
|
||||
public double getTotal_earning() {
|
||||
return total_earning;
|
||||
}
|
||||
|
||||
public void setTotal_earning(double total_earning) {
|
||||
this.total_earning = total_earning;
|
||||
}
|
||||
|
||||
public String getTime() {
|
||||
return time;
|
||||
}
|
||||
|
||||
public void setTime(String time) {
|
||||
this.time = time;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
package com.qxcm.moduleutil.bean.details;
|
||||
|
||||
public class RoomDeatil extends BaseMultiItemEntity {
|
||||
private String user_head_picture;
|
||||
private String user_nickname;
|
||||
private String get_user_nickname;
|
||||
private String gift_name;
|
||||
private int gift_number;
|
||||
private double total_price;
|
||||
private String time;
|
||||
private double earning;
|
||||
|
||||
public RoomDeatil() {
|
||||
super(BaseMultiItemEntity.TYPE_B);
|
||||
}
|
||||
|
||||
// 添加相应的 getter 和 setter 方法
|
||||
public String getUser_head_picture() {
|
||||
return user_head_picture;
|
||||
}
|
||||
|
||||
public void setUser_head_picture(String user_head_picture) {
|
||||
this.user_head_picture = user_head_picture;
|
||||
}
|
||||
|
||||
public String getUser_nickname() {
|
||||
return user_nickname;
|
||||
}
|
||||
|
||||
public void setUser_nickname(String user_nickname) {
|
||||
this.user_nickname = user_nickname;
|
||||
}
|
||||
|
||||
public String getGet_user_nickname() {
|
||||
return get_user_nickname;
|
||||
}
|
||||
|
||||
public void setGet_user_nickname(String get_user_nickname) {
|
||||
this.get_user_nickname = get_user_nickname;
|
||||
}
|
||||
|
||||
public String getGift_name() {
|
||||
return gift_name;
|
||||
}
|
||||
|
||||
public void setGift_name(String gift_name) {
|
||||
this.gift_name = gift_name;
|
||||
}
|
||||
|
||||
public int getGift_number() {
|
||||
return gift_number;
|
||||
}
|
||||
|
||||
public void setGift_number(int gift_number) {
|
||||
this.gift_number = gift_number;
|
||||
}
|
||||
|
||||
public double getTotal_price() {
|
||||
return total_price;
|
||||
}
|
||||
|
||||
public void setTotal_price(double total_price) {
|
||||
this.total_price = total_price;
|
||||
}
|
||||
|
||||
public String getTime() {
|
||||
return time;
|
||||
}
|
||||
|
||||
public void setTime(String time) {
|
||||
this.time = time;
|
||||
}
|
||||
|
||||
public double getEarning() {
|
||||
return earning;
|
||||
}
|
||||
|
||||
public void setEarning(double earning) {
|
||||
this.earning = earning;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.qxcm.moduleutil.bean.zhuangb;
|
||||
|
||||
import lombok.Data;
|
||||
/**
|
||||
*@author qx
|
||||
*@data 2025/5/16
|
||||
*@description: 装扮数据
|
||||
*/
|
||||
@Data
|
||||
public class ZhuangBanShangChengBean {
|
||||
|
||||
private int did = 0;
|
||||
private String title = "";
|
||||
private String type = "";
|
||||
private String integral = "";
|
||||
private int period = 0;
|
||||
private String base_image = "";
|
||||
private boolean is_select = false;
|
||||
|
||||
public boolean isIs_select() {
|
||||
return is_select;
|
||||
}
|
||||
|
||||
public void setIs_select(boolean is_select) {
|
||||
this.is_select = is_select;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,240 @@
|
||||
package com.qxcm.moduleutil.dialog;
|
||||
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.DatePicker;
|
||||
|
||||
|
||||
import com.qxcm.moduleutil.R;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
/**
|
||||
* A simple dialog containing an {@link DatePicker}.
|
||||
*
|
||||
* <p>
|
||||
* See the <a href="{@docRoot}guide/topics/ui/controls/pickers.html">Pickers</a>
|
||||
* guide.
|
||||
* </p>
|
||||
*/
|
||||
public class DoubleDatePickerDialog extends AlertDialog implements DialogInterface.OnClickListener, DatePicker.OnDateChangedListener {
|
||||
|
||||
private static final String START_YEAR = "start_year";
|
||||
private static final String END_YEAR = "end_year";
|
||||
private static final String START_MONTH = "start_month";
|
||||
private static final String END_MONTH = "end_month";
|
||||
private static final String START_DAY = "start_day";
|
||||
private static final String END_DAY = "end_day";
|
||||
|
||||
private final DatePicker mDatePicker_start;
|
||||
private final DatePicker mDatePicker_end;
|
||||
private final OnDateSetListener mCallBack;
|
||||
|
||||
/**
|
||||
* The callback used to indicate the user is done filling in the date.
|
||||
*/
|
||||
public interface OnDateSetListener {
|
||||
|
||||
void onDateSet(DatePicker startDatePicker, int startYear, int startMonthOfYear, int startDayOfMonth,
|
||||
DatePicker endDatePicker, int endYear, int endMonthOfYear, int endDayOfMonth);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param context
|
||||
* The context the dialog is to run in.
|
||||
* @param callBack
|
||||
* How the parent is notified that the date is set.
|
||||
* @param year
|
||||
* The initial year of the dialog.
|
||||
* @param monthOfYear
|
||||
* The initial month of the dialog.
|
||||
* @param dayOfMonth
|
||||
* The initial day of the dialog.
|
||||
*/
|
||||
public DoubleDatePickerDialog(Context context, OnDateSetListener callBack, int year, int monthOfYear, int dayOfMonth) {
|
||||
this(context, 0, callBack, year, monthOfYear, dayOfMonth);
|
||||
}
|
||||
|
||||
public DoubleDatePickerDialog(Context context, int theme, OnDateSetListener callBack, int year, int monthOfYear,
|
||||
int dayOfMonth) {
|
||||
this(context, 0, callBack, year, monthOfYear, dayOfMonth, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param context
|
||||
* The context the dialog is to run in.
|
||||
* @param theme
|
||||
* the theme to apply to this dialog
|
||||
* @param callBack
|
||||
* How the parent is notified that the date is set.
|
||||
* @param year
|
||||
* The initial year of the dialog.
|
||||
* @param monthOfYear
|
||||
* The initial month of the dialog.
|
||||
* @param dayOfMonth
|
||||
* The initial day of the dialog.
|
||||
*/
|
||||
public DoubleDatePickerDialog(Context context, int theme, OnDateSetListener callBack, int year, int monthOfYear,
|
||||
int dayOfMonth, boolean isDayVisible) {
|
||||
super(context, theme);
|
||||
|
||||
mCallBack = callBack;
|
||||
|
||||
Context themeContext = getContext();
|
||||
setButton(BUTTON_POSITIVE, "确 定", this);
|
||||
setButton(BUTTON_NEGATIVE, "取 消", this);
|
||||
// setButton(BUTTON_POSITIVE,
|
||||
// themeContext.getText(android.R.string.date_time_done), this);
|
||||
setIcon(0);
|
||||
|
||||
LayoutInflater inflater = (LayoutInflater) themeContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
View view = inflater.inflate(R.layout.date_picker_dialog, null);
|
||||
setView(view);
|
||||
mDatePicker_start = (DatePicker) view.findViewById(R.id.datePickerStart);
|
||||
mDatePicker_end = (DatePicker) view.findViewById(R.id.datePickerEnd);
|
||||
mDatePicker_start.init(year, monthOfYear, dayOfMonth, this);
|
||||
mDatePicker_end.init(year, monthOfYear, dayOfMonth, this);
|
||||
// updateTitle(year, monthOfYear, dayOfMonth);
|
||||
|
||||
// 如果要隐藏当前日期,则使用下面方法。
|
||||
if (!isDayVisible) {
|
||||
hidDay(mDatePicker_start);
|
||||
hidDay(mDatePicker_end);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 隐藏DatePicker中的日期显示
|
||||
*
|
||||
* @param mDatePicker
|
||||
*/
|
||||
private void hidDay(DatePicker mDatePicker) {
|
||||
Field[] datePickerfFields = mDatePicker.getClass().getDeclaredFields();
|
||||
for (Field datePickerField : datePickerfFields) {
|
||||
if ("mDaySpinner".equals(datePickerField.getName())) {
|
||||
datePickerField.setAccessible(true);
|
||||
Object dayPicker = new Object();
|
||||
try {
|
||||
dayPicker = datePickerField.get(mDatePicker);
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IllegalArgumentException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
// datePicker.getCalendarView().setVisibility(View.GONE);
|
||||
((View) dayPicker).setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
// Log.d(this.getClass().getSimpleName(), String.format("which:%d",
|
||||
// which));
|
||||
// 如果是“取 消”按钮,则返回,如果是“确 定”按钮,则往下执行
|
||||
if (which == BUTTON_POSITIVE)
|
||||
tryNotifyDateSet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDateChanged(DatePicker view, int year, int month, int day) {
|
||||
if (view.getId() == R.id.datePickerStart)
|
||||
mDatePicker_start.init(year, month, day, this);
|
||||
if (view.getId() == R.id.datePickerEnd)
|
||||
mDatePicker_end.init(year, month, day, this);
|
||||
// updateTitle(year, month, day);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得开始日期的DatePicker
|
||||
*
|
||||
* @return The calendar view.
|
||||
*/
|
||||
public DatePicker getDatePickerStart() {
|
||||
return mDatePicker_start;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得结束日期的DatePicker
|
||||
*
|
||||
* @return The calendar view.
|
||||
*/
|
||||
public DatePicker getDatePickerEnd() {
|
||||
return mDatePicker_end;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the start date.
|
||||
*
|
||||
* @param year
|
||||
* The date year.
|
||||
* @param monthOfYear
|
||||
* The date month.
|
||||
* @param dayOfMonth
|
||||
* The date day of month.
|
||||
*/
|
||||
public void updateStartDate(int year, int monthOfYear, int dayOfMonth) {
|
||||
mDatePicker_start.updateDate(year, monthOfYear, dayOfMonth);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the end date.
|
||||
*
|
||||
* @param year
|
||||
* The date year.
|
||||
* @param monthOfYear
|
||||
* The date month.
|
||||
* @param dayOfMonth
|
||||
* The date day of month.
|
||||
*/
|
||||
public void updateEndDate(int year, int monthOfYear, int dayOfMonth) {
|
||||
mDatePicker_end.updateDate(year, monthOfYear, dayOfMonth);
|
||||
}
|
||||
|
||||
private void tryNotifyDateSet() {
|
||||
if (mCallBack != null) {
|
||||
mDatePicker_start.clearFocus();
|
||||
mDatePicker_end.clearFocus();
|
||||
mCallBack.onDateSet(mDatePicker_start, mDatePicker_start.getYear(), mDatePicker_start.getMonth(),
|
||||
mDatePicker_start.getDayOfMonth(), mDatePicker_end, mDatePicker_end.getYear(),
|
||||
mDatePicker_end.getMonth(), mDatePicker_end.getDayOfMonth());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
// tryNotifyDateSet();
|
||||
super.onStop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bundle onSaveInstanceState() {
|
||||
Bundle state = super.onSaveInstanceState();
|
||||
state.putInt(START_YEAR, mDatePicker_start.getYear());
|
||||
state.putInt(START_MONTH, mDatePicker_start.getMonth());
|
||||
state.putInt(START_DAY, mDatePicker_start.getDayOfMonth());
|
||||
state.putInt(END_YEAR, mDatePicker_end.getYear());
|
||||
state.putInt(END_MONTH, mDatePicker_end.getMonth());
|
||||
state.putInt(END_DAY, mDatePicker_end.getDayOfMonth());
|
||||
return state;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRestoreInstanceState(Bundle savedInstanceState) {
|
||||
super.onRestoreInstanceState(savedInstanceState);
|
||||
int start_year = savedInstanceState.getInt(START_YEAR);
|
||||
int start_month = savedInstanceState.getInt(START_MONTH);
|
||||
int start_day = savedInstanceState.getInt(START_DAY);
|
||||
mDatePicker_start.init(start_year, start_month, start_day, this);
|
||||
|
||||
int end_year = savedInstanceState.getInt(END_YEAR);
|
||||
int end_month = savedInstanceState.getInt(END_MONTH);
|
||||
int end_day = savedInstanceState.getInt(END_DAY);
|
||||
mDatePicker_end.init(end_year, end_month, end_day, this);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.qxcm.moduleutil.http;
|
||||
|
||||
|
||||
import com.qxcm.moduleutil.utils.Sha1Util;
|
||||
import com.qxcm.moduleutil.utils.SpUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import okhttp3.Headers;
|
||||
import okhttp3.Interceptor;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
|
||||
|
||||
public class AccessTokenInterceptor implements Interceptor {
|
||||
|
||||
private final static String TAG = AccessTokenInterceptor.class.getCanonicalName();
|
||||
|
||||
private static final Map<String, String> mHeaderMap = new ConcurrentHashMap<>();
|
||||
|
||||
public static String token = "";
|
||||
|
||||
public AccessTokenInterceptor(Map<String, String> headers) {
|
||||
mHeaderMap.putAll(headers);
|
||||
}
|
||||
|
||||
|
||||
public Map<String, String> getHeaders() {
|
||||
return mHeaderMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response intercept(Chain chain) throws IOException {
|
||||
Request request = chain.request();
|
||||
mHeaderMap.put("X-Token", SpUtil.getToken());
|
||||
mHeaderMap.put("token", SpUtil.getToken());
|
||||
long timestamp = System.currentTimeMillis() / 1000;
|
||||
mHeaderMap.put("timestamp", String.valueOf(timestamp));
|
||||
mHeaderMap.put("sign", Sha1Util.shaEncode(timestamp));
|
||||
Request newRequest = request.newBuilder().headers(Headers.of(mHeaderMap)).build();
|
||||
Response response = chain.proceed(newRequest);
|
||||
return response;
|
||||
}
|
||||
}
|
||||
282
moduleUtil/src/main/java/com/qxcm/moduleutil/http/ApiServer.java
Normal file
282
moduleUtil/src/main/java/com/qxcm/moduleutil/http/ApiServer.java
Normal file
@@ -0,0 +1,282 @@
|
||||
package com.qxcm.moduleutil.http;
|
||||
|
||||
|
||||
import com.qxcm.moduleutil.bean.UserBean;
|
||||
import com.qxcm.moduleutil.widget.Constants;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import io.reactivex.Observable;
|
||||
import okhttp3.ResponseBody;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.http.Field;
|
||||
import retrofit2.http.FieldMap;
|
||||
import retrofit2.http.FormUrlEncoded;
|
||||
import retrofit2.http.GET;
|
||||
import retrofit2.http.POST;
|
||||
|
||||
public interface ApiServer {
|
||||
|
||||
|
||||
@FormUrlEncoded //请求验证码
|
||||
@POST(Constants.SEND_CODE)
|
||||
Observable<BaseModel<Object>> sendCode(@Field("mobile") String mobile,@Field("event")String event);
|
||||
|
||||
@FormUrlEncoded
|
||||
@POST(Constants.LOGIN)
|
||||
Observable<BaseModel<List<UserBean>>> login(@Field("user_login") String user_login, @Field("sms_code") String sms_code);
|
||||
|
||||
@FormUrlEncoded
|
||||
@POST(Constants.USER_LOGIN)
|
||||
Observable<BaseModel<List<UserBean>>> userLogin(@Field("user_login") String user_login, @Field("password") String password);
|
||||
|
||||
@POST(Constants.UPLOAD_NICK_NAME)
|
||||
Observable<BaseModel<String>> upUserNickname();
|
||||
|
||||
@FormUrlEncoded
|
||||
@POST(Constants.UPLOAD_USER_PIC)
|
||||
Observable<BaseModel<String>> upUserPic(@Field("sex") String sex);
|
||||
|
||||
@FormUrlEncoded
|
||||
@POST(Constants.SWITCH_ACCOUNTS)
|
||||
Observable<BaseModel<List<UserBean>>> switchAccounts(@Field("user_login") String user_id);
|
||||
|
||||
@FormUrlEncoded
|
||||
@POST(Constants.USER_UPDATE)
|
||||
Observable<BaseModel<UserBean>> userUpdate(@FieldMap Map<String, String> map);
|
||||
|
||||
@GET(Constants.AUTHORIZATION)
|
||||
Observable<BaseModel<String>> authorization();
|
||||
|
||||
@FormUrlEncoded
|
||||
@POST(Constants.CHANGE_PASSWORD)
|
||||
Call<BaseModel<String>> getPostData(@Field("new_password") String new_password,@Field("mobile") String mobile,@Field("sms_code") String code,@Field("user_id") String userId);
|
||||
|
||||
@FormUrlEncoded
|
||||
@POST(Constants.URL_LOGIN)
|
||||
Observable<BaseModel<List<UserBean>>> oauthLogin(@Field("login_token") String login_token);
|
||||
|
||||
@FormUrlEncoded
|
||||
@POST(Constants.URL_AUTH_CODE)
|
||||
Observable<BaseModel<List<UserBean>>> authCode(@Field("auth_code") String login_token);
|
||||
@FormUrlEncoded
|
||||
@POST(Constants.URL_WX_CODE)
|
||||
Observable<BaseModel<List<UserBean>>> authCode1(@Field("code") String login_token);
|
||||
|
||||
|
||||
// @FormUrlEncoded
|
||||
// @POST(Constant.URL.ADDBANK)
|
||||
// Observable<BaseModel<String>> addBank(@Field("token") String token, @Field("bank_num") String bankNum, @Field("cardholder") String cardholder, @Field("bank_type") int bankType,
|
||||
// @Field("bank_name") String bankName, @Field("mobile") String mobile, @Field("bank_zhi") String bankZhi, @Field("card_number") String cardNumber,
|
||||
// @Field("code") String code
|
||||
// );
|
||||
//
|
||||
// @FormUrlEncoded
|
||||
// @POST(Constant.URL.SEND_CODE)
|
||||
// Observable<BaseModel<String>> sendCode(@Field("token") String token, @Field("mobile") String mobile, @Field("type") int type);
|
||||
//
|
||||
// @FormUrlEncoded
|
||||
// @POST(URLConstants.CHECK_SMS_CODE)
|
||||
// Observable<BaseModel<String>> checkSmsCode(@Field("mobile") String mobile, @Field("code") String code);
|
||||
//
|
||||
// @FormUrlEncoded
|
||||
// @POST(Constant.URL.USER_BANK)
|
||||
// Observable<BaseModel<UserBankModel>> getUserBank(@Field("token") String token);
|
||||
//
|
||||
// @FormUrlEncoded
|
||||
// @POST(Constant.URL.RECHARGE)
|
||||
// Observable<BaseModel<String>> userRecharge(@Field("token") String token, @Field("money") String money, @Field("type") int type);
|
||||
// @FormUrlEncoded
|
||||
// @POST(Constant.URL.THREE_PARTY_LOGIN)
|
||||
// Observable<BaseModel<BeanPayData>> threePay(@Field("user_id") String userId, @Field("type") int type, @Field("id") String id);
|
||||
//
|
||||
// @FormUrlEncoded
|
||||
// @POST(Constant.URL.ALIPAYMENT)
|
||||
// Observable<BaseModel<String>> aliPay(@Field("token") String token, @Field("user_id") String userId, @Field("type") int type, @Field("id") String id);
|
||||
//
|
||||
// @FormUrlEncoded
|
||||
// @POST(Constant.URL.WXPAYMENT)
|
||||
// Observable<BaseModel<WxPayModel>> wxPay(@Field("token") String token, @Field("user_id") String userId, @Field("type") int type, @Field("id") String id);
|
||||
//
|
||||
//
|
||||
// @FormUrlEncoded
|
||||
// @POST(Constant.URL.EDITBANK)
|
||||
// Observable<BaseModel<String>> editBank(@Field("token") String token, @Field("cardholder") String cardholder, @Field("bank_name") String bank_name,
|
||||
// @Field("mobile") String mobile, @Field("card_number") String card_number, @Field("id") String id,
|
||||
// @Field("bank_num") String bank_num, @Field("bank_zhi") String bank_zhi, @Field("code") String code);
|
||||
//
|
||||
// @FormUrlEncoded
|
||||
// @POST(Constant.URL.USERWITHDRAW)
|
||||
// Observable<BaseModel<String>> userWithdraw(@Field("token") String token, @Field("bank_id") String bank_id, @Field("number") String number, @Field("password") String password);
|
||||
//
|
||||
// @FormUrlEncoded
|
||||
// @POST(Constant.URL.LOGIN)
|
||||
// Observable<BaseModel<UserBean>> login(@Field("mobile") String mobile, @Field("password") String password, @Field("code") String code, @Field("type") int type);
|
||||
//
|
||||
// @FormUrlEncoded
|
||||
// @POST(Constant.URL.LOGIN)
|
||||
// Observable<BaseModel<UserBean>> oauthLogin(@Field("netease_token") String netease_token, @Field("access_token") String access_token, @Field("type") int type);
|
||||
//
|
||||
// @FormUrlEncoded
|
||||
// @POST(Constant.URL.SETUSERSEX)
|
||||
// Observable<BaseModel<String>> setUserSex(@Field("user_id") String user_id, @Field("sex") int sex);
|
||||
//
|
||||
// @FormUrlEncoded
|
||||
// @POST(Constant.URL.THIRDPARTYLOGIN)
|
||||
// Observable<BaseModel<UserBean>> thirdPartyLogin(@Field("openid") String openid, @Field("three_party") int three_party, @Field("nickname") String nickname,@Field("openid_old") String oldOpenId, @Field("head_pic") String head_pic);
|
||||
//
|
||||
//
|
||||
// @FormUrlEncoded
|
||||
// @POST(Constant.URL.EARNINGS)
|
||||
// Observable<BaseModel<EarningsModel>> getEarnings(@Field("token") String token);
|
||||
//
|
||||
// @FormUrlEncoded
|
||||
// @POST(Constant.URL.CONVERTEARNINGS)
|
||||
// Observable<BaseModel<String>> convertEarnings(@Field("token") String token, @Field("number") String number, @Field("password") String password);
|
||||
//
|
||||
//
|
||||
// @FormUrlEncoded
|
||||
// @POST(Constant.URL.CASHLOG)
|
||||
// Observable<BaseModel<List<EarningsModel.EarningInfo>>> getCashLog(@Field("token") String token, @Field("p") int p, @Field("change_type") int change_type);
|
||||
//
|
||||
// @FormUrlEncoded
|
||||
// @POST(Constant.URL.BALANCE)
|
||||
// Observable<BaseModel<UserMoneyBean>> getBalance(@Field("token") String token, @Field("type") int type);
|
||||
//
|
||||
// @FormUrlEncoded
|
||||
// @POST(Constant.URL.FOLLOW)
|
||||
// Observable<BaseModel<String>> follow(@Field("token") String token, @Field("user_id") String userId, @Field("type") int type);
|
||||
//
|
||||
// @FormUrlEncoded
|
||||
// @POST(Constant.URL.FRIENDLIST)
|
||||
// Observable<BaseModel<List<FriendModel>>> friendList(@Field("p") int p);
|
||||
//
|
||||
// @FormUrlEncoded
|
||||
// @POST(Constant.URL.FOLLOWLIST)
|
||||
// Observable<BaseModel<List<FriendModel>>> followList(@Field("p") int p);
|
||||
//
|
||||
// @FormUrlEncoded
|
||||
// @POST(Constant.URL.FANSLIST)
|
||||
// Observable<BaseModel<List<FriendModel>>> fansList(@Field("p") int p);
|
||||
//
|
||||
// @POST(Constant.URL.MYINFO)
|
||||
// Observable<BaseModel<UserInfoModel>> userInfo();
|
||||
//
|
||||
// @FormUrlEncoded
|
||||
// @POST(Constant.URL.USERINFO)
|
||||
// Observable<BaseModel<UserInfoDataModel>> userInfoData(@Field("user_id") String userId, @Field("emchat_username") String emchatUsername, @Field("visit") int visit);
|
||||
//
|
||||
// @POST(Constant.URL.VIPINFO)
|
||||
// Observable<BaseModel<VipInfo>> vipinfo();
|
||||
//
|
||||
// @POST(Constant.URL.SERVICEUSER)
|
||||
// Observable<BaseModel<String>> serviceUser();
|
||||
//
|
||||
// @POST(Constant.URL.ARTICLE_CATEGORIES)
|
||||
// Observable<BaseModel<List<HelpTitleModel>>> articleCategories();
|
||||
//
|
||||
// @FormUrlEncoded
|
||||
// @POST(Constant.URL.ARTICLE_LIST)
|
||||
// Observable<BaseModel<List<HelpModel>>> articleList(@Field("article_cat_id") String articleCatId);
|
||||
//
|
||||
// @POST(Constant.URL.USER_NOBILITYINFO)
|
||||
// Observable<BaseModel<NobilityInfo>> userNobilityInfo();
|
||||
//
|
||||
// @POST(Constant.URL.NOBILITY)
|
||||
// Observable<BaseModel<List<NobilityModel>>> nobility();
|
||||
//
|
||||
// @FormUrlEncoded
|
||||
// @POST(Constant.URL.BUYNOBILITY)
|
||||
// Observable<BaseModel<String>> buyNobility(@Field("nobility_id") String nobilityId);
|
||||
//
|
||||
// @FormUrlEncoded
|
||||
// @POST(Constant.URL.RENEWNOBILITY)
|
||||
// Observable<BaseModel<String>> renewNobility(@Field("day") String day);
|
||||
//
|
||||
// @FormUrlEncoded
|
||||
// @POST(Constant.URL.UPDATE_USERINFO)
|
||||
// Observable<BaseModel<String>> updateUserInfo(@Field("signature") String signature, @Field("birthday") String birthday, @Field("constellation") String constellation, @Field("profession") String profession,
|
||||
// @Field("city_id") String city_id, @Field("user_photo") String user_photo, @Field("sex") String sex, @Field("head_picture") String head_picture,
|
||||
// @Field("nickname") String nickname, @Field("province_id") String province_id, @Field("user_no") String userNo, @Field("county_id") String county_id);
|
||||
//
|
||||
// @FormUrlEncoded
|
||||
// @POST(Constant.URL.INDEX_LABEL)
|
||||
// Observable<BaseModel<List<LabelModel>>> indexLabel(@Field("category_id") String categoryId, @Field("p") int p);
|
||||
//
|
||||
// @FormUrlEncoded
|
||||
// @POST(Constant.URL.ADDLABEL)
|
||||
// Observable<BaseModel<String>> addLabel(@Field("ids") String ids);
|
||||
//
|
||||
// @POST(Constant.URL.CASHTYPE)
|
||||
// Observable<BaseModel<List<CashTypeModel>>> cashType();
|
||||
//
|
||||
// @FormUrlEncoded
|
||||
// @POST(Constant.URL.COMEUSER)
|
||||
// Observable<BaseModel<List<LatelyVisitInfo>>> comeUser(@Field("token") String token, @Field("p") int p);
|
||||
//
|
||||
// @GET(Constant.URL.APPUPDATE)
|
||||
// Observable<BaseModel<AppUpdateModel>> appUpdate();
|
||||
//
|
||||
// @GET(Constant.URL.CHECK_UPDATE)
|
||||
// Observable<BaseModel<AppUpdateModel>> checkUpdate();
|
||||
//
|
||||
// @POST(Constant.URL.USER_FILES)
|
||||
// Observable<BaseModel<UserBean>> userFiles();
|
||||
//
|
||||
// @FormUrlEncoded
|
||||
// @POST(Constant.URL.BIND_MOBILE)
|
||||
// Observable<BaseModel<String>> bindMobile(@Field("mobile") String mobile, @Field("code") String code);
|
||||
//
|
||||
// @FormUrlEncoded
|
||||
// @POST(Constant.URL.RESET_PASSWORD)
|
||||
// Observable<BaseModel<String>> resetPassword(@Field("mobile") String mobile, @Field("code") String code, @Field("password") String password);
|
||||
//
|
||||
// @FormUrlEncoded
|
||||
// @POST(Constant.URL.MESSAGE_SETTING)
|
||||
// Observable<BaseModel<String>> messageSetting(@Field("broadcast") int broadcast, @Field("fans") int fans, @Field("news_voice") int news_voice, @Field("news_vibrate") int news_vibrate, @Field("only_friend") int only_friend );
|
||||
//
|
||||
// @FormUrlEncoded
|
||||
// @POST(Constant.URL.USER_BLACK_LIST)
|
||||
// Observable<BaseModel<List<BlacListSectionBean>>> userBlackList(@Field("p") int page, @Field("keyword") String keyword);
|
||||
//
|
||||
// @FormUrlEncoded
|
||||
// @POST(Constant.URL.ADD_BLACK_USER)
|
||||
// Observable<BaseModel<String>> removeBlackUser(@Field("black_id") String blackId, @Field("type") int type);
|
||||
//
|
||||
// @FormUrlEncoded
|
||||
// @POST(Constant.URL.QUIT_ROOM_WITH_USER_ID)
|
||||
// Observable<BaseModel<String>> quitRoomWithUserId(@Field("room_id") String roomId, @Field("user_id") String userId);
|
||||
//
|
||||
// @POST(Constant.URL.SIGN_SWITCH)
|
||||
// Observable<BaseModel<SignSwitchModel>> signSwitch();
|
||||
//
|
||||
// @FormUrlEncoded
|
||||
// @POST(Constant.URL.SET_SECOND_PASSWORD)
|
||||
// Observable<BaseModel<String>> setSecondPassword(@Field("mobile") String mobile, @Field("password") String password, @Field("code") String code);
|
||||
//
|
||||
// @FormUrlEncoded
|
||||
// @POST(URLConstants.NAME_AUTH)
|
||||
// Observable<BaseModel<String>> nameAuth(@Field("userId") String userId, @Field("fullName") String fullName, @Field("idNumber") String idNumber, @Field("idCard") String idCard, @Field("front") String front, @Field("back") String back);
|
||||
//
|
||||
// @POST(Constant.URL.USER_PHOTO)
|
||||
// Observable<BaseModel<List<MyPhotoItem>>> userPhotos();
|
||||
//
|
||||
// @FormUrlEncoded
|
||||
// @POST(Constant.URL.DELETE_USER_PHOTO)
|
||||
// Observable<BaseModel<String>> deleteUserPhoto(@Field("id") String ids);
|
||||
//
|
||||
// @FormUrlEncoded
|
||||
// @POST(Constant.URL.ADD_USER_PHOTO)
|
||||
// Observable<BaseModel<String>> addUserPhoto(@Field("photo") String photo);
|
||||
//
|
||||
// @FormUrlEncoded
|
||||
// @POST(Constant.URL.LOGOUT_REASON)
|
||||
// Observable<BaseModel<String>> logoutReason(@Field("token") String token, @Field("mobile") String mobile,
|
||||
// @Field("reason") String reason,@Field("code") String code);
|
||||
//
|
||||
// @FormUrlEncoded
|
||||
// @POST(Constant.URL.LOGOUT_STATUS)
|
||||
// Observable<BaseModel<LogoutReasonModel>> getlogoutStatus(@Field("token") String token, @Field("mobile") String mobile);
|
||||
|
||||
}
|
||||
@@ -4,9 +4,9 @@ import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
public class BaseModel<T> implements Parcelable {
|
||||
private int status;
|
||||
private int code;
|
||||
private T data;
|
||||
private String info;
|
||||
private String msg;
|
||||
|
||||
public T getData() {
|
||||
return data;
|
||||
@@ -16,26 +16,28 @@ public class BaseModel<T> implements Parcelable {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public Integer getStatus() {
|
||||
return status;
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setStatus(Integer status) {
|
||||
this.status = status;
|
||||
public void setCode(int code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getInfo() {
|
||||
return info;
|
||||
public String getMsg() {
|
||||
return msg;
|
||||
}
|
||||
|
||||
public void setInfo(String info) {
|
||||
this.info = info;
|
||||
public void setMsg(String msg) {
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
public BaseModel() {
|
||||
|
||||
}
|
||||
|
||||
public boolean isSuccess() {
|
||||
return code == 1; // 根据实际业务码判断是否成功
|
||||
}
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
@@ -43,13 +45,13 @@ public class BaseModel<T> implements Parcelable {
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeInt(this.status);
|
||||
dest.writeString(this.info);
|
||||
dest.writeInt(this.code);
|
||||
dest.writeString(this.msg);
|
||||
}
|
||||
|
||||
protected BaseModel(Parcel in) {
|
||||
this.status = in.readInt();
|
||||
this.info = in.readString();
|
||||
this.code = in.readInt();
|
||||
this.msg = in.readString();
|
||||
}
|
||||
|
||||
public static final Creator<BaseModel> CREATOR = new Creator<BaseModel>() {
|
||||
|
||||
@@ -23,7 +23,7 @@ import io.reactivex.Observer;
|
||||
* Created by Administrator on 2017/11/22.
|
||||
*/
|
||||
|
||||
public abstract class BaseObserver<T> implements Observer<T> {
|
||||
public abstract class BaseObserver<T> implements Observer<T> {
|
||||
|
||||
private boolean showErrMsg;
|
||||
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.qxcm.moduleutil.http;
|
||||
|
||||
|
||||
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.ObservableSource;
|
||||
import io.reactivex.ObservableTransformer;
|
||||
|
||||
public class DefaultTransformer<T extends BaseModel<R>, R>
|
||||
implements ObservableTransformer<T, R> {
|
||||
|
||||
@Override
|
||||
public ObservableSource<R> apply(Observable<T> upstream) {
|
||||
return upstream.compose(SchedulerTransformer.<T>create())
|
||||
.compose(new ErrorCheckerTransformer<T, R>());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.qxcm.moduleutil.http;
|
||||
|
||||
|
||||
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.ObservableSource;
|
||||
import io.reactivex.ObservableTransformer;
|
||||
import io.reactivex.functions.Function;
|
||||
|
||||
|
||||
public class ErrorCheckerTransformer<T extends BaseModel<R>, R>
|
||||
implements ObservableTransformer<T, R> {
|
||||
|
||||
@Override
|
||||
public ObservableSource<R> apply(Observable<T> upstream) {
|
||||
return upstream.map(new Function<T, R>() {
|
||||
@Override
|
||||
public R apply(T t) throws Exception {
|
||||
if (t != null) {
|
||||
int code = t.getCode();
|
||||
String msg = t.getMsg();
|
||||
if (code == 1) {//请求成功
|
||||
return t.getData();
|
||||
} else if (code == 301) {
|
||||
throw new APIException(code, msg);
|
||||
} else {
|
||||
String message = ErrorMessage.get(code);
|
||||
if (message != null) {
|
||||
throw new APIException(code, message);
|
||||
} else {
|
||||
throw new APIException(code, t.getMsg());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package com.qxcm.moduleutil.http;
|
||||
|
||||
/**
|
||||
* 常见错误码对应的错误信息
|
||||
* Created by dmx on 16/12/1.
|
||||
*/
|
||||
|
||||
public class ErrorMessage {
|
||||
|
||||
|
||||
/**
|
||||
* 根据编码获取对应的错误信息,没有对应编码则返回null
|
||||
*
|
||||
* @param code
|
||||
* @return
|
||||
*/
|
||||
public static String get(int code) {
|
||||
switch (code) {
|
||||
|
||||
case 0:
|
||||
return "失败";
|
||||
//
|
||||
case 203:
|
||||
return "系统维护中";
|
||||
|
||||
case 301:
|
||||
return "token失效";
|
||||
//
|
||||
// case 20:
|
||||
// return "用户不存在";
|
||||
//
|
||||
// case 21:
|
||||
// return "用户已经存在";
|
||||
//
|
||||
// case 22:
|
||||
// return "用户状态已改变,请刷新重试";
|
||||
//
|
||||
// case 100:
|
||||
// return "验证码获取太频繁";
|
||||
//
|
||||
// case 101:
|
||||
// return "获取验证码次数限制";
|
||||
//
|
||||
// case 102:
|
||||
// return "验证码错误";
|
||||
// case 103:
|
||||
// return "密码错误";
|
||||
// case 104:
|
||||
// return "第一次登录需要设置密码";
|
||||
// case 500:
|
||||
// return "接口不存在";
|
||||
//
|
||||
// case 502:
|
||||
// return "已经提交代理申请";
|
||||
//
|
||||
// case 504:
|
||||
// return "已经是雇员了";
|
||||
//// case 528:
|
||||
//// return "不在续保期内,无法续保";
|
||||
// case 661:
|
||||
// return "客户身份证重复";
|
||||
//
|
||||
// case 1000:
|
||||
// return "employeeId错误或者缺少";
|
||||
//
|
||||
//
|
||||
// case 1009:
|
||||
// return "手机号错误";
|
||||
//
|
||||
// case 1020:
|
||||
// return "参数pwd错误或者缺失";
|
||||
//
|
||||
// case 1022:
|
||||
// return "参数id错误或者缺失";
|
||||
default:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package com.qxcm.moduleutil.http;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.TypeAdapter;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.qxcm.moduleutil.utils.GsonUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
import okhttp3.MediaType;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.ResponseBody;
|
||||
import retrofit2.Converter;
|
||||
import retrofit2.Retrofit;
|
||||
|
||||
public class MyConverterFactory extends Converter.Factory {
|
||||
|
||||
/**
|
||||
* Create an instance using a default {@link Gson} instance for conversion. Encoding to JSON and
|
||||
* decoding from JSON (when no charset is specified by a header) will use UTF-8.
|
||||
*/
|
||||
public static MyConverterFactory create() {
|
||||
return create(GsonUtils.getGSON());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance using {@code gson} for conversion. Encoding to JSON and
|
||||
* decoding from JSON (when no charset is specified by a header) will use UTF-8.
|
||||
*/
|
||||
@SuppressWarnings("ConstantConditions") // Guarding public API nullability.
|
||||
public static MyConverterFactory create(Gson gson) {
|
||||
if (gson == null) {
|
||||
throw new NullPointerException("gson == null");
|
||||
}
|
||||
return new MyConverterFactory(gson);
|
||||
}
|
||||
|
||||
private final Gson gson;
|
||||
|
||||
private MyConverterFactory(Gson gson) {
|
||||
this.gson = gson;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Converter<?, RequestBody> requestBodyConverter(Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
|
||||
TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
|
||||
return new RequestBodyConverter<>(gson, adapter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
|
||||
TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
|
||||
return new ResponseBodyConverter<>(gson, adapter);
|
||||
// return new ResponseBodyConverter<>(gson, TypeToken.get(type));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.qxcm.moduleutil.http;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.TypeAdapter;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.Writer;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
import okhttp3.MediaType;
|
||||
import okhttp3.RequestBody;
|
||||
import okio.Buffer;
|
||||
import retrofit2.Converter;
|
||||
|
||||
public class RequestBodyConverter<T> implements Converter<T, RequestBody> {
|
||||
|
||||
private static final MediaType MEDIA_TYPE = MediaType.parse("application/json; charset=UTF-8");
|
||||
private static final Charset UTF_8 = Charset.forName("UTF-8");
|
||||
|
||||
private final Gson gson;
|
||||
private final TypeAdapter<T> adapter;
|
||||
|
||||
RequestBodyConverter(Gson gson, TypeAdapter<T> adapter) {
|
||||
this.gson = gson;
|
||||
this.adapter = adapter;
|
||||
}
|
||||
|
||||
@Override public RequestBody convert(@NonNull T value) throws IOException {
|
||||
Buffer buffer = new Buffer();
|
||||
Writer writer = new OutputStreamWriter(buffer.outputStream(), UTF_8);
|
||||
JsonWriter jsonWriter = gson.newJsonWriter(writer);
|
||||
adapter.write(jsonWriter, value);
|
||||
jsonWriter.close();
|
||||
return RequestBody.create(MEDIA_TYPE, buffer.readByteString());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.qxcm.moduleutil.http;
|
||||
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.TypeAdapter;
|
||||
import com.qxcm.moduleutil.utils.GsonUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import io.reactivex.annotations.NonNull;
|
||||
import okhttp3.ResponseBody;
|
||||
import retrofit2.Converter;
|
||||
|
||||
public class ResponseBodyConverter<T> implements Converter<ResponseBody, T> {
|
||||
|
||||
private final Gson gson;
|
||||
private final TypeAdapter<T> adapter;
|
||||
|
||||
ResponseBodyConverter(Gson gson, TypeAdapter<T> adapter) {
|
||||
this.gson = gson;
|
||||
this.adapter = adapter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T convert(@NonNull ResponseBody value) throws IOException {
|
||||
String json = value.string();
|
||||
BaseModel obj = GsonUtils.GsonToBean(json, BaseModel.class);
|
||||
if (obj.getCode() != 1) {
|
||||
String info = obj.getMsg();
|
||||
if ("当前余额不足".equals(info)) {
|
||||
value.close();
|
||||
return adapter.fromJson(json);
|
||||
} else {
|
||||
throw new APIException(obj.getCode(), info);
|
||||
}
|
||||
}
|
||||
value.close();
|
||||
return adapter.fromJson(json);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,198 @@
|
||||
package com.qxcm.moduleutil.http;
|
||||
|
||||
//import com.blankj.utilcode.util.LogUtils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.NetworkInfo;
|
||||
|
||||
import com.franmontiel.persistentcookiejar.ClearableCookieJar;
|
||||
import com.franmontiel.persistentcookiejar.PersistentCookieJar;
|
||||
import com.franmontiel.persistentcookiejar.cache.SetCookieCache;
|
||||
import com.franmontiel.persistentcookiejar.persistence.SharedPrefsCookiePersistor;
|
||||
import com.qxcm.moduleutil.base.CommonAppContext;
|
||||
import com.qxcm.moduleutil.bean.UserBean;
|
||||
import com.qxcm.moduleutil.presenter.BasePresenter;
|
||||
import com.qxcm.moduleutil.utils.SystemUtils;
|
||||
import com.qxcm.moduleutil.utils.logger.DataLogger;
|
||||
import com.qxcm.moduleutil.utils.logger.DataLoggingInterceptor;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.Proxy;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import okhttp3.Cache;
|
||||
import okhttp3.OkHttpClient;
|
||||
import retrofit2.Retrofit;
|
||||
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
|
||||
|
||||
public class RetrofitClient {
|
||||
|
||||
private static RetrofitClient INSTANCE;
|
||||
private static ApiServer sApiServer;
|
||||
public static final int DEFAULT_TIME_OUT = 60;
|
||||
private static OkHttpClient client;
|
||||
private final Retrofit mRetrofit;
|
||||
|
||||
public OkHttpClient getHttpClient() {
|
||||
return client;
|
||||
}
|
||||
|
||||
File cacheFile = new File(CommonAppContext.getInstance().getCacheDir(), "cache");
|
||||
Cache cache = new Cache(cacheFile, 1024 * 1024 * 100); // 100MB 的缓存空间
|
||||
boolean isNetworkAvailable = checkNetworkConnection(); // 实现这个方法来检查网络状态
|
||||
String cacheControl = isNetworkAvailable ? "max-age=0" : "only-if-cached, max-stale=86400";
|
||||
|
||||
private boolean checkNetworkConnection() {
|
||||
ConnectivityManager connectivityManager = (ConnectivityManager) CommonAppContext.getInstance().getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
|
||||
return activeNetworkInfo != null && activeNetworkInfo.isConnected();
|
||||
}
|
||||
|
||||
private OkHttpClient provideOkHttpClient() {
|
||||
Map<String, String> headers = SystemUtils.getSystemParams();
|
||||
SetCookieCache cookieCache = new SetCookieCache();
|
||||
ClearableCookieJar cookieJar =
|
||||
new PersistentCookieJar(cookieCache, new SharedPrefsCookiePersistor(CommonAppContext.getInstance()));
|
||||
try {
|
||||
X509TrustManager trustAllCert = new X509TrustManager() {
|
||||
@Override
|
||||
public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
|
||||
return new java.security.cert.X509Certificate[]{};
|
||||
}
|
||||
};
|
||||
SSLContext sslContext = null;
|
||||
sslContext = SSLContext.getInstance("SSL");
|
||||
sslContext.init(null, new TrustManager[]{trustAllCert}, new SecureRandom());
|
||||
|
||||
final OkHttpClient client = new OkHttpClient.Builder()
|
||||
// .addInterceptor(new LoggerInterceptor("HttpLog", true))
|
||||
.addInterceptor(new DataLoggingInterceptor(new DataLogger()))
|
||||
.addInterceptor(new AccessTokenInterceptor(headers))
|
||||
.proxy(Proxy.NO_PROXY)
|
||||
.sslSocketFactory(sslContext.getSocketFactory(), trustAllCert)
|
||||
.hostnameVerifier((hostname, session) -> true)
|
||||
.cookieJar(cookieJar)
|
||||
.connectTimeout(DEFAULT_TIME_OUT, TimeUnit.SECONDS)
|
||||
.readTimeout(DEFAULT_TIME_OUT, TimeUnit.SECONDS)
|
||||
.writeTimeout(DEFAULT_TIME_OUT, TimeUnit.SECONDS)
|
||||
.build();
|
||||
RetrofitClient.client = client;
|
||||
} catch (KeyManagementException | NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return client;
|
||||
}
|
||||
|
||||
private Retrofit provideRetrofit(OkHttpClient client) {
|
||||
return new Retrofit.Builder()
|
||||
.addConverterFactory(MyConverterFactory.create())
|
||||
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
|
||||
.baseUrl(CommonAppContext.getInstance().getCurrentEnvironment().getServerUrl())
|
||||
.client(client)
|
||||
|
||||
.build();
|
||||
}
|
||||
|
||||
// private static Retrofit mRainRetrofit;
|
||||
//
|
||||
// public static Retrofit getRainRetrofit() {
|
||||
// if (mRainRetrofit == null) {
|
||||
// synchronized (RetrofitClient.class) {
|
||||
// mRainRetrofit = new Retrofit.Builder()
|
||||
// .addConverterFactory(RainConverterFactory.create())
|
||||
// .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
|
||||
// .baseUrl(BuildConfig.RAIN_GAME_BASE_URL)
|
||||
// .client(client)
|
||||
// .build();
|
||||
// }
|
||||
// }
|
||||
// return mRainRetrofit;
|
||||
// }
|
||||
|
||||
private RetrofitClient() {
|
||||
mRetrofit = provideRetrofit(provideOkHttpClient());
|
||||
sApiServer = mRetrofit.create(ApiServer.class);
|
||||
}
|
||||
|
||||
public static RetrofitClient getInstance() {
|
||||
if (INSTANCE == null) {
|
||||
synchronized (RetrofitClient.class) {
|
||||
if (INSTANCE == null) {
|
||||
INSTANCE = new RetrofitClient();
|
||||
}
|
||||
}
|
||||
}
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
public <T> T createApiClient(Class<T> apiClientClass) {
|
||||
return mRetrofit.create(apiClientClass);
|
||||
}
|
||||
|
||||
public void login() {
|
||||
// login("18473492252", "123456");
|
||||
// login("18229732986", "123456");
|
||||
}
|
||||
|
||||
|
||||
public void sendCode(String mobile,String event,BaseObserver<Object> observer) {
|
||||
sApiServer.sendCode(mobile,event).compose(new DefaultTransformer<>()).subscribe(observer);
|
||||
}
|
||||
|
||||
public void login(String mobile, String password, BaseObserver<List<UserBean>> observer) {
|
||||
sApiServer.login(mobile, password).compose(new DefaultTransformer<>()).subscribe(observer);
|
||||
}
|
||||
public void userLogin(String mobile, String password, BaseObserver<List<UserBean>> observer) {
|
||||
sApiServer.userLogin(mobile, password).compose(new DefaultTransformer<>()).subscribe(observer);
|
||||
}
|
||||
|
||||
public void upUserNickname(BaseObserver<String> observer){
|
||||
sApiServer.upUserNickname().compose(new DefaultTransformer<>()).subscribe(observer);
|
||||
}
|
||||
|
||||
public void upUserPic(String sex,BaseObserver<String> observer){
|
||||
sApiServer.upUserPic(sex).compose(new DefaultTransformer<>()).subscribe(observer);
|
||||
}
|
||||
|
||||
public void switchAccounts(String loginId, BaseObserver<List<UserBean>> observer){
|
||||
sApiServer.switchAccounts(loginId).compose(new DefaultTransformer<>()).subscribe(observer);
|
||||
}
|
||||
|
||||
public void userUpdate(Map<String, String> map, BaseObserver<UserBean> observer) {
|
||||
sApiServer.userUpdate(map).compose(new DefaultTransformer<>()).subscribe(observer);
|
||||
}
|
||||
|
||||
public void authorization(BaseObserver<String> observer){
|
||||
sApiServer.authorization().compose(new DefaultTransformer<>()).subscribe(observer);
|
||||
}
|
||||
|
||||
public void oauthLogin(String netease_token, BaseObserver<List<UserBean>> observer) {
|
||||
sApiServer.oauthLogin(netease_token).compose(new DefaultTransformer<>()).subscribe(observer);
|
||||
}
|
||||
public void authCode(String netease_token,int type, BaseObserver<List<UserBean>> observer) {
|
||||
if (type==1){
|
||||
sApiServer.authCode1(netease_token).compose(new DefaultTransformer<>()).subscribe(observer);
|
||||
}else {
|
||||
sApiServer.authCode(netease_token).compose(new DefaultTransformer<>()).subscribe(observer);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.qxcm.moduleutil.http;
|
||||
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.ObservableSource;
|
||||
import io.reactivex.ObservableTransformer;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
|
||||
/**
|
||||
* 默认线程调度
|
||||
* Created by dmx on 16/12/1.
|
||||
*/
|
||||
|
||||
public class SchedulerTransformer<T> implements ObservableTransformer<T, T> {
|
||||
|
||||
public static <T> SchedulerTransformer<T> create() {
|
||||
return new SchedulerTransformer<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObservableSource<T> apply(Observable<T> upstream) {
|
||||
return upstream.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread());
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import android.content.Context;
|
||||
|
||||
import com.qxcm.moduleutil.activity.IPresenter;
|
||||
import com.qxcm.moduleutil.activity.IView;
|
||||
import com.qxcm.moduleutil.http.RetrofitClient;
|
||||
|
||||
import java.lang.ref.Reference;
|
||||
import java.lang.ref.WeakReference;
|
||||
@@ -15,7 +16,7 @@ import io.reactivex.disposables.Disposable;
|
||||
|
||||
public abstract class BasePresenter<V extends IView> implements IPresenter {
|
||||
protected CompositeDisposable mDisposables = new CompositeDisposable();
|
||||
// private RemoteDataSource api;
|
||||
protected RetrofitClient api = RetrofitClient.getInstance();
|
||||
protected Reference<V> MvpRef;
|
||||
protected Context mContext;
|
||||
|
||||
@@ -40,12 +41,6 @@ public abstract class BasePresenter<V extends IView> implements IPresenter {
|
||||
return null;
|
||||
}
|
||||
|
||||
// protected RemoteDataSource getApi() {
|
||||
// if (api == null) {
|
||||
// api = RemoteDataSource.getInstance();
|
||||
// }
|
||||
// return api;
|
||||
// }
|
||||
|
||||
/**
|
||||
* 主要用于判断IView的生命周期是否结束,防止出现内存泄露状况
|
||||
@@ -63,9 +58,9 @@ public abstract class BasePresenter<V extends IView> implements IPresenter {
|
||||
MvpRef.clear();
|
||||
MvpRef = null;
|
||||
}
|
||||
// if (api != null) {
|
||||
// api = null;
|
||||
// }
|
||||
if (api != null) {
|
||||
api = null;
|
||||
}
|
||||
unBindView();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.qxcm.moduleutil.utils;
|
||||
|
||||
import android.content.Intent;
|
||||
|
||||
/**
|
||||
* Created by cxf on 2018/9/29.
|
||||
*/
|
||||
|
||||
public abstract class ActivityResultCallback {
|
||||
public abstract void onSuccess(Intent intent);
|
||||
|
||||
public void onFailure() {
|
||||
|
||||
}
|
||||
|
||||
public void onResult(int resultCode, Intent intent) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import android.os.Build;
|
||||
import android.provider.Settings;
|
||||
|
||||
import com.qxcm.moduleutil.widget.CommonAppConfig;
|
||||
import com.qxcm.moduleutil.widget.CommonAppContext;
|
||||
import com.qxcm.moduleutil.base.CommonAppContext;
|
||||
|
||||
|
||||
public class DeviceUtils {
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
package com.qxcm.moduleutil.utils;
|
||||
|
||||
//import com.blankj.utilcode.util.LogUtils;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonParser;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.qxcm.moduleutil.utils.logger.Logger;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class GsonUtils {
|
||||
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
|
||||
private static final JsonParser PARSER = new JsonParser();
|
||||
|
||||
public static Gson getGSON() {
|
||||
return GSON;
|
||||
}
|
||||
|
||||
/**
|
||||
* Object 转 json
|
||||
*
|
||||
* @param object object
|
||||
* @return object
|
||||
*/
|
||||
public static String GsonString(Object object) {
|
||||
return GSON.toJson(object);
|
||||
}
|
||||
|
||||
public static <T> T GsonToBean(String gsonString, Class<T> cls) {
|
||||
return GSON.fromJson(gsonString, cls);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转成map的
|
||||
*
|
||||
* @param gsonString str
|
||||
* @return map
|
||||
*/
|
||||
public static Map<String, Object> GsonToMaps(String gsonString) {
|
||||
return GSON.fromJson(gsonString, new TypeToken<Map<String, Object>>() {
|
||||
}.getType());
|
||||
}
|
||||
|
||||
/**
|
||||
* 将json 格式化输出
|
||||
*
|
||||
* @param str str
|
||||
* @return str
|
||||
*/
|
||||
public static String GsonToString(String str) {
|
||||
try {
|
||||
return GSON.toJson(PARSER.parse(str));
|
||||
} catch (JsonSyntaxException e) {
|
||||
e.printStackTrace();
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* json字符串转成list
|
||||
* 解决泛型问题
|
||||
* 备注:
|
||||
* List<T> list=gson.fromJson(gsonString, new TypeToken<List<T>>() {}.getType());
|
||||
* 该方法会报泛型类型擦除问题
|
||||
*
|
||||
* @param gsonString
|
||||
* @param cls
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
public static <T> List<T> GsonToList(String gsonString, Class<T> cls) {
|
||||
List<T> list = new ArrayList<T>();
|
||||
try {
|
||||
if (GSON != null) {
|
||||
JsonArray array = new JsonParser().parse(gsonString).getAsJsonArray();
|
||||
for (final JsonElement elem : array) {
|
||||
list.add(GSON.fromJson(elem, cls));
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Logger.e("非法json字符串", e);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -12,7 +12,7 @@ import androidx.core.os.ConfigurationCompat;
|
||||
import androidx.core.os.LocaleListCompat;
|
||||
|
||||
|
||||
import com.qxcm.moduleutil.widget.CommonAppContext;
|
||||
import com.qxcm.moduleutil.base.CommonAppContext;
|
||||
import com.qxcm.moduleutil.widget.Constants;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.qxcm.moduleutil.utils;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
public abstract class PermissionCallback {
|
||||
|
||||
public abstract void onAllGranted();
|
||||
|
||||
public void onResult(HashMap<String, Boolean> resultMap) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.qxcm.moduleutil.utils;
|
||||
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentTransaction;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 申请权限
|
||||
*/
|
||||
public class PermissionUtil {
|
||||
|
||||
public static void request(FragmentActivity activity, PermissionCallback callback, String... permission) {
|
||||
ProcessFragment processFragment = null;
|
||||
FragmentManager fragmentManager = activity.getSupportFragmentManager();
|
||||
List<Fragment> list = fragmentManager.getFragments();
|
||||
if (list != null && list.size() > 0) {
|
||||
for (Fragment fragment : list) {
|
||||
if (fragment != null && fragment instanceof ProcessFragment) {
|
||||
processFragment = (ProcessFragment) fragment;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (processFragment == null) {
|
||||
processFragment = new ProcessFragment();
|
||||
FragmentTransaction tx = fragmentManager.beginTransaction();
|
||||
tx.add(processFragment, "ProcessFragment").commitNow();
|
||||
}
|
||||
processFragment.requestPermissions(callback, permission);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
package com.qxcm.moduleutil.utils;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
|
||||
import com.blankj.utilcode.util.ToastUtils;
|
||||
import com.qxcm.moduleutil.R;
|
||||
import com.qxcm.moduleutil.base.CommonAppContext;
|
||||
import com.qxcm.moduleutil.widget.CommonAppConfig;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Created by cxf on 2018/9/29.
|
||||
* 处理 检查权限和 startActivityForResult 的回调的Fragment
|
||||
*/
|
||||
|
||||
public class ProcessFragment extends Fragment {
|
||||
|
||||
private Context mContext;
|
||||
private PermissionCallback mPermissionCallback;
|
||||
private ActivityResultCallback mActivityResultCallback;
|
||||
private LinkedHashMap<String, Boolean> mMap;
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
mContext = getActivity();
|
||||
}
|
||||
|
||||
/**
|
||||
* 申请权限
|
||||
*/
|
||||
public void requestPermissions(PermissionCallback callback, String... permissions) {
|
||||
if (callback == null || permissions == null || permissions.length == 0) {
|
||||
return;
|
||||
}
|
||||
boolean isAllGranted = true;
|
||||
if (mMap == null) {
|
||||
mMap = new LinkedHashMap<>();
|
||||
} else {
|
||||
mMap.clear();
|
||||
}
|
||||
for (String permission : permissions) {
|
||||
boolean isGranted = ContextCompat.checkSelfPermission(mContext, permission) == PackageManager.PERMISSION_GRANTED;
|
||||
mMap.put(permission, isGranted);
|
||||
if (!isGranted) {
|
||||
isAllGranted = false;
|
||||
}
|
||||
}
|
||||
if (isAllGranted) {
|
||||
callback.onAllGranted();
|
||||
callback.onResult(mMap);
|
||||
mPermissionCallback = null;
|
||||
} else {
|
||||
mPermissionCallback = callback;
|
||||
requestPermissions(permissions, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 申请权限结果
|
||||
*/
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||
boolean isAllGranted = true;
|
||||
if (mMap == null) {
|
||||
mMap = new LinkedHashMap<>();
|
||||
} else {
|
||||
mMap.clear();
|
||||
}
|
||||
for (int i = 0, len = grantResults.length; i < len; i++) {
|
||||
boolean isGranted = grantResults[i] == PackageManager.PERMISSION_GRANTED;
|
||||
mMap.put(permissions[i], isGranted);
|
||||
if (!isGranted) {
|
||||
isAllGranted = false;
|
||||
}
|
||||
}
|
||||
if (isAllGranted) {
|
||||
if (mPermissionCallback != null) {
|
||||
mPermissionCallback.onAllGranted();
|
||||
}
|
||||
} else {
|
||||
showTip();
|
||||
}
|
||||
if (mPermissionCallback != null) {
|
||||
mPermissionCallback.onResult(mMap);
|
||||
}
|
||||
mPermissionCallback = null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 拒绝某项权限时候的提示
|
||||
*/
|
||||
private void showTip() {
|
||||
if (mMap == null && mMap.size() == 0) {
|
||||
return;
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (Map.Entry<String, Boolean> entry : mMap.entrySet()) {
|
||||
if (!entry.getValue()) {
|
||||
switch (entry.getKey()) {
|
||||
case Manifest.permission.READ_EXTERNAL_STORAGE:
|
||||
case Manifest.permission.WRITE_EXTERNAL_STORAGE:
|
||||
String permissionStorage = WordUtil.getString(R.string.permission_storage);
|
||||
if (sb.indexOf(permissionStorage) < 0) {
|
||||
sb.append(permissionStorage);
|
||||
sb.append(",");
|
||||
}
|
||||
break;
|
||||
case Manifest.permission.CAMERA:
|
||||
sb.append(WordUtil.getString(R.string.permission_camera));
|
||||
sb.append(",");
|
||||
break;
|
||||
case Manifest.permission.RECORD_AUDIO:
|
||||
sb.append(WordUtil.getString(R.string.permission_record_audio));
|
||||
sb.append(",");
|
||||
break;
|
||||
case Manifest.permission.ACCESS_COARSE_LOCATION:
|
||||
sb.append(WordUtil.getString(R.string.permission_location));
|
||||
sb.append(",");
|
||||
CommonAppConfig.getInstance().clearLocationInfo();
|
||||
break;
|
||||
case Manifest.permission.READ_PHONE_STATE:
|
||||
sb.append(WordUtil.getString(R.string.permission_read_phone_state));
|
||||
sb.append(",");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
String s = sb.toString();
|
||||
if (!TextUtils.isEmpty(s) && s.length() > 1) {
|
||||
s = s.substring(0, s.length() - 1);
|
||||
}
|
||||
final String tip = String.format(WordUtil.getString(R.string.permission_refused), s);
|
||||
CommonAppContext.postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
ToastUtils.showShort(tip);
|
||||
}
|
||||
}, 300);
|
||||
}
|
||||
|
||||
|
||||
public void startActivityForResult(Intent intent, ActivityResultCallback callback) {
|
||||
mActivityResultCallback = callback;
|
||||
super.startActivityForResult(intent, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
if (mActivityResultCallback != null) {
|
||||
mActivityResultCallback.onResult(resultCode, data);
|
||||
if (resultCode == -1) {//RESULT_OK
|
||||
mActivityResultCallback.onSuccess(data);
|
||||
} else {
|
||||
mActivityResultCallback.onFailure();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -26,4 +26,6 @@ public class SPConstants {
|
||||
public static final String SPECIAL_EMOJI_LIST = "specialEmojiList";//房间专属表情列表
|
||||
|
||||
public static final String CABIN="cabin";//小黑屋
|
||||
|
||||
public static final String REAL_NAME = "realName";//是否实名认证
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.qxcm.moduleutil.utils;
|
||||
|
||||
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
public class Sha1Util {
|
||||
|
||||
|
||||
public static String shaEncode(long timestamp) {
|
||||
String val = "token" + SpUtil.getToken() + "timestamp" + timestamp ;
|
||||
// String val = "token" + SpUtil.getToken() + "timestamp" + timestamp + BuildConfig.SALT;
|
||||
MessageDigest md5 = null;
|
||||
try {
|
||||
md5 = MessageDigest.getInstance("SHA-1");
|
||||
md5.update(val.getBytes());
|
||||
byte[] m = md5.digest();//加密
|
||||
return byteArrayToHex(m).toLowerCase();
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
public static String byteArrayToHex(byte[] byteArray) {
|
||||
// 首先初始化一个字符数组,用来存放每个16进制字符
|
||||
char[] hexDigits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
|
||||
char[] resultCharArray = new char[byteArray.length * 2];
|
||||
// 遍历字节数组,通过位运算(位运算效率高),转换成字符放到字符数组中去
|
||||
int index = 0;
|
||||
for (byte b : byteArray) {
|
||||
resultCharArray[index++] = hexDigits[b >>> 4 & 0xf];
|
||||
resultCharArray[index++] = hexDigits[b & 0xf];
|
||||
}
|
||||
// 字符数组组合成字符串返回
|
||||
return new String(resultCharArray);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -7,7 +7,8 @@ import android.text.TextUtils;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.blankj.utilcode.util.SPUtils;
|
||||
import com.qxcm.moduleutil.widget.CommonAppContext;
|
||||
import com.qxcm.moduleutil.base.CommonAppContext;
|
||||
import com.qxcm.moduleutil.bean.UserBean;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@@ -229,18 +230,21 @@ public class SpUtil {
|
||||
return SPUtils.getInstance(SPConstants.PREFERENCE_NAME).getString(SPConstants.TOKEN);
|
||||
}
|
||||
|
||||
public static String getUserId() {
|
||||
return SPUtils.getInstance(SPConstants.PREFERENCE_NAME).getString(SPConstants.USER_ID);
|
||||
public static int getUserId() {
|
||||
return SPUtils.getInstance(SPConstants.PREFERENCE_NAME).getInt(SPConstants.USER_ID);
|
||||
}
|
||||
|
||||
public static void saveUserId(String userId) {
|
||||
public static void saveUserId(int userId) {
|
||||
SPUtils.getInstance(SPConstants.PREFERENCE_NAME).put(SPConstants.USER_ID, userId, true);
|
||||
}
|
||||
|
||||
public static String getMyRoomId() {
|
||||
return SPUtils.getInstance(SPConstants.PREFERENCE_NAME).getString("MyRoomId");
|
||||
}
|
||||
|
||||
public static void saveUserInfo(UserBean userBean) {
|
||||
String s = JSON.toJSONString(userBean);
|
||||
SPUtils.getInstance(SPConstants.PREFERENCE_NAME).put(SPConstants.USER_INFO, s, true);
|
||||
}
|
||||
public static void saveMyRoomId(String roomId) {
|
||||
SPUtils.getInstance(SPConstants.PREFERENCE_NAME).put("MyRoomId", roomId, true);
|
||||
}
|
||||
@@ -448,6 +452,26 @@ public class SpUtil {
|
||||
return SPUtils.getInstance(SPConstants.PREFERENCE_NAME).getString("roomModel", "");
|
||||
}
|
||||
|
||||
// TODO: 2025/5/16 设置实名认证
|
||||
public static void setRealName(boolean realName){
|
||||
SPUtils.getInstance(SPConstants.REAL_NAME).put("realName", realName, true);
|
||||
}
|
||||
|
||||
// TODO: 2025/5/16 是否实名认证
|
||||
public static boolean getRealName(){
|
||||
return SPUtils.getInstance(SPConstants.REAL_NAME).getBoolean("realName", false);
|
||||
}
|
||||
public static UserBean getUserInfo() {
|
||||
String s = SPUtils.getInstance(SPConstants.PREFERENCE_NAME).getString(SPConstants.USER_INFO);
|
||||
if (TextUtils.isEmpty(s)) {
|
||||
return new UserBean();
|
||||
}
|
||||
UserBean userBean = JSON.parseObject(s, UserBean.class);
|
||||
if (userBean == null) {
|
||||
return new UserBean();
|
||||
}
|
||||
return userBean;
|
||||
}
|
||||
/**
|
||||
* 是否同意协议
|
||||
*
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
package com.qxcm.moduleutil.utils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
|
||||
import com.blankj.utilcode.util.AppUtils;
|
||||
import com.blankj.utilcode.util.DeviceUtils;
|
||||
import com.blankj.utilcode.util.MetaDataUtils;
|
||||
import com.chad.library.BuildConfig;
|
||||
import com.qxcm.moduleutil.base.CommonAppContext;
|
||||
import com.qxcm.moduleutil.utils.config.ConfigUtils;
|
||||
import com.qxcm.moduleutil.widget.CommonAppConfig;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
public class SystemUtils {
|
||||
/**
|
||||
* 获取当前手机系统语言。
|
||||
*
|
||||
* @return 返回当前系统语言。例如:当前设置的是“中文-中国”,则返回“zh-CN”
|
||||
*/
|
||||
public static String getSystemLanguage() {
|
||||
return Locale.getDefault().getLanguage();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前手机系统版本号
|
||||
*
|
||||
* @return 系统版本号
|
||||
*/
|
||||
public static String getSystemVersion() {
|
||||
return Build.VERSION.RELEASE;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取手机型号
|
||||
*
|
||||
* @return 手机型号
|
||||
*/
|
||||
public static String getSystemModel() {
|
||||
return Build.MODEL;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取手机厂商
|
||||
*
|
||||
* @return 手机厂商
|
||||
*/
|
||||
public static String getDeviceBrand() {
|
||||
return Build.BRAND;
|
||||
}
|
||||
|
||||
public static String getClientID(String channel) {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
sb.append(channel);
|
||||
sb.append(Build.SERIAL);
|
||||
sb.append(Build.BOARD.length() % 10);
|
||||
sb.append(Build.BRAND.length() % 10);
|
||||
sb.append(Build.CPU_ABI.length() % 10);
|
||||
sb.append(Build.DEVICE.length() % 10);
|
||||
sb.append(Build.DISPLAY.length() % 10);
|
||||
sb.append(Build.HOST.length() % 10);
|
||||
sb.append(Build.ID.length() % 10);
|
||||
sb.append(Build.MANUFACTURER.length() % 10);
|
||||
sb.append(Build.MODEL.length() % 10);
|
||||
sb.append(Build.PRODUCT.length() % 10);
|
||||
sb.append(Build.TAGS.length() % 10);
|
||||
sb.append(Build.TYPE.length() % 10);
|
||||
sb.append(Build.USER.length() % 10);
|
||||
|
||||
return Md5Utils.get(sb.toString());
|
||||
}
|
||||
|
||||
|
||||
public static String getShortClientID(Context context, String channel) {
|
||||
String cid = getClientID(channel);
|
||||
return Md5Utils.get(cid + getUUID(context));
|
||||
}
|
||||
|
||||
public static String getShortClientID(Context context) {
|
||||
String cid = getClientID("xqipaoandroid");
|
||||
return Md5Utils.get(cid + getUUID(context));
|
||||
}
|
||||
|
||||
/**
|
||||
* 得到全局唯一UUID
|
||||
*/
|
||||
public static String getUUID(Context context) {
|
||||
String system_uuid_key = "system_uuid";
|
||||
ConfigUtils configUtils = ConfigUtils.getInstance(context);
|
||||
configUtils.setConfigName(system_uuid_key);
|
||||
String system_config_uuid = configUtils.findStringByKey(system_uuid_key);
|
||||
if (system_config_uuid == null) {
|
||||
system_config_uuid = DeviceUtils.getAndroidID();
|
||||
configUtils.addOrUpdateText(system_uuid_key, system_config_uuid);
|
||||
}
|
||||
return system_config_uuid;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 系统参数
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static Map<String, String> getSystemParams() {
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
headers.put("deviceId", SystemUtils.getShortClientID(CommonAppContext.getInstance()));
|
||||
headers.put("appVersion", BuildConfig.VERSION_NAME + "." + BuildConfig.VERSION_CODE);
|
||||
headers.put("versionName", BuildConfig.VERSION_NAME);
|
||||
headers.put("versionCode", String.valueOf(BuildConfig.VERSION_CODE));
|
||||
headers.put("clientType", "android");
|
||||
headers.put("emulator", CommonAppContext.getInstance().emulator);
|
||||
headers.put("deviceName", SystemUtils.getDeviceBrand() + SystemUtils.getSystemModel() + SystemUtils.getSystemVersion());
|
||||
try {
|
||||
String channelId = MetaDataUtils.getMetaDataInApp("TD_CHANNEL_ID");
|
||||
headers.put("CHANNELID", channelId);
|
||||
headers.put("appName", encodeHeadInfo(AppUtils.getAppName()));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return headers;
|
||||
}
|
||||
|
||||
private static String encodeHeadInfo( String headInfo ) {
|
||||
StringBuffer stringBuffer = new StringBuffer();
|
||||
for (int i = 0, length = headInfo.length(); i < length; i++) {
|
||||
char c = headInfo.charAt(i);
|
||||
if (c <= '\u001f' || c >= '\u007f') {
|
||||
stringBuffer.append( String.format ("\\u%04x", (int)c) );
|
||||
} else {
|
||||
stringBuffer.append(c);
|
||||
}
|
||||
}
|
||||
return stringBuffer.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.qxcm.moduleutil.utils;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.lahm.library.EasyProtectorLib;
|
||||
import com.lahm.library.EmulatorCheckCallback;
|
||||
|
||||
/**
|
||||
* ProjectName: BubbleVoice
|
||||
* Package: com.lnkj.lib_utils
|
||||
* Description: java类作用描述
|
||||
* Author: 姚闻达
|
||||
* CreateDate: 2020/12/15 17:27
|
||||
* UpdateUser: 更新者
|
||||
* UpdateDate: 2020/12/15 17:27
|
||||
* UpdateRemark: 更新说明
|
||||
* Version: 1.0
|
||||
*/
|
||||
public class UtilConfig {
|
||||
private static Context context;
|
||||
public static String emulator = "0";
|
||||
public static String salt = "";
|
||||
|
||||
public static void init(Context context) {
|
||||
setContext(context);
|
||||
}
|
||||
|
||||
public static Context getContext() {
|
||||
return context;
|
||||
}
|
||||
|
||||
public static void setContext(Context context) {
|
||||
UtilConfig.context = context;
|
||||
// checkInEmulator();
|
||||
}
|
||||
|
||||
public static void checkInEmulator() {
|
||||
emulator = EasyProtectorLib.checkIsRunningInEmulator(getContext(), new EmulatorCheckCallback() {
|
||||
@Override
|
||||
public void findEmulator(String emulatorInfo) {
|
||||
// Logger.e(emulatorInfo);
|
||||
}
|
||||
}) ? "1" : "0";
|
||||
}
|
||||
|
||||
public static String getSalt() {
|
||||
return salt;
|
||||
}
|
||||
|
||||
public static void setSalt(String salt) {
|
||||
UtilConfig.salt = salt;
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import android.os.LocaleList;
|
||||
import android.util.DisplayMetrics;
|
||||
|
||||
|
||||
import com.qxcm.moduleutil.widget.CommonAppContext;
|
||||
import com.qxcm.moduleutil.base.CommonAppContext;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
|
||||
@@ -0,0 +1,163 @@
|
||||
package com.qxcm.moduleutil.utils.config;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class ConfigUtils {
|
||||
|
||||
private static SharedPreferences sp = null;
|
||||
private static SharedPreferences.Editor edit = null;
|
||||
private static ConfigUtils instance = null;
|
||||
private static Map<String, Object> data_list = null;
|
||||
private static String data_list_name = "config";
|
||||
|
||||
|
||||
public static ConfigUtils getInstance(Context context) {
|
||||
if (instance == null) {
|
||||
sp = context.getSharedPreferences(data_list_name, Context.MODE_PRIVATE);
|
||||
instance = new ConfigUtils();
|
||||
if (data_list == null)
|
||||
data_list = new HashMap<String, Object>();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
//设置配置文件的名称
|
||||
public void setConfigName(String configName) {
|
||||
this.data_list_name = configName;
|
||||
}
|
||||
|
||||
/*
|
||||
* 添加或者更新一个方法
|
||||
*/
|
||||
public boolean addOrUpdateText(String key, String content) {
|
||||
if (edit == null) {
|
||||
edit = sp.edit();
|
||||
}
|
||||
data_list.put(key, content);
|
||||
edit.putString(key, content);
|
||||
return edit.commit();
|
||||
}
|
||||
|
||||
public boolean addOrUpdateIntNumber(String key, int content) {
|
||||
if (edit == null) {
|
||||
edit = sp.edit();
|
||||
}
|
||||
data_list.put(key, content);
|
||||
edit.putInt(key, content);
|
||||
return edit.commit();
|
||||
}
|
||||
|
||||
public boolean addOrUpdateFloatNumber(String key, float content) {
|
||||
if (edit == null) {
|
||||
edit = sp.edit();
|
||||
}
|
||||
data_list.put(key, content);
|
||||
edit.putFloat(key, content);
|
||||
return edit.commit();
|
||||
}
|
||||
|
||||
public boolean addOrUpdateBoolean(String key, boolean content) {
|
||||
if (edit == null) {
|
||||
edit = sp.edit();
|
||||
}
|
||||
data_list.put(key, content);
|
||||
edit.putBoolean(key, content);
|
||||
return edit.commit();
|
||||
}
|
||||
|
||||
/*
|
||||
* 查询
|
||||
*/
|
||||
|
||||
public String findStringByKey(String key) {
|
||||
if (data_list.containsKey(key))
|
||||
return data_list.get(key).toString();
|
||||
else
|
||||
return sp.getString(key, null);
|
||||
}
|
||||
|
||||
|
||||
public String findStringByKey(String key, String default_value) {
|
||||
if (data_list.containsKey(key)) {
|
||||
return data_list.get(key).toString();
|
||||
} else {
|
||||
return sp.getString(key, default_value);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean findBoolByKey(String key) {
|
||||
if (data_list.containsKey(key)) {
|
||||
return (Boolean) data_list.get(key);
|
||||
} else {
|
||||
return sp.getBoolean(key, false);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean findBoolByKey(String key, boolean default_value) {
|
||||
if (data_list.containsKey(key)) {
|
||||
return (Boolean) data_list.get(key);
|
||||
} else {
|
||||
return sp.getBoolean(key, default_value);
|
||||
}
|
||||
}
|
||||
|
||||
public int findIntByKey(String key) {
|
||||
if (data_list.containsKey(key)) {
|
||||
return (Integer) data_list.get(key);
|
||||
} else {
|
||||
return sp.getInt(key, -1);
|
||||
}
|
||||
}
|
||||
|
||||
public int findIntByKey(String key, int default_value) {
|
||||
if (data_list.containsKey(key)) {
|
||||
return (Integer) data_list.get(key);
|
||||
} else {
|
||||
return sp.getInt(key, default_value);
|
||||
}
|
||||
}
|
||||
|
||||
public float findFloatByKey(String key) {
|
||||
if (data_list.containsKey(key)) {
|
||||
return (Float) data_list.get(key);
|
||||
} else {
|
||||
return sp.getFloat(key, -1);
|
||||
}
|
||||
}
|
||||
|
||||
public float findFloatByKey(String key, float default_value) {
|
||||
if (data_list.containsKey(key)) {
|
||||
return (Float) data_list.get(key);
|
||||
} else {
|
||||
return sp.getFloat(key, default_value);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 删除
|
||||
*/
|
||||
public void deleteByKey(String key) {
|
||||
if (edit == null) {
|
||||
edit = sp.edit();
|
||||
}
|
||||
data_list.remove(key);
|
||||
edit.remove(key);
|
||||
edit.commit();
|
||||
}
|
||||
|
||||
/*
|
||||
* 清空
|
||||
*/
|
||||
public void clearConfig() {
|
||||
if (edit == null) {
|
||||
edit = sp.edit();
|
||||
}
|
||||
data_list.clear();
|
||||
edit.clear();
|
||||
edit.commit();
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,48 @@
|
||||
package com.qxcm.moduleutil.utils.config;
|
||||
|
||||
public enum EnvironmentEnum {
|
||||
PRODUCTION("https://api.production.com", "6rdWuz058oq5OahdbFiGEybUcdahd12J83L34Uc7MrPIrxtFG+rXiwDvRcqNvjwbClbbmvMrmxKVkIysFByBsl0Qe9kqd2w8T/nhK5G6eXXlk2V9AjYCieIU+jRnjZBB+Cfechr6rCGJ2aeBARIsXcRPW7wm9WFK9euh5T+v6Pyte68yNaNdcYCll3+U4/uCEog7HygCnMIbAU+kqoPdmn2H+51YOHW+VsnsHd4w1+I3f8Tt0xLIXGM4GWnQueZ5GR46GTWiSYMy8dCIh9SPIMRyC91GosVcfGPMJSdcXqc="),
|
||||
TEST("https://api.test.com", "6rdWuz058oq5OahdbFiGEybUcdahd12J83L34Uc7MrPIrxtFG+rXiwDvRcqNvjwbClbbmvMrmxKVkIysFByBsl0Qe9kqd2w8T/nhK5G6eXXlk2V9AjYCieIU+jRnjZBB+Cfechr6rCGJ2aeBARIsXcRPW7wm9WFK9euh5T+v6Pyte68yNaNdcYCll3+U4/uCEog7HygCnMIbAU+kqoPdmn2H+51YOHW+VsnsHd4w1+I3f8Tt0xLIXGM4GWnQueZ5GR46GTWiSYMy8dCIh9SPIMRyC91GosVcfGPMJSdcXqc=");
|
||||
PRODUCTION("http://chat.qxmier.com/",
|
||||
"6rdWuz058oq5OahdbFiGEybUcdahd12J83L34Uc7MrPIrxtFG+rXiwDvRcqNvjwbClbbmvMrmxKVkIysFByBsl0Qe9kqd2w8T/nhK5G6eXXlk2V9AjYCieIU+jRnjZBB+Cfechr6rCGJ2aeBARIsXcRPW7wm9WFK9euh5T+v6Pyte68yNaNdcYCll3+U4/uCEog7HygCnMIbAU+kqoPdmn2H+51YOHW+VsnsHd4w1+I3f8Tt0xLIXGM4GWnQueZ5GR46GTWiSYMy8dCIh9SPIMRyC91GosVcfGPMJSdcXqc=",
|
||||
"https://oss-cn-hangzhou.aliyuncs.com/",
|
||||
"LTAI5tJ2UYfFNF7K3F4e1siv",
|
||||
"DhpCS82gaigZljYqsWsUWUAZ20dREz",
|
||||
"qx-yusheng",
|
||||
"https://qx-yusheng.oss-cn-hangzhou.aliyuncs.com/",
|
||||
"wx9b6db036ca1073a2"),
|
||||
TEST(
|
||||
"http://chat.qxmier.com/",
|
||||
"6rdWuz058oq5OahdbFiGEybUcdahd12J83L34Uc7MrPIrxtFG+rXiwDvRcqNvjwbClbbmvMrmxKVkIysFByBsl0Qe9kqd2w8T/nhK5G6eXXlk2V9AjYCieIU+jRnjZBB+Cfechr6rCGJ2aeBARIsXcRPW7wm9WFK9euh5T+v6Pyte68yNaNdcYCll3+U4/uCEog7HygCnMIbAU+kqoPdmn2H+51YOHW+VsnsHd4w1+I3f8Tt0xLIXGM4GWnQueZ5GR46GTWiSYMy8dCIh9SPIMRyC91GosVcfGPMJSdcXqc=",
|
||||
"https://oss-cn-hangzhou.aliyuncs.com/",
|
||||
"LTAI5tJ2UYfFNF7K3F4e1siv",
|
||||
"DhpCS82gaigZljYqsWsUWUAZ20dREz",
|
||||
"qx-yusheng",
|
||||
"https://qx-yusheng.oss-cn-hangzhou.aliyuncs.com/",
|
||||
"wx9b6db036ca1073a2");
|
||||
|
||||
private final String serverUrl;
|
||||
private final String ALI_AUTH_KEY;//阿里云授权key
|
||||
|
||||
EnvironmentEnum(String serverUrl, String ALI_AUTH_KEY) {
|
||||
private final String ossEndPoint;
|
||||
private final String ossaAcessKeyId;
|
||||
private final String ossAccessKeySecret;
|
||||
private final String ossBucketName;
|
||||
private final String ossBaseUrl;
|
||||
|
||||
private final String wxAppId;
|
||||
|
||||
EnvironmentEnum(String serverUrl, String ALI_AUTH_KEY, String ossEndPoint, String ossaAcessKeyId, String ossAccessKeySecret, String ossBucketName, String ossBaseUrl,String wxAppId) {
|
||||
this.serverUrl = serverUrl;
|
||||
this.ALI_AUTH_KEY = ALI_AUTH_KEY;
|
||||
this.ossEndPoint = ossEndPoint;
|
||||
this.ossaAcessKeyId = ossaAcessKeyId;
|
||||
this.ossAccessKeySecret = ossAccessKeySecret;
|
||||
this.ossBucketName = ossBucketName;
|
||||
this.ossBaseUrl = ossBaseUrl;
|
||||
this.wxAppId=wxAppId;
|
||||
}
|
||||
|
||||
public String getWxAppId() {
|
||||
return wxAppId;
|
||||
}
|
||||
|
||||
public String getServerUrl() {
|
||||
@@ -20,6 +53,23 @@ public enum EnvironmentEnum {
|
||||
return ALI_AUTH_KEY;
|
||||
}
|
||||
|
||||
public String getOssEndPoint() {
|
||||
return ossEndPoint;
|
||||
}
|
||||
|
||||
public String getOssaAcessKeyId() {
|
||||
return ossaAcessKeyId;
|
||||
}
|
||||
|
||||
public String getOssAccessKeySecret() {
|
||||
return ossAccessKeySecret;
|
||||
}
|
||||
|
||||
public String getOssBucketName() {
|
||||
return ossBucketName;
|
||||
}
|
||||
|
||||
public String getOssBaseUrl() {
|
||||
return ossBaseUrl;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,206 @@
|
||||
package com.qxcm.moduleutil.utils.logger;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* SCore
|
||||
* Created by ShangChuanliang
|
||||
* on 2017/10/12.
|
||||
*/
|
||||
public final class DataLogger implements DataLoggingInterceptor.Logger {
|
||||
private final StringBuilder mMessage = new StringBuilder();
|
||||
|
||||
public static class LogAdapter extends com.orhanobut.logger.AndroidLogAdapter {
|
||||
private final boolean mDebuggable;
|
||||
|
||||
public LogAdapter(boolean debuggable) {
|
||||
super();
|
||||
mDebuggable = debuggable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLoggable(int priority, @Nullable String tag) {
|
||||
// return super.isLoggable(priority, tag);
|
||||
return mDebuggable;
|
||||
}
|
||||
}
|
||||
|
||||
public static class LogUtil {
|
||||
public static void init(boolean debuggable) {
|
||||
com.orhanobut.logger.Logger.addLogAdapter(new LogAdapter(debuggable));
|
||||
}
|
||||
|
||||
public static void d(String message) {
|
||||
com.orhanobut.logger.Logger.d(message);
|
||||
}
|
||||
|
||||
public static void i(String message) {
|
||||
com.orhanobut.logger.Logger.i(message);
|
||||
}
|
||||
|
||||
public static void w(String message, Throwable e) {
|
||||
String info = e != null ? e.toString() : "null";
|
||||
com.orhanobut.logger.Logger.w(message + ":" + info);
|
||||
}
|
||||
|
||||
public static void e(String message, Throwable e) {
|
||||
com.orhanobut.logger.Logger.e(e, message);
|
||||
}
|
||||
|
||||
public static void json(String json) {
|
||||
com.orhanobut.logger.Logger.json(json);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(String message) {
|
||||
// 请求或者响应开始
|
||||
if (message.startsWith(DataLoggingInterceptor.sLogStartFlag)) {
|
||||
mMessage.setLength(0);
|
||||
return;
|
||||
}
|
||||
// 响应结束,打印整条日志
|
||||
if (message.startsWith(DataLoggingInterceptor.sLogEndFlag)) {
|
||||
LogUtil.d(mMessage.toString());
|
||||
return;
|
||||
}
|
||||
|
||||
// 以{}或者[]形式的说明是响应结果的json数据,需要进行格式化
|
||||
if ((message.startsWith("{") && message.endsWith("}"))
|
||||
|| (message.startsWith("[") && message.endsWith("]"))) {
|
||||
message = formatJson(decodeUnicode(message));
|
||||
}
|
||||
// 追加消息
|
||||
try { mMessage.append(message.concat("\n"));
|
||||
} catch (Exception ignored) {}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
mMessage.setLength(0);
|
||||
}
|
||||
|
||||
private static String formatJson(String jsonStr) {
|
||||
if (null == jsonStr || "".equals(jsonStr)) return "";
|
||||
StringBuilder sb = new StringBuilder();
|
||||
char last = '\0';
|
||||
char current = '\0';
|
||||
int indent = 0;
|
||||
for (int i = 0; i < jsonStr.length(); i++) {
|
||||
last = current;
|
||||
current = jsonStr.charAt(i);
|
||||
//遇到{ [换行,且下一行缩进
|
||||
switch (current) {
|
||||
case '{':
|
||||
case '[':
|
||||
sb.append(current);
|
||||
sb.append('\n');
|
||||
indent++;
|
||||
addIndentBlank(sb, indent);
|
||||
break;
|
||||
//遇到} ]换行,当前行缩进
|
||||
case '}':
|
||||
case ']':
|
||||
sb.append('\n');
|
||||
indent--;
|
||||
addIndentBlank(sb, indent);
|
||||
sb.append(current);
|
||||
break;
|
||||
//遇到,换行
|
||||
case ',':
|
||||
sb.append(current);
|
||||
if (last != '\\') {
|
||||
sb.append('\n');
|
||||
addIndentBlank(sb, indent);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
sb.append(current);
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加space
|
||||
*
|
||||
* @param sb
|
||||
* @param indent
|
||||
*/
|
||||
private static void addIndentBlank(StringBuilder sb, int indent) {
|
||||
for (int i = 0; i < indent; i++) {
|
||||
sb.append('\t');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* http 请求数据返回 json 中中文字符为 unicode 编码转汉字转码
|
||||
*
|
||||
* @param theString
|
||||
* @return 转化后的结果.
|
||||
*/
|
||||
private static String decodeUnicode(String theString) {
|
||||
char aChar;
|
||||
int len = theString.length();
|
||||
StringBuilder outBuffer = new StringBuilder(len);
|
||||
for (int x = 0; x < len; ) {
|
||||
aChar = theString.charAt(x++);
|
||||
if (aChar == '\\') {
|
||||
aChar = theString.charAt(x++);
|
||||
if (aChar == 'u') {
|
||||
int value = 0;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
aChar = theString.charAt(x++);
|
||||
switch (aChar) {
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
value = (value << 4) + aChar - '0';
|
||||
break;
|
||||
case 'a':
|
||||
case 'b':
|
||||
case 'c':
|
||||
case 'd':
|
||||
case 'e':
|
||||
case 'f':
|
||||
value = (value << 4) + 10 + aChar - 'a';
|
||||
break;
|
||||
case 'A':
|
||||
case 'B':
|
||||
case 'C':
|
||||
case 'D':
|
||||
case 'E':
|
||||
case 'F':
|
||||
value = (value << 4) + 10 + aChar - 'A';
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException(
|
||||
"Malformed \\uxxxx encoding.");
|
||||
}
|
||||
|
||||
}
|
||||
outBuffer.append((char) value);
|
||||
} else {
|
||||
if (aChar == 't')
|
||||
aChar = '\t';
|
||||
else if (aChar == 'r')
|
||||
aChar = '\r';
|
||||
else if (aChar == 'n')
|
||||
aChar = '\n';
|
||||
else if (aChar == 'f')
|
||||
aChar = '\f';
|
||||
outBuffer.append(aChar);
|
||||
}
|
||||
} else
|
||||
outBuffer.append(aChar);
|
||||
}
|
||||
return outBuffer.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,215 @@
|
||||
package com.qxcm.moduleutil.utils.logger;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.net.URLDecoder;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import okhttp3.Connection;
|
||||
import okhttp3.Headers;
|
||||
import okhttp3.Interceptor;
|
||||
import okhttp3.MediaType;
|
||||
import okhttp3.Protocol;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.Response;
|
||||
import okhttp3.ResponseBody;
|
||||
import okhttp3.internal.http.HttpHeaders;
|
||||
import okio.Buffer;
|
||||
import okio.BufferedSource;
|
||||
|
||||
/**
|
||||
* SCore
|
||||
* Created by ShangChuanliang
|
||||
* on 2017/12/21.
|
||||
*/
|
||||
public class DataLoggingInterceptor implements Interceptor {
|
||||
private static final String sFormatLine =
|
||||
"===========================================================================================";
|
||||
public static final String sLogStartFlag = "==scliang==log==start==";
|
||||
public static final String sLogEndFlag = "==scliang==log==end==";
|
||||
private static final Charset UTF8 = StandardCharsets.UTF_8;
|
||||
|
||||
public interface Logger {
|
||||
void reset();
|
||||
void log(String message);
|
||||
}
|
||||
|
||||
public DataLoggingInterceptor(Logger logger) {
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
private final Logger logger;
|
||||
|
||||
@NotNull
|
||||
@Override public Response intercept(@NonNull Chain chain) throws IOException {
|
||||
Request request = chain.request();
|
||||
|
||||
logger.reset();
|
||||
logger.log(sLogStartFlag);
|
||||
logger.log(sFormatLine);
|
||||
|
||||
RequestBody requestBody = request.body();
|
||||
boolean hasRequestBody = requestBody != null;
|
||||
|
||||
Connection connection = chain.connection();
|
||||
Protocol protocol = connection != null ? connection.protocol() : Protocol.HTTP_1_1;
|
||||
String requestStartMessage = request.method() + " " + request.url() + " " + protocol;
|
||||
logger.log(requestStartMessage);
|
||||
|
||||
if (hasRequestBody) {
|
||||
// Request body headers are only present when installed as a network interceptor. Force
|
||||
// them to be included (when available) so there values are known.
|
||||
if (requestBody.contentType() != null) {
|
||||
logger.log("Content-Type: " + requestBody.contentType());
|
||||
}
|
||||
if (requestBody.contentLength() != -1) {
|
||||
logger.log("Content-Length: " + requestBody.contentLength());
|
||||
}
|
||||
}
|
||||
|
||||
Headers rHeaders = request.headers();
|
||||
for (int i = 0, count = rHeaders.size(); i < count; i++) {
|
||||
String name = rHeaders.name(i);
|
||||
// Skip headers from the request body as they are explicitly logged above.
|
||||
if (!"Content-Type".equalsIgnoreCase(name) && !"Content-Length".equalsIgnoreCase(name)) {
|
||||
logger.log(name + ": " + rHeaders.value(i));
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasRequestBody) {
|
||||
logger.log("END " + request.method());
|
||||
} else if (bodyEncoded(request.headers())) {
|
||||
logger.log("END " + request.method() + " (encoded body omitted)");
|
||||
} else {
|
||||
Buffer buffer = new Buffer();
|
||||
requestBody.writeTo(buffer);
|
||||
|
||||
Charset charset = UTF8;
|
||||
MediaType contentType = requestBody.contentType();
|
||||
if (contentType != null) {
|
||||
charset = contentType.charset(UTF8);
|
||||
}
|
||||
|
||||
if (charset != null) {
|
||||
logger.log(sFormatLine);
|
||||
if (isPlaintext(buffer)) {
|
||||
try {
|
||||
String requestStr = URLDecoder.decode(buffer.readString(charset), "UTF-8");
|
||||
String[] strs = requestStr.split("&");
|
||||
for (String str : strs) {
|
||||
logger.log(str);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.log(buffer.readString(charset));
|
||||
}
|
||||
logger.log("END " + request.method()
|
||||
+ " (" + requestBody.contentLength() + "-byte body)");
|
||||
} else {
|
||||
logger.log("END " + request.method() + " (binary "
|
||||
+ requestBody.contentLength() + "-byte body omitted)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logger.log(sFormatLine);
|
||||
|
||||
long startNs = System.nanoTime();
|
||||
Response response;
|
||||
try {
|
||||
response = chain.proceed(request);
|
||||
} catch (Exception e) {
|
||||
logger.log("HTTP FAILED: " + e);
|
||||
logger.log(sLogEndFlag);
|
||||
throw e;
|
||||
}
|
||||
|
||||
long tookMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs);
|
||||
ResponseBody responseBody = response.body();
|
||||
if (responseBody != null) {
|
||||
long contentLength = responseBody.contentLength();
|
||||
String bodySize = contentLength != -1 ? contentLength + "-byte" : "unknown-length";
|
||||
logger.log(response.code() + " " + response.message() + " "
|
||||
+ response.request().url() + " (" + tookMs + "ms)");
|
||||
|
||||
Headers headers = response.headers();
|
||||
for (int i = 0, count = headers.size(); i < count; i++) {
|
||||
logger.log(headers.name(i) + ": " + headers.value(i));
|
||||
}
|
||||
|
||||
if (!HttpHeaders.hasBody(response)) {
|
||||
logger.log("END HTTP");
|
||||
} else if (bodyEncoded(response.headers())) {
|
||||
logger.log("END HTTP (encoded body omitted)");
|
||||
} else {
|
||||
BufferedSource source = responseBody.source();
|
||||
if (source.isOpen()) {
|
||||
source.request(Long.MAX_VALUE); // Buffer the entire body.
|
||||
Buffer buffer = source.buffer();
|
||||
|
||||
Charset charset = UTF8;
|
||||
MediaType contentType = responseBody.contentType();
|
||||
if (contentType != null) {
|
||||
charset = contentType.charset(UTF8);
|
||||
}
|
||||
|
||||
if (!isPlaintext(buffer)) {
|
||||
logger.log("END HTTP (binary " + buffer.size() + "-byte body omitted)");
|
||||
logger.log(sFormatLine);
|
||||
logger.log(sLogEndFlag);
|
||||
return response;
|
||||
}
|
||||
|
||||
if (contentLength != 0 && charset != null) {
|
||||
logger.log(sFormatLine);
|
||||
logger.log(buffer.clone().readString(charset));
|
||||
}
|
||||
|
||||
logger.log("END HTTP (" + buffer.size() + "-byte body)");
|
||||
} else {
|
||||
logger.log("END HTTP");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logger.log(sFormatLine);
|
||||
logger.log(sLogEndFlag);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the body in question probably contains human readable text. Uses a small sample
|
||||
* of code points to detect unicode control characters commonly used in binary file signatures.
|
||||
*/
|
||||
private static boolean isPlaintext(Buffer buffer) {
|
||||
try {
|
||||
Buffer prefix = new Buffer();
|
||||
long byteCount = buffer.size() < 64 ? buffer.size() : 64;
|
||||
buffer.copyTo(prefix, 0, byteCount);
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (prefix.exhausted()) {
|
||||
break;
|
||||
}
|
||||
int codePoint = prefix.readUtf8CodePoint();
|
||||
if (Character.isISOControl(codePoint) && !Character.isWhitespace(codePoint)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} catch (EOFException e) {
|
||||
return false; // Truncated UTF-8 sequence.
|
||||
}
|
||||
}
|
||||
|
||||
private boolean bodyEncoded(Headers headers) {
|
||||
String contentEncoding = headers.get("Content-Encoding");
|
||||
return contentEncoding != null && !contentEncoding.equalsIgnoreCase("identity");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,312 @@
|
||||
package com.qxcm.moduleutil.utils.oss;
|
||||
|
||||
import com.alibaba.sdk.android.oss.ClientConfiguration;
|
||||
import com.alibaba.sdk.android.oss.ClientException;
|
||||
import com.alibaba.sdk.android.oss.OSS;
|
||||
import com.alibaba.sdk.android.oss.OSSClient;
|
||||
import com.alibaba.sdk.android.oss.ServiceException;
|
||||
import com.alibaba.sdk.android.oss.callback.OSSCompletedCallback;
|
||||
import com.alibaba.sdk.android.oss.callback.OSSProgressCallback;
|
||||
import com.alibaba.sdk.android.oss.common.OSSLog;
|
||||
import com.alibaba.sdk.android.oss.common.auth.OSSCredentialProvider;
|
||||
import com.alibaba.sdk.android.oss.common.auth.OSSPlainTextAKSKCredentialProvider;
|
||||
import com.alibaba.sdk.android.oss.internal.OSSAsyncTask;
|
||||
import com.alibaba.sdk.android.oss.model.PutObjectRequest;
|
||||
import com.alibaba.sdk.android.oss.model.PutObjectResult;
|
||||
import com.blankj.utilcode.util.FileUtils;
|
||||
import com.blankj.utilcode.util.ThreadUtils;
|
||||
import com.qxcm.moduleutil.base.CommonAppContext;
|
||||
import com.qxcm.moduleutil.utils.Md5Utils;
|
||||
import com.qxcm.moduleutil.utils.logger.Logger;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
|
||||
/**
|
||||
* Created by zjh on 2016/2/22.
|
||||
*/
|
||||
public class OSSOperUtils {
|
||||
private static OSS oss;
|
||||
private static OSSOperUtils utils;
|
||||
|
||||
/**
|
||||
* GetObjectRequest get = new GetObjectRequest("duia-log", name);
|
||||
* <p>
|
||||
* String endpoint = "https://oss-cn-beijing.aliyuncs.com";
|
||||
* <p>
|
||||
* // 明文设置secret的方式建议只在测试时使用,更多鉴权模式请参考后面的`访问控制`章节
|
||||
* OSSCredentialProvider credentialProvider = new OSSPlainTextAKSKCredentialProvider("qgR150FXSbdnCi5e", "wi4jUJvFgKZXkPp63vCY8nA849GpLT");
|
||||
* <p>
|
||||
* OSS
|
||||
* Access Key ID: LTAIdLLZpWaeXb6j
|
||||
* Access Key Secret: rBUwiyKNk8ddRaJMDYBucrTrxIidgr
|
||||
* AliYunOSSURLFile: zholiao.oss-cn-shenzhen.aliyuncs.com
|
||||
*/
|
||||
|
||||
|
||||
// 运行sample前需要配置以下字段为有效的值
|
||||
private static final String endpoint = CommonAppContext.getInstance().getCurrentEnvironment().getOssEndPoint();
|
||||
private static final String accessKeyId = CommonAppContext.getInstance().getCurrentEnvironment().getOssaAcessKeyId();
|
||||
private static final String accessKeySecret = CommonAppContext.getInstance().getCurrentEnvironment().getOssAccessKeySecret();
|
||||
private static final String bucketName = CommonAppContext.getInstance().getCurrentEnvironment().getOssBucketName();
|
||||
public static final String AliYunOSSURLFile = CommonAppContext.getInstance().getCurrentEnvironment().getOssBaseUrl();
|
||||
public static final String img = "img/";
|
||||
public static final String video = "video/";
|
||||
public static final String audio = "audio/";
|
||||
|
||||
|
||||
/**
|
||||
* 文件
|
||||
*
|
||||
* @param file
|
||||
* @param type 0 图片 1音频 2视频
|
||||
* @return
|
||||
*/
|
||||
public static String getPath(File file, int type) {
|
||||
String fileMD5 = Md5Utils.getFileMD5(file);
|
||||
String suffix = "";
|
||||
if (file.getName().contains(".")) {
|
||||
suffix = file.getName().substring(file.getName().lastIndexOf("."));
|
||||
}
|
||||
if (type == 0) {
|
||||
return img + fileMD5 + suffix;
|
||||
} else if (type == 1) {
|
||||
return audio + fileMD5 + suffix;
|
||||
} else {
|
||||
return video + fileMD5 + suffix;
|
||||
}
|
||||
}
|
||||
|
||||
public static OSSOperUtils newInstance() {
|
||||
if (null == utils) {
|
||||
utils = new OSSOperUtils();
|
||||
}
|
||||
if (null == oss) {
|
||||
OSSCredentialProvider credentialProvider = new OSSPlainTextAKSKCredentialProvider(accessKeyId, accessKeySecret);
|
||||
ClientConfiguration conf = new ClientConfiguration();
|
||||
conf.setConnectionTimeout(150 * 1000); // 连接超时,默认15秒
|
||||
conf.setSocketTimeout(150 * 1000); // socket超时,默认15秒
|
||||
conf.setMaxConcurrentRequest(5); // 最大并发请求书,默认5个
|
||||
conf.setMaxErrorRetry(2); // 失败后最大重试次数,默认2次
|
||||
OSSLog.enableLog();
|
||||
oss = new OSSClient(CommonAppContext.getInstance(), endpoint, credentialProvider, conf);
|
||||
}
|
||||
return utils;
|
||||
}
|
||||
|
||||
//上传
|
||||
public OSSAsyncTask putObjectMethod(String uploadObject, String uploadFilePath, OssCallback ossCallback) {
|
||||
PutObjectSamples put = new PutObjectSamples(oss, bucketName, uploadObject, uploadFilePath);
|
||||
return put.asyncPutObjectFromLocalFile(new OSSProgressCallback<PutObjectRequest>() {
|
||||
@Override
|
||||
public void onProgress(PutObjectRequest request, long currentSize, long totalSize) {
|
||||
|
||||
}
|
||||
}, new OSSCompletedCallback<PutObjectRequest, PutObjectResult>() {
|
||||
@Override
|
||||
public void onSuccess(PutObjectRequest request, PutObjectResult result) {
|
||||
ThreadUtils.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (ossCallback != null) {
|
||||
ossCallback.onSuccess();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(PutObjectRequest request, ClientException clientException, ServiceException serviceException) {
|
||||
ThreadUtils.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (ossCallback != null) {
|
||||
ossCallback.onFail();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
//上传
|
||||
public OSSAsyncTask putObjectMethod(String uploadObject, String uploadFilePath, OSSProgressCallback mossProgressCallback, OSSCompletedCallback mcompletedCallback) {
|
||||
PutObjectSamples put = new PutObjectSamples(oss, bucketName, uploadObject, uploadFilePath);
|
||||
return put.asyncPutObjectFromLocalFile(mossProgressCallback, mcompletedCallback);
|
||||
}
|
||||
|
||||
//判断文件是否存在
|
||||
public String fileExist(String fileName) {
|
||||
try {
|
||||
boolean exist = oss.doesObjectExist(bucketName, fileName);
|
||||
Logger.e(bucketName, fileName, exist);
|
||||
if (exist) {
|
||||
//加个参数去缓存
|
||||
return String.format("%s/%s?v=1", AliYunOSSURLFile, fileName);
|
||||
}
|
||||
} catch (ClientException e) {
|
||||
e.printStackTrace();
|
||||
} catch (ServiceException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public interface OssCallback {
|
||||
void onSuccess();
|
||||
|
||||
void onFail();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 发布动态上传图片或者视频
|
||||
*
|
||||
* @param file
|
||||
* @param type 0 img 1 audio 2 video
|
||||
*/
|
||||
public void uploadImageOrVideo(File file, int type, UploadCallback uploadCallback) {
|
||||
uploadCallback.startUpload();
|
||||
String objectKey = getPath(file, type);
|
||||
long size;
|
||||
size = FileUtils.getFileLength(file.getPath());
|
||||
if (-1 == size) { //文件不存在
|
||||
uploadCallback.uploadFailed();
|
||||
return;
|
||||
}
|
||||
boolean exist;
|
||||
try {
|
||||
exist = oss.doesObjectExist(bucketName, objectKey);
|
||||
String format = String.format("%s%s", AliYunOSSURLFile, objectKey);
|
||||
|
||||
if (false) { //Oss服务器文件存在
|
||||
uploadCallback.onProgress(size, size);
|
||||
uploadCallback.uploadEnd(format);
|
||||
} else { //文件不存在
|
||||
PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, objectKey, file.getAbsolutePath());
|
||||
putObjectRequest.setProgressCallback((request, currentSize, totalSize) -> {
|
||||
ThreadUtils.runOnUiThread(() -> uploadCallback.onProgress(currentSize, totalSize));
|
||||
});
|
||||
oss.asyncPutObject(putObjectRequest, new OSSCompletedCallback<PutObjectRequest, PutObjectResult>() {
|
||||
@Override
|
||||
public void onSuccess(PutObjectRequest request, PutObjectResult result) {
|
||||
ThreadUtils.runOnUiThread(() -> uploadCallback.uploadEnd(format));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(PutObjectRequest request, ClientException clientException, ServiceException serviceException) {
|
||||
ThreadUtils.runOnUiThread(uploadCallback::uploadFailed);
|
||||
clientException.printStackTrace();
|
||||
serviceException.printStackTrace();
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (ClientException | ServiceException e) {
|
||||
e.printStackTrace();
|
||||
uploadCallback.uploadFailed();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发布动态上传图片或者视频
|
||||
*
|
||||
* @param file
|
||||
* @param type 0 img 1 audio 2 video
|
||||
*/
|
||||
public String uploadImageOrVideo(File file, int type) {
|
||||
String objectKey = getPath(file, type);
|
||||
long size;
|
||||
size = FileUtils.getFileLength(file.getPath());
|
||||
if (-1 == size) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, objectKey, file.getAbsolutePath());
|
||||
putObjectRequest.setProgressCallback((request, currentSize, totalSize) -> {});
|
||||
oss.putObject(putObjectRequest);
|
||||
return String.format("%s%s", AliYunOSSURLFile, objectKey);
|
||||
} catch (ClientException | ServiceException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发布动态上传封面用
|
||||
*
|
||||
* @param bytes
|
||||
* @param uploadCallback
|
||||
*/
|
||||
public void uploadBitmap(byte[] bytes, UploadCallback uploadCallback) {
|
||||
uploadCallback.startUpload();
|
||||
int size = bytes.length;
|
||||
String md5 = Md5Utils.getMD5String(bytes);
|
||||
String objectKey = img + md5 + ".jpg";
|
||||
boolean exist;
|
||||
try {
|
||||
exist = oss.doesObjectExist(bucketName, objectKey);
|
||||
String format = String.format("%s%s", AliYunOSSURLFile, objectKey);
|
||||
|
||||
if (false) { //Oss服务器文件存在
|
||||
uploadCallback.onProgress(size, size);
|
||||
uploadCallback.uploadEnd(format);
|
||||
} else { //文件不存在
|
||||
PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, objectKey, bytes);
|
||||
putObjectRequest.setProgressCallback((request, currentSize, totalSize) -> {
|
||||
ThreadUtils.runOnUiThread(() -> uploadCallback.onProgress(currentSize, totalSize));
|
||||
});
|
||||
oss.asyncPutObject(putObjectRequest, new OSSCompletedCallback<PutObjectRequest, PutObjectResult>() {
|
||||
@Override
|
||||
public void onSuccess(PutObjectRequest request, PutObjectResult result) {
|
||||
ThreadUtils.runOnUiThread(() -> uploadCallback.uploadEnd(format));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(PutObjectRequest request, ClientException clientException, ServiceException serviceException) {
|
||||
ThreadUtils.runOnUiThread(uploadCallback::uploadFailed);
|
||||
clientException.printStackTrace();
|
||||
serviceException.printStackTrace();
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (ClientException | ServiceException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发布动态上传封面用
|
||||
*
|
||||
* @param bytes
|
||||
*/
|
||||
public String uploadBitmap(byte[] bytes) {
|
||||
String md5 = Md5Utils.getMD5String(bytes);
|
||||
String objectKey = img + md5 + ".jpg";
|
||||
try {
|
||||
PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, objectKey, bytes);
|
||||
putObjectRequest.setProgressCallback((request, currentSize, totalSize) -> {
|
||||
});
|
||||
oss.putObject(putObjectRequest);
|
||||
return String.format("%s%s", AliYunOSSURLFile, objectKey);
|
||||
} catch (ClientException | ServiceException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传进度条回调
|
||||
*/
|
||||
public interface UploadCallback {
|
||||
|
||||
void startUpload();
|
||||
|
||||
void onProgress(long currentSize, long totalSize);
|
||||
|
||||
void uploadEnd(String url);
|
||||
|
||||
void uploadFailed();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package com.qxcm.moduleutil.utils.oss;
|
||||
|
||||
import com.alibaba.sdk.android.oss.ClientException;
|
||||
import com.alibaba.sdk.android.oss.OSS;
|
||||
import com.alibaba.sdk.android.oss.ServiceException;
|
||||
import com.alibaba.sdk.android.oss.callback.OSSCompletedCallback;
|
||||
import com.alibaba.sdk.android.oss.callback.OSSProgressCallback;
|
||||
import com.alibaba.sdk.android.oss.internal.OSSAsyncTask;
|
||||
import com.alibaba.sdk.android.oss.model.PutObjectRequest;
|
||||
import com.alibaba.sdk.android.oss.model.PutObjectResult;
|
||||
|
||||
/**
|
||||
* Created by zhouzhuo on 12/3/15.
|
||||
*/
|
||||
public class PutObjectSamples {
|
||||
|
||||
private OSS oss;
|
||||
private String testBucket;
|
||||
private String testObject;
|
||||
private String uploadFilePath;
|
||||
|
||||
|
||||
public PutObjectSamples(OSS client, String testBucket, String testObject, String uploadFilePath) {
|
||||
this.oss = client;
|
||||
this.testBucket = testBucket;
|
||||
this.testObject = testObject;
|
||||
this.uploadFilePath = uploadFilePath;
|
||||
}
|
||||
|
||||
// 从本地文件上传,使用非阻塞的异步接口
|
||||
public OSSAsyncTask asyncPutObjectFromLocalFile(final OSSProgressCallback<PutObjectRequest> mossProgressCallback, final OSSCompletedCallback<PutObjectRequest, PutObjectResult> mcompletedCallback) {
|
||||
// 构造上传请求
|
||||
PutObjectRequest put = new PutObjectRequest(testBucket, testObject, uploadFilePath);
|
||||
|
||||
// 异步上传时可以设置进度回调
|
||||
put.setProgressCallback(new OSSProgressCallback<PutObjectRequest>() {
|
||||
@Override
|
||||
public void onProgress(PutObjectRequest request, long currentSize, long totalSize) {
|
||||
if (mossProgressCallback != null) {
|
||||
mossProgressCallback.onProgress(request, currentSize, totalSize);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
OSSAsyncTask task = oss.asyncPutObject(put, new OSSCompletedCallback<PutObjectRequest, PutObjectResult>() {
|
||||
@Override
|
||||
public void onSuccess(PutObjectRequest request, PutObjectResult result) {
|
||||
if (mcompletedCallback != null) {
|
||||
mcompletedCallback.onSuccess(request, result);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(PutObjectRequest request, ClientException clientExcepion, ServiceException serviceException) {
|
||||
// 请求异常
|
||||
if (clientExcepion != null) {
|
||||
// 本地异常如网络异常等
|
||||
clientExcepion.printStackTrace();
|
||||
}
|
||||
if (mcompletedCallback != null) {
|
||||
mcompletedCallback.onFailure(request, clientExcepion, serviceException);
|
||||
}
|
||||
}
|
||||
});
|
||||
return task;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
package com.qxcm.moduleutil.utils.permission;
|
||||
|
||||
import android.app.AppOpsManager;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Binder;
|
||||
import android.os.Build;
|
||||
import android.provider.Settings;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.app.AppOpsManagerCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import com.blankj.utilcode.util.AppUtils;
|
||||
import com.qxcm.moduleutil.utils.permission.rom.HuaweiUtils;
|
||||
import com.qxcm.moduleutil.utils.permission.rom.MeizuUtils;
|
||||
import com.qxcm.moduleutil.utils.permission.rom.MiuiUtils;
|
||||
import com.qxcm.moduleutil.utils.permission.rom.OppoUtils;
|
||||
import com.qxcm.moduleutil.utils.permission.rom.QikuUtils;
|
||||
import com.qxcm.moduleutil.utils.permission.rom.RomUtils;
|
||||
import com.qxcm.moduleutil.widget.dialog.CommonDialog;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by liyong on 2018/9/21.
|
||||
*/
|
||||
public class PermissionUtils {
|
||||
|
||||
public static final String TAG = "PermissionUtils";
|
||||
|
||||
public static void showDenied(Context context, String msg, CommonDialog.OnClickListener listener) {
|
||||
CommonDialog commonDialog = new CommonDialog(context);
|
||||
commonDialog.setContent(msg);
|
||||
commonDialog.setmOnClickListener(listener);
|
||||
commonDialog.show();
|
||||
}
|
||||
|
||||
public static void showDenied(Context context, String msg) {
|
||||
CommonDialog commonDialog = new CommonDialog(context);
|
||||
commonDialog.setContent(msg);
|
||||
commonDialog.setmOnClickListener(new CommonDialog.OnClickListener() {
|
||||
@Override
|
||||
public void onLeftClick() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRightClick() {
|
||||
AppUtils.launchAppDetailsSettings();
|
||||
}
|
||||
});
|
||||
commonDialog.show();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查悬浮窗权限
|
||||
*
|
||||
* @param context
|
||||
* @return
|
||||
*/
|
||||
public boolean checkFloatWindowPermission(Context context) {
|
||||
//6.0 版本之后由于 google 增加了对悬浮窗权限的管理,所以方式就统一了
|
||||
if (Build.VERSION.SDK_INT < 23) {
|
||||
if (RomUtils.checkIsMiuiRom()) {
|
||||
return MiuiUtils.checkFloatWindowPermission(context);
|
||||
} else if (RomUtils.checkIsMeizuRom()) {
|
||||
return MeizuUtils.checkFloatWindowPermission(context);
|
||||
} else if (RomUtils.checkIsHuaweiRom()) {
|
||||
return HuaweiUtils.checkFloatWindowPermission(context);
|
||||
} else if (RomUtils.checkIs360Rom()) {
|
||||
return QikuUtils.checkFloatWindowPermission(context);
|
||||
} else if (RomUtils.checkIsOppoRom()) {
|
||||
return OppoUtils.checkFloatWindowPermission(context);
|
||||
}
|
||||
}
|
||||
return commonROMPermissionCheck(context);
|
||||
}
|
||||
|
||||
public static boolean hasPermission(@NonNull Context context, @NonNull String permission) {
|
||||
List<String> permisstions = new ArrayList<>();
|
||||
permisstions.add(permission);
|
||||
return hasPermission(context, permisstions);
|
||||
}
|
||||
|
||||
/**
|
||||
* 系统层的权限判断
|
||||
*
|
||||
* @param context 上下文
|
||||
* @param permissions 申请的权限 Manifest.permission.READ_CONTACTS
|
||||
* @return 是否有权限 :其中有一个获取不了就是失败了
|
||||
*/
|
||||
public static boolean hasPermission(@NonNull Context context, @NonNull List<String> permissions) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return true;
|
||||
for (String permission : permissions) {
|
||||
try {
|
||||
String op = AppOpsManagerCompat.permissionToOp(permission);
|
||||
if (TextUtils.isEmpty(op)) continue;
|
||||
int result = AppOpsManagerCompat.noteOp(context, op, android.os.Process.myUid(), context.getPackageName());
|
||||
if (result == AppOpsManagerCompat.MODE_IGNORED) return false;
|
||||
AppOpsManager appOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
|
||||
String ops = AppOpsManager.permissionToOp(permission);
|
||||
int locationOp = appOpsManager.checkOp(ops, Binder.getCallingUid(), context.getPackageName());
|
||||
if (locationOp == AppOpsManager.MODE_IGNORED) return false;
|
||||
result = ContextCompat.checkSelfPermission(context, permission);
|
||||
if (result != PackageManager.PERMISSION_GRANTED) return false;
|
||||
} catch (Exception ex) {
|
||||
Log.e(TAG, "[hasPermission] error ", ex);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳转到权限设置界面
|
||||
*
|
||||
* @param context
|
||||
*/
|
||||
public static void toPermissionSetting(Context context) throws NoSuchFieldException, IllegalAccessException {
|
||||
if (Build.VERSION.SDK_INT < 23) {
|
||||
if (RomUtils.checkIsMiuiRom()) {
|
||||
MiuiUtils.applyMiuiPermission(context);
|
||||
} else if (RomUtils.checkIsMeizuRom()) {
|
||||
MeizuUtils.applyPermission(context);
|
||||
} else if (RomUtils.checkIsHuaweiRom()) {
|
||||
HuaweiUtils.applyPermission(context);
|
||||
} else if (RomUtils.checkIs360Rom()) {
|
||||
QikuUtils.applyPermission(context);
|
||||
} else if (RomUtils.checkIsOppoRom()) {
|
||||
OppoUtils.applyOppoPermission(context);
|
||||
} else {
|
||||
RomUtils.getAppDetailSettingIntent(context);
|
||||
}
|
||||
} else {
|
||||
if (RomUtils.checkIsMeizuRom()) {
|
||||
MeizuUtils.applyPermission(context);
|
||||
} else {
|
||||
if (RomUtils.checkIsOppoRom() || RomUtils.checkIsVivoRom()
|
||||
|| RomUtils.checkIsHuaweiRom() || RomUtils.checkIsSamsunRom()) {
|
||||
RomUtils.getAppDetailSettingIntent(context);
|
||||
} else if (RomUtils.checkIsMiuiRom()) {
|
||||
MiuiUtils.toPermisstionSetting(context);
|
||||
} else {
|
||||
RomUtils.commonROMPermissionApplyInternal(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean commonROMPermissionCheck(Context context) {
|
||||
//最新发现魅族6.0的系统这种方式不好用,天杀的,只有你是奇葩,没办法,单独适配一下
|
||||
if (RomUtils.checkIsMeizuRom()) {
|
||||
return MeizuUtils.checkFloatWindowPermission(context);
|
||||
} else {
|
||||
Boolean result = true;
|
||||
if (Build.VERSION.SDK_INT >= 23) {
|
||||
try {
|
||||
Class clazz = Settings.class;
|
||||
Method canDrawOverlays = clazz.getDeclaredMethod("canDrawOverlays", Context.class);
|
||||
result = (Boolean) canDrawOverlays.invoke(null, context);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, Log.getStackTraceString(e));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Facishare Technology Co., Ltd. All Rights Reserved.
|
||||
*/
|
||||
package com.qxcm.moduleutil.utils.permission.rom;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.AppOpsManager;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Binder;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class HuaweiUtils {
|
||||
private static final String TAG = "HuaweiUtils";
|
||||
|
||||
/**
|
||||
* 检测 Huawei 悬浮窗权限
|
||||
*/
|
||||
public static boolean checkFloatWindowPermission(Context context) {
|
||||
final int version = Build.VERSION.SDK_INT;
|
||||
if (version >= 19) {
|
||||
return checkOp(context, 24); //OP_SYSTEM_ALERT_WINDOW = 24;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 去华为权限申请页面
|
||||
*/
|
||||
public static void applyPermission(Context context) {
|
||||
try {
|
||||
Intent intent = new Intent();
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
// ComponentName comp = new ComponentName("com.huawei.systemmanager","com.huawei.permissionmanager.ui.MainActivity");//华为权限管理
|
||||
// ComponentName comp = new ComponentName("com.huawei.systemmanager",
|
||||
// "com.huawei.permissionmanager.ui.SingleAppActivity");//华为权限管理,跳转到指定app的权限管理位置需要华为接口权限,未解决
|
||||
ComponentName comp = new ComponentName("com.huawei.systemmanager", "com.huawei.systemmanager.addviewmonitor.AddViewMonitorActivity");//悬浮窗管理页面
|
||||
intent.setComponent(comp);
|
||||
if (RomUtils.getEmuiVersion() == 3.1) {
|
||||
//emui 3.1 的适配
|
||||
context.startActivity(intent);
|
||||
} else {
|
||||
//emui 3.0 的适配
|
||||
comp = new ComponentName("com.huawei.systemmanager", "com.huawei.notificationmanager.ui.NotificationManagmentActivity");//悬浮窗管理页面
|
||||
intent.setComponent(comp);
|
||||
context.startActivity(intent);
|
||||
}
|
||||
} catch (SecurityException e) {
|
||||
Intent intent = new Intent();
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
// ComponentName comp = new ComponentName("com.huawei.systemmanager","com.huawei.permissionmanager.ui.MainActivity");//华为权限管理
|
||||
ComponentName comp = new ComponentName("com.huawei.systemmanager",
|
||||
"com.huawei.permissionmanager.ui.MainActivity");//华为权限管理,跳转到本app的权限管理页面,这个需要华为接口权限,未解决
|
||||
// ComponentName comp = new ComponentName("com.huawei.systemmanager","com.huawei.systemmanager.addviewmonitor.AddViewMonitorActivity");//悬浮窗管理页面
|
||||
intent.setComponent(comp);
|
||||
context.startActivity(intent);
|
||||
Log.e(TAG, Log.getStackTraceString(e));
|
||||
} catch (ActivityNotFoundException e) {
|
||||
/**
|
||||
* 手机管家版本较低 HUAWEI SC-UL10
|
||||
*/
|
||||
// Toast.makeText(MainActivity.this, "act找不到", Toast.LENGTH_LONG).show();
|
||||
Intent intent = new Intent();
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
ComponentName comp = new ComponentName("com.Android.settings", "com.android.settings.permission.TabItem");//权限管理页面 android4.4
|
||||
// ComponentName comp = new ComponentName("com.android.settings","com.android.settings.permission.single_app_activity");//此处可跳转到指定app对应的权限管理页面,但是需要相关权限,未解决
|
||||
intent.setComponent(comp);
|
||||
context.startActivity(intent);
|
||||
Log.e(TAG, Log.getStackTraceString(e));
|
||||
} catch (Exception e) {
|
||||
//抛出异常时提示信息
|
||||
Toast.makeText(context, "进入设置页面失败,请手动设置", Toast.LENGTH_LONG).show();
|
||||
Log.e(TAG, Log.getStackTraceString(e));
|
||||
}
|
||||
}
|
||||
|
||||
//com.android.packageinstaller/.permission.ui.ManagePermissionsActivity
|
||||
public static void toPermisstionSetting(Context context) {
|
||||
Intent intent = new Intent();
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.setAction("android.intent.action.MANAGE_APP_PERMISSIONS");
|
||||
ComponentName comp = new ComponentName("com.android.packageinstaller", "com.android.packageinstaller.permission.ui.ManagePermissionsActivity");
|
||||
intent.setComponent(comp);
|
||||
context.startActivity(intent);
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.KITKAT)
|
||||
private static boolean checkOp(Context context, int op) {
|
||||
final int version = Build.VERSION.SDK_INT;
|
||||
if (version >= 19) {
|
||||
AppOpsManager manager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
|
||||
try {
|
||||
Class clazz = AppOpsManager.class;
|
||||
Method method = clazz.getDeclaredMethod("checkOp", int.class, int.class, String.class);
|
||||
return AppOpsManager.MODE_ALLOWED == (int) method.invoke(manager, op, Binder.getCallingUid(), context.getPackageName());
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, Log.getStackTraceString(e));
|
||||
}
|
||||
} else {
|
||||
Log.e(TAG, "Below API 19 cannot invoke!");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Facishare Technology Co., Ltd. All Rights Reserved.
|
||||
*/
|
||||
package com.qxcm.moduleutil.utils.permission.rom;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.AppOpsManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Binder;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class MeizuUtils {
|
||||
private static final String TAG = "MeizuUtils";
|
||||
|
||||
/**
|
||||
* 检测 meizu 悬浮窗权限
|
||||
*/
|
||||
public static boolean checkFloatWindowPermission(Context context) {
|
||||
final int version = Build.VERSION.SDK_INT;
|
||||
if (version >= 19) {
|
||||
return checkOp(context, 24); //OP_SYSTEM_ALERT_WINDOW = 24;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 去魅族权限申请页面
|
||||
*/
|
||||
public static void applyPermission(Context context) {
|
||||
try {
|
||||
Intent intent = new Intent("com.meizu.safe.security.SHOW_APPSEC");
|
||||
// intent.setClassName("com.meizu.safe", "com.meizu.safe.security.AppSecActivity");//remove this line code for fix flyme6.3
|
||||
intent.putExtra("packageName", context.getPackageName());
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
context.startActivity(intent);
|
||||
} catch (Exception e) {
|
||||
try {
|
||||
Log.e(TAG, "获取悬浮窗权限, 打开AppSecActivity失败, " + Log.getStackTraceString(e));
|
||||
// 最新的魅族flyme 6.2.5 用上述方法获取权限失败, 不过又可以用下述方法获取权限了
|
||||
RomUtils.commonROMPermissionApplyInternal(context);
|
||||
} catch (Exception eFinal) {
|
||||
Log.e(TAG, "获取悬浮窗权限失败, 通用获取方法失败, " + Log.getStackTraceString(eFinal));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.KITKAT)
|
||||
private static boolean checkOp(Context context, int op) {
|
||||
final int version = Build.VERSION.SDK_INT;
|
||||
if (version >= 19) {
|
||||
AppOpsManager manager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
|
||||
try {
|
||||
Class clazz = AppOpsManager.class;
|
||||
Method method = clazz.getDeclaredMethod("checkOp", int.class, int.class, String.class);
|
||||
return AppOpsManager.MODE_ALLOWED == (int) method.invoke(manager, op, Binder.getCallingUid(), context.getPackageName());
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, Log.getStackTraceString(e));
|
||||
}
|
||||
} else {
|
||||
Log.e(TAG, "Below API 19 cannot invoke!");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Facishare Technology Co., Ltd. All Rights Reserved.
|
||||
*/
|
||||
package com.qxcm.moduleutil.utils.permission.rom;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.AppOpsManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Binder;
|
||||
import android.os.Build;
|
||||
import android.provider.Settings;
|
||||
import android.util.Log;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class MiuiUtils {
|
||||
private static final String TAG = "MiuiUtils";
|
||||
|
||||
/**
|
||||
* 获取小米 rom 版本号,获取失败返回 -1
|
||||
*
|
||||
* @return miui rom version code, if fail , return -1
|
||||
*/
|
||||
public static int getMiuiVersion() {
|
||||
String version = RomUtils.getSystemProperty("ro.miui.ui.version.name");
|
||||
if (version != null) {
|
||||
try {
|
||||
return Integer.parseInt(version.substring(1));
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "get miui version code error, version : " + version);
|
||||
Log.e(TAG, Log.getStackTraceString(e));
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测 miui 悬浮窗权限
|
||||
*/
|
||||
public static boolean checkFloatWindowPermission(Context context) {
|
||||
final int version = Build.VERSION.SDK_INT;
|
||||
|
||||
if (version >= 19) {
|
||||
return checkOp(context, 24); //OP_SYSTEM_ALERT_WINDOW = 24;
|
||||
} else {
|
||||
// if ((context.getApplicationInfo().flags & 1 << 27) == 1) {
|
||||
// return true;
|
||||
// } else {
|
||||
// return false;
|
||||
// }
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.KITKAT)
|
||||
private static boolean checkOp(Context context, int op) {
|
||||
final int version = Build.VERSION.SDK_INT;
|
||||
if (version >= 19) {
|
||||
AppOpsManager manager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
|
||||
try {
|
||||
Class clazz = AppOpsManager.class;
|
||||
Method method = clazz.getDeclaredMethod("checkOp", int.class, int.class, String.class);
|
||||
return AppOpsManager.MODE_ALLOWED == (int) method.invoke(manager, op, Binder.getCallingUid(), context.getPackageName());
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, Log.getStackTraceString(e));
|
||||
}
|
||||
} else {
|
||||
Log.e(TAG, "Below API 19 cannot invoke!");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 小米 ROM 权限申请
|
||||
*/
|
||||
public static void applyMiuiPermission(Context context) {
|
||||
int versionCode = getMiuiVersion();
|
||||
if (versionCode == 5) {
|
||||
goToMiuiPermissionActivity_V5(context);
|
||||
} else if (versionCode == 6) {
|
||||
goToMiuiPermissionActivity_V6(context);
|
||||
} else if (versionCode == 7) {
|
||||
goToMiuiPermissionActivity_V7(context);
|
||||
} else if (versionCode == 8) {
|
||||
goToMiuiPermissionActivity_V8(context);
|
||||
} else {
|
||||
Log.e(TAG, "this is a special MIUI rom version, its version code " + versionCode);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isIntentAvailable(Intent intent, Context context) {
|
||||
if (intent == null) {
|
||||
return false;
|
||||
}
|
||||
return context.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY).size() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 小米 V5 版本 ROM权限申请
|
||||
*/
|
||||
public static void goToMiuiPermissionActivity_V5(Context context) {
|
||||
Intent intent = null;
|
||||
String packageName = context.getPackageName();
|
||||
intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
|
||||
Uri uri = Uri.fromParts("package", packageName, null);
|
||||
intent.setData(uri);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
if (isIntentAvailable(intent, context)) {
|
||||
context.startActivity(intent);
|
||||
} else {
|
||||
Log.e(TAG, "intent is not available!");
|
||||
}
|
||||
|
||||
//设置页面在应用详情页面
|
||||
// Intent intent = new Intent("miui.intent.action.APP_PERM_EDITOR");
|
||||
// PackageInfo pInfo = null;
|
||||
// try {
|
||||
// pInfo = context.getPackageManager().getPackageInfo
|
||||
// (HostInterfaceManager.getHostInterface().getApp().getPackageName(), 0);
|
||||
// } catch (PackageManager.NameNotFoundException e) {
|
||||
// AVLogger.e(TAG, e.getMessage());
|
||||
// }
|
||||
// intent.setClassName("com.android.settings", "com.miui.securitycenter.permission.AppPermissionsEditor");
|
||||
// intent.putExtra("extra_package_uid", pInfo.applicationInfo.uid);
|
||||
// intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
// if (isIntentAvailable(intent, context)) {
|
||||
// context.startActivity(intent);
|
||||
// } else {
|
||||
// AVLogger.e(TAG, "Intent is not available!");
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
* 小米 V6 版本 ROM权限申请
|
||||
*/
|
||||
public static void goToMiuiPermissionActivity_V6(Context context) {
|
||||
Intent intent = new Intent("miui.intent.action.APP_PERM_EDITOR");
|
||||
intent.setClassName("com.miui.securitycenter", "com.miui.permcenter.permissions.AppPermissionsEditorActivity");
|
||||
intent.putExtra("extra_pkgname", context.getPackageName());
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
|
||||
if (isIntentAvailable(intent, context)) {
|
||||
context.startActivity(intent);
|
||||
} else {
|
||||
Log.e(TAG, "Intent is not available!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 小米 V7 版本 ROM权限申请
|
||||
*/
|
||||
public static void goToMiuiPermissionActivity_V7(Context context) {
|
||||
Intent intent = new Intent("miui.intent.action.APP_PERM_EDITOR");
|
||||
intent.setClassName("com.miui.securitycenter", "com.miui.permcenter.permissions.AppPermissionsEditorActivity");
|
||||
intent.putExtra("extra_pkgname", context.getPackageName());
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
|
||||
if (isIntentAvailable(intent, context)) {
|
||||
context.startActivity(intent);
|
||||
} else {
|
||||
Log.e(TAG, "Intent is not available!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 小米 V8 版本 ROM权限申请
|
||||
*/
|
||||
public static void goToMiuiPermissionActivity_V8(Context context) {
|
||||
Intent intent = new Intent("miui.intent.action.APP_PERM_EDITOR");
|
||||
intent.setClassName("com.miui.securitycenter", "com.miui.permcenter.permissions.PermissionsEditorActivity");
|
||||
// intent.setPackage("com.miui.securitycenter");
|
||||
intent.putExtra("extra_pkgname", context.getPackageName());
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
|
||||
if (isIntentAvailable(intent, context)) {
|
||||
context.startActivity(intent);
|
||||
} else {
|
||||
intent = new Intent("miui.intent.action.APP_PERM_EDITOR");
|
||||
intent.setPackage("com.miui.securitycenter");
|
||||
intent.putExtra("extra_pkgname", context.getPackageName());
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
|
||||
if (isIntentAvailable(intent, context)) {
|
||||
context.startActivity(intent);
|
||||
} else {
|
||||
Log.e(TAG, "Intent is not available!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void toPermisstionSetting(Context context) throws NoSuchFieldException, IllegalAccessException {
|
||||
int rom = getMiuiVersion();
|
||||
Intent intent = null;
|
||||
if (5 == rom) {
|
||||
Uri packageURI = Uri.parse("package:" + context.getApplicationInfo().packageName);
|
||||
intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, packageURI);
|
||||
} else if (rom == 6 || rom == 7) {
|
||||
intent = new Intent("miui.intent.action.APP_PERM_EDITOR");
|
||||
intent.setClassName("com.miui.securitycenter", "com.miui.permcenter.permissions.AppPermissionsEditorActivity");
|
||||
intent.putExtra("extra_pkgname", context.getPackageName());
|
||||
} else if (rom == 8 || rom == 9) {
|
||||
intent = new Intent("miui.intent.action.APP_PERM_EDITOR");
|
||||
intent.setClassName("com.miui.securitycenter", "com.miui.permcenter.permissions.PermissionsEditorActivity");
|
||||
intent.putExtra("extra_pkgname", context.getPackageName());
|
||||
} else {
|
||||
RomUtils.commonROMPermissionApplyInternal(context);
|
||||
return;
|
||||
}
|
||||
context.startActivity(intent);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package com.qxcm.moduleutil.utils.permission.rom;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.AppOpsManager;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Binder;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* Description:
|
||||
*
|
||||
* @author Shawn_Dut
|
||||
* @since 2018-02-01
|
||||
*/
|
||||
public class OppoUtils {
|
||||
|
||||
private static final String TAG = "OppoUtils";
|
||||
|
||||
/**
|
||||
* 检测 360 悬浮窗权限
|
||||
*/
|
||||
public static boolean checkFloatWindowPermission(Context context) {
|
||||
final int version = Build.VERSION.SDK_INT;
|
||||
if (version >= 19) {
|
||||
return checkOp(context, 24); //OP_SYSTEM_ALERT_WINDOW = 24;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.KITKAT)
|
||||
private static boolean checkOp(Context context, int op) {
|
||||
final int version = Build.VERSION.SDK_INT;
|
||||
if (version >= 19) {
|
||||
AppOpsManager manager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
|
||||
try {
|
||||
Class clazz = AppOpsManager.class;
|
||||
Method method = clazz.getDeclaredMethod("checkOp", int.class, int.class, String.class);
|
||||
return AppOpsManager.MODE_ALLOWED == (int) method.invoke(manager, op, Binder.getCallingUid(), context.getPackageName());
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, Log.getStackTraceString(e));
|
||||
}
|
||||
} else {
|
||||
Log.e(TAG, "Below API 19 cannot invoke!");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* oppo ROM 权限申请
|
||||
*/
|
||||
public static void applyOppoPermission(Context context) {
|
||||
//merge request from https://github.com/zhaozepeng/FloatWindowPermission/pull/26
|
||||
try {
|
||||
Intent intent = new Intent();
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
//com.coloros.safecenter/.sysfloatwindow.FloatWindowListActivity
|
||||
ComponentName comp = new ComponentName("com.coloros.safecenter", "com.coloros.safecenter.sysfloatwindow.FloatWindowListActivity");//悬浮窗管理页面
|
||||
intent.setComponent(comp);
|
||||
context.startActivity(intent);
|
||||
}
|
||||
catch(Exception e){
|
||||
Log.e(TAG, "",e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Facishare Technology Co., Ltd. All Rights Reserved.
|
||||
*/
|
||||
package com.qxcm.moduleutil.utils.permission.rom;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.AppOpsManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Binder;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class QikuUtils {
|
||||
private static final String TAG = "QikuUtils";
|
||||
|
||||
/**
|
||||
* 检测 360 悬浮窗权限
|
||||
*/
|
||||
public static boolean checkFloatWindowPermission(Context context) {
|
||||
final int version = Build.VERSION.SDK_INT;
|
||||
if (version >= 19) {
|
||||
return checkOp(context, 24); //OP_SYSTEM_ALERT_WINDOW = 24;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.KITKAT)
|
||||
private static boolean checkOp(Context context, int op) {
|
||||
final int version = Build.VERSION.SDK_INT;
|
||||
if (version >= 19) {
|
||||
AppOpsManager manager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
|
||||
try {
|
||||
Class clazz = AppOpsManager.class;
|
||||
Method method = clazz.getDeclaredMethod("checkOp", int.class, int.class, String.class);
|
||||
return AppOpsManager.MODE_ALLOWED == (int)method.invoke(manager, op, Binder.getCallingUid(), context.getPackageName());
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, Log.getStackTraceString(e));
|
||||
}
|
||||
} else {
|
||||
Log.e("", "Below API 19 cannot invoke!");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 去360权限申请页面
|
||||
*/
|
||||
public static void applyPermission(Context context) {
|
||||
Intent intent = new Intent();
|
||||
intent.setClassName("com.android.settings", "com.android.settings.Settings$OverlaySettingsActivity");
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
if (isIntentAvailable(intent, context)) {
|
||||
context.startActivity(intent);
|
||||
} else {
|
||||
intent.setClassName("com.qihoo360.mobilesafe", "com.qihoo360.mobilesafe.ui.index.AppEnterActivity");
|
||||
if (isIntentAvailable(intent, context)) {
|
||||
context.startActivity(intent);
|
||||
} else {
|
||||
Log.e(TAG, "can't open permission page with particular name, please use " +
|
||||
"\"adb shell dumpsys activity\" command and tell me the name of the float window permission page");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isIntentAvailable(Intent intent, Context context) {
|
||||
if (intent == null) {
|
||||
return false;
|
||||
}
|
||||
return context.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY).size() > 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Facishare Technology Co., Ltd. All Rights Reserved.
|
||||
*/
|
||||
package com.qxcm.moduleutil.utils.permission.rom;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.AppOpsManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Binder;
|
||||
import android.os.Build;
|
||||
import android.provider.Settings;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
/**
|
||||
* Description:
|
||||
*
|
||||
* @author zhaozp
|
||||
* @since 2016-05-23
|
||||
*/
|
||||
public class RomUtils {
|
||||
private static final String TAG = "RomUtils";
|
||||
|
||||
/**
|
||||
* 获取 emui 版本号
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static double getEmuiVersion() {
|
||||
try {
|
||||
String emuiVersion = getSystemProperty("ro.build.version.emui");
|
||||
String version = emuiVersion.substring(emuiVersion.indexOf("_") + 1);
|
||||
return Double.parseDouble(version);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "", e);
|
||||
}
|
||||
return 4.0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取小米 rom 版本号,获取失败返回 -1
|
||||
*
|
||||
* @return miui rom version code, if fail , return -1
|
||||
*/
|
||||
public static int getMiuiVersion() {
|
||||
String version = getSystemProperty("ro.miui.ui.version.name");
|
||||
if (version != null) {
|
||||
try {
|
||||
return Integer.parseInt(version.substring(1));
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "get miui version code error, version : " + version);
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
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.e(TAG, "Unable to read sysprop " + propName, ex);
|
||||
return null;
|
||||
} finally {
|
||||
if (input != null) {
|
||||
try {
|
||||
input.close();
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Exception while closing InputStream", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return line;
|
||||
}
|
||||
|
||||
public static boolean checkIsHuaweiRom() {
|
||||
return Build.MANUFACTURER.contains("HUAWEI");
|
||||
}
|
||||
|
||||
/**
|
||||
* check if is miui ROM
|
||||
*/
|
||||
public static boolean checkIsMiuiRom() {
|
||||
return !TextUtils.isEmpty(getSystemProperty("ro.miui.ui.version.name"));
|
||||
}
|
||||
|
||||
public static boolean checkIsMeizuRom() {
|
||||
//return Build.MANUFACTURER.contains("Meizu");
|
||||
String meizuFlymeOSFlag = getSystemProperty("ro.build.display.id");
|
||||
if (TextUtils.isEmpty(meizuFlymeOSFlag)) {
|
||||
return false;
|
||||
} else if (meizuFlymeOSFlag.contains("flyme") || meizuFlymeOSFlag.toLowerCase().contains("flyme")) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean checkIs360Rom() {
|
||||
//fix issue https://github.com/zhaozepeng/FloatWindowPermission/issues/9
|
||||
return Build.MANUFACTURER.contains("QiKU")
|
||||
|| Build.MANUFACTURER.contains("360");
|
||||
}
|
||||
|
||||
public static boolean checkIsOppoRom() {
|
||||
//https://github.com/zhaozepeng/FloatWindowPermission/pull/26
|
||||
return Build.MANUFACTURER.contains("OPPO") || Build.MANUFACTURER.contains("oppo");
|
||||
}
|
||||
|
||||
public static boolean checkIsVivoRom() {
|
||||
return Build.MANUFACTURER.contains("vivo") || Build.MANUFACTURER.contains("VIVO");
|
||||
}
|
||||
|
||||
public static boolean checkIsSamsunRom() {
|
||||
return Build.MANUFACTURER.contains("samsung");
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断小米MIUI系统中授权管理中对应的权限授取
|
||||
*
|
||||
* @return false 存在核心的未收取的权限 true 核心权限已经全部授权
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.KITKAT)
|
||||
public static boolean hasMiuiPermission(Context context) {
|
||||
AppOpsManager appOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
|
||||
int locationOp = appOpsManager.checkOp(AppOpsManager.OPSTR_FINE_LOCATION, Binder.getCallingUid(), context.getPackageName());
|
||||
if (locationOp == AppOpsManager.MODE_IGNORED) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void commonROMPermissionApplyInternal(Context context) throws NoSuchFieldException, IllegalAccessException {
|
||||
Class clazz = Settings.class;
|
||||
Field field = clazz.getDeclaredField("ACTION_MANAGE_OVERLAY_PERMISSION");
|
||||
|
||||
Intent intent = new Intent(field.get(null).toString());
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.setData(Uri.parse("package:" + context.getPackageName()));
|
||||
context.startActivity(intent);
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳转到权限设置界面
|
||||
*/
|
||||
public static void getAppDetailSettingIntent(Context context) {
|
||||
|
||||
// vivo 点击设置图标>加速白名单>我的app
|
||||
// 点击软件管理>软件管理权限>软件>我的app>信任该软件
|
||||
Intent appIntent = context.getPackageManager().getLaunchIntentForPackage("com.iqoo.secure");
|
||||
if (appIntent != null) {
|
||||
context.startActivity(appIntent);
|
||||
return;
|
||||
}
|
||||
|
||||
// oppo 点击设置图标>应用权限管理>按应用程序管理>我的app>我信任该应用
|
||||
// 点击权限隐私>自启动管理>我的app
|
||||
appIntent = context.getPackageManager().getLaunchIntentForPackage("com.oppo.safe");
|
||||
if (appIntent != null) {
|
||||
context.startActivity(appIntent);
|
||||
return;
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
context.startActivity(intent);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.qxcm.moduleutil.widget;
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import android.text.TextUtils;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.blankj.utilcode.util.LogUtils;
|
||||
import com.qxcm.moduleutil.base.CommonAppContext;
|
||||
import com.qxcm.moduleutil.bean.ConfigBean;
|
||||
import com.qxcm.moduleutil.interfaces.CommonCallback;
|
||||
import com.qxcm.moduleutil.utils.DeviceUtils;
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
package com.qxcm.moduleutil.widget;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.qxcm.moduleutil.R;
|
||||
|
||||
/**
|
||||
*@author
|
||||
*@data 2025/5/15
|
||||
*@description: 当没有数据时展示的view
|
||||
*/
|
||||
public class CommonEmptyView extends FrameLayout {
|
||||
private ImageView ivEmpty;
|
||||
private TextView tvEmptyText;
|
||||
private TextView tvOtherOperation;
|
||||
private LinearLayout layoutEmpty;
|
||||
private OtherOpListener otherOpListener;
|
||||
|
||||
public CommonEmptyView(@NonNull Context context) {
|
||||
super(context);
|
||||
initViews();
|
||||
}
|
||||
|
||||
public CommonEmptyView(@NonNull Context context, @Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
initViews();
|
||||
}
|
||||
|
||||
public CommonEmptyView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
initViews();
|
||||
}
|
||||
|
||||
|
||||
private void initViews() {
|
||||
LayoutInflater.from(getContext()).inflate(R.layout.layout_empty, this);
|
||||
ivEmpty = findViewById(R.id.iv_empty);
|
||||
tvEmptyText = findViewById(R.id.tv_empty_text);
|
||||
tvOtherOperation = findViewById(R.id.tv_other_operation);
|
||||
layoutEmpty = findViewById(R.id.layout_empty);
|
||||
tvOtherOperation.setOnClickListener(v -> otherOpListener.otherClick());
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置提示文字
|
||||
*
|
||||
* @param text
|
||||
*/
|
||||
public void setEmptyText(String text) {
|
||||
if (null != tvEmptyText) {
|
||||
tvEmptyText.setText(text);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置提示文字的颜色
|
||||
*/
|
||||
public void setTextColor(int color) {
|
||||
if (null != tvEmptyText) {
|
||||
tvEmptyText.setTextColor(color);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置按钮文字
|
||||
*
|
||||
* @param txt
|
||||
*/
|
||||
public void setBtnText(String txt) {
|
||||
if (null != tvOtherOperation) {
|
||||
tvOtherOperation.setText(txt);
|
||||
tvOtherOperation.setVisibility(VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置占位图
|
||||
*/
|
||||
public void setImg(int res) {
|
||||
ivEmpty.setImageResource(res);
|
||||
}
|
||||
|
||||
public void addOnOtherListener(OtherOpListener otherOpListener) {
|
||||
this.otherOpListener = otherOpListener;
|
||||
}
|
||||
|
||||
public interface OtherOpListener {
|
||||
void otherClick();
|
||||
}
|
||||
}
|
||||
@@ -340,4 +340,25 @@ public class Constants {
|
||||
|
||||
public static final String FILE_PATH = "/YuTang";
|
||||
|
||||
|
||||
//接口地址
|
||||
public static final String SEND_CODE = "/api/Sms/send";
|
||||
public static final String LOGIN = "/api/Login/phone_code";
|
||||
public static final String USER_LOGIN = "/api/Login/user_login";
|
||||
//随机昵称
|
||||
public static final String UPLOAD_NICK_NAME = "/api/UserData/random_nickname";
|
||||
public static final String UPLOAD_USER_PIC = "/api/UserData/modify_user_sex_pic";
|
||||
|
||||
public static final String SWITCH_ACCOUNTS = "/api/Login/multi_account_login";
|
||||
|
||||
public static final String USER_UPDATE = "/api/UserData/modify_fist_user_info";
|
||||
|
||||
public static final String CHANGE_PASSWORD = "/api/UserData/modify_password";
|
||||
|
||||
public static final String URL_LOGIN = "/api/Login/one_click_login";//一键登录
|
||||
public static final String URL_AUTH_CODE = "/api/Login/aliLogin";//支付宝登录
|
||||
public static final String URL_WX_CODE = "/api/Login/wechatLogin";//微信登录
|
||||
|
||||
public static final String AUTHORIZATION = "/api/Login/AlipayUserInfo";
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
package com.qxcm.moduleutil.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.Editable;
|
||||
import android.text.InputFilter;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.widget.EditText;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.qxcm.moduleutil.R;
|
||||
|
||||
/**
|
||||
*@author
|
||||
*@data 2025/5/15
|
||||
*@description: 输入框添加字数文字提示
|
||||
*/
|
||||
public class CustomEditText extends LinearLayout {
|
||||
|
||||
private EditText editText;
|
||||
private TextView lengthTextView;
|
||||
private int maxLength = 100; // 默认最大长度
|
||||
|
||||
public CustomEditText(Context context) {
|
||||
super(context);
|
||||
init(context);
|
||||
}
|
||||
|
||||
public CustomEditText(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init(context);
|
||||
}
|
||||
|
||||
public CustomEditText(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
init(context);
|
||||
}
|
||||
|
||||
private void init(Context context) {
|
||||
// 加载布局文件
|
||||
LayoutInflater.from(context).inflate(R.layout.custom_edit_text_layout, this, true);
|
||||
|
||||
// 找到 EditText 和 TextView 的引用
|
||||
editText = findViewById(R.id.edit_text);
|
||||
lengthTextView = findViewById(R.id.length_text_view);
|
||||
|
||||
// 初始化监听器
|
||||
editText.addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
updateLengthDisplay();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {}
|
||||
});
|
||||
|
||||
// 设置默认的最大长度
|
||||
setMaxLength(maxLength);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置最大长度
|
||||
*/
|
||||
public void setMaxLength(int maxLength) {
|
||||
this.maxLength = maxLength;
|
||||
editText.setFilters(new InputFilter[]{new InputFilter.LengthFilter(maxLength)});
|
||||
updateLengthDisplay(); // 初始化显示
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新显示的字数
|
||||
*/
|
||||
private void updateLengthDisplay() {
|
||||
int currentLength = editText.getText().length();
|
||||
lengthTextView.setText(currentLength + "/" + maxLength);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置提示文本
|
||||
*/
|
||||
public void setHint(String hint) {
|
||||
editText.setHint(hint);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前文本
|
||||
*/
|
||||
public String getText() {
|
||||
return editText.getText().toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置文本
|
||||
*/
|
||||
public void setText(String text) {
|
||||
editText.setText(text);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package com.qxcm.moduleutil.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.TextUtils;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||
|
||||
import com.qxcm.moduleutil.R;
|
||||
import com.qxcm.moduleutil.utils.ImageUtils;
|
||||
import com.qxcm.moduleutil.utils.logger.Logger;
|
||||
|
||||
|
||||
/**
|
||||
* 项目名称 qipao-android
|
||||
* 包名:com.yutang.xqipao.utils.view
|
||||
* 创建人 王欧
|
||||
* 创建时间 2020/4/9 12:46 PM
|
||||
* 描述 describe
|
||||
*/
|
||||
public class DecorationHeadView extends ConstraintLayout {
|
||||
private ImageView mRiv;
|
||||
private ImageView mIvFrame;
|
||||
private ImageView mIvSex;
|
||||
private ImageView mIvOnline;
|
||||
|
||||
public DecorationHeadView(Context context) {
|
||||
this(context, null, 0);
|
||||
}
|
||||
|
||||
public DecorationHeadView(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public DecorationHeadView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
initView(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
private void initView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
LayoutInflater.from(context).inflate(R.layout.common_view_decoration_head, this, true);
|
||||
mRiv = findViewById(R.id.riv);
|
||||
mIvFrame = findViewById(R.id.iv_frame);
|
||||
mIvSex = findViewById(R.id.iv_sex);
|
||||
mIvOnline = findViewById(R.id.iv_online);
|
||||
}
|
||||
|
||||
public void setData(String headPicture, String framePicture, String sex) {
|
||||
Logger.e(headPicture, framePicture, sex);
|
||||
if (!TextUtils.isEmpty(headPicture)) {
|
||||
ImageUtils.loadHeadCC(headPicture, mRiv);
|
||||
}
|
||||
// if (TextUtils.isEmpty(framePicture)) {
|
||||
// mIvSex.setVisibility(VISIBLE);
|
||||
// if (!TextUtils.isEmpty(sex)) {
|
||||
// if (UserBean.MALE.equals(sex)) {
|
||||
// mIvSex.setBackgroundResource(R.mipmap.common_ic_headportriat_boy);
|
||||
// } else {
|
||||
// mIvSex.setBackgroundResource(R.mipmap.common_ic_headportriat_girl);
|
||||
// }
|
||||
// } else {
|
||||
// mIvSex.setBackgroundResource(R.drawable.common_bg_head_white);
|
||||
// }
|
||||
// } else {
|
||||
// mIvSex.setVisibility(GONE);
|
||||
// }
|
||||
ImageUtils.loadImageView(framePicture, mIvFrame);
|
||||
}
|
||||
|
||||
public void setOnline(boolean isOnline) {
|
||||
mIvOnline.setVisibility(VISIBLE);
|
||||
mIvOnline.setImageResource(isOnline ? R.mipmap.me_online_icon : R.mipmap.me_icon_unchecked);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,640 @@
|
||||
package com.qxcm.moduleutil.widget;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.text.InputFilter;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.TypedValue;
|
||||
import android.view.InflateException;
|
||||
|
||||
import androidx.appcompat.widget.AppCompatEditText;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import com.qxcm.moduleutil.R;
|
||||
|
||||
|
||||
public class SplitEditText extends AppCompatEditText {
|
||||
//密码显示模式:隐藏密码,显示圆形
|
||||
public static final int CONTENT_SHOW_MODE_PASSWORD = 1;
|
||||
//密码显示模式:显示密码
|
||||
public static final int CONTENT_SHOW_MODE_TEXT = 2;
|
||||
//输入框相连的样式
|
||||
public static final int INPUT_BOX_STYLE_CONNECT = 1;
|
||||
//单个的输入框样式
|
||||
public static final int INPUT_BOX_STYLE_SINGLE = 2;
|
||||
//下划线输入框样式
|
||||
public static final int INPUT_BOX_STYLE_UNDERLINE = 3;
|
||||
//画笔
|
||||
private RectF mRectFConnect;
|
||||
private RectF mRectFSingleBox;
|
||||
private Paint mPaintDivisionLine;
|
||||
private Paint mPaintContent;
|
||||
private Paint mPaintBorder;
|
||||
private Paint mPaintUnderline;
|
||||
//边框大小
|
||||
private Float mBorderSize;
|
||||
//边框颜色
|
||||
private int mBorderColor;
|
||||
//圆角大小
|
||||
private float mCornerSize;
|
||||
//分割线大小
|
||||
private float mDivisionLineSize;
|
||||
//分割线颜色
|
||||
private int mDivisionColor;
|
||||
//圆形密码的半径大小
|
||||
private float mCircleRadius;
|
||||
//密码框长度
|
||||
private int mContentNumber;
|
||||
//密码显示模式
|
||||
private int mContentShowMode;
|
||||
//单框和下划线输入样式下,每个输入框的间距
|
||||
private float mSpaceSize;
|
||||
//输入框样式
|
||||
private int mInputBoxStyle;
|
||||
//字体大小
|
||||
private float mTextSize;
|
||||
//字体颜色
|
||||
private int mTextColor;
|
||||
//每个输入框是否是正方形标识
|
||||
private boolean mInputBoxSquare;
|
||||
private OnInputListener inputListener;
|
||||
private Paint mPaintCursor;
|
||||
private CursorRunnable cursorRunnable;
|
||||
|
||||
private int mCursorColor;//光标颜色
|
||||
private float mCursorWidth;//光标宽度
|
||||
private int mCursorHeight;//光标高度
|
||||
private int mCursorDuration;//光标闪烁时长
|
||||
|
||||
private int mUnderlineFocusColor;//下划线输入样式下,输入框获取焦点时下划线颜色
|
||||
private int mUnderlineNormalColor;//下划线输入样式下,下划线颜色
|
||||
|
||||
public SplitEditText(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
//这里没有写成默认的EditText属性样式android.R.attr.editTextStyle,这样会存在EditText默认的样式
|
||||
public SplitEditText(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
/*public SplitEditText(Context context, AttributeSet attrs) {
|
||||
super(context, attrs, android.R.attr.editTextStyle);
|
||||
initAttrs(context, attrs);
|
||||
}*/
|
||||
|
||||
public SplitEditText(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
initAttrs(context, attrs);
|
||||
}
|
||||
|
||||
private void initAttrs(Context c, AttributeSet attrs) {
|
||||
TypedArray array = c.obtainStyledAttributes(attrs, R.styleable.SplitEditText);
|
||||
mBorderSize = array.getDimension(R.styleable.SplitEditText_borderSize, dp2px(1f));
|
||||
mBorderColor = array.getColor(R.styleable.SplitEditText_borderColor, Color.BLACK);
|
||||
mCornerSize = array.getDimension(R.styleable.SplitEditText_corner_size, 0f);
|
||||
mDivisionLineSize = array.getDimension(R.styleable.SplitEditText_divisionLineSize, dp2px(1f));
|
||||
mDivisionColor = array.getColor(R.styleable.SplitEditText_divisionLineColor, Color.BLACK);
|
||||
mCircleRadius = array.getDimension(R.styleable.SplitEditText_circleRadius, dp2px(5f));
|
||||
mContentNumber = array.getInt(R.styleable.SplitEditText_contentNumber, 6);
|
||||
mContentShowMode = array.getInteger(R.styleable.SplitEditText_contentShowMode, CONTENT_SHOW_MODE_PASSWORD);
|
||||
mInputBoxStyle = array.getInteger(R.styleable.SplitEditText_inputBoxStyle, INPUT_BOX_STYLE_CONNECT);
|
||||
mSpaceSize = array.getDimension(R.styleable.SplitEditText_spaceSize, dp2px(10f));
|
||||
mTextSize = array.getDimension(R.styleable.SplitEditText_android_textSize, sp2px(16f));
|
||||
mTextColor = array.getColor(R.styleable.SplitEditText_android_textColor, Color.BLACK);
|
||||
mInputBoxSquare = array.getBoolean(R.styleable.SplitEditText_inputBoxSquare, true);
|
||||
mCursorColor = array.getColor(R.styleable.SplitEditText_cursorColor, ContextCompat.getColor(c,R.color.colorAccent));
|
||||
mCursorDuration = array.getInt(R.styleable.SplitEditText_cursorDuration, 500);
|
||||
mCursorWidth = array.getDimension(R.styleable.SplitEditText_cursorWidth, dp2px(2f));
|
||||
mCursorHeight = (int) array.getDimension(R.styleable.SplitEditText_cursorHeight, 0);
|
||||
mUnderlineNormalColor = array.getInt(R.styleable.SplitEditText_underlineNormalColor, Color.BLACK);
|
||||
mUnderlineFocusColor = array.getInt(R.styleable.SplitEditText_underlineFocusColor, 0);
|
||||
array.recycle();
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
mPaintBorder = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
mPaintBorder.setStyle(Paint.Style.FILL_AND_STROKE);
|
||||
mPaintBorder.setStrokeWidth(mBorderSize);
|
||||
mPaintBorder.setColor(mBorderColor);
|
||||
|
||||
mPaintDivisionLine = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
mPaintDivisionLine.setStyle(Paint.Style.STROKE);
|
||||
mPaintDivisionLine.setStrokeWidth(mDivisionLineSize);
|
||||
mPaintDivisionLine.setColor(mDivisionColor);
|
||||
|
||||
mPaintContent = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
mPaintContent.setTextSize(mTextSize);
|
||||
|
||||
mPaintCursor = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
mPaintCursor.setStrokeWidth(mCursorWidth);
|
||||
mPaintCursor.setColor(mCursorColor);
|
||||
|
||||
mPaintUnderline = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
mPaintUnderline.setStrokeWidth(mBorderSize);
|
||||
mPaintUnderline.setColor(mUnderlineNormalColor);
|
||||
|
||||
|
||||
//避免onDraw里面重复创建RectF对象,先初始化RectF对象,在绘制时调用set()方法
|
||||
//单个输入框样式的RectF
|
||||
mRectFSingleBox = new RectF();
|
||||
//绘制Connect样式的矩形框
|
||||
mRectFConnect = new RectF();
|
||||
//设置单行输入
|
||||
setSingleLine();
|
||||
|
||||
//若构造方法中没有写成android.R.attr.editTextStyle的属性,应该需要设置该属性,EditText默认是获取焦点的
|
||||
setFocusableInTouchMode(true);
|
||||
|
||||
//取消默认的光标
|
||||
//这里默认不设置该属性,不然长按粘贴有问题(一开始长按不能粘贴,输入内容就可以长按粘贴)
|
||||
//setCursorVisible(false);
|
||||
|
||||
//设置透明光标,若是直接不显示光标的话,长按粘贴会没效果
|
||||
/*try {
|
||||
Field f = TextView.class.getDeclaredField("mCursorDrawableRes");
|
||||
f.setAccessible(true);
|
||||
f.set(this, 0);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}*/
|
||||
//设置光标的TextSelectHandle
|
||||
//这里判断版本,10.0以及以上直接通过方法调用,以下通过反射设置
|
||||
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
// setTextSelectHandle(android.R.color.transparent);
|
||||
// } else {
|
||||
// //通过反射改变光标TextSelectHandle的样式
|
||||
// try {
|
||||
// Field f = TextView.class.getDeclaredField("mTextSelectHandleRes");
|
||||
// f.setAccessible(true);
|
||||
// f.set(this, android.R.color.transparent);
|
||||
// } catch (Exception e) {
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
// }
|
||||
//设置InputFilter,设置输入的最大字符长度为设置的长度
|
||||
setFilters(new InputFilter[]{new InputFilter.LengthFilter(mContentNumber)});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAttachedToWindow() {
|
||||
// L.e("SplitEditText","onAttachedToWindow------->");
|
||||
super.onAttachedToWindow();
|
||||
if(hasFocus()){
|
||||
startCursor();
|
||||
}
|
||||
}
|
||||
|
||||
private void startCursor(){
|
||||
if(cursorRunnable==null){
|
||||
cursorRunnable = new CursorRunnable();
|
||||
}
|
||||
postDelayed(cursorRunnable, mCursorDuration);
|
||||
}
|
||||
|
||||
private void stopCursor(){
|
||||
if(cursorRunnable!=null){
|
||||
removeCallbacks(cursorRunnable);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
|
||||
super.onFocusChanged(focused, direction, previouslyFocusedRect);
|
||||
if(focused){
|
||||
startCursor();
|
||||
}else{
|
||||
stopCursor();
|
||||
}
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDetachedFromWindow() {
|
||||
// L.e("SplitEditText","onDetachedFromWindow------->");
|
||||
stopCursor();
|
||||
super.onDetachedFromWindow();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
|
||||
if (mInputBoxSquare) {
|
||||
int width = MeasureSpec.getSize(widthMeasureSpec);
|
||||
//计算view高度,使view高度和每个item的宽度相等,确保每个item是一个正方形
|
||||
float itemWidth = getContentItemWidthOnMeasure(width);
|
||||
switch (mInputBoxStyle) {
|
||||
case INPUT_BOX_STYLE_UNDERLINE:
|
||||
setMeasuredDimension(width, (int) (itemWidth + mBorderSize));
|
||||
break;
|
||||
case INPUT_BOX_STYLE_SINGLE:
|
||||
case INPUT_BOX_STYLE_CONNECT:
|
||||
default:
|
||||
setMeasuredDimension(width, (int) (itemWidth + mBorderSize * 2));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
//绘制输入框
|
||||
switch (mInputBoxStyle) {
|
||||
case INPUT_BOX_STYLE_SINGLE:
|
||||
drawSingleStyle(canvas);
|
||||
break;
|
||||
case INPUT_BOX_STYLE_UNDERLINE:
|
||||
drawUnderlineStyle(canvas);
|
||||
break;
|
||||
case INPUT_BOX_STYLE_CONNECT:
|
||||
default:
|
||||
drawConnectStyle(canvas);
|
||||
break;
|
||||
}
|
||||
//绘制输入框内容
|
||||
drawContent(canvas);
|
||||
//绘制光标
|
||||
if(hasFocus()){
|
||||
drawCursor(canvas);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据输入内容显示模式,绘制内容是圆心还是明文的text
|
||||
*/
|
||||
private void drawContent(Canvas canvas) {
|
||||
int cy = getHeight() / 2;
|
||||
String password = getText().toString().trim();
|
||||
if (mContentShowMode == CONTENT_SHOW_MODE_PASSWORD) {
|
||||
mPaintContent.setColor(Color.BLACK);
|
||||
for (int i = 0; i < password.length(); i++) {
|
||||
float startX = getDrawContentStartX(i);
|
||||
canvas.drawCircle(startX, cy, mCircleRadius, mPaintContent);
|
||||
}
|
||||
} else {
|
||||
mPaintContent.setColor(mTextColor);
|
||||
//计算baseline
|
||||
float baselineText = getTextBaseline(mPaintContent, cy);
|
||||
for (int i = 0; i < password.length(); i++) {
|
||||
float startX = getDrawContentStartX(i);
|
||||
//计算文字宽度
|
||||
String text = String.valueOf(password.charAt(i));
|
||||
float textWidth = mPaintContent.measureText(text);
|
||||
//绘制文字x应该还需要减去文字宽度的一半
|
||||
canvas.drawText(text, startX - textWidth / 2, baselineText, mPaintContent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 绘制光标
|
||||
* 光标只有一个,所以不需要根据循环来绘制,只需绘制第N个就行
|
||||
* 即:
|
||||
* 当输入内容长度为0,光标在第0个位置
|
||||
* 当输入内容长度为1,光标应在第1个位置
|
||||
* ...
|
||||
* 所以光标所在位置为输入内容的长度
|
||||
* 这里光标的长度默认就是 height/2
|
||||
*/
|
||||
private void drawCursor(Canvas canvas) {
|
||||
if (mCursorHeight > getHeight()) {
|
||||
throw new InflateException("cursor height must smaller than view height");
|
||||
}
|
||||
String content = getText().toString().trim();
|
||||
float startX = getDrawContentStartX(content.length());
|
||||
//如果设置得有光标高度,那么startY = (高度-光标高度)/2+边框宽度
|
||||
if (mCursorHeight == 0) {
|
||||
mCursorHeight = getHeight() / 2;
|
||||
}
|
||||
int sy = (getHeight() - mCursorHeight) / 2;
|
||||
float startY = sy + mBorderSize;
|
||||
float stopY = getHeight() - sy - mBorderSize;
|
||||
|
||||
//此时的绘制光标竖直线,startX = stopX
|
||||
canvas.drawLine(startX, startY, startX, stopY, mPaintCursor);
|
||||
}
|
||||
|
||||
/**
|
||||
* 、
|
||||
* 计算三种输入框样式下绘制圆和文字的x坐标
|
||||
*
|
||||
* @param index 循环里面的下标 i
|
||||
*/
|
||||
private float getDrawContentStartX(int index) {
|
||||
switch (mInputBoxStyle) {
|
||||
case INPUT_BOX_STYLE_SINGLE:
|
||||
//单个输入框样式下的startX
|
||||
//即 itemWidth/2 + i*itemWidth + i*每一个间距宽度 + 前面所有的左右边框
|
||||
// i = 0,左侧1个边框
|
||||
// i = 1,左侧3个边框(一个完整的item的左右边框+ 一个左侧边框)
|
||||
// i = ..., (2*i+1)*mBorderSize
|
||||
return getContentItemWidth() / 2 + index * getContentItemWidth() + index * mSpaceSize + (2 * index + 1) * mBorderSize;
|
||||
case INPUT_BOX_STYLE_UNDERLINE:
|
||||
//下划线输入框样式下的startX
|
||||
//即 itemWidth/2 + i*itemWidth + i*每一个间距宽度
|
||||
return getContentItemWidth() / 2 + index * mSpaceSize + index * getContentItemWidth();
|
||||
case INPUT_BOX_STYLE_CONNECT:
|
||||
//矩形输入框样式下的startX
|
||||
//即 itemWidth/2 + i*itemWidth + i*分割线宽度 + 左侧的一个边框宽度
|
||||
default:
|
||||
return getContentItemWidth() / 2 + index * getContentItemWidth() + index * mDivisionLineSize + mBorderSize;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 绘制下划线输入框样式
|
||||
* 线条起点startX:每个字符所占宽度itemWidth + 每个字符item之间的间距mSpaceSize
|
||||
* 线条终点stopX:stopX与startX之间就是一个itemWidth的宽度
|
||||
*/
|
||||
private void drawUnderlineStyle(Canvas canvas) {
|
||||
String content = getText().toString().trim();
|
||||
for (int i = 0; i < mContentNumber; i++) {
|
||||
//计算绘制下划线的startX
|
||||
float startX = i * getContentItemWidth() + i * mSpaceSize;
|
||||
//stopX
|
||||
float stopX = getContentItemWidth() + startX;
|
||||
//对于下划线这种样式,startY = stopY
|
||||
float startY = getHeight() - mBorderSize / 2;
|
||||
//这里判断是否设置有输入框获取焦点时,下划线的颜色
|
||||
if (mUnderlineFocusColor != 0) {
|
||||
if (content.length() >= i) {
|
||||
mPaintUnderline.setColor(mUnderlineFocusColor);
|
||||
} else {
|
||||
mPaintUnderline.setColor(mUnderlineNormalColor);
|
||||
}
|
||||
}
|
||||
canvas.drawLine(startX, startY, stopX, startY, mPaintUnderline);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 绘制单框输入模式
|
||||
* 这里计算left、right时有点饶,
|
||||
* 理解、计算时最好根据图形、参照drawConnectStyle()绘制带边框的矩形
|
||||
* left:绘制带边框的矩形等图形时,去掉边框的一半即 + mBorderSize / 2,同时加上每个字符item的间距 + i*mSpaceSize
|
||||
* 另外,每个字符item的宽度 + i*itemWidth
|
||||
* 最后,绘制时都是以整个view的宽度计算,绘制第N个时,都应该加上以前的边框宽度
|
||||
* 即第一个:i = 0 ,边框的宽度为0
|
||||
* 第二个:i = 1,边框的宽度 2*mBorderSize,左右两个的边框宽度
|
||||
* 以此...最后应该 + i*2*mBorderSize
|
||||
* 同理
|
||||
* right:去掉边框的一半: -mBorderSize/2,还应该加上前面一个item的宽度:+(i+1)*itemWidth
|
||||
* 同样,绘制时都是以整个view的宽度计算,绘制后面的,都应该加上前面的所有宽度
|
||||
* 即 间距:+i*mSpaceSize
|
||||
* 边框:(注意是计算整个view)
|
||||
* 第一个:i = 0,2个边框2*mBorderSize
|
||||
* 第二个:i = 1,4个边框,即 (1+1)*2*mBorderSize
|
||||
* 所以算上边框 +(i+1)*2*mBorderSize
|
||||
*/
|
||||
private void drawSingleStyle(Canvas canvas) {
|
||||
for (int i = 0; i < mContentNumber; i++) {
|
||||
mRectFSingleBox.setEmpty();
|
||||
float left = i * getContentItemWidth() + i * mSpaceSize + i * mBorderSize * 2 + mBorderSize / 2;
|
||||
float right = i * mSpaceSize + (i + 1) * getContentItemWidth() + (i + 1) * 2 * mBorderSize - mBorderSize / 2;
|
||||
//为避免在onDraw里面创建RectF对象,这里使用rectF.set()方法
|
||||
mRectFSingleBox.set(left, mBorderSize / 2, right, getHeight() - mBorderSize / 2);
|
||||
canvas.drawRoundRect(mRectFSingleBox, mCornerSize, mCornerSize, mPaintBorder);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 绘制矩形外框
|
||||
* 在绘制圆角矩形的时候,应该减掉边框的宽度
|
||||
* 不然会有所偏差
|
||||
* <p>
|
||||
* 在绘制矩形以及其它图形的时候,矩形(图形)的边界是边框的中心,不是边框的边界
|
||||
* 所以在绘制带边框的图形的时候应该减去边框宽度的一半
|
||||
* https://blog.csdn.net/a512337862/article/details/74161988
|
||||
*/
|
||||
private void drawConnectStyle(Canvas canvas) {
|
||||
//每次重新绘制时,先将rectF重置下
|
||||
mRectFConnect.setEmpty();
|
||||
//需要减去边框的一半
|
||||
mRectFConnect.set(
|
||||
mBorderSize / 2,
|
||||
mBorderSize / 2,
|
||||
getWidth() - mBorderSize / 2,
|
||||
getHeight() - mBorderSize / 2
|
||||
);
|
||||
canvas.drawRoundRect(mRectFConnect, mCornerSize, mCornerSize, mPaintBorder);
|
||||
//绘制分割线
|
||||
drawDivisionLine(canvas);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分割线条数为内容框数目-1
|
||||
* 这里startX应该要加上左侧边框的宽度
|
||||
* 应该还需要加上分割线的一半
|
||||
* 至于startY和stopY不是 mBorderSize/2 而是 mBorderSize
|
||||
* startX是计算整个宽度的,需要算上左侧的边框宽度,所以不是+mBorderSize/2 而是+mBorderSize
|
||||
* startY和stopY:分割线是紧挨着边框内部的,所以应该是mBorderSize,而不是mBorderSize/2
|
||||
*/
|
||||
private void drawDivisionLine(Canvas canvas) {
|
||||
float stopY = getHeight() - mBorderSize;
|
||||
for (int i = 0; i < mContentNumber - 1; i++) {
|
||||
//对于分割线条,startX = stopX
|
||||
float startX = (i + 1) * getContentItemWidth() + i * mDivisionLineSize + mBorderSize + mDivisionLineSize / 2;
|
||||
canvas.drawLine(startX, mBorderSize, startX, stopY, mPaintDivisionLine);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算3种样式下,相应每个字符item的宽度
|
||||
*/
|
||||
private float getContentItemWidth() {
|
||||
//计算每个密码字符所占的宽度,每种输入框样式下,每个字符item所占宽度也不一样
|
||||
float tempWidth;
|
||||
switch (mInputBoxStyle) {
|
||||
case INPUT_BOX_STYLE_SINGLE:
|
||||
//单个输入框样式:宽度-间距宽度(字符数-1)*每个间距宽度-每个输入框的左右边框宽度
|
||||
tempWidth = getWidth() - (mContentNumber - 1) * mSpaceSize - 2 * mContentNumber * mBorderSize;
|
||||
break;
|
||||
case INPUT_BOX_STYLE_UNDERLINE:
|
||||
//下划线样式:宽度-间距宽度(字符数-1)*每个间距宽度
|
||||
tempWidth = getWidth() - (mContentNumber - 1) * mSpaceSize;
|
||||
break;
|
||||
case INPUT_BOX_STYLE_CONNECT:
|
||||
//矩形输入框样式:宽度-左右两边框宽度-分割线宽度(字符数-1)*每个分割线宽度
|
||||
default:
|
||||
tempWidth = getWidth() - (mDivisionLineSize * (mContentNumber - 1)) - 2 * mBorderSize;
|
||||
break;
|
||||
}
|
||||
return tempWidth / mContentNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据view的测量宽度,计算每个item的宽度
|
||||
*
|
||||
* @param measureWidth view的measure
|
||||
* @return onMeasure时的每个item宽度
|
||||
*/
|
||||
private float getContentItemWidthOnMeasure(int measureWidth) {
|
||||
//计算每个密码字符所占的宽度,每种输入框样式下,每个字符item所占宽度也不一样
|
||||
float tempWidth;
|
||||
switch (mInputBoxStyle) {
|
||||
case INPUT_BOX_STYLE_SINGLE:
|
||||
//单个输入框样式:宽度-间距宽度(字符数-1)*每个间距宽度-每个输入框的左右边框宽度
|
||||
tempWidth = measureWidth - (mContentNumber - 1) * mSpaceSize - 2 * mContentNumber * mBorderSize;
|
||||
break;
|
||||
case INPUT_BOX_STYLE_UNDERLINE:
|
||||
//下划线样式:宽度-间距宽度(字符数-1)*每个间距宽度
|
||||
tempWidth = measureWidth - (mContentNumber - 1) * mSpaceSize;
|
||||
break;
|
||||
case INPUT_BOX_STYLE_CONNECT:
|
||||
//矩形输入框样式:宽度-左右两边框宽度-分割线宽度(字符数-1)*每个分割线宽度
|
||||
default:
|
||||
tempWidth = measureWidth - (mDivisionLineSize * (mContentNumber - 1)) - 2 * mBorderSize;
|
||||
break;
|
||||
}
|
||||
return tempWidth / mContentNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算绘制文本的基线
|
||||
*
|
||||
* @param paint 绘制文字的画笔
|
||||
* @param halfHeight 高度的一半
|
||||
*/
|
||||
private float getTextBaseline(Paint paint, float halfHeight) {
|
||||
Paint.FontMetrics fontMetrics = paint.getFontMetrics();
|
||||
float dy = (fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.bottom;
|
||||
return halfHeight + dy;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置密码是否可见
|
||||
*/
|
||||
public void setContentShowMode(int mode) {
|
||||
if (mode != CONTENT_SHOW_MODE_PASSWORD && mode != CONTENT_SHOW_MODE_TEXT) {
|
||||
throw new IllegalArgumentException(
|
||||
"the value of the parameter must be one of" +
|
||||
"{1:EDIT_SHOW_MODE_PASSWORD} or " +
|
||||
"{2:EDIT_SHOW_MODE_TEXT}"
|
||||
);
|
||||
}
|
||||
mContentShowMode = mode;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取密码显示模式
|
||||
*/
|
||||
public int getContentShowMode() {
|
||||
return mContentShowMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置输入框样式
|
||||
*/
|
||||
public void setInputBoxStyle(int inputBoxStyle) {
|
||||
if (inputBoxStyle != INPUT_BOX_STYLE_CONNECT
|
||||
&& inputBoxStyle != INPUT_BOX_STYLE_SINGLE
|
||||
&& inputBoxStyle != INPUT_BOX_STYLE_UNDERLINE
|
||||
) {
|
||||
throw new IllegalArgumentException(
|
||||
"the value of the parameter must be one of" +
|
||||
"{1:INPUT_BOX_STYLE_CONNECT}, " +
|
||||
"{2:INPUT_BOX_STYLE_SINGLE} or " +
|
||||
"{3:INPUT_BOX_STYLE_UNDERLINE}"
|
||||
);
|
||||
}
|
||||
mInputBoxStyle = inputBoxStyle;
|
||||
// 这里没有调用invalidate因为会存在问题
|
||||
// invalidate会重绘,但是不会去重新测量,当输入框样式切换的之后,item的宽度其实是有变化的,所以此时需要重新测量
|
||||
// requestLayout,调用onMeasure和onLayout,不一定会调用onDraw,当view的l,t,r,b发生改变时会调用onDraw
|
||||
requestLayout();
|
||||
//invalidate();
|
||||
}
|
||||
|
||||
public void setOnInputListener(OnInputListener listener) {
|
||||
this.inputListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过复写onTextChanged来实现对输入的监听
|
||||
* 如果在onDraw里面监听text的输入长度来实现,会重复的调用该方法,就不妥当
|
||||
*/
|
||||
@Override
|
||||
protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
|
||||
super.onTextChanged(text, start, lengthBefore, lengthAfter);
|
||||
String content = text.toString().trim();
|
||||
if (inputListener != null) {
|
||||
inputListener.onInputChanged(content);
|
||||
if (content.length() == mContentNumber) {
|
||||
inputListener.onInputFinished(content);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取输入框样式
|
||||
*/
|
||||
public int getInputBoxStyle() {
|
||||
return mInputBoxStyle;
|
||||
}
|
||||
|
||||
private float dp2px(float dpValue) {
|
||||
return TypedValue.applyDimension(
|
||||
TypedValue.COMPLEX_UNIT_DIP,
|
||||
dpValue,
|
||||
Resources.getSystem().getDisplayMetrics()
|
||||
);
|
||||
}
|
||||
|
||||
private float sp2px(float spValue) {
|
||||
return TypedValue.applyDimension(
|
||||
TypedValue.COMPLEX_UNIT_SP,
|
||||
spValue,
|
||||
Resources.getSystem().getDisplayMetrics()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 光标Runnable
|
||||
* 通过Runnable每500ms执行重绘,每次runnable通过改变画笔的alpha值来使光标产生闪烁的效果
|
||||
*/
|
||||
private class CursorRunnable implements Runnable {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
//获取光标画笔的alpha值
|
||||
int alpha = mPaintCursor.getAlpha();
|
||||
//设置光标画笔的alpha值
|
||||
mPaintCursor.setAlpha(alpha == 0 ? 255 : 0);
|
||||
invalidate();
|
||||
postDelayed(this, mCursorDuration);
|
||||
// L.e("SplitEditText","CursorRunnable-----run-->");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 输入的监听抽象类
|
||||
* 没定义接口的原因是可以在抽象类里面定义空实现的方法,可以让用户根据需求选择性的复写某些方法
|
||||
*/
|
||||
public interface OnInputListener {
|
||||
|
||||
/**
|
||||
* 输入完成的抽象方法
|
||||
*
|
||||
* @param content 输入内容
|
||||
*/
|
||||
public abstract void onInputFinished(String content);
|
||||
|
||||
/**
|
||||
* 输入的内容
|
||||
*/
|
||||
public void onInputChanged(String text) ;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package com.qxcm.moduleutil.widget.pagerecyclerview;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* ProjectName: BubbleVoice
|
||||
* Package: com.qpyy.room.widget.pagerecyclerview
|
||||
* Description: Pager配置
|
||||
* Author: 姚闻达
|
||||
* CreateDate: 2021/1/13 21:00
|
||||
* UpdateUser: 更新者
|
||||
* UpdateDate: 2021/1/13 21:00
|
||||
* UpdateRemark: 更新说明
|
||||
* Version: 1.0
|
||||
*/
|
||||
public class PagerConfig {
|
||||
private static final String TAG = "PagerGrid";
|
||||
private static boolean sShowLog = false;
|
||||
private static int sFlingThreshold = 1000; // Fling 阀值,滚动速度超过该阀值才会触发滚动
|
||||
private static float sMillisecondsPreInch = 60f; // 每一个英寸滚动需要的微秒数,数值越大,速度越慢
|
||||
|
||||
/**
|
||||
* 判断是否输出日志
|
||||
*
|
||||
* @return true 输出,false 不输出
|
||||
*/
|
||||
public static boolean isShowLog() {
|
||||
return sShowLog;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置是否输出日志
|
||||
*
|
||||
* @param showLog 是否输出
|
||||
*/
|
||||
public static void setShowLog(boolean showLog) {
|
||||
sShowLog = showLog;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前滚动速度阀值
|
||||
*
|
||||
* @return 当前滚动速度阀值
|
||||
*/
|
||||
public static int getFlingThreshold() {
|
||||
return sFlingThreshold;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置当前滚动速度阀值
|
||||
*
|
||||
* @param flingThreshold 滚动速度阀值
|
||||
*/
|
||||
public static void setFlingThreshold(int flingThreshold) {
|
||||
sFlingThreshold = flingThreshold;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取滚动速度 英寸/微秒
|
||||
*
|
||||
* @return 英寸滚动速度
|
||||
*/
|
||||
public static float getMillisecondsPreInch() {
|
||||
return sMillisecondsPreInch;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置像素滚动速度 英寸/微秒
|
||||
*
|
||||
* @param millisecondsPreInch 英寸滚动速度
|
||||
*/
|
||||
public static void setMillisecondsPreInch(float millisecondsPreInch) {
|
||||
sMillisecondsPreInch = millisecondsPreInch;
|
||||
}
|
||||
|
||||
//--- 日志 -------------------------------------------------------------------------------------
|
||||
|
||||
public static void Logi(String msg) {
|
||||
if (!PagerConfig.isShowLog()) return;
|
||||
Log.i(TAG, msg);
|
||||
}
|
||||
|
||||
public static void Loge(String msg) {
|
||||
if (!PagerConfig.isShowLog()) return;
|
||||
Log.e(TAG, msg);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,892 @@
|
||||
package com.qxcm.moduleutil.widget.pagerecyclerview;
|
||||
|
||||
import static android.view.View.MeasureSpec.EXACTLY;
|
||||
import static androidx.recyclerview.widget.RecyclerView.SCROLL_STATE_IDLE;
|
||||
|
||||
import static com.qxcm.moduleutil.widget.pagerecyclerview.PagerConfig.Loge;
|
||||
import static com.qxcm.moduleutil.widget.pagerecyclerview.PagerConfig.Logi;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.graphics.PointF;
|
||||
import android.graphics.Rect;
|
||||
import android.util.Log;
|
||||
import android.util.SparseArray;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.IntDef;
|
||||
import androidx.annotation.IntRange;
|
||||
import androidx.recyclerview.widget.LinearSmoothScroller;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
|
||||
/**
|
||||
* ProjectName: BubbleVoice
|
||||
* Package: com.qpyy.room.widget.pagerecyclerview
|
||||
* Description: 分页的网格布局管理器
|
||||
* Author: 姚闻达
|
||||
* CreateDate: 2021/1/13 21:03
|
||||
* UpdateUser: 更新者
|
||||
* UpdateDate: 2021/1/13 21:03
|
||||
* UpdateRemark: 更新说明
|
||||
* Version: 1.0
|
||||
*/
|
||||
public class PagerGridLayoutManager extends RecyclerView.LayoutManager
|
||||
implements RecyclerView.SmoothScroller.ScrollVectorProvider {
|
||||
|
||||
private static final String TAG = PagerGridLayoutManager.class.getSimpleName();
|
||||
|
||||
public static final int VERTICAL = 0; // 垂直滚动
|
||||
public static final int HORIZONTAL = 1; // 水平滚动
|
||||
|
||||
@IntDef({VERTICAL, HORIZONTAL})
|
||||
public @interface OrientationType {
|
||||
} // 滚动类型
|
||||
|
||||
@OrientationType
|
||||
private int mOrientation; // 默认水平滚动
|
||||
|
||||
private int mOffsetX = 0; // 水平滚动距离(偏移量)
|
||||
private int mOffsetY = 0; // 垂直滚动距离(偏移量)
|
||||
|
||||
private int mRows; // 行数
|
||||
private int mColumns; // 列数
|
||||
private int mOnePageSize; // 一页的条目数量
|
||||
|
||||
private SparseArray<Rect> mItemFrames; // 条目的显示区域
|
||||
|
||||
private int mItemWidth = 0; // 条目宽度
|
||||
private int mItemHeight = 0; // 条目高度
|
||||
|
||||
private int mWidthUsed = 0; // 已经使用空间,用于测量View
|
||||
private int mHeightUsed = 0; // 已经使用空间,用于测量View
|
||||
|
||||
private int mMaxScrollX; // 最大允许滑动的宽度
|
||||
private int mMaxScrollY; // 最大允许滑动的高度
|
||||
private int mScrollState = SCROLL_STATE_IDLE; // 滚动状态
|
||||
|
||||
private boolean mAllowContinuousScroll = true; // 是否允许连续滚动
|
||||
|
||||
private RecyclerView mRecyclerView;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
*
|
||||
* @param rows 行数
|
||||
* @param columns 列数
|
||||
* @param orientation 方向
|
||||
*/
|
||||
public PagerGridLayoutManager(@IntRange(from = 1, to = 100) int rows,
|
||||
@IntRange(from = 1, to = 100) int columns,
|
||||
@OrientationType int orientation) {
|
||||
mItemFrames = new SparseArray<>();
|
||||
mOrientation = orientation;
|
||||
mRows = rows;
|
||||
mColumns = columns;
|
||||
mOnePageSize = mRows * mColumns;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttachedToWindow(RecyclerView view) {
|
||||
super.onAttachedToWindow(view);
|
||||
mRecyclerView = view;
|
||||
}
|
||||
|
||||
//--- 处理布局 ----------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* 布局子View
|
||||
*
|
||||
* @param recycler Recycler
|
||||
* @param state State
|
||||
*/
|
||||
@Override
|
||||
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
|
||||
Logi("Item onLayoutChildren");
|
||||
Logi("Item onLayoutChildren isPreLayout = " + state.isPreLayout());
|
||||
Logi("Item onLayoutChildren isMeasuring = " + state.isMeasuring());
|
||||
Loge("Item onLayoutChildren state = " + state);
|
||||
|
||||
// 如果是 preLayout 则不重新布局
|
||||
if (state.isPreLayout() || !state.didStructureChange()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (getItemCount() == 0) {
|
||||
removeAndRecycleAllViews(recycler);
|
||||
// 页面变化回调
|
||||
setPageCount(0);
|
||||
setPageIndex(0, false);
|
||||
return;
|
||||
} else {
|
||||
setPageCount(getTotalPageCount());
|
||||
setPageIndex(getPageIndexByOffset(), false);
|
||||
}
|
||||
|
||||
// 计算页面数量
|
||||
int mPageCount = getItemCount() / mOnePageSize;
|
||||
if (getItemCount() % mOnePageSize != 0) {
|
||||
mPageCount++;
|
||||
}
|
||||
|
||||
// 计算可以滚动的最大数值,并对滚动距离进行修正
|
||||
if (canScrollHorizontally()) {
|
||||
mMaxScrollX = (mPageCount - 1) * getUsableWidth();
|
||||
mMaxScrollY = 0;
|
||||
if (mOffsetX > mMaxScrollX) {
|
||||
mOffsetX = mMaxScrollX;
|
||||
}
|
||||
} else {
|
||||
mMaxScrollX = 0;
|
||||
mMaxScrollY = (mPageCount - 1) * getUsableHeight();
|
||||
if (mOffsetY > mMaxScrollY) {
|
||||
mOffsetY = mMaxScrollY;
|
||||
}
|
||||
}
|
||||
|
||||
// 接口回调
|
||||
// setPageCount(mPageCount);
|
||||
// setPageIndex(mCurrentPageIndex, false);
|
||||
|
||||
Logi("count = " + getItemCount());
|
||||
|
||||
if (mItemWidth <= 0) {
|
||||
mItemWidth = getUsableWidth() / mColumns;
|
||||
}
|
||||
if (mItemHeight <= 0) {
|
||||
mItemHeight = getUsableHeight() / mRows;
|
||||
}
|
||||
|
||||
mWidthUsed = getUsableWidth() - mItemWidth;
|
||||
mHeightUsed = getUsableHeight() - mItemHeight;
|
||||
|
||||
// 预存储两页的View显示区域
|
||||
for (int i = 0; i < mOnePageSize * 2; i++) {
|
||||
getItemFrameByPosition(i);
|
||||
}
|
||||
|
||||
if (mOffsetX == 0 && mOffsetY == 0) {
|
||||
// 预存储View
|
||||
for (int i = 0; i < mOnePageSize; i++) {
|
||||
if (i >= getItemCount()) break; // 防止数据过少时导致数组越界异常
|
||||
View view = recycler.getViewForPosition(i);
|
||||
addView(view);
|
||||
measureChildWithMargins(view, mWidthUsed, mHeightUsed);
|
||||
}
|
||||
}
|
||||
|
||||
// 回收和填充布局
|
||||
recycleAndFillItems(recycler, state, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 布局结束
|
||||
*
|
||||
* @param state State
|
||||
*/
|
||||
@Override
|
||||
public void onLayoutCompleted(RecyclerView.State state) {
|
||||
super.onLayoutCompleted(state);
|
||||
if (state.isPreLayout()) return;
|
||||
// 页面状态回调
|
||||
setPageCount(getTotalPageCount());
|
||||
setPageIndex(getPageIndexByOffset(), false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 回收和填充布局
|
||||
*
|
||||
* @param recycler Recycler
|
||||
* @param state State
|
||||
* @param isStart 是否从头开始,用于控制View遍历方向,true 为从头到尾,false 为从尾到头
|
||||
*/
|
||||
@SuppressLint("CheckResult")
|
||||
private void recycleAndFillItems(RecyclerView.Recycler recycler, RecyclerView.State state,
|
||||
boolean isStart) {
|
||||
if (state.isPreLayout()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Logi("mOffsetX = " + mOffsetX);
|
||||
Logi("mOffsetY = " + mOffsetY);
|
||||
|
||||
// 计算显示区域区前后多存储一列或则一行
|
||||
Rect displayRect = new Rect(mOffsetX - mItemWidth, mOffsetY - mItemHeight,
|
||||
getUsableWidth() + mOffsetX + mItemWidth, getUsableHeight() + mOffsetY + mItemHeight);
|
||||
// 对显显示区域进行修正(计算当前显示区域和最大显示区域对交集)
|
||||
displayRect.intersect(0, 0, mMaxScrollX + getUsableWidth(), mMaxScrollY + getUsableHeight());
|
||||
Loge("displayRect = " + displayRect.toString());
|
||||
|
||||
int startPos = 0; // 获取第一个条目的Pos
|
||||
int pageIndex = getPageIndexByOffset();
|
||||
startPos = pageIndex * mOnePageSize;
|
||||
Logi("startPos = " + startPos);
|
||||
startPos = startPos - mOnePageSize * 2;
|
||||
if (startPos < 0) {
|
||||
startPos = 0;
|
||||
}
|
||||
int stopPos = startPos + mOnePageSize * 4;
|
||||
if (stopPos > getItemCount()) {
|
||||
stopPos = getItemCount();
|
||||
}
|
||||
|
||||
Loge("startPos = " + startPos);
|
||||
Loge("stopPos = " + stopPos);
|
||||
|
||||
detachAndScrapAttachedViews(recycler); // 移除所有View
|
||||
|
||||
if (isStart) {
|
||||
for (int i = startPos; i < stopPos; i++) {
|
||||
addOrRemove(recycler, displayRect, i);
|
||||
}
|
||||
} else {
|
||||
for (int i = stopPos - 1; i >= startPos; i--) {
|
||||
addOrRemove(recycler, displayRect, i);
|
||||
}
|
||||
}
|
||||
Loge("child count = " + getChildCount());
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加或者移除条目
|
||||
*
|
||||
* @param recycler RecyclerView
|
||||
* @param displayRect 显示区域
|
||||
* @param i 条目下标
|
||||
*/
|
||||
private void addOrRemove(RecyclerView.Recycler recycler, Rect displayRect, int i) {
|
||||
View child = recycler.getViewForPosition(i);
|
||||
Rect rect = getItemFrameByPosition(i);
|
||||
if (!Rect.intersects(displayRect, rect)) {
|
||||
removeAndRecycleView(child, recycler); // 回收入暂存区
|
||||
} else {
|
||||
addView(child);
|
||||
measureChildWithMargins(child, mWidthUsed, mHeightUsed);
|
||||
RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) child.getLayoutParams();
|
||||
layoutDecorated(child,
|
||||
rect.left - mOffsetX + lp.leftMargin + getPaddingLeft(),
|
||||
rect.top - mOffsetY + lp.topMargin + getPaddingTop(),
|
||||
rect.right - mOffsetX - lp.rightMargin + getPaddingLeft(),
|
||||
rect.bottom - mOffsetY - lp.bottomMargin + getPaddingTop());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//--- 处理滚动 ----------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* 水平滚动
|
||||
*
|
||||
* @param dx 滚动距离
|
||||
* @param recycler 回收器
|
||||
* @param state 滚动状态
|
||||
* @return 实际滚动距离
|
||||
*/
|
||||
@Override
|
||||
public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, RecyclerView.State
|
||||
state) {
|
||||
int newX = mOffsetX + dx;
|
||||
int result = dx;
|
||||
if (newX > mMaxScrollX) {
|
||||
result = mMaxScrollX - mOffsetX;
|
||||
} else if (newX < 0) {
|
||||
result = 0 - mOffsetX;
|
||||
}
|
||||
mOffsetX += result;
|
||||
setPageIndex(getPageIndexByOffset(), true);
|
||||
offsetChildrenHorizontal(-result);
|
||||
if (result > 0) {
|
||||
recycleAndFillItems(recycler, state, true);
|
||||
} else {
|
||||
recycleAndFillItems(recycler, state, false);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 垂直滚动
|
||||
*
|
||||
* @param dy 滚动距离
|
||||
* @param recycler 回收器
|
||||
* @param state 滚动状态
|
||||
* @return 实际滚动距离
|
||||
*/
|
||||
@Override
|
||||
public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State
|
||||
state) {
|
||||
int newY = mOffsetY + dy;
|
||||
int result = dy;
|
||||
if (newY > mMaxScrollY) {
|
||||
result = mMaxScrollY - mOffsetY;
|
||||
} else if (newY < 0) {
|
||||
result = 0 - mOffsetY;
|
||||
}
|
||||
mOffsetY += result;
|
||||
setPageIndex(getPageIndexByOffset(), true);
|
||||
offsetChildrenVertical(-result);
|
||||
if (result > 0) {
|
||||
recycleAndFillItems(recycler, state, true);
|
||||
} else {
|
||||
recycleAndFillItems(recycler, state, false);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 监听滚动状态,滚动结束后通知当前选中的页面
|
||||
*
|
||||
* @param state 滚动状态
|
||||
*/
|
||||
@Override
|
||||
public void onScrollStateChanged(int state) {
|
||||
Logi("onScrollStateChanged = " + state);
|
||||
mScrollState = state;
|
||||
super.onScrollStateChanged(state);
|
||||
if (state == SCROLL_STATE_IDLE) {
|
||||
setPageIndex(getPageIndexByOffset(), false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//--- 私有方法 ----------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* 获取条目显示区域
|
||||
*
|
||||
* @param pos 位置下标
|
||||
* @return 显示区域
|
||||
*/
|
||||
private Rect getItemFrameByPosition(int pos) {
|
||||
Rect rect = mItemFrames.get(pos);
|
||||
if (null == rect) {
|
||||
rect = new Rect();
|
||||
// 计算显示区域 Rect
|
||||
|
||||
// 1. 获取当前View所在页数
|
||||
int page = pos / mOnePageSize;
|
||||
|
||||
// 2. 计算当前页数左上角的总偏移量
|
||||
int offsetX = 0;
|
||||
int offsetY = 0;
|
||||
if (canScrollHorizontally()) {
|
||||
offsetX += getUsableWidth() * page;
|
||||
} else {
|
||||
offsetY += getUsableHeight() * page;
|
||||
}
|
||||
|
||||
// 3. 根据在当前页面中的位置确定具体偏移量
|
||||
int pagePos = pos % mOnePageSize; // 在当前页面中是第几个
|
||||
int row = pagePos / mColumns; // 获取所在行
|
||||
int col = pagePos - (row * mColumns); // 获取所在列
|
||||
|
||||
offsetX += col * mItemWidth;
|
||||
offsetY += row * mItemHeight;
|
||||
|
||||
// 状态输出,用于调试
|
||||
Logi("pagePos = " + pagePos);
|
||||
Logi("行 = " + row);
|
||||
Logi("列 = " + col);
|
||||
|
||||
Logi("offsetX = " + offsetX);
|
||||
Logi("offsetY = " + offsetY);
|
||||
|
||||
rect.left = offsetX;
|
||||
rect.top = offsetY;
|
||||
rect.right = offsetX + mItemWidth;
|
||||
rect.bottom = offsetY + mItemHeight;
|
||||
|
||||
// 存储
|
||||
mItemFrames.put(pos, rect);
|
||||
}
|
||||
return rect;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取可用的宽度
|
||||
*
|
||||
* @return 宽度 - padding
|
||||
*/
|
||||
private int getUsableWidth() {
|
||||
return getWidth() - getPaddingLeft() - getPaddingRight();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取可用的高度
|
||||
*
|
||||
* @return 高度 - padding
|
||||
*/
|
||||
private int getUsableHeight() {
|
||||
return getHeight() - getPaddingTop() - getPaddingBottom();
|
||||
}
|
||||
|
||||
|
||||
//--- 页面相关(私有) -----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* 获取总页数
|
||||
*/
|
||||
private int getTotalPageCount() {
|
||||
if (getItemCount() <= 0) return 0;
|
||||
int totalCount = getItemCount() / mOnePageSize;
|
||||
if (getItemCount() % mOnePageSize != 0) {
|
||||
totalCount++;
|
||||
}
|
||||
return totalCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据pos,获取该View所在的页面
|
||||
*
|
||||
* @param pos position
|
||||
* @return 页面的页码
|
||||
*/
|
||||
private int getPageIndexByPos(int pos) {
|
||||
return pos / mOnePageSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 offset 获取页面Index
|
||||
*
|
||||
* @return 页面 Index
|
||||
*/
|
||||
private int getPageIndexByOffset() {
|
||||
int pageIndex;
|
||||
if (canScrollVertically()) {
|
||||
int pageHeight = getUsableHeight();
|
||||
if (mOffsetY <= 0 || pageHeight <= 0) {
|
||||
pageIndex = 0;
|
||||
} else {
|
||||
pageIndex = mOffsetY / pageHeight;
|
||||
if (mOffsetY % pageHeight > pageHeight / 2) {
|
||||
pageIndex++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
int pageWidth = getUsableWidth();
|
||||
if (mOffsetX <= 0 || pageWidth <= 0) {
|
||||
pageIndex = 0;
|
||||
} else {
|
||||
pageIndex = mOffsetX / pageWidth;
|
||||
if (mOffsetX % pageWidth > pageWidth / 2) {
|
||||
pageIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
Logi("getPageIndexByOffset pageIndex = " + pageIndex);
|
||||
return pageIndex;
|
||||
}
|
||||
|
||||
|
||||
//--- 公开方法 ----------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* 创建默认布局参数
|
||||
*
|
||||
* @return 默认布局参数
|
||||
*/
|
||||
@Override
|
||||
public RecyclerView.LayoutParams generateDefaultLayoutParams() {
|
||||
return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理测量逻辑
|
||||
*
|
||||
* @param recycler RecyclerView
|
||||
* @param state 状态
|
||||
* @param widthMeasureSpec 宽度属性
|
||||
* @param heightMeasureSpec 高估属性
|
||||
*/
|
||||
@Override
|
||||
public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state, int widthMeasureSpec, int heightMeasureSpec) {
|
||||
super.onMeasure(recycler, state, widthMeasureSpec, heightMeasureSpec);
|
||||
int widthsize = View.MeasureSpec.getSize(widthMeasureSpec); //取出宽度的确切数值
|
||||
int widthmode = View.MeasureSpec.getMode(widthMeasureSpec); //取出宽度的测量模式
|
||||
|
||||
int heightsize = View.MeasureSpec.getSize(heightMeasureSpec); //取出高度的确切数值
|
||||
int heightmode = View.MeasureSpec.getMode(heightMeasureSpec); //取出高度的测量模式
|
||||
|
||||
// 将 wrap_content 转换为 match_parent
|
||||
if (widthmode != EXACTLY && widthsize > 0) {
|
||||
widthmode = EXACTLY;
|
||||
}
|
||||
if (heightmode != EXACTLY && heightsize > 0) {
|
||||
heightmode = EXACTLY;
|
||||
}
|
||||
setMeasuredDimension(View.MeasureSpec.makeMeasureSpec(widthsize, widthmode),
|
||||
View.MeasureSpec.makeMeasureSpec(heightsize, heightmode));
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否可以水平滚动
|
||||
*
|
||||
* @return true 是,false 不是。
|
||||
*/
|
||||
@Override
|
||||
public boolean canScrollHorizontally() {
|
||||
return mOrientation == HORIZONTAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否可以垂直滚动
|
||||
*
|
||||
* @return true 是,false 不是。
|
||||
*/
|
||||
@Override
|
||||
public boolean canScrollVertically() {
|
||||
return mOrientation == VERTICAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* 找到下一页第一个条目的位置
|
||||
*
|
||||
* @return 第一个搞条目的位置
|
||||
*/
|
||||
int findNextPageFirstPos() {
|
||||
int page = mLastPageIndex;
|
||||
page++;
|
||||
if (page >= getTotalPageCount()) {
|
||||
page = getTotalPageCount() - 1;
|
||||
}
|
||||
Loge("computeScrollVectorForPosition next = " + page);
|
||||
return page * mOnePageSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* 找到上一页的第一个条目的位置
|
||||
*
|
||||
* @return 第一个条目的位置
|
||||
*/
|
||||
int findPrePageFirstPos() {
|
||||
// 在获取时由于前一页的View预加载出来了,所以获取到的直接就是前一页
|
||||
int page = mLastPageIndex;
|
||||
page--;
|
||||
Loge("computeScrollVectorForPosition pre = " + page);
|
||||
if (page < 0) {
|
||||
page = 0;
|
||||
}
|
||||
Loge("computeScrollVectorForPosition pre = " + page);
|
||||
return page * mOnePageSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前 X 轴偏移量
|
||||
*
|
||||
* @return X 轴偏移量
|
||||
*/
|
||||
public int getOffsetX() {
|
||||
return mOffsetX;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前 Y 轴偏移量
|
||||
*
|
||||
* @return Y 轴偏移量
|
||||
*/
|
||||
public int getOffsetY() {
|
||||
return mOffsetY;
|
||||
}
|
||||
|
||||
|
||||
//--- 页面对齐 ----------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* 计算到目标位置需要滚动的距离{@link RecyclerView.SmoothScroller.ScrollVectorProvider}
|
||||
*
|
||||
* @param targetPosition 目标控件
|
||||
* @return 需要滚动的距离
|
||||
*/
|
||||
@Override
|
||||
public PointF computeScrollVectorForPosition(int targetPosition) {
|
||||
PointF vector = new PointF();
|
||||
int[] pos = getSnapOffset(targetPosition);
|
||||
vector.x = pos[0];
|
||||
vector.y = pos[1];
|
||||
return vector;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取偏移量(为PagerGridSnapHelper准备)
|
||||
* 用于分页滚动,确定需要滚动的距离。
|
||||
* {@link PagerGridSnapHelper}
|
||||
*
|
||||
* @param targetPosition 条目下标
|
||||
*/
|
||||
int[] getSnapOffset(int targetPosition) {
|
||||
int[] offset = new int[2];
|
||||
int[] pos = getPageLeftTopByPosition(targetPosition);
|
||||
offset[0] = pos[0] - mOffsetX;
|
||||
offset[1] = pos[1] - mOffsetY;
|
||||
return offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据条目下标获取该条目所在页面的左上角位置
|
||||
*
|
||||
* @param pos 条目下标
|
||||
* @return 左上角位置
|
||||
*/
|
||||
private int[] getPageLeftTopByPosition(int pos) {
|
||||
int[] leftTop = new int[2];
|
||||
int page = getPageIndexByPos(pos);
|
||||
if (canScrollHorizontally()) {
|
||||
leftTop[0] = page * getUsableWidth();
|
||||
leftTop[1] = 0;
|
||||
} else {
|
||||
leftTop[0] = 0;
|
||||
leftTop[1] = page * getUsableHeight();
|
||||
}
|
||||
return leftTop;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取需要对齐的View
|
||||
*
|
||||
* @return 需要对齐的View
|
||||
*/
|
||||
public View findSnapView() {
|
||||
if (null != getFocusedChild()) {
|
||||
return getFocusedChild();
|
||||
}
|
||||
if (getChildCount() <= 0) {
|
||||
return null;
|
||||
}
|
||||
int targetPos = getPageIndexByOffset() * mOnePageSize; // 目标Pos
|
||||
for (int i = 0; i < getChildCount(); i++) {
|
||||
int childPos = getPosition(getChildAt(i));
|
||||
if (childPos == targetPos) {
|
||||
return getChildAt(i);
|
||||
}
|
||||
}
|
||||
return getChildAt(0);
|
||||
}
|
||||
|
||||
|
||||
//--- 处理页码变化 -------------------------------------------------------------------------------
|
||||
|
||||
private boolean mChangeSelectInScrolling = true; // 是否在滚动过程中对页面变化回调
|
||||
private int mLastPageCount = -1; // 上次页面总数
|
||||
private int mLastPageIndex = -1; // 上次页面下标
|
||||
|
||||
/**
|
||||
* 设置页面总数
|
||||
*
|
||||
* @param pageCount 页面总数
|
||||
*/
|
||||
private void setPageCount(int pageCount) {
|
||||
if (pageCount >= 0) {
|
||||
if (mPageListener != null && pageCount != mLastPageCount) {
|
||||
mPageListener.onPageSizeChanged(pageCount);
|
||||
}
|
||||
mLastPageCount = pageCount;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置当前选中页面
|
||||
*
|
||||
* @param pageIndex 页面下标
|
||||
* @param isScrolling 是否处于滚动状态
|
||||
*/
|
||||
private void setPageIndex(int pageIndex, boolean isScrolling) {
|
||||
Loge("setPageIndex = " + pageIndex + ":" + isScrolling);
|
||||
if (pageIndex == mLastPageIndex) return;
|
||||
// 如果允许连续滚动,那么在滚动过程中就会更新页码记录
|
||||
if (isAllowContinuousScroll()) {
|
||||
mLastPageIndex = pageIndex;
|
||||
} else {
|
||||
// 否则,只有等滚动停下时才会更新页码记录
|
||||
if (!isScrolling) {
|
||||
mLastPageIndex = pageIndex;
|
||||
}
|
||||
}
|
||||
if (isScrolling && !mChangeSelectInScrolling) return;
|
||||
if (pageIndex >= 0) {
|
||||
if (null != mPageListener) {
|
||||
mPageListener.onPageSelect(pageIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置是否在滚动状态更新选中页码
|
||||
*
|
||||
* @param changeSelectInScrolling true:更新、false:不更新
|
||||
*/
|
||||
public void setChangeSelectInScrolling(boolean changeSelectInScrolling) {
|
||||
mChangeSelectInScrolling = changeSelectInScrolling;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置滚动方向
|
||||
*
|
||||
* @param orientation 滚动方向
|
||||
* @return 最终的滚动方向
|
||||
*/
|
||||
@OrientationType
|
||||
public int setOrientationType(@OrientationType int orientation) {
|
||||
if (mOrientation == orientation || mScrollState != SCROLL_STATE_IDLE) return mOrientation;
|
||||
mOrientation = orientation;
|
||||
mItemFrames.clear();
|
||||
int x = mOffsetX;
|
||||
int y = mOffsetY;
|
||||
mOffsetX = y / getUsableHeight() * getUsableWidth();
|
||||
mOffsetY = x / getUsableWidth() * getUsableHeight();
|
||||
int mx = mMaxScrollX;
|
||||
int my = mMaxScrollY;
|
||||
mMaxScrollX = my / getUsableHeight() * getUsableWidth();
|
||||
mMaxScrollY = mx / getUsableWidth() * getUsableHeight();
|
||||
return mOrientation;
|
||||
}
|
||||
|
||||
//--- 滚动到指定位置 -----------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) {
|
||||
int targetPageIndex = getPageIndexByPos(position);
|
||||
smoothScrollToPage(targetPageIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* 平滑滚动到上一页
|
||||
*/
|
||||
public void smoothPrePage() {
|
||||
smoothScrollToPage(getPageIndexByOffset() - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 平滑滚动到下一页
|
||||
*/
|
||||
public void smoothNextPage() {
|
||||
smoothScrollToPage(getPageIndexByOffset() + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 平滑滚动到指定页面
|
||||
*
|
||||
* @param pageIndex 页面下标
|
||||
*/
|
||||
public void smoothScrollToPage(int pageIndex) {
|
||||
if (pageIndex < 0 || pageIndex >= mLastPageCount) {
|
||||
Log.e(TAG, "pageIndex is outOfIndex, must in [0, " + mLastPageCount + ").");
|
||||
return;
|
||||
}
|
||||
if (null == mRecyclerView) {
|
||||
Log.e(TAG, "RecyclerView Not Found!");
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果滚动到页面之间距离过大,先直接滚动到目标页面到临近页面,在使用 smoothScroll 最终滚动到目标
|
||||
// 否则在滚动距离很大时,会导致滚动耗费的时间非常长
|
||||
int currentPageIndex = getPageIndexByOffset();
|
||||
if (Math.abs(pageIndex - currentPageIndex) > 3) {
|
||||
if (pageIndex > currentPageIndex) {
|
||||
scrollToPage(pageIndex - 3);
|
||||
} else if (pageIndex < currentPageIndex) {
|
||||
scrollToPage(pageIndex + 3);
|
||||
}
|
||||
}
|
||||
|
||||
// 具体执行滚动
|
||||
LinearSmoothScroller smoothScroller = new PagerGridSmoothScroller(mRecyclerView);
|
||||
int position = pageIndex * mOnePageSize;
|
||||
smoothScroller.setTargetPosition(position);
|
||||
startSmoothScroll(smoothScroller);
|
||||
}
|
||||
|
||||
//=== 直接滚动 ===
|
||||
|
||||
@Override
|
||||
public void scrollToPosition(int position) {
|
||||
int pageIndex = getPageIndexByPos(position);
|
||||
scrollToPage(pageIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* 上一页
|
||||
*/
|
||||
public void prePage() {
|
||||
scrollToPage(getPageIndexByOffset() - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 下一页
|
||||
*/
|
||||
public void nextPage() {
|
||||
scrollToPage(getPageIndexByOffset() + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 滚动到指定页面
|
||||
*
|
||||
* @param pageIndex 页面下标
|
||||
*/
|
||||
public void scrollToPage(int pageIndex) {
|
||||
if (pageIndex < 0 || pageIndex >= mLastPageCount) {
|
||||
Log.e(TAG, "pageIndex = " + pageIndex + " is out of bounds, mast in [0, " + mLastPageCount + ")");
|
||||
return;
|
||||
}
|
||||
|
||||
if (null == mRecyclerView) {
|
||||
Log.e(TAG, "RecyclerView Not Found!");
|
||||
return;
|
||||
}
|
||||
|
||||
int mTargetOffsetXBy = 0;
|
||||
int mTargetOffsetYBy = 0;
|
||||
if (canScrollVertically()) {
|
||||
mTargetOffsetXBy = 0;
|
||||
mTargetOffsetYBy = pageIndex * getUsableHeight() - mOffsetY;
|
||||
} else {
|
||||
mTargetOffsetXBy = pageIndex * getUsableWidth() - mOffsetX;
|
||||
mTargetOffsetYBy = 0;
|
||||
}
|
||||
Loge("mTargetOffsetXBy = " + mTargetOffsetXBy);
|
||||
Loge("mTargetOffsetYBy = " + mTargetOffsetYBy);
|
||||
mRecyclerView.scrollBy(mTargetOffsetXBy, mTargetOffsetYBy);
|
||||
setPageIndex(pageIndex, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否允许连续滚动,默认为允许
|
||||
*
|
||||
* @return true 允许, false 不允许
|
||||
*/
|
||||
public boolean isAllowContinuousScroll() {
|
||||
return mAllowContinuousScroll;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置是否允许连续滚动
|
||||
*
|
||||
* @param allowContinuousScroll true 允许,false 不允许
|
||||
*/
|
||||
public void setAllowContinuousScroll(boolean allowContinuousScroll) {
|
||||
mAllowContinuousScroll = allowContinuousScroll;
|
||||
}
|
||||
|
||||
//--- 对外接口 ----------------------------------------------------------------------------------
|
||||
|
||||
private PageListener mPageListener = null;
|
||||
|
||||
public void setPageListener(PageListener pageListener) {
|
||||
mPageListener = pageListener;
|
||||
}
|
||||
|
||||
public interface PageListener {
|
||||
/**
|
||||
* 页面总数量变化
|
||||
*
|
||||
* @param pageSize 页面总数
|
||||
*/
|
||||
void onPageSizeChanged(int pageSize);
|
||||
|
||||
/**
|
||||
* 页面被选中
|
||||
*
|
||||
* @param pageIndex 选中的页面
|
||||
*/
|
||||
void onPageSelect(int pageIndex);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.qxcm.moduleutil.widget.pagerecyclerview;
|
||||
|
||||
|
||||
import static com.qxcm.moduleutil.widget.pagerecyclerview.PagerConfig.Logi;
|
||||
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.LinearSmoothScroller;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
/**
|
||||
* ProjectName: BubbleVoice
|
||||
* Package: com.qpyy.room.widget.pagerecyclerview
|
||||
* Description: 用于处理平滑滚动,用于用户手指抬起后页面对齐或者 Fling 事件
|
||||
* Author: 姚闻达
|
||||
* CreateDate: 2021/1/13 21:08
|
||||
* UpdateUser: 更新者
|
||||
* UpdateDate: 2021/1/13 21:08
|
||||
* UpdateRemark: 更新说明
|
||||
* Version: 1.0
|
||||
*/
|
||||
public class PagerGridSmoothScroller extends LinearSmoothScroller {
|
||||
private RecyclerView mRecyclerView;
|
||||
|
||||
public PagerGridSmoothScroller(@NonNull RecyclerView recyclerView) {
|
||||
super(recyclerView.getContext());
|
||||
mRecyclerView = recyclerView;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onTargetFound(View targetView, RecyclerView.State state, Action action) {
|
||||
RecyclerView.LayoutManager manager = mRecyclerView.getLayoutManager();
|
||||
if (null == manager) return;
|
||||
if (manager instanceof PagerGridLayoutManager) {
|
||||
PagerGridLayoutManager layoutManager = (PagerGridLayoutManager) manager;
|
||||
int pos = mRecyclerView.getChildAdapterPosition(targetView);
|
||||
int[] snapDistances = layoutManager.getSnapOffset(pos);
|
||||
final int dx = snapDistances[0];
|
||||
final int dy = snapDistances[1];
|
||||
Logi("dx = " + dx);
|
||||
Logi("dy = " + dy);
|
||||
final int time = calculateTimeForScrolling(Math.max(Math.abs(dx), Math.abs(dy)));
|
||||
if (time > 0) {
|
||||
action.update(dx, dy, time, mDecelerateInterpolator);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
|
||||
return PagerConfig.getMillisecondsPreInch() / displayMetrics.densityDpi;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
package com.qxcm.moduleutil.widget.pagerecyclerview;
|
||||
|
||||
|
||||
import static com.qxcm.moduleutil.widget.pagerecyclerview.PagerConfig.Loge;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.recyclerview.widget.LinearSmoothScroller;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.recyclerview.widget.SnapHelper;
|
||||
|
||||
/**
|
||||
* ProjectName: BubbleVoice
|
||||
* Package: com.qpyy.room.widget.pagerecyclerview
|
||||
* Description: 分页居中工具 每次只滚动一个页面
|
||||
* Author: 姚闻达
|
||||
* CreateDate: 2021/1/13 21:10
|
||||
* UpdateUser: 更新者
|
||||
* UpdateDate: 2021/1/13 21:10
|
||||
* UpdateRemark: 更新说明
|
||||
* Version: 1.0
|
||||
*/
|
||||
public class PagerGridSnapHelper extends SnapHelper {
|
||||
private RecyclerView mRecyclerView; // RecyclerView
|
||||
|
||||
/**
|
||||
* 用于将滚动工具和 Recycler 绑定
|
||||
*
|
||||
* @param recyclerView RecyclerView
|
||||
* @throws IllegalStateException 状态异常
|
||||
*/
|
||||
@Override
|
||||
public void attachToRecyclerView(@Nullable RecyclerView recyclerView) throws
|
||||
IllegalStateException {
|
||||
super.attachToRecyclerView(recyclerView);
|
||||
mRecyclerView = recyclerView;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算需要滚动的向量,用于页面自动回滚对齐
|
||||
*
|
||||
* @param layoutManager 布局管理器
|
||||
* @param targetView 目标控件
|
||||
* @return 需要滚动的距离
|
||||
*/
|
||||
@Nullable
|
||||
@Override
|
||||
public int[] calculateDistanceToFinalSnap(@NonNull RecyclerView.LayoutManager layoutManager,
|
||||
@NonNull View targetView) {
|
||||
int pos = layoutManager.getPosition(targetView);
|
||||
Loge("findTargetSnapPosition, pos = " + pos);
|
||||
int[] offset = new int[2];
|
||||
if (layoutManager instanceof PagerGridLayoutManager) {
|
||||
PagerGridLayoutManager manager = (PagerGridLayoutManager) layoutManager;
|
||||
offset = manager.getSnapOffset(pos);
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得需要对齐的View,对于分页布局来说,就是页面第一个
|
||||
*
|
||||
* @param layoutManager 布局管理器
|
||||
* @return 目标控件
|
||||
*/
|
||||
@Nullable
|
||||
@Override
|
||||
public View findSnapView(RecyclerView.LayoutManager layoutManager) {
|
||||
if (layoutManager instanceof PagerGridLayoutManager) {
|
||||
PagerGridLayoutManager manager = (PagerGridLayoutManager) layoutManager;
|
||||
return manager.findSnapView();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取目标控件的位置下标
|
||||
* (获取滚动后第一个View的下标)
|
||||
*
|
||||
* @param layoutManager 布局管理器
|
||||
* @param velocityX X 轴滚动速率
|
||||
* @param velocityY Y 轴滚动速率
|
||||
* @return 目标控件的下标
|
||||
*/
|
||||
@Override
|
||||
public int findTargetSnapPosition(RecyclerView.LayoutManager layoutManager,
|
||||
int velocityX, int velocityY) {
|
||||
int target = RecyclerView.NO_POSITION;
|
||||
Loge("findTargetSnapPosition, velocityX = " + velocityX + ", velocityY" + velocityY);
|
||||
if (null != layoutManager && layoutManager instanceof PagerGridLayoutManager) {
|
||||
PagerGridLayoutManager manager = (PagerGridLayoutManager) layoutManager;
|
||||
if (manager.canScrollHorizontally()) {
|
||||
if (velocityX > PagerConfig.getFlingThreshold()) {
|
||||
target = manager.findNextPageFirstPos();
|
||||
} else if (velocityX < -PagerConfig.getFlingThreshold()) {
|
||||
target = manager.findPrePageFirstPos();
|
||||
}
|
||||
} else if (manager.canScrollVertically()) {
|
||||
if (velocityY > PagerConfig.getFlingThreshold()) {
|
||||
target = manager.findNextPageFirstPos();
|
||||
} else if (velocityY < -PagerConfig.getFlingThreshold()) {
|
||||
target = manager.findPrePageFirstPos();
|
||||
}
|
||||
}
|
||||
}
|
||||
Loge("findTargetSnapPosition, target = " + target);
|
||||
return target;
|
||||
}
|
||||
|
||||
/**
|
||||
* 一扔(快速滚动)
|
||||
*
|
||||
* @param velocityX X 轴滚动速率
|
||||
* @param velocityY Y 轴滚动速率
|
||||
* @return 是否消费该事件
|
||||
*/
|
||||
@Override
|
||||
public boolean onFling(int velocityX, int velocityY) {
|
||||
RecyclerView.LayoutManager layoutManager = mRecyclerView.getLayoutManager();
|
||||
if (layoutManager == null) {
|
||||
return false;
|
||||
}
|
||||
RecyclerView.Adapter adapter = mRecyclerView.getAdapter();
|
||||
if (adapter == null) {
|
||||
return false;
|
||||
}
|
||||
int minFlingVelocity = PagerConfig.getFlingThreshold();
|
||||
Loge("minFlingVelocity = " + minFlingVelocity);
|
||||
return (Math.abs(velocityY) > minFlingVelocity || Math.abs(velocityX) > minFlingVelocity)
|
||||
&& snapFromFling(layoutManager, velocityX, velocityY);
|
||||
}
|
||||
|
||||
/**
|
||||
* 快速滚动的具体处理方案
|
||||
*
|
||||
* @param layoutManager 布局管理器
|
||||
* @param velocityX X 轴滚动速率
|
||||
* @param velocityY Y 轴滚动速率
|
||||
* @return 是否消费该事件
|
||||
*/
|
||||
private boolean snapFromFling(@NonNull RecyclerView.LayoutManager layoutManager, int velocityX,
|
||||
int velocityY) {
|
||||
if (!(layoutManager instanceof RecyclerView.SmoothScroller.ScrollVectorProvider)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RecyclerView.SmoothScroller smoothScroller = createSnapScroller(layoutManager);
|
||||
if (smoothScroller == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int targetPosition = findTargetSnapPosition(layoutManager, velocityX, velocityY);
|
||||
if (targetPosition == RecyclerView.NO_POSITION) {
|
||||
return false;
|
||||
}
|
||||
|
||||
smoothScroller.setTargetPosition(targetPosition);
|
||||
layoutManager.startSmoothScroll(smoothScroller);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过自定义 LinearSmoothScroller 来控制速度
|
||||
*
|
||||
* @param layoutManager 布局故哪里去
|
||||
* @return 自定义 LinearSmoothScroller
|
||||
*/
|
||||
protected LinearSmoothScroller createSnapScroller(RecyclerView.LayoutManager layoutManager) {
|
||||
if (!(layoutManager instanceof RecyclerView.SmoothScroller.ScrollVectorProvider)) {
|
||||
return null;
|
||||
}
|
||||
return new PagerGridSmoothScroller(mRecyclerView);
|
||||
}
|
||||
|
||||
//--- 公开方法 ----------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* 设置滚动阀值
|
||||
*
|
||||
* @param threshold 滚动阀值
|
||||
*/
|
||||
public void setFlingThreshold(int threshold) {
|
||||
PagerConfig.setFlingThreshold(threshold);
|
||||
}
|
||||
}
|
||||
BIN
moduleUtil/src/main/res/drawable-xxxhdpi/detail_icon_go.png
Normal file
BIN
moduleUtil/src/main/res/drawable-xxxhdpi/detail_icon_go.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 502 B |
Binary file not shown.
|
Before Width: | Height: | Size: 482 B |
BIN
moduleUtil/src/main/res/drawable-xxxhdpi/icon_pay_select.png
Normal file
BIN
moduleUtil/src/main/res/drawable-xxxhdpi/icon_pay_select.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.6 KiB |
BIN
moduleUtil/src/main/res/drawable-xxxhdpi/icon_pay_unselect.png
Normal file
BIN
moduleUtil/src/main/res/drawable-xxxhdpi/icon_pay_unselect.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.7 KiB |
9
moduleUtil/src/main/res/drawable/bg_10_white_sele.xml
Normal file
9
moduleUtil/src/main/res/drawable/bg_10_white_sele.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="@color/white" />
|
||||
<corners android:radius="@dimen/dp_10" />
|
||||
|
||||
<stroke
|
||||
android:width="@dimen/dp_1"
|
||||
android:color="@color/black" /> <!-- 黑色边框 -->
|
||||
</shape>
|
||||
5
moduleUtil/src/main/res/drawable/bg_r16_tb_ffffff.xml
Normal file
5
moduleUtil/src/main/res/drawable/bg_r16_tb_ffffff.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<corners android:topLeftRadius="@dimen/dp_15" android:topRightRadius="@dimen/dp_15" android:bottomLeftRadius="0dp" android:bottomRightRadius="0dp"/>
|
||||
<solid android:color="@color/white" />
|
||||
</shape>
|
||||
5
moduleUtil/src/main/res/drawable/bg_r48_ffff.xml
Normal file
5
moduleUtil/src/main/res/drawable/bg_r48_ffff.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<corners android:radius="@dimen/dp_48" />
|
||||
<solid android:color="@color/white" />
|
||||
</shape>
|
||||
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item>
|
||||
<shape>
|
||||
<corners android:radius="99dp" />
|
||||
<solid android:color="@color/color_FF333333"/>
|
||||
</shape>
|
||||
</item>
|
||||
<item
|
||||
android:bottom="2dp"
|
||||
android:left="2dp"
|
||||
android:right="2dp"
|
||||
android:top="2dp">
|
||||
<shape>
|
||||
<solid android:color="@color/color_white" />
|
||||
<corners android:radius="99dp" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
</layer-list>
|
||||
7
moduleUtil/src/main/res/drawable/level_pay.xml
Normal file
7
moduleUtil/src/main/res/drawable/level_pay.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<level-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@drawable/icon_pay_unselect"
|
||||
android:maxLevel="0"/>
|
||||
<item android:drawable="@drawable/icon_pay_select"
|
||||
android:maxLevel="1"/>
|
||||
</level-list>
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item android:color="@color/color_FF333333" android:state_selected="true" />
|
||||
<item android:color="@color/color_FF666666" android:state_selected="false" />
|
||||
|
||||
</selector>
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:width="4dp" android:height="4dp">
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="#80ffffff" />
|
||||
<corners android:topLeftRadius="3dp" android:topRightRadius="3dp" android:bottomLeftRadius="3dp" android:bottomRightRadius="3dp" />
|
||||
</shape>
|
||||
</item>
|
||||
</layer-list>
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:width="13dp" android:height="4dp">
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="#ffffffff" />
|
||||
<corners android:topLeftRadius="3dp" android:topRightRadius="3dp" android:bottomLeftRadius="3dp" android:bottomRightRadius="3dp" />
|
||||
</shape>
|
||||
</item>
|
||||
</layer-list>
|
||||
@@ -0,0 +1,57 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<com.qxcm.moduleutil.widget.GifAvatarOvalView
|
||||
android:id="@+id/riv"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:src="@mipmap/default_avatar"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintDimensionRatio="1:1"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintWidth_percent="0.70154"
|
||||
app:riv_oval="true" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_sex"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:background="@mipmap/common_ic_headportriat_boy"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="@id/riv"
|
||||
app:layout_constraintEnd_toEndOf="@id/riv"
|
||||
app:layout_constraintStart_toStartOf="@id/riv"
|
||||
app:layout_constraintTop_toTopOf="@id/riv" />
|
||||
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_frame"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:contentDescription="@null"
|
||||
android:scaleType="fitXY"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintDimensionRatio="1:1"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_online"
|
||||
android:layout_width="@dimen/dp_13"
|
||||
android:layout_height="@dimen/dp_13"
|
||||
android:src="@mipmap/me_online_icon"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="@id/riv"
|
||||
app:layout_constraintEnd_toEndOf="@id/riv"
|
||||
app:layout_constraintHorizontal_bias="0.9"
|
||||
app:layout_constraintStart_toStartOf="@id/riv"
|
||||
app:layout_constraintTop_toTopOf="@id/riv"
|
||||
app:layout_constraintVertical_bias="0.9" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
22
moduleUtil/src/main/res/layout/custom_edit_text_layout.xml
Normal file
22
moduleUtil/src/main/res/layout/custom_edit_text_layout.xml
Normal file
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/edit_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="请输入内容"
|
||||
android:background="@drawable/bg_r100_hui"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/length_text_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="0/100"
|
||||
android:textColor="@color/color_FF999999"
|
||||
android:textSize="14sp"
|
||||
android:layout_gravity="end" />
|
||||
</LinearLayout>
|
||||
57
moduleUtil/src/main/res/layout/date_picker_dialog.xml
Normal file
57
moduleUtil/src/main/res/layout/date_picker_dialog.xml
Normal file
@@ -0,0 +1,57 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_horizontal"
|
||||
android:orientation="vertical"
|
||||
android:paddingTop="10dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center_horizontal"
|
||||
android:orientation="vertical"
|
||||
>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="开始日期" />
|
||||
|
||||
<DatePicker
|
||||
android:id="@+id/datePickerStart"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:calendarViewShown="false"
|
||||
android:datePickerMode="spinner"
|
||||
android:spinnersShown="true" />
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="fill_parent" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center_horizontal"
|
||||
android:orientation="vertical"
|
||||
>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="结束日期" />
|
||||
|
||||
<DatePicker
|
||||
android:id="@+id/datePickerEnd"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:calendarViewShown="false"
|
||||
android:datePickerMode="spinner"
|
||||
android:spinnersShown="true"/>
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
304
moduleUtil/src/main/res/layout/item_cirle_list.xml
Normal file
304
moduleUtil/src/main/res/layout/item_cirle_list.xml
Normal file
@@ -0,0 +1,304 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/bg_r16_fff"
|
||||
android:layout_marginBottom="@dimen/dp_12"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="15sp"
|
||||
android:paddingBottom="@dimen/dp_10"
|
||||
android:paddingTop="@dimen/dp_10"
|
||||
android:paddingRight="15sp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<com.qxcm.moduleutil.widget.CircularImage
|
||||
android:id="@+id/dy_head_image"
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="50dp" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="10dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/dy_name_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="#212121"
|
||||
android:textStyle="bold"
|
||||
android:text="萌新驾到"
|
||||
android:textSize="14sp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="5dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
|
||||
<TextView
|
||||
android:id="@+id/dy_time_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="发布于12:52"
|
||||
android:layout_gravity="center"
|
||||
android:textColor="#999999"
|
||||
android:textSize="11sp" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<View
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="1dp"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<!-- <ImageView-->
|
||||
<!-- android:id="@+id/delete"-->
|
||||
<!-- android:layout_width="25dp"-->
|
||||
<!-- android:layout_height="25dp"-->
|
||||
<!-- android:layout_gravity="right"-->
|
||||
<!-- android:scaleType="center"-->
|
||||
<!-- android:src="@mipmap/icon_dongtai_del2"-->
|
||||
<!-- android:visibility="gone" />-->
|
||||
|
||||
<TextView
|
||||
android:id="@+id/gensui"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@mipmap/no_follow"
|
||||
android:gravity="center"
|
||||
android:visibility="visible" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/diandian"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/dp_14"
|
||||
android:background="@mipmap/shequ_dongtai_gengduo"
|
||||
android:gravity="center"
|
||||
android:visibility="visible" />
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/aaa"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp">
|
||||
|
||||
<com.qxcm.moduleutil.widget.MyGridView
|
||||
android:id="@+id/dy_image_recyc"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:numColumns="3"
|
||||
android:scrollbars="none"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
|
||||
<com.makeramen.roundedimageview.RoundedImageView
|
||||
android:id="@+id/dy_oneimage_iv"
|
||||
android:layout_width="200dp"
|
||||
android:layout_height="200dp"
|
||||
android:scaleType="centerCrop"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:riv_corner_radius="5dp" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/dy_content_tv"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="0dp"
|
||||
android:layout_marginTop="14dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="7"
|
||||
android:textStyle="bold"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="14sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/dy_lookmore_tv"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:layout_marginStart="0dp"
|
||||
android:layout_marginRight="6dp"
|
||||
android:text="查看更多"
|
||||
android:textColor="@color/color_FF333333"
|
||||
android:textSize="12sp"
|
||||
android:visibility="gone" />
|
||||
|
||||
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/dp_24"
|
||||
android:layout_marginStart="0dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_gravity="center"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/dp_24"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:visibility="gone">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="15dp"
|
||||
android:layout_height="15dp"
|
||||
android:src="@mipmap/weizhixinxi" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvAddress"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="5dp"
|
||||
android:text="未知"
|
||||
android:textColor="#ffa7a5ae"
|
||||
android:textSize="12sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/dy_more_image"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1.7"
|
||||
android:paddingHorizontal="5dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="right"
|
||||
android:src="@mipmap/mx"
|
||||
android:visibility="visible" />
|
||||
<View
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="2"
|
||||
android:layout_height="1dp"/>
|
||||
<LinearLayout
|
||||
android:id="@+id/pinglun"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginEnd="@dimen/dp_12"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal"
|
||||
android:visibility="visible">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/pinglun_image"
|
||||
android:layout_width="@dimen/dp_24"
|
||||
android:layout_height="@dimen/dp_24"
|
||||
android:src="@mipmap/dongtai_hudong_pinglun" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/dy_comment"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="@dimen/dp_18"
|
||||
android:layout_marginLeft="5dp"
|
||||
android:gravity="center_vertical"
|
||||
android:text="0"
|
||||
android:textColor="@color/color_FF333333"
|
||||
android:textSize="12sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/dianzan"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginEnd="@dimen/dp_12"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/dianzan_image"
|
||||
android:layout_width="@dimen/dp_24"
|
||||
android:layout_height="@dimen/dp_24"
|
||||
android:src="@mipmap/dongtai_hudong_dianzan" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/dy_fabulous"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="@dimen/dp_18"
|
||||
android:layout_marginLeft="5dp"
|
||||
android:text="0"
|
||||
android:textColor="@color/color_FF333333"
|
||||
android:textSize="12sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/zs"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginEnd="0dp"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/zs_image"
|
||||
android:layout_width="@dimen/dp_24"
|
||||
android:layout_height="@dimen/dp_24"
|
||||
android:src="@mipmap/zs" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/dy_zs"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="@dimen/dp_18"
|
||||
android:layout_marginLeft="5dp"
|
||||
android:text="0"
|
||||
android:textColor="@color/color_FF333333"
|
||||
android:textSize="12sp" />
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_jubao"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end" />
|
||||
|
||||
|
||||
</FrameLayout>
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user