1、修改登录功能并验证,除了支付宝登录其他都已验证

2、完成个人中心的功能,个人主页完成、钱包完成、背包完成
This commit is contained in:
2025-05-22 19:03:01 +08:00
parent 25ecfc55ab
commit 314c484cea
324 changed files with 19255 additions and 476 deletions

View File

@@ -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">

View File

@@ -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());
});
}
}
}

View File

@@ -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);
}
}

View File

@@ -1,7 +0,0 @@
package com.qxcm.moduleutil.base;
import android.app.Application;
public class BaseApplication extends Application {
}

View File

@@ -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; //未安装支付宝
}
}
}

View File

@@ -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;
}
}

View File

@@ -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已关注
}

View File

@@ -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;
}
}

View File

@@ -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;

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}
}
}

View File

@@ -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;
}
}

View File

@@ -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已发放
}

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -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; // 公会信息
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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 数据模型类

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View 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);
}

View File

@@ -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>() {

View File

@@ -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;

View File

@@ -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>());
}
}

View File

@@ -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;
}
}
});
}
}

View File

@@ -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;
}
}
}

View File

@@ -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));
}
}

View File

@@ -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());
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}
}

View File

@@ -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());
}
}

View File

@@ -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();
}

View File

@@ -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) {
}
}

View File

@@ -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 {

View File

@@ -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;
}
}

View File

@@ -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;

View File

@@ -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) {
}
}

View File

@@ -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);
}
}

View File

@@ -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();
}
}
}
}

View File

@@ -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";//是否实名认证
}

View File

@@ -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);
}
}

View File

@@ -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;
}
/**
* 是否同意协议
*

View File

@@ -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();
}
}

View File

@@ -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;
}
}

View File

@@ -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;

View File

@@ -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();
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}
}

View File

@@ -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");
}
}

View File

@@ -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();
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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();
}
}

View File

@@ -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";
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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) ;
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 502 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 482 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

View 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>

View 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>

View 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>

View File

@@ -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>

View 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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View 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>

View 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>

View 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>

View File

@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/layout_empty"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:paddingBottom="@dimen/dp_20"
android:visibility="visible">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="vertical">
<ImageView
android:id="@+id/iv_empty"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:scaleType="centerInside"
android:src="@mipmap/common_img_empty" />
<TextView
android:id="@+id/tv_empty_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="15dp"
android:text="暂无数据"
android:textColor="@color/color_FF333333"
android:textSize="@dimen/sp_14" />
<TextView
android:id="@+id/tv_other_operation"
android:layout_width="@dimen/dp_110"
android:layout_height="@dimen/dp_35"
android:layout_marginTop="@dimen/dp_36"
android:background="@drawable/bg_r99_trans_stroke_main"
android:gravity="center"
android:text="去获取"
android:textColor="@color/color_FF333333"
android:textSize="15sp"
android:visibility="gone"
tools:visibility="visible" />
</LinearLayout>
</LinearLayout>

Some files were not shown because too many files have changed in this diff Show More