app/build.gradle
@@ -1,5 +1,6 @@ plugins { id 'com.android.application' id 'kotlin-android' } android { @@ -32,6 +33,9 @@ compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } kotlinOptions { jvmTarget = '1.8' } buildFeatures { viewBinding true @@ -71,6 +75,5 @@ annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0' implementation 'com.facebook.rebound:rebound:0.3.6'//Rebound “弹簧”动画效果的第三方工具包,由FaceBook implementation 'com.github.zhaolei9527:BottomMenu:v1.0.1'//底部菜单弹框 implementation 'com.github.LuckSiege.PictureSelector:picture_library:v2.2.3'//图片选择 implementation 'com.github.wildma:PictureSelector:1.1.1'//图片选择裁切工具 implementation 'com.github.LuckSiege.PictureSelector:picture_library:v2.6.0'//图片选择 } app/src/main/AndroidManifest.xml
@@ -42,16 +42,7 @@ <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".ui.main.MainActivity" android:launchMode="singleTask" tools:ignore="WrongManifestParent" android:exported="true"> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <action android:name="com.zfwl.merchant.activities.MainActivity" /> </intent-filter> </activity> <activity android:name=".ui.main.MainActivity" /> <activity android:name=".ui.login.RegisterLoginActivity" /> <activity android:name=".ui.web.WebViewActivity" /> </application> app/src/main/java/com/runt/open/mvvm/config/Configuration.java
@@ -9,6 +9,7 @@ public final static String KEY_CODE= "code"; public static final String KEY_TOKEN = "token"; public static final String KEY_USERINFO = "userinfo"; public static final String KEY_USERNAME = "username"; public static final String KEY_PHONE = "phone"; public static final String KEY_USERPASS = "userpass"; app/src/main/java/com/runt/open/mvvm/retrofit/Interceptor/HttpLoggingInterceptor.java
@@ -1,15 +1,18 @@ package com.runt.open.mvvm.retrofit.Interceptor; import android.util.Log; import com.google.gson.Gson; import com.runt.open.mvvm.MyApplication; import com.runt.open.mvvm.data.PhoneDevice; import com.runt.open.mvvm.retrofit.net.NetWorkCost; import com.runt.open.mvvm.retrofit.net.NetWorkListenear; import com.runt.open.mvvm.retrofit.utils.HttpPrintUtils; import com.runt.open.mvvm.ui.login.UserBean; import com.runt.open.mvvm.util.DeviceUtil; import okhttp3.*; import okhttp3.internal.http.HttpHeaders; import okio.Buffer; import okio.BufferedSource; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; @@ -21,18 +24,6 @@ import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import okhttp3.FormBody; import okhttp3.Headers; import okhttp3.MediaType; import okhttp3.MultipartBody; import okhttp3.Request; import okhttp3.RequestBody; import okhttp3.Response; import okhttp3.ResponseBody; import okhttp3.internal.http.HttpHeaders; import okio.Buffer; import okio.BufferedSource; /** * My father is Object, ites purpose of log打印 @@ -65,6 +56,9 @@ .addHeader("device", new Gson().toJson(PhoneDevice.getDevice())) .addHeader("appVersion", DeviceUtil.getAppVersionName(MyApplication.getApplication())) .addHeader("os", DeviceUtil.isHarmonyOS()? "harmony" : "android"); if(UserBean.getUser() != null){ requestBuild.addHeader("token",UserBean.getUser().getToken()); } Request request = requestBuild.build().newBuilder().build(); ArrayList<String> logArrays = new ArrayList<>(); Response response = null; @@ -208,7 +202,14 @@ if (isPlaintext(buffer)) { logArrays.add("---------->RESPONSE BODY<----------"); if (contentLength != 0) { logArrays.add(new JSONObject(buffer.clone().readString((charset))).toString(4)); String str = buffer.clone().readString(charset); if(str.trim().indexOf("{") == 0) { logArrays.add(new JSONObject(str).toString(4)); }else if(str.trim().indexOf("[") == 0) { logArrays.add(new JSONArray(str).toString(4)); }else{ logArrays.add(str); } } logArrays.add("<-- END HTTP (" + buffer.size() + "-byte body)"); app/src/main/java/com/runt/open/mvvm/retrofit/api/CommonApiCenter.java
@@ -5,23 +5,11 @@ import com.runt.open.mvvm.data.HttpApiResult; import com.runt.open.mvvm.data.PageResult; import com.runt.open.mvvm.data.Results; import java.util.Map; import io.reactivex.Observable; import okhttp3.MultipartBody; 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.Multipart; import retrofit2.http.POST; import retrofit2.http.Part; import retrofit2.http.Query; import retrofit2.http.QueryMap; import retrofit2.http.Url; import retrofit2.http.*; import java.util.Map; /** * My father is Object, ites purpose of 常用接口 @@ -74,7 +62,7 @@ @Multipart @POST("updatehead") Call<ResponseBody> updateHead(@Part MultipartBody.Part file); Observable<Results.StringApiResult> updateHead(@Part MultipartBody.Part file); /** app/src/main/java/com/runt/open/mvvm/ui/loadpage/PageViewModels.java
@@ -11,7 +11,7 @@ * @purpose Created by Runt (qingingrunt2010@qq.com) on 2022/8/9. */ public class PageViewModels { public class HomeViewModel extends LoadPageViewModel<Results.Message> { public static class HomeViewModel extends LoadPageViewModel<Results.Message> { @Override public Observable<HttpApiResult<PageResult<Results.Message>>> request(int page, Object... objects) { return commonApi.getMsgList(page,SIZE); app/src/main/java/com/runt/open/mvvm/ui/login/RegisterLoginActivity.java
@@ -46,13 +46,10 @@ } }); mViewModel.getLoginResult().observe(this, loggedInUser -> { putBooleanProjectPrefrence(Configuration.IS_LOGIN,true); putStringProjectPrefrence(Configuration.KEY_USERNAME, mBinding.editPhone.getText().toString()); UserBean user = new Gson().fromJson(new Gson().toJson(loggedInUser) ,UserBean.class); UserBean.setUser(user); putStringProjectPrefrence(Configuration.KEY_TOKEN, user.getToken()); MyLog.i("registerlogin",user.toString()); UserBean.setUser(loggedInUser); putStringProjectPrefrence(Configuration.KEY_USERINFO, new Gson().toJson(loggedInUser)); MyLog.i("registerlogin",loggedInUser.toString()); showToast(R.string.login_success); setResult(RESULT_CODE_SUCESS); finish(); app/src/main/java/com/runt/open/mvvm/ui/main/mine/MineFragment.java
@@ -2,27 +2,32 @@ import android.app.Activity; import android.content.Intent; import android.content.pm.ActivityInfo; import android.util.Log; import android.view.View; import com.bumptech.glide.Glide; import com.bumptech.glide.request.RequestOptions; import com.permissionx.guolindev.PermissionX; import com.luck.picture.lib.PictureSelector; import com.luck.picture.lib.config.PictureConfig; import com.luck.picture.lib.config.PictureMimeType; import com.luck.picture.lib.entity.LocalMedia; import com.runt.open.mvvm.BuildConfig; import com.runt.open.mvvm.R; import com.runt.open.mvvm.base.fragments.BaseFragment; import com.runt.open.mvvm.data.HttpApiResult; import com.runt.open.mvvm.data.Results; import com.runt.open.mvvm.databinding.FragmentMineBinding; import com.runt.open.mvvm.listener.ResPonse; import com.runt.open.mvvm.retrofit.observable.HttpObserver; import com.runt.open.mvvm.ui.login.UserBean; import com.runt.open.mvvm.util.GlideEngine; import com.runt.open.mvvm.util.MyLog; import com.wildma.pictureselector.PictureSelector; import java.io.File; import okhttp3.ResponseBody; import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; import java.io.File; import java.util.List; /** * My father is Object, ites purpose of @@ -118,27 +123,25 @@ * 打开相册 */ public void openAlthum(){ PermissionX.init(this) .permissions(mActivity.CAMERA_PERMISSIONS) .request((allGranted, grantedList, deniedList) -> { if(allGranted){ PictureSelector .create(this, PictureSelector.SELECT_REQUEST_CODE) .selectPicture(true, 300, 300, 20, 20); }else{ mActivity.showDialog("警告", "软件需要权限才能运行", "申请权限", "取消", new ResPonse() { @Override public void doSuccess(Object obj) { openAlthum(); } @Override public void doError(Object obj) { } }); } }); PictureSelector.create(this) .openGallery(PictureMimeType.ofImage()) // 全部.PictureMimeType.ofAll()、图片.ofImage()、视频.ofVideo()、音频.ofAudio() .imageEngine(GlideEngine.Companion.getInstance()) // 外部传入图片加载引擎,必传项 .maxSelectNum(1) // 最大图片选择数量 .minSelectNum(1) // 最小选择数量 .imageSpanCount(3) // 每行显示个数 .isReturnEmpty(true) // 未选择数据时点击按钮是否可以返回 .isAndroidQTransform(false) // 是否需要处理Android Q 拷贝至应用沙盒的操作,只针对compress(false); && .isEnableCrop(false);有效,默认处理 .setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) // 设置相册Activity方向,不设置默认使用系统 .isSingleDirectReturn(true) // 单选模式下是否直接返回,PictureConfig.SINGLE模式下有效 .isPreviewImage(true) // 是否可预览图片 .isCamera(true) // 是否显示拍照按钮 .isZoomAnim(false) // 图片列表点击 缩放效果 默认true .isEnableCrop(true) // 是否裁剪 .withAspectRatio(1, 1) // 裁剪比例 如16:9 3:2 3:4 1:1 可自定义 .isCompress(false) // 是否压缩 .cutOutQuality(100) // 裁剪输出质量 默认100 .selectionMode(PictureConfig.SINGLE) .forResult(PictureConfig.CHOOSE_REQUEST);//结果回调onActivityResult code } @@ -147,24 +150,18 @@ public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); /*结果回调*/ if (requestCode == PictureSelector.SELECT_REQUEST_CODE) { if (requestCode == PictureConfig.CHOOSE_REQUEST) { if (data != null) { String picturePath = data.getStringExtra(PictureSelector.PICTURE_PATH); MyLog.i("mineActivity","picturePath:"+picturePath); final File file = new File(picturePath); mViewModel.updateHead(file).enqueue(new Callback<ResponseBody>() { List<LocalMedia> selectList = PictureSelector.obtainMultipleResult(data); final File file = new File(selectList.get(0).getCutPath()); MyLog.i("mineActivity","picturePath:"+selectList.get(0).getCutPath()+" exists:"+file.exists()); mViewModel.updateHead(file, new HttpObserver<String>() { @Override public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) { /*UserBean.getUser().setHead(obj.toString()); file.delete(); Glide.with(getContext()).load(BuildConfig.HOST_IP_ADDR+UserBean.getUser().getHead()) .into(mBinding.img); //获取选取的图片*/ protected void onSuccess(String data) { UserBean.getUser().setHead(data); Glide.with(getContext()).load(BuildConfig.HOST_IP_ADDR+UserBean.getUser().getHead()) .into(mBinding.img); //获取选取的图片 } @Override public void onFailure(Call<ResponseBody> call, Throwable t) { file.delete(); } }); } }else if(requestCode == REQUEST_CODE_SIGN && resultCode == Activity.RESULT_OK){ app/src/main/java/com/runt/open/mvvm/ui/main/mine/MineViewModel.java
@@ -1,17 +1,16 @@ package com.runt.open.mvvm.ui.main.mine; import com.runt.open.mvvm.base.model.BaseViewModel; import com.runt.open.mvvm.data.Results; import com.runt.open.mvvm.listener.ResPonse; import com.runt.open.mvvm.retrofit.observable.HttpObserver; import com.runt.open.mvvm.ui.login.UserBean; import java.io.File; import io.reactivex.Observable; import okhttp3.MediaType; import okhttp3.MultipartBody; import okhttp3.RequestBody; import okhttp3.ResponseBody; import retrofit2.Call; import java.io.File; /** * @purpose Created by Runt (qingingrunt2010@qq.com) on 2022/7/27. @@ -29,8 +28,9 @@ } } public Call<ResponseBody> updateHead(File file){ return commonApi.updateHead(MultipartBody.Part.createFormData("head",file.getName(), RequestBody.create(MediaType.parse("text/plain"), file))); public void updateHead(File file, HttpObserver<String> observer){ Observable<Results.StringApiResult> observable = commonApi.updateHead(MultipartBody.Part.createFormData("head", file.getName(), RequestBody.create(MediaType.parse("text/plain"), file))); httpObserverOnLoading(observable,observer); } } app/src/main/java/com/runt/open/mvvm/ui/splash/SplashActivity.java
@@ -5,10 +5,12 @@ import android.os.Handler; import android.view.View; import android.view.WindowManager; import com.google.gson.Gson; import com.runt.open.mvvm.base.activities.BaseActivity; import com.runt.open.mvvm.base.model.ImpViewModel; import com.runt.open.mvvm.config.Configuration; import com.runt.open.mvvm.databinding.ActivitySplashBinding; import com.runt.open.mvvm.ui.login.UserBean; import com.runt.open.mvvm.ui.main.MainActivity; @@ -36,6 +38,11 @@ @Override public void loadData() { String json = getStringProjectPrefrence(Configuration.KEY_USERINFO); if(!isNull(json)){ UserBean user = new Gson().fromJson(json ,UserBean.class); UserBean.setUser(user); } new Handler().postDelayed(new Runnable() { @Override public void run() { app/src/main/java/com/runt/open/mvvm/util/GlideEngine.kt
New file @@ -0,0 +1,212 @@ package com.runt.open.mvvm.util import android.content.Context import android.graphics.Bitmap import android.graphics.PointF import android.graphics.drawable.Drawable import android.view.View import android.widget.ImageView import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory import com.bumptech.glide.Glide import com.bumptech.glide.request.RequestOptions import com.bumptech.glide.request.target.BitmapImageViewTarget import com.bumptech.glide.request.target.ImageViewTarget import com.luck.picture.lib.engine.ImageEngine import com.luck.picture.lib.listener.OnImageCompleteCallback import com.luck.picture.lib.tools.MediaUtils import com.luck.picture.lib.widget.longimage.ImageSource import com.luck.picture.lib.widget.longimage.ImageViewState import com.luck.picture.lib.widget.longimage.SubsamplingScaleImageView import com.runt.open.mvvm.R class GlideEngine : ImageEngine { private constructor() companion object { val instance = GlideEngineInit.init } private object GlideEngineInit { val init = GlideEngine() } /** * 加载图片 * * @param context * @param url * @param imageView */ override fun loadImage(context: Context, url: String, imageView: ImageView) { Glide.with(context) .load(url) .into(imageView) } /** * 加载网络图片适配长图方案 * # 注意:此方法只有加载网络图片才会回调 * * @param context * @param url * @param imageView * @param longImageView * @param callback 网络图片加载回调监听 {link after version 2.5.1 Please use the #OnImageCompleteCallback#} */ override fun loadImage( context: Context, url: String, imageView: ImageView, longImageView: SubsamplingScaleImageView, callback: OnImageCompleteCallback? ) { Glide.with(context) .asBitmap() .load(url) .into(object : ImageViewTarget<Bitmap?>(imageView) { override fun onLoadStarted(placeholder: Drawable?) { super.onLoadStarted(placeholder) callback?.onShowLoading() } override fun onLoadFailed(errorDrawable: Drawable?) { super.onLoadFailed(errorDrawable) callback?.onHideLoading() } override fun setResource(resource: Bitmap?) { callback?.onHideLoading() if (resource != null) { val eqLongImage = MediaUtils.isLongImg( resource.width, resource.height ) longImageView.visibility = if (eqLongImage) View.VISIBLE else View.GONE imageView.visibility = if (eqLongImage) View.GONE else View.VISIBLE if (eqLongImage) { // 加载长图 longImageView.isQuickScaleEnabled = true longImageView.isZoomEnabled = true longImageView.setDoubleTapZoomDuration(100) longImageView.setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_CENTER_CROP) longImageView.setDoubleTapZoomDpi(SubsamplingScaleImageView.ZOOM_FOCUS_CENTER) longImageView.setImage( ImageSource.bitmap(resource), ImageViewState(0f, PointF(0f, 0f), 0) ) } else { // 普通图片 imageView.setImageBitmap(resource) } } } }) } /** * 加载网络图片适配长图方案 * # 注意:此方法只有加载网络图片才会回调 * * @param context * @param url * @param imageView * @param longImageView * @ 已废弃 */ override fun loadImage( context: Context, url: String, imageView: ImageView, longImageView: SubsamplingScaleImageView ) { Glide.with(context) .asBitmap() .load(url) .into(object : ImageViewTarget<Bitmap?>(imageView) { override fun setResource(resource: Bitmap?) { if (resource != null) { val eqLongImage = MediaUtils.isLongImg( resource.width, resource.height ) longImageView.visibility = if (eqLongImage) View.VISIBLE else View.GONE imageView.visibility = if (eqLongImage) View.GONE else View.VISIBLE if (eqLongImage) { // 加载长图 longImageView.isQuickScaleEnabled = true longImageView.isZoomEnabled = true longImageView.setDoubleTapZoomDuration(100) longImageView.setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_CENTER_CROP) longImageView.setDoubleTapZoomDpi(SubsamplingScaleImageView.ZOOM_FOCUS_CENTER) longImageView.setImage( ImageSource.bitmap(resource), ImageViewState(0f, PointF(0f, 0f), 0) ) } else { // 普通图片 imageView.setImageBitmap(resource) } } } }) } /** * 加载相册目录 * * @param context 上下文 * @param url 图片路径 * @param imageView 承载图片ImageView */ override fun loadFolderImage(context: Context, url: String, imageView: ImageView) { Glide.with(context) .asBitmap() .load(url) .override(180, 180) .centerCrop() .sizeMultiplier(0.5f) .apply(RequestOptions().placeholder(R.drawable.picture_image_placeholder)) .into(object : BitmapImageViewTarget(imageView) { override fun setResource(resource: Bitmap?) { val circularBitmapDrawable = RoundedBitmapDrawableFactory.create(context.resources, resource) circularBitmapDrawable.cornerRadius = 8f imageView.setImageDrawable(circularBitmapDrawable) } }) } /** * 加载gif * * @param context 上下文 * @param url 图片路径 * @param imageView 承载图片ImageView */ override fun loadAsGifImage( context: Context, url: String, imageView: ImageView ) { Glide.with(context) .asGif() .load(url) .into(imageView) } /** * 加载图片列表图片 * * @param context 上下文 * @param url 图片路径 * @param imageView 承载图片ImageView */ override fun loadGridImage(context: Context, url: String, imageView: ImageView) { Glide.with(context) .load(url) .override(200, 200) .centerCrop() .apply(RequestOptions().placeholder(R.drawable.picture_image_placeholder)) .into(imageView) } } build.gradle
@@ -1,11 +1,13 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { ext.kotlin_version = "1.4.31" repositories { google() mavenCentral() } dependencies { classpath "com.android.tools.build:gradle:7.0.3" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.20" // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files