Compare View
Commits (5)
Showing
36 changed files
Show diff stats
.idea/inspectionProfiles/Project_Default.xml
| ... | ... | @@ -6,5 +6,10 @@ |
| 6 | 6 | <option name="m_reportAllNonLibraryCalls" value="false" /> |
| 7 | 7 | <option name="callCheckString" value="java.io.File,.*,java.io.InputStream,read|skip|available|markSupported,java.io.Reader,read|skip|ready|markSupported,java.lang.AbstractStringBuilder,capacity|codePointAt|codePointBefore|codePointCount|indexOf|lastIndexOf|offsetByCodePoints|substring|subSequence,java.lang.Boolean,.*,java.lang.Byte,.*,java.lang.Character,.*,java.lang.Double,.*,java.lang.Float,.*,java.lang.Integer,.*,java.lang.Long,.*,java.lang.Math,.*,java.lang.Object,equals|hashCode|toString,java.lang.Short,.*,java.lang.StrictMath,.*,java.lang.String,.*,java.lang.Thread,interrupted,java.math.BigDecimal,.*,java.math.BigInteger,.*,java.net.InetAddress,.*,java.net.URI,.*,java.nio.channels.AsynchronousChannelGroup,.*,java.nio.channels.Channel,isOpen,java.nio.channels.FileChannel,open|map|lock|tryLock|write,java.nio.channels.ScatteringByteChannel,read,java.nio.channels.SocketChannel,open|socket|isConnected|isConnectionPending,java.util.Arrays,.*,java.util.Collections,(?!addAll).*,java.util.List,of,java.util.Map,of|ofEntries|entry,java.util.Set,of,java.util.UUID,.*,java.util.concurrent.BlockingQueue,offer|remove,java.util.concurrent.CountDownLatch,await|getCount,java.util.concurrent.ExecutorService,awaitTermination|isShutdown|isTerminated,java.util.concurrent.ForkJoinPool,awaitQuiescence,java.util.concurrent.Semaphore,tryAcquire|availablePermits|isFair|hasQueuedThreads|getQueueLength|getQueuedThreads,java.util.concurrent.locks.Condition,await|awaitNanos|awaitUntil,java.util.concurrent.locks.Lock,tryLock|newCondition,java.util.regex.Matcher,pattern|toMatchResult|start|end|group|groupCount|matches|find|lookingAt|quoteReplacement|replaceAll|replaceFirst|regionStart|regionEnd|hasTransparentBounds|hasAnchoringBounds|hitEnd|requireEnd,java.util.regex.Pattern,.*,java.util.stream.BaseStream,.*,java.util.stream.DoubleStream,.*,java.util.stream.IntStream,.*,java.util.stream.LongStream,.*,java.util.stream.Stream,.*" /> |
| 8 | 8 | </inspection_tool> |
| 9 | + <inspection_tool class="SameReturnValue" enabled="false" level="WARNING" enabled_by_default="false" /> | |
| 10 | + <inspection_tool class="UnnecessaryLocalVariable" enabled="false" level="WARNING" enabled_by_default="false"> | |
| 11 | + <option name="m_ignoreImmediatelyReturnedVariables" value="false" /> | |
| 12 | + <option name="m_ignoreAnnotatedVariables" value="false" /> | |
| 13 | + </inspection_tool> | |
| 9 | 14 | </profile> |
| 10 | 15 | </component> |
| 11 | 16 | \ No newline at end of file | ... | ... |
app/build.gradle
| ... | ... | @@ -97,4 +97,9 @@ dependencies { |
| 97 | 97 | |
| 98 | 98 | implementation 'com.jakewharton:butterknife:10.2.3' |
| 99 | 99 | annotationProcessor 'com.jakewharton:butterknife-compiler:10.2.3' |
| 100 | + | |
| 101 | + | |
| 102 | + /** 鲁班压缩 */ | |
| 103 | + implementation 'top.zibin:Luban:1.1.8' | |
| 104 | + | |
| 100 | 105 | } |
| 101 | 106 | \ No newline at end of file | ... | ... |
app/src/main/AndroidManifest.xml
app/src/main/java/com/hjx/parent/ImageActivity.java
| ... | ... | @@ -24,11 +24,13 @@ import com.google.gson.Gson; |
| 24 | 24 | import com.google.gson.JsonObject; |
| 25 | 25 | import com.hjx.parent.adapter.ImageAdapter; |
| 26 | 26 | import com.hjx.parent.databinding.ActivityImageBinding; |
| 27 | +import com.hjx.parent.databinding.ItemImageBinding; | |
| 27 | 28 | import com.hjx.parent.dialog.AddHomeworkDialog; |
| 28 | 29 | import com.hjx.parent.dialog.ErrorInputDialog; |
| 29 | 30 | import com.hjx.parent.dialog.PhotoViewDialog; |
| 30 | 31 | import com.hjx.parent.dialog.TipDialog; |
| 31 | 32 | import com.hjx.parent.rx.BaseRxActivity; |
| 33 | +import com.hjx.parent.utils.CutUtil; | |
| 32 | 34 | import com.prws.common.bean.CutPicBean; |
| 33 | 35 | import com.prws.common.bean.ImageBean; |
| 34 | 36 | import com.prws.common.bean.TopicBean; |
| ... | ... | @@ -40,16 +42,15 @@ import com.xiasuhuei321.loadingdialog.view.LoadingDialog; |
| 40 | 42 | import java.io.File; |
| 41 | 43 | import java.util.ArrayList; |
| 42 | 44 | import java.util.List; |
| 45 | +import java.util.stream.Collectors; | |
| 43 | 46 | |
| 44 | 47 | import io.reactivex.Observer; |
| 48 | +import io.reactivex.Single; | |
| 45 | 49 | import io.reactivex.disposables.Disposable; |
| 46 | -import retrofit2.Call; | |
| 47 | -import retrofit2.Callback; | |
| 48 | -import retrofit2.Response; | |
| 49 | 50 | |
| 50 | 51 | public class ImageActivity extends BaseRxActivity<ActivityImageBinding> implements View.OnClickListener { |
| 51 | 52 | |
| 52 | - private List<String> images = new ArrayList<>(); | |
| 53 | + private final List<String> images = new ArrayList<>(); | |
| 53 | 54 | private ImageAdapter imageAdapter; |
| 54 | 55 | private List<ImageBean> list = new ArrayList<>(); |
| 55 | 56 | private String filePath; |
| ... | ... | @@ -57,12 +58,30 @@ public class ImageActivity extends BaseRxActivity<ActivityImageBinding> implemen |
| 57 | 58 | private int type = 0; |
| 58 | 59 | private AddHomeworkDialog addHomeworkDialog; |
| 59 | 60 | |
| 61 | + @SuppressLint("CheckResult") | |
| 60 | 62 | @Override |
| 61 | 63 | public void initView(Bundle savedInstanceState) { |
| 62 | 64 | type = getIntent().getIntExtra("type", 0); |
| 63 | 65 | binding.tvSave.setText(type == 0 ? "保存错题" : "录入题目"); |
| 64 | 66 | |
| 65 | - images = getIntent().getStringArrayListExtra("images"); | |
| 67 | + List<String> originList = getIntent().getStringArrayListExtra("images"); | |
| 68 | + List<String> originListFinal = originList == null ? new ArrayList<>() : originList; | |
| 69 | + Single.fromCallable(() -> CutUtil.compressAllSync(this, originListFinal, 200)) | |
| 70 | + .map(it -> it.stream().map(File::getAbsolutePath).collect(Collectors.toList())) | |
| 71 | + .compose(transformSingle()) | |
| 72 | + .subscribe((it, throwable) -> { | |
| 73 | + if (throwable != null) Log.e(getClass().getName(), "", throwable); | |
| 74 | + images.clear(); | |
| 75 | + if (it == null) { | |
| 76 | + images.addAll(originListFinal); | |
| 77 | + } else { | |
| 78 | + images.addAll(it); | |
| 79 | + } | |
| 80 | + start(); | |
| 81 | + }); | |
| 82 | + } | |
| 83 | + | |
| 84 | + private void start() { | |
| 66 | 85 | if (images == null || images.size() <= 1) { |
| 67 | 86 | binding.rlPrevious.setVisibility(View.GONE); |
| 68 | 87 | binding.rlNext.setVisibility(View.GONE); |
| ... | ... | @@ -82,24 +101,18 @@ public class ImageActivity extends BaseRxActivity<ActivityImageBinding> implemen |
| 82 | 101 | @Override |
| 83 | 102 | public void onPageSelected(int position) { |
| 84 | 103 | if (list.get(position).getCuts() == null) { |
| 85 | -// binding.ivPrevious.setEnabled(false); | |
| 86 | -// binding.ivNext.setEnabled(false); | |
| 87 | 104 | clearText(); |
| 88 | 105 | } |
| 89 | 106 | binding.tvTitle.setText((binding.viewPager.getCurrentItem() + 1) + "/" + list.size()); |
| 90 | 107 | if (position == 0) { |
| 91 | 108 | binding.ivPrevious.setImageResource(R.mipmap.ic_previous_gray); |
| 92 | -// binding.ivPrevious.setEnabled(true); | |
| 93 | 109 | } else { |
| 94 | 110 | binding.ivPrevious.setImageResource(R.mipmap.ic_previous); |
| 95 | -// binding.ivPrevious.setEnabled(false); | |
| 96 | 111 | } |
| 97 | 112 | if (position == list.size() - 1) { |
| 98 | 113 | binding.ivNext.setImageResource(R.mipmap.ic_next_gray); |
| 99 | -// binding.ivNext.setEnabled(true); | |
| 100 | 114 | } else { |
| 101 | 115 | binding.ivNext.setImageResource(R.mipmap.ic_next); |
| 102 | -// binding.ivNext.setEnabled(false); | |
| 103 | 116 | } |
| 104 | 117 | super.onPageSelected(position); |
| 105 | 118 | } |
| ... | ... | @@ -116,6 +129,62 @@ public class ImageActivity extends BaseRxActivity<ActivityImageBinding> implemen |
| 116 | 129 | ImageBean data = list.get(binding.viewPager.getCurrentItem()); |
| 117 | 130 | new PhotoViewDialog(this).show(data.source); |
| 118 | 131 | }); |
| 132 | + | |
| 133 | + binding.btnJudge.setVisibility(type == 0 ? View.VISIBLE : View.GONE); | |
| 134 | + binding.btnJudge.setOnClickListener(v -> { | |
| 135 | + goJudge(); | |
| 136 | + }); | |
| 137 | + } | |
| 138 | + | |
| 139 | + private void goJudge() { | |
| 140 | + if (list.isEmpty()) return; | |
| 141 | + int position = binding.viewPager.getCurrentItem(); | |
| 142 | + ImageBean imageBean = list.get(position); | |
| 143 | + Bitmap bitmap = BitmapFactory.decodeFile(imageBean.getPath()); | |
| 144 | + if (bitmap == null || imageBean.getCuts() == null) { | |
| 145 | + showToast("请先框选题目"); | |
| 146 | + return; | |
| 147 | + } | |
| 148 | + ItemImageBinding ib = imageAdapter.viewArray.get(position); | |
| 149 | + if (ib == null) { | |
| 150 | + showToast("出错"); | |
| 151 | + return; | |
| 152 | + } | |
| 153 | + View view = ib.ivPic; | |
| 154 | + float[] size = CutUtil.measureBitmap(view, bitmap); | |
| 155 | + | |
| 156 | + ArrayList<Rect> rects = new ArrayList<>(); | |
| 157 | + for (ImageBean.Cut cut: imageBean.getCuts()) { | |
| 158 | + if (cut.getRect1() == null) continue; | |
| 159 | + Rect rectV = cut.getRect1(); | |
| 160 | + rects.add(getRectB(rectV, bitmap, size)); | |
| 161 | + } | |
| 162 | + if (list.isEmpty()) { | |
| 163 | + showToast("请先框选题目"); | |
| 164 | + return; | |
| 165 | + } | |
| 166 | + | |
| 167 | + Intent intent = new Intent(this, JudgeActivity.class); | |
| 168 | + intent.putExtra("path", imageBean.getPath()); | |
| 169 | + intent.putParcelableArrayListExtra("rects", rects); | |
| 170 | + | |
| 171 | + startActivity(intent); | |
| 172 | + } | |
| 173 | + | |
| 174 | + private Rect getRectB(Rect rectV, Bitmap bitmap, float[] measuredSize) { | |
| 175 | + float realW = measuredSize[0]; | |
| 176 | + float realH = measuredSize[1]; | |
| 177 | + float offsetX = measuredSize[2]; | |
| 178 | + float offsetY = measuredSize[3]; | |
| 179 | + float percentX = bitmap.getWidth() / realW; | |
| 180 | + float percentY = bitmap.getHeight() / realH; | |
| 181 | + | |
| 182 | + float left = (rectV.left - offsetX) * percentX; | |
| 183 | + float right = (rectV.right - offsetX) * percentX; | |
| 184 | + float top = (rectV.top - offsetY) * percentY; | |
| 185 | + float bottom = (rectV.bottom - offsetY) * percentY; | |
| 186 | + Rect rectB = new Rect((int) left, (int) top, (int) right, (int) bottom); | |
| 187 | + return rectB; | |
| 119 | 188 | } |
| 120 | 189 | |
| 121 | 190 | public void clearText() { |
| ... | ... | @@ -128,8 +197,8 @@ public class ImageActivity extends BaseRxActivity<ActivityImageBinding> implemen |
| 128 | 197 | } |
| 129 | 198 | }); |
| 130 | 199 | Log.i("clearText", list.get(index).getPath()); |
| 131 | - String base64 = BitmapUtils.imageToBase64(list.get(index).getPath()); | |
| 132 | - NetWorks.removeWriting(base64, new Observer<JsonObject>() { | |
| 200 | + String base64 = BitmapUtils.imageToBase64(list.get(index).getPath(), false); | |
| 201 | + NetWorks.baiduEnhance(base64, new Observer<JsonObject>() { | |
| 133 | 202 | @Override |
| 134 | 203 | public void onSubscribe(Disposable d) { |
| 135 | 204 | |
| ... | ... | @@ -170,7 +239,7 @@ public class ImageActivity extends BaseRxActivity<ActivityImageBinding> implemen |
| 170 | 239 | } |
| 171 | 240 | |
| 172 | 241 | public void cut(String path) { |
| 173 | - String base64 = BitmapUtils.imageToBase64(path); | |
| 242 | + String base64 = BitmapUtils.imageToBase64WithUrlEncode(path); | |
| 174 | 243 | Log.i("figLoaction", path); |
| 175 | 244 | NetWorks.cut(base64, new Observer<CutPicBean>() { |
| 176 | 245 | @Override | ... | ... |
app/src/main/java/com/hjx/parent/JudgeActivity.java
| ... | ... | @@ -0,0 +1,173 @@ |
| 1 | +package com.hjx.parent; | |
| 2 | + | |
| 3 | +import android.annotation.SuppressLint; | |
| 4 | +import android.graphics.Bitmap; | |
| 5 | +import android.graphics.Rect; | |
| 6 | +import android.os.Bundle; | |
| 7 | +import android.util.Log; | |
| 8 | + | |
| 9 | +import com.bumptech.glide.Glide; | |
| 10 | +import com.hjx.parent.api.JudgeRepository; | |
| 11 | +import com.hjx.parent.databinding.ActivityJudgeBinding; | |
| 12 | +import com.hjx.parent.databinding.LayoutJudgeRectBinding; | |
| 13 | +import com.hjx.parent.rx.BaseRxActivity; | |
| 14 | +import com.hjx.parent.utils.CutUtil; | |
| 15 | + | |
| 16 | +import java.io.File; | |
| 17 | +import java.util.ArrayList; | |
| 18 | +import java.util.List; | |
| 19 | + | |
| 20 | +import io.reactivex.Observable; | |
| 21 | +import io.reactivex.Single; | |
| 22 | +import top.zibin.luban.Luban; | |
| 23 | + | |
| 24 | +public class JudgeActivity extends BaseRxActivity<ActivityJudgeBinding> { | |
| 25 | + | |
| 26 | + private Bitmap bitmap; | |
| 27 | + | |
| 28 | + private final List<JudgeCut> mList = new ArrayList<>(); | |
| 29 | + | |
| 30 | + @SuppressLint("CheckResult") | |
| 31 | + @Override | |
| 32 | + public void initView(Bundle savedInstanceState) { | |
| 33 | + binding.ivBack.setOnClickListener(v -> { | |
| 34 | + onBackPressed(); | |
| 35 | + }); | |
| 36 | + String path = getIntent().getStringExtra("path"); | |
| 37 | + if (path == null) return; | |
| 38 | + final ArrayList<Rect> rects = getIntent().getParcelableArrayListExtra("rects"); | |
| 39 | + if (rects == null) return; | |
| 40 | + | |
| 41 | + Single.just(path) | |
| 42 | + .map(origin -> Luban.with(this).load(origin).ignoreBy(300).get().get(0)) | |
| 43 | + .map(file -> Glide.with(this).asBitmap().load(file).submit().get()) | |
| 44 | + .compose(transformSingle()) | |
| 45 | + .subscribe((bitmap, th) -> { | |
| 46 | + if (th != null) th.printStackTrace(); | |
| 47 | + if (bitmap == null) return; | |
| 48 | + this.bitmap = bitmap; | |
| 49 | + | |
| 50 | + binding.ivPic.setImageBitmap(bitmap); | |
| 51 | + CutUtil.onLayoutReady(binding.ivPic, v -> { | |
| 52 | + prepareRects(rects); | |
| 53 | + }); | |
| 54 | + }); | |
| 55 | + binding.btnSelectAll.setOnClickListener(v -> { | |
| 56 | + if (mList.isEmpty()) return; | |
| 57 | + mList.forEach(it -> it.vb.getRoot().setSelected(true)); | |
| 58 | + checkCount(); | |
| 59 | + }); | |
| 60 | + } | |
| 61 | + | |
| 62 | + private void prepareRects(List<Rect> rects) { | |
| 63 | + mList.clear(); | |
| 64 | + binding.flRects.removeAllViews(); | |
| 65 | + float[] measuredSize = CutUtil.measureBitmap(binding.ivPic, bitmap); | |
| 66 | + for (int i = 0; i < rects.size(); i++) { | |
| 67 | + Rect it = rects.get(i); | |
| 68 | + Bitmap bitmap = CutUtil.cut(this.bitmap, it.left, it.top, it.width(), it.height(), this); | |
| 69 | + Rect rect = measureRect(it, measuredSize); | |
| 70 | + LayoutJudgeRectBinding vb = showRect(rect); | |
| 71 | + mList.add(new JudgeCut(i, bitmap, rect, vb)); | |
| 72 | + } | |
| 73 | + | |
| 74 | + judge(); | |
| 75 | + } | |
| 76 | + | |
| 77 | + @SuppressLint("CheckResult") | |
| 78 | + private void judge() { | |
| 79 | + showLoadingDialog(""); | |
| 80 | + Observable.fromIterable(mList) | |
| 81 | + .map(it -> { | |
| 82 | + if (it.url != null && !it.url.isEmpty()) return it; | |
| 83 | + String base64 = CutUtil.bitmapToBase64(it.bitmap, true); | |
| 84 | + it.url = base64; | |
| 85 | + return it; | |
| 86 | + }) | |
| 87 | + .flatMap(it -> JudgeRepository.singleJudge(it.url) | |
| 88 | + .map(result -> { | |
| 89 | + it.correctResult = result; | |
| 90 | + return it; | |
| 91 | + }) | |
| 92 | + .toObservable() | |
| 93 | + ) | |
| 94 | + .compose(transform()) | |
| 95 | + .subscribe(it -> { | |
| 96 | + if (it.correctResult == 1) { | |
| 97 | + it.vb.ivResult.setImageResource(R.drawable.png_ic_judge_correct); | |
| 98 | + } else if (it.correctResult == 2) { | |
| 99 | + it.vb.ivResult.setImageResource(R.drawable.png_ic_judge_wrong); | |
| 100 | + } | |
| 101 | + it.vb.getRoot().setSelected(it.correctResult != 1); | |
| 102 | + checkCount(); | |
| 103 | + }, th -> { | |
| 104 | + cancelLoadingDialog(); | |
| 105 | + Log.e(getClass().getName(), "", th); | |
| 106 | + }, this::cancelLoadingDialog) | |
| 107 | + ; | |
| 108 | + } | |
| 109 | + | |
| 110 | + private LayoutJudgeRectBinding showRect(Rect rect) { | |
| 111 | + LayoutJudgeRectBinding vb = LayoutJudgeRectBinding.inflate(getLayoutInflater(), binding.flRects, false); | |
| 112 | + | |
| 113 | + vb.getRoot().setMinimumWidth(rect.width()); | |
| 114 | + vb.getRoot().setMinimumHeight(rect.height()); | |
| 115 | + vb.getRoot().setTranslationX((float) rect.left); | |
| 116 | + vb.getRoot().setTranslationY((float) rect.top); | |
| 117 | + vb.getRoot().setOnClickListener(v -> { | |
| 118 | + v.setSelected(!v.isSelected()); | |
| 119 | + checkCount(); | |
| 120 | + }); | |
| 121 | + binding.flRects.addView(vb.getRoot()); | |
| 122 | + | |
| 123 | + return vb; | |
| 124 | + } | |
| 125 | + | |
| 126 | + @SuppressLint("SetTextI18n") | |
| 127 | + private void checkCount() { | |
| 128 | + int count = (int) mList.stream().filter(it -> it.vb.getRoot().isSelected()).count(); | |
| 129 | + if (count == 0) binding.btnAdd.setText("加入错题"); | |
| 130 | + else binding.btnAdd.setText("加入错题(" + count + ")"); | |
| 131 | + binding.btnAdd.setEnabled(count > 0); | |
| 132 | + } | |
| 133 | + | |
| 134 | + /** bitmapRect 转换为 viewRect */ | |
| 135 | + private Rect measureRect(Rect rect, float[] measuredSize) { | |
| 136 | + float realW = measuredSize[0]; | |
| 137 | + float realH = measuredSize[1]; | |
| 138 | + float offsetX = measuredSize[2]; | |
| 139 | + float offsetY = measuredSize[3]; | |
| 140 | + float percentX = realW / bitmap.getWidth(); | |
| 141 | + float percentY = realH / bitmap.getHeight(); | |
| 142 | + | |
| 143 | + float left = rect.left * percentX + offsetX; | |
| 144 | + float right = rect.right * percentX + offsetX; | |
| 145 | + float top = rect.top * percentY + offsetY; | |
| 146 | + float bottom = rect.bottom * percentY + offsetY; | |
| 147 | + return new Rect((int) left, (int) top, (int) right, (int) bottom); | |
| 148 | + } | |
| 149 | + | |
| 150 | + @Override | |
| 151 | + protected ActivityJudgeBinding getViewBinding() { | |
| 152 | + return ActivityJudgeBinding.inflate(getLayoutInflater()); | |
| 153 | + } | |
| 154 | + | |
| 155 | + static class JudgeCut { | |
| 156 | + final int index; | |
| 157 | + final Bitmap bitmap; | |
| 158 | + final Rect rect; | |
| 159 | + final LayoutJudgeRectBinding vb; | |
| 160 | + | |
| 161 | + String url; | |
| 162 | + | |
| 163 | + /** 0:未批改;1:正确;2:错误;3:未作答 */ | |
| 164 | + int correctResult = 0; | |
| 165 | + | |
| 166 | + public JudgeCut(int index, Bitmap bitmap, Rect rect, LayoutJudgeRectBinding vb) { | |
| 167 | + this.index = index; | |
| 168 | + this.bitmap = bitmap; | |
| 169 | + this.rect = rect; | |
| 170 | + this.vb = vb; | |
| 171 | + } | |
| 172 | + } | |
| 173 | +} | ... | ... |
app/src/main/java/com/hjx/parent/adapter/ImageAdapter.java
| ... | ... | @@ -7,6 +7,7 @@ import android.graphics.BitmapFactory; |
| 7 | 7 | import android.graphics.Rect; |
| 8 | 8 | import android.graphics.RectF; |
| 9 | 9 | import android.util.Log; |
| 10 | +import android.util.SparseArray; | |
| 10 | 11 | import android.view.LayoutInflater; |
| 11 | 12 | import android.view.View; |
| 12 | 13 | import android.view.ViewGroup; |
| ... | ... | @@ -27,9 +28,12 @@ import com.prws.common.utils.CommonUtil; |
| 27 | 28 | import java.util.List; |
| 28 | 29 | |
| 29 | 30 | public class ImageAdapter extends RecyclerView.Adapter<ImageAdapter.ImageHolder> { |
| 31 | + public SparseArray<ItemImageBinding> viewArray = new SparseArray<>(); | |
| 32 | + | |
| 30 | 33 | private List<ImageBean> list; |
| 31 | 34 | private ImageActivity context; |
| 32 | 35 | |
| 36 | + | |
| 33 | 37 | public ImageAdapter(ImageActivity context, List<ImageBean> list) { |
| 34 | 38 | this.context = context; |
| 35 | 39 | this.list = list; |
| ... | ... | @@ -44,6 +48,8 @@ public class ImageAdapter extends RecyclerView.Adapter<ImageAdapter.ImageHolder> |
| 44 | 48 | |
| 45 | 49 | @Override |
| 46 | 50 | public void onBindViewHolder(@NonNull ImageHolder holder, int position) { |
| 51 | + viewArray.put(position, holder.binding); | |
| 52 | + | |
| 47 | 53 | ImageBean imageBean = list.get(position); |
| 48 | 54 | Bitmap bitmap = BitmapFactory.decodeFile(imageBean.getPath()); |
| 49 | 55 | float imageHeight = bitmap.getHeight(); | ... | ... |
app/src/main/java/com/hjx/parent/api/ChatApi.java
| ... | ... | @@ -0,0 +1,17 @@ |
| 1 | +package com.hjx.parent.api; | |
| 2 | + | |
| 3 | +import io.reactivex.Single; | |
| 4 | +import retrofit2.http.Body; | |
| 5 | +import retrofit2.http.Header; | |
| 6 | +import retrofit2.http.POST; | |
| 7 | +import retrofit2.http.Url; | |
| 8 | + | |
| 9 | +public interface ChatApi { | |
| 10 | + @POST | |
| 11 | + Single<ChatResponse> aiChat( | |
| 12 | + @Url String url, | |
| 13 | + @Header("Authorization") String token, | |
| 14 | + @Body ChatRequest body | |
| 15 | + ); | |
| 16 | + | |
| 17 | +} | ... | ... |
app/src/main/java/com/hjx/parent/api/ChatChoices.java
| ... | ... | @@ -0,0 +1,13 @@ |
| 1 | +package com.hjx.parent.api; | |
| 2 | + | |
| 3 | +import com.google.gson.annotations.SerializedName; | |
| 4 | + | |
| 5 | +public class ChatChoices { | |
| 6 | + | |
| 7 | + public ChatMessage message; | |
| 8 | + public int index; | |
| 9 | + @SerializedName("finish_reason") | |
| 10 | + public String finishReason; | |
| 11 | + public String logprobs; | |
| 12 | + | |
| 13 | +} | ... | ... |
app/src/main/java/com/hjx/parent/api/ChatError.java
app/src/main/java/com/hjx/parent/api/ChatMessage.java
| ... | ... | @@ -0,0 +1,45 @@ |
| 1 | +package com.hjx.parent.api; | |
| 2 | + | |
| 3 | +import java.util.ArrayList; | |
| 4 | +import java.util.List; | |
| 5 | +import java.util.Objects; | |
| 6 | + | |
| 7 | +public class ChatMessage { | |
| 8 | + public ChatMessage() { | |
| 9 | + } | |
| 10 | + public ChatMessage(String role, Object content) { | |
| 11 | + this.role = role; | |
| 12 | + this.content = content; | |
| 13 | + } | |
| 14 | + | |
| 15 | + public String role; | |
| 16 | + public Object content; | |
| 17 | + | |
| 18 | + public static ChatMessage fromImage(String role, String text, String... urls) { | |
| 19 | + List<ContentItem> items = new ArrayList<>(); | |
| 20 | + if (text != null && !text.isEmpty()) { | |
| 21 | + items.add(new ContentItem(ContentItem.TYPE_TEXT, text)); | |
| 22 | + } | |
| 23 | + for (String it: urls) { | |
| 24 | + ContentItem.ContentImage image = new ContentItem.ContentImage(it); | |
| 25 | + items.add(new ContentItem(ContentItem.TYPE_IMAGE, image)); | |
| 26 | + } | |
| 27 | + return new ChatMessage(role, items); | |
| 28 | + } | |
| 29 | + | |
| 30 | + public String getTextContent() { | |
| 31 | + if (content == null) return null; | |
| 32 | + if (content instanceof String) return (String) content; | |
| 33 | + ContentItem item = firstText(); | |
| 34 | + return item == null ? null : item.text; | |
| 35 | + } | |
| 36 | + | |
| 37 | + private ContentItem firstText() { | |
| 38 | + if (!(content instanceof List<?>)) return null; | |
| 39 | + List<?> anyList = (List<?>) content; | |
| 40 | + return anyList.stream() | |
| 41 | + .map(ContentItem::reSerialize) | |
| 42 | + .filter(Objects::nonNull) | |
| 43 | + .findFirst().orElse(null); | |
| 44 | + } | |
| 45 | +} | ... | ... |
app/src/main/java/com/hjx/parent/api/ChatRequest.java
| ... | ... | @@ -0,0 +1,15 @@ |
| 1 | +package com.hjx.parent.api; | |
| 2 | + | |
| 3 | +import java.util.List; | |
| 4 | + | |
| 5 | +public class ChatRequest { | |
| 6 | + public String model; | |
| 7 | + public List<ChatMessage> messages; | |
| 8 | + public boolean stream; | |
| 9 | + | |
| 10 | + public ChatRequest(String model, List<ChatMessage> messages) { | |
| 11 | + this.model = model; | |
| 12 | + this.messages = messages; | |
| 13 | + this.stream = false; | |
| 14 | + } | |
| 15 | +} | ... | ... |
app/src/main/java/com/hjx/parent/api/ChatResponse.java
| ... | ... | @@ -0,0 +1,19 @@ |
| 1 | +package com.hjx.parent.api; | |
| 2 | + | |
| 3 | +import com.google.gson.annotations.SerializedName; | |
| 4 | + | |
| 5 | +import java.util.List; | |
| 6 | + | |
| 7 | +public class ChatResponse { | |
| 8 | + public List<ChatChoices> choices; | |
| 9 | + public String created; | |
| 10 | + public String id; | |
| 11 | + public String model; | |
| 12 | + @SerializedName("service_tier") | |
| 13 | + public String serviceTier; | |
| 14 | + @SerializedName("object") | |
| 15 | + public String obj; | |
| 16 | + public ChatUsage usage; | |
| 17 | + | |
| 18 | + public ChatError error; | |
| 19 | +} | ... | ... |
app/src/main/java/com/hjx/parent/api/ChatUsage.java
| ... | ... | @@ -0,0 +1,16 @@ |
| 1 | +package com.hjx.parent.api; | |
| 2 | + | |
| 3 | +import com.google.gson.annotations.SerializedName; | |
| 4 | + | |
| 5 | +public class ChatUsage { | |
| 6 | + @SerializedName("completion_tokens") | |
| 7 | + public int completionTokens; | |
| 8 | + @SerializedName("prompt_tokens") | |
| 9 | + public int promptTokens; | |
| 10 | + @SerializedName("total_tokens") | |
| 11 | + public int totalTokens; | |
| 12 | + @SerializedName("prompt_tokens_details") | |
| 13 | + public PromptTokensDetails promptTokensDetails; | |
| 14 | + @SerializedName("completion_tokens_details") | |
| 15 | + public CompletionTokensDetails completionTokensDetails; | |
| 16 | +} | ... | ... |
app/src/main/java/com/hjx/parent/api/CompletionTokensDetails.java
app/src/main/java/com/hjx/parent/api/ContentItem.java
| ... | ... | @@ -0,0 +1,48 @@ |
| 1 | +package com.hjx.parent.api; | |
| 2 | + | |
| 3 | +import com.google.gson.Gson; | |
| 4 | +import com.google.gson.annotations.SerializedName; | |
| 5 | + | |
| 6 | +public class ContentItem { | |
| 7 | + public static final String TYPE_TEXT = "text"; | |
| 8 | + public static final String TYPE_IMAGE = "image_url"; | |
| 9 | + | |
| 10 | + | |
| 11 | + public ContentItem() { | |
| 12 | + } | |
| 13 | + public ContentItem(String type, String text) { | |
| 14 | + this.type = type; | |
| 15 | + this.text = text; | |
| 16 | + } | |
| 17 | + public ContentItem(String type, ContentImage imageUrl) { | |
| 18 | + this.type = type; | |
| 19 | + this.imageUrl = imageUrl; | |
| 20 | + } | |
| 21 | + | |
| 22 | + public String type; | |
| 23 | + public String text; | |
| 24 | + @SerializedName("image_url") | |
| 25 | + public ContentImage imageUrl; | |
| 26 | + | |
| 27 | + public static class ContentImage { | |
| 28 | + public ContentImage(String url) { | |
| 29 | + this.url = url; | |
| 30 | + } | |
| 31 | + | |
| 32 | + public String url; | |
| 33 | + } | |
| 34 | + | |
| 35 | + | |
| 36 | + private static final Gson gson = new Gson(); | |
| 37 | + static ContentItem reSerialize(Object any) { | |
| 38 | + if (any == null) return null; | |
| 39 | + try { | |
| 40 | + String json = gson.toJson(any); | |
| 41 | + ContentItem it = gson.fromJson(json, ContentItem.class); | |
| 42 | + return it; | |
| 43 | + } catch (Throwable t) { | |
| 44 | + t.printStackTrace(); | |
| 45 | + } | |
| 46 | + return null; | |
| 47 | + } | |
| 48 | +} | ... | ... |
app/src/main/java/com/hjx/parent/api/JudgeRepository.java
| ... | ... | @@ -0,0 +1,55 @@ |
| 1 | +package com.hjx.parent.api; | |
| 2 | + | |
| 3 | +import android.annotation.SuppressLint; | |
| 4 | + | |
| 5 | +import com.google.gson.Gson; | |
| 6 | +import com.prws.common.net.NetWorks; | |
| 7 | + | |
| 8 | +import java.util.ArrayList; | |
| 9 | +import java.util.List; | |
| 10 | + | |
| 11 | +import io.reactivex.Single; | |
| 12 | +import retrofit2.Retrofit; | |
| 13 | + | |
| 14 | +public class JudgeRepository { | |
| 15 | + private static final Gson gson = new Gson(); | |
| 16 | + private static final ChatApi api; | |
| 17 | + static { | |
| 18 | + Retrofit retrofit = NetWorks.retrofit; | |
| 19 | + api = retrofit.create(ChatApi.class); | |
| 20 | + } | |
| 21 | + | |
| 22 | + | |
| 23 | + public static Single<Integer> singleJudge(String url) { | |
| 24 | + List<ChatMessage> messages = new ArrayList<>(); | |
| 25 | + messages.add(ChatMessage.fromImage("system", setting, url)); | |
| 26 | + | |
| 27 | + return api.aiChat(JudgeRepository.url, token, new ChatRequest(model, messages)) | |
| 28 | + .map(response -> { | |
| 29 | + if (response.choices == null) return null; | |
| 30 | + return response.choices.stream().findFirst() | |
| 31 | + .map(it -> it.message) | |
| 32 | + .map(ChatMessage::getTextContent) | |
| 33 | + .orElse(null); | |
| 34 | + }) | |
| 35 | + .map(json -> { | |
| 36 | + JudgeResult result = gson.fromJson(json, JudgeResult.class); | |
| 37 | + return result.result; | |
| 38 | + }) | |
| 39 | + ; | |
| 40 | + } | |
| 41 | + | |
| 42 | + | |
| 43 | + private static final String model = "doubao-1-5-thinking-vision-pro-250428"; | |
| 44 | + private static final String url = "https://ark.cn-beijing.volces.com/api/v3/chat/completions"; | |
| 45 | + private static final String token = "Bearer a877087e-bb74-471b-8f2a-01e6b2220699"; | |
| 46 | + private static final String setting = | |
| 47 | + "批改图片中的题目, 判断作答是否正确,并以Json格式返回结果,需要包含以下字段: \n" + | |
| 48 | + "result(批改结果, 枚举 0:不确定; 1:正确; 2:错误; 3:未作答).\n" + | |
| 49 | + "Json示例(供参考,无需复制): {\"result\": 1}\n" + | |
| 50 | + "一个图片中一般只会有一个题目, 返回一个结果即可."; | |
| 51 | + | |
| 52 | + private static class JudgeResult { | |
| 53 | + public int result; | |
| 54 | + } | |
| 55 | +} | ... | ... |
app/src/main/java/com/hjx/parent/api/PromptTokensDetails.java
app/src/main/java/com/hjx/parent/utils/CutUtil.java
| ... | ... | @@ -0,0 +1,102 @@ |
| 1 | +package com.hjx.parent.utils; | |
| 2 | + | |
| 3 | +import android.content.Context; | |
| 4 | +import android.graphics.Bitmap; | |
| 5 | +import android.graphics.Canvas; | |
| 6 | +import android.graphics.Color; | |
| 7 | +import android.graphics.Paint; | |
| 8 | +import android.util.Base64; | |
| 9 | +import android.view.View; | |
| 10 | +import android.view.ViewTreeObserver; | |
| 11 | + | |
| 12 | +import com.bumptech.glide.Glide; | |
| 13 | +import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool; | |
| 14 | +import com.hjx.parent.function.Function1; | |
| 15 | + | |
| 16 | +import java.io.ByteArrayOutputStream; | |
| 17 | +import java.io.File; | |
| 18 | +import java.io.IOException; | |
| 19 | +import java.util.List; | |
| 20 | + | |
| 21 | +import top.zibin.luban.Luban; | |
| 22 | + | |
| 23 | +public class CutUtil { | |
| 24 | + | |
| 25 | + public static float[] measureBitmap(View view, Bitmap bitmap) { | |
| 26 | + float realW; | |
| 27 | + float realH; | |
| 28 | + float offsetX; | |
| 29 | + float offsetY; | |
| 30 | + | |
| 31 | + float vp = 1f * view.getWidth() / view.getHeight(); | |
| 32 | + float bp = 1f * bitmap.getWidth() / bitmap.getHeight(); | |
| 33 | + | |
| 34 | + if (vp > bp) { | |
| 35 | + realW = view.getHeight() * bp; | |
| 36 | + realH = (float) view.getHeight(); | |
| 37 | + offsetY = 0f; | |
| 38 | + offsetX = (view.getWidth() - realW) * 0.5f; | |
| 39 | + } else if (vp < bp) { | |
| 40 | + realW = (float) view.getWidth(); | |
| 41 | + realH = view.getWidth() * (1f / bp); | |
| 42 | + offsetX = 0f; | |
| 43 | + offsetY = (view.getHeight() - realH) * 0.5f; | |
| 44 | + } else { | |
| 45 | + realW = (float) view.getWidth(); | |
| 46 | + realH = (float) view.getHeight(); | |
| 47 | + offsetX = 0f; | |
| 48 | + offsetY = 0f; | |
| 49 | + } | |
| 50 | + | |
| 51 | + return new float[]{realW, realH, offsetX, offsetY}; | |
| 52 | + } | |
| 53 | + | |
| 54 | + public static Bitmap cut(Bitmap source, float x, float y, int w, int h, Context context) { | |
| 55 | + // 获取 Glide 的 BitmapPool | |
| 56 | + BitmapPool bitmapPool = Glide.get(context).getBitmapPool(); | |
| 57 | + | |
| 58 | + // 从 BitmapPool 中获取可复用的 Bitmap | |
| 59 | + Bitmap result = bitmapPool.get(w, h, source.getConfig() != null ? source.getConfig() : Bitmap.Config.ARGB_8888); | |
| 60 | + | |
| 61 | + // 填充白色背景 | |
| 62 | + result.eraseColor(Color.WHITE); | |
| 63 | + | |
| 64 | + // 创建画布并绘制 | |
| 65 | + Canvas canvas = new Canvas(result); | |
| 66 | + Paint paint = new Paint(); | |
| 67 | + canvas.drawBitmap(source, -x, -y, paint); | |
| 68 | + | |
| 69 | + return result; | |
| 70 | + } | |
| 71 | + | |
| 72 | + public static void onLayoutReady(final View view, final Function1<View> action) { | |
| 73 | + view.getViewTreeObserver().addOnGlobalLayoutListener( | |
| 74 | + new ViewTreeObserver.OnGlobalLayoutListener() { | |
| 75 | + @Override | |
| 76 | + public void onGlobalLayout() { | |
| 77 | + view.getViewTreeObserver().removeOnGlobalLayoutListener(this); | |
| 78 | + action.invoke(view); | |
| 79 | + } | |
| 80 | + } | |
| 81 | + ); | |
| 82 | + } | |
| 83 | + | |
| 84 | + public static byte[] bitmapToByteArray(Bitmap bitmap) { | |
| 85 | + ByteArrayOutputStream output = new ByteArrayOutputStream(); | |
| 86 | + bitmap.compress(Bitmap.CompressFormat.JPEG, 100, output); | |
| 87 | + return output.toByteArray(); | |
| 88 | + } | |
| 89 | + | |
| 90 | + public static String bitmapToBase64(Bitmap bitmap, Boolean usePrefix) { | |
| 91 | + String base64 = Base64.encodeToString(bitmapToByteArray(bitmap), Base64.NO_WRAP); | |
| 92 | + if (usePrefix) return "data:image/jpeg;base64," + base64; | |
| 93 | + else return base64; | |
| 94 | + } | |
| 95 | + | |
| 96 | + public static List<File> compressAllSync(Context context, List<String> originList, int ignore) throws IOException { | |
| 97 | + return Luban.with(context).ignoreBy(ignore) | |
| 98 | + .setTargetDir(context.getExternalCacheDir().getAbsolutePath()) | |
| 99 | + .load(originList) | |
| 100 | + .get(); | |
| 101 | + } | |
| 102 | +} | ... | ... |
app/src/main/res/drawable/bg_judge_cut.xml
| ... | ... | @@ -0,0 +1,15 @@ |
| 1 | +<?xml version="1.0" encoding="utf-8"?> | |
| 2 | +<selector xmlns:android="http://schemas.android.com/apk/res/android"> | |
| 3 | + <item android:state_selected="true"> | |
| 4 | + <shape> | |
| 5 | + <solid android:color="#1A1C90F3"/> | |
| 6 | + <stroke android:color="#1C90F3" android:width="1px"/> | |
| 7 | + </shape> | |
| 8 | + </item> | |
| 9 | + <item> | |
| 10 | + <shape> | |
| 11 | + <solid android:color="#33000000"/> | |
| 12 | + <stroke android:color="#666666" android:width="1px"/> | |
| 13 | + </shape> | |
| 14 | + </item> | |
| 15 | +</selector> | |
| 0 | 16 | \ No newline at end of file | ... | ... |
app/src/main/res/drawable/check_circle_12.xml
| ... | ... | @@ -0,0 +1,15 @@ |
| 1 | +<?xml version="1.0" encoding="utf-8"?> | |
| 2 | +<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> | |
| 3 | + <item android:width="12dp" android:height="12dp"> | |
| 4 | + <selector> | |
| 5 | + <item android:state_checked="true" | |
| 6 | + android:drawable="@drawable/png_ic_check_circle"/> | |
| 7 | + <item> | |
| 8 | + <shape> | |
| 9 | + <corners android:radius="20dp"/> | |
| 10 | + <stroke android:width="1.5dp" android:color="#333"/> | |
| 11 | + </shape> | |
| 12 | + </item> | |
| 13 | + </selector> | |
| 14 | + </item> | |
| 15 | +</layer-list> | |
| 0 | 16 | \ No newline at end of file | ... | ... |
app/src/main/res/drawable/ic_add.png
1.12 KB
app/src/main/res/drawable/ic_judge_rect_select.xml
| ... | ... | @@ -0,0 +1,5 @@ |
| 1 | +<?xml version="1.0" encoding="utf-8"?> | |
| 2 | +<selector xmlns:android="http://schemas.android.com/apk/res/android"> | |
| 3 | + <item android:drawable="@drawable/png_rect_selected" android:state_selected="true"/> | |
| 4 | + <item android:drawable="@drawable/png_rect_add"/> | |
| 5 | +</selector> | |
| 0 | 6 | \ No newline at end of file | ... | ... |
app/src/main/res/drawable/ic_rect_20.xml
app/src/main/res/drawable/png_ic_check_circle.png
1.05 KB
app/src/main/res/drawable/png_ic_judge_correct.png
1.09 KB
app/src/main/res/drawable/png_ic_judge_wrong.png
1.12 KB
app/src/main/res/drawable/png_ic_rect.png
1.72 KB
app/src/main/res/drawable/png_rect_add.png
1.12 KB
app/src/main/res/drawable/png_rect_selected.png
902 Bytes
app/src/main/res/layout/activity_image.xml
| 1 | 1 | <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |
| 2 | + xmlns:tools="http://schemas.android.com/tools" | |
| 2 | 3 | android:layout_width="match_parent" |
| 3 | 4 | android:layout_height="match_parent" |
| 4 | - android:orientation="vertical"> | |
| 5 | + xmlns:app="http://schemas.android.com/apk/res-auto" | |
| 6 | + android:orientation="vertical" | |
| 7 | + tools:ignore="HardcodedText"> | |
| 5 | 8 | |
| 6 | 9 | <RelativeLayout |
| 7 | 10 | android:layout_width="match_parent" |
| ... | ... | @@ -102,8 +105,10 @@ |
| 102 | 105 | android:src="@mipmap/ic_next_gray" /> |
| 103 | 106 | </RelativeLayout> |
| 104 | 107 | |
| 105 | - <RelativeLayout | |
| 108 | + <LinearLayout | |
| 106 | 109 | android:id="@+id/rl_bottom" |
| 110 | + android:orientation="horizontal" | |
| 111 | + android:gravity="center_vertical" | |
| 107 | 112 | android:layout_width="match_parent" |
| 108 | 113 | android:layout_height="70dp" |
| 109 | 114 | android:layout_alignParentBottom="true" |
| ... | ... | @@ -113,13 +118,12 @@ |
| 113 | 118 | android:id="@+id/ll_edit" |
| 114 | 119 | android:layout_width="wrap_content" |
| 115 | 120 | android:layout_height="wrap_content" |
| 116 | - android:layout_centerVertical="true" | |
| 117 | - android:layout_marginLeft="15dp" | |
| 121 | + android:layout_marginStart="15dp" | |
| 118 | 122 | android:orientation="vertical"> |
| 119 | 123 | |
| 120 | 124 | <ImageView |
| 121 | - android:layout_width="25dp" | |
| 122 | - android:layout_height="25dp" | |
| 125 | + android:layout_width="20dp" | |
| 126 | + android:layout_height="20dp" | |
| 123 | 127 | android:layout_gravity="center_horizontal" |
| 124 | 128 | android:src="@mipmap/ic_pic_edit" /> |
| 125 | 129 | |
| ... | ... | @@ -128,35 +132,45 @@ |
| 128 | 132 | android:layout_height="wrap_content" |
| 129 | 133 | android:layout_marginTop="5dp" |
| 130 | 134 | android:text="图片编辑" |
| 135 | + android:textColor="#333" | |
| 131 | 136 | android:textSize="11sp" /> |
| 132 | 137 | </LinearLayout> |
| 133 | 138 | |
| 134 | - <TextView | |
| 139 | + <androidx.appcompat.widget.AppCompatTextView | |
| 135 | 140 | android:id="@+id/tv_add" |
| 136 | - android:layout_width="140dp" | |
| 137 | - android:layout_height="40dp" | |
| 138 | - android:layout_centerVertical="true" | |
| 139 | - android:layout_marginRight="15dp" | |
| 140 | - android:layout_toLeftOf="@+id/tv_save" | |
| 141 | + android:text="手动框选" | |
| 142 | + android:textSize="11sp" | |
| 143 | + android:textColor="#333" | |
| 144 | + android:gravity="center_horizontal" | |
| 145 | + android:drawableTop="@drawable/ic_rect_20" | |
| 146 | + android:drawablePadding="5dp" | |
| 147 | + android:layout_marginStart="12dp" | |
| 148 | + android:layout_width="wrap_content" | |
| 149 | + android:layout_height="wrap_content" /> | |
| 150 | + | |
| 151 | + <Space style="@style/empty_space"/> | |
| 152 | + | |
| 153 | + <TextView | |
| 154 | + android:id="@+id/btnJudge" | |
| 155 | + android:layout_width="112dp" | |
| 156 | + android:layout_height="36dp" | |
| 141 | 157 | android:background="@drawable/bg_soild_orange_5" |
| 142 | 158 | android:gravity="center" |
| 143 | - android:text="手动框选" | |
| 159 | + android:text="去批改" | |
| 144 | 160 | android:textColor="@color/white" |
| 145 | - android:textSize="18sp" /> | |
| 161 | + android:textSize="16sp" /> | |
| 146 | 162 | |
| 147 | 163 | <TextView |
| 148 | 164 | android:id="@+id/tv_save" |
| 149 | - android:layout_width="140dp" | |
| 150 | - android:layout_height="40dp" | |
| 151 | - android:layout_alignParentRight="true" | |
| 152 | - android:layout_centerVertical="true" | |
| 153 | - android:layout_marginRight="15dp" | |
| 165 | + android:layout_width="112dp" | |
| 166 | + android:layout_height="36dp" | |
| 167 | + android:layout_marginHorizontal="15dp" | |
| 154 | 168 | android:background="@drawable/bg_soild_blue_light_5" |
| 155 | 169 | android:enabled="false" |
| 156 | 170 | android:gravity="center" |
| 157 | 171 | android:text="保存错题" |
| 158 | 172 | android:textColor="@color/white" |
| 159 | - android:textSize="18sp" /> | |
| 160 | - </RelativeLayout> | |
| 173 | + android:textSize="16sp" /> | |
| 174 | + </LinearLayout> | |
| 161 | 175 | </RelativeLayout> |
| 162 | 176 | </LinearLayout> |
| 163 | 177 | \ No newline at end of file | ... | ... |
app/src/main/res/layout/activity_judge.xml
| ... | ... | @@ -0,0 +1,73 @@ |
| 1 | +<?xml version="1.0" encoding="utf-8"?> | |
| 2 | +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" | |
| 3 | + xmlns:app="http://schemas.android.com/apk/res-auto" | |
| 4 | + xmlns:tools="http://schemas.android.com/tools" | |
| 5 | + android:orientation="vertical" | |
| 6 | + android:layout_width="match_parent" | |
| 7 | + android:layout_height="match_parent" | |
| 8 | + tools:ignore="UseCompoundDrawables,HardcodedText,SmallSp,ContentDescription"> | |
| 9 | + | |
| 10 | + <androidx.appcompat.widget.Toolbar | |
| 11 | + android:background="@color/white" | |
| 12 | + app:contentInsetStart="0dp" | |
| 13 | + android:layout_width="match_parent" | |
| 14 | + android:layout_height="40dp"> | |
| 15 | + <ImageView | |
| 16 | + android:id="@+id/ivBack" | |
| 17 | + android:layout_width="30dp" | |
| 18 | + android:layout_height="36dp" | |
| 19 | + android:layout_centerVertical="true" | |
| 20 | + android:layout_marginStart="5dp" | |
| 21 | + android:padding="10dp" | |
| 22 | + android:src="@mipmap/icon_back" /> | |
| 23 | + </androidx.appcompat.widget.Toolbar> | |
| 24 | + | |
| 25 | + <FrameLayout | |
| 26 | + android:background="@color/white" | |
| 27 | + android:padding="8dp" | |
| 28 | + android:layout_margin="16dp" | |
| 29 | + android:layout_width="match_parent" | |
| 30 | + android:layout_height="0px" | |
| 31 | + android:layout_weight="1"> | |
| 32 | + <ImageView | |
| 33 | + android:id="@+id/ivPic" | |
| 34 | + android:layout_width="match_parent" | |
| 35 | + android:layout_height="match_parent"/> | |
| 36 | + <FrameLayout | |
| 37 | + android:id="@+id/flRects" | |
| 38 | + android:layout_width="match_parent" | |
| 39 | + android:layout_height="match_parent" /> | |
| 40 | + </FrameLayout> | |
| 41 | + | |
| 42 | + <LinearLayout | |
| 43 | + android:orientation="horizontal" | |
| 44 | + android:gravity="center_vertical" | |
| 45 | + android:paddingHorizontal="16dp" | |
| 46 | + android:background="@color/white" | |
| 47 | + android:layout_width="match_parent" | |
| 48 | + android:layout_height="64dp"> | |
| 49 | + <androidx.appcompat.widget.AppCompatTextView | |
| 50 | + android:id="@+id/btnSelectAll" | |
| 51 | + android:text="全选" | |
| 52 | + android:textSize="16sp" | |
| 53 | + android:textColor="#333" | |
| 54 | + android:gravity="center_vertical" | |
| 55 | + android:paddingHorizontal="8dp" | |
| 56 | + android:paddingVertical="4dp" | |
| 57 | + android:layout_width="wrap_content" | |
| 58 | + android:layout_height="wrap_content"/> | |
| 59 | + | |
| 60 | + <Space style="@style/empty_space"/> | |
| 61 | + <TextView | |
| 62 | + android:id="@+id/btnAdd" | |
| 63 | + android:text="加入错题" | |
| 64 | + android:textSize="16sp" | |
| 65 | + android:textColor="@color/white" | |
| 66 | + android:gravity="center" | |
| 67 | + android:background="@drawable/shape_radius_5" | |
| 68 | + android:backgroundTint="#1C90F3" | |
| 69 | + android:layout_width="112dp" | |
| 70 | + android:layout_height="36dp"/> | |
| 71 | + </LinearLayout> | |
| 72 | + | |
| 73 | +</LinearLayout> | |
| 0 | 74 | \ No newline at end of file | ... | ... |
app/src/main/res/layout/layout_judge_rect.xml
| ... | ... | @@ -0,0 +1,27 @@ |
| 1 | +<?xml version="1.0" encoding="utf-8"?> | |
| 2 | +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" | |
| 3 | + xmlns:app="http://schemas.android.com/apk/res-auto" | |
| 4 | + xmlns:tools="http://schemas.android.com/tools" | |
| 5 | + android:orientation="vertical" | |
| 6 | + android:background="@drawable/bg_judge_cut" | |
| 7 | + android:minWidth="160dp" | |
| 8 | + android:minHeight="48dp" | |
| 9 | + android:layout_width="wrap_content" | |
| 10 | + android:layout_height="wrap_content" | |
| 11 | + tools:ignore="UseCompoundDrawables,HardcodedText,SmallSp,ContentDescription"> | |
| 12 | + | |
| 13 | + <ImageView | |
| 14 | + android:id="@+id/ivResult" | |
| 15 | + tools:src="@drawable/png_ic_judge_correct" | |
| 16 | + android:layout_gravity="end" | |
| 17 | + android:layout_margin="8dp" | |
| 18 | + android:layout_width="12dp" | |
| 19 | + android:layout_height="12dp"/> | |
| 20 | + | |
| 21 | + <ImageView | |
| 22 | + android:src="@drawable/ic_judge_rect_select" | |
| 23 | + android:layout_gravity="center" | |
| 24 | + android:layout_width="12dp" | |
| 25 | + android:layout_height="12dp"/> | |
| 26 | + | |
| 27 | +</FrameLayout> | |
| 0 | 28 | \ No newline at end of file | ... | ... |
libs/common/build.gradle
| ... | ... | @@ -18,23 +18,21 @@ android { |
| 18 | 18 | |
| 19 | 19 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" |
| 20 | 20 | |
| 21 | - buildConfigField("String", "APIKey1", "\"eCa3YzEjTBBiNjlXcNnjnldP\"") | |
| 22 | - buildConfigField("String", "SecretKey1", "\"KnZPmjF0ZzmxWV964mzmm6yuDljnETLt\"") | |
| 21 | + buildConfigField("String", "APIKey", "\"oj9XT8AOeTeLkdqaEX7RJA0N\"") | |
| 22 | + buildConfigField("String", "SecretKey", "\"AfUFmACkfbd0RMuk11qbw1TW5LtxtJHK\"") | |
| 23 | + buildConfigField("String", "APIKey1", "\"oj9XT8AOeTeLkdqaEX7RJA0N\"") | |
| 24 | + buildConfigField("String", "SecretKey1", "\"AfUFmACkfbd0RMuk11qbw1TW5LtxtJHK\"") | |
| 23 | 25 | } |
| 24 | 26 | |
| 25 | 27 | buildTypes { |
| 26 | 28 | debug { |
| 27 | 29 | minifyEnabled false |
| 28 | 30 | buildConfigField("String", "SERVER_URL", "\"${url.server_url}\"") |
| 29 | - buildConfigField("String", "APIKey", "\"OmmSqorNFXjVHXlZWhHAyGyQ\"") | |
| 30 | - buildConfigField("String", "SecretKey", "\"Uwjz4XGi89RN4Mvl5mievKpjqpGgUy6D\"") | |
| 31 | 31 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' |
| 32 | 32 | } |
| 33 | 33 | release { |
| 34 | 34 | minifyEnabled false |
| 35 | 35 | buildConfigField("String", "SERVER_URL", "\"${url.server_url_online}\"") |
| 36 | - buildConfigField("String", "APIKey", "\"OmmSqorNFXjVHXlZWhHAyGyQ\"") | |
| 37 | - buildConfigField("String", "SecretKey", "\"Uwjz4XGi89RN4Mvl5mievKpjqpGgUy6D\"") | |
| 38 | 36 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' |
| 39 | 37 | } |
| 40 | 38 | } | ... | ... |
libs/common/src/main/java/com/prws/common/net/NetWorks.java
| ... | ... | @@ -24,6 +24,7 @@ import com.prws.common.utils.SharedPreferencesUtil; |
| 24 | 24 | |
| 25 | 25 | import java.io.File; |
| 26 | 26 | import java.util.HashMap; |
| 27 | +import java.util.LinkedHashMap; | |
| 27 | 28 | import java.util.List; |
| 28 | 29 | import java.util.Map; |
| 29 | 30 | |
| ... | ... | @@ -38,6 +39,7 @@ import okhttp3.RequestBody; |
| 38 | 39 | import okhttp3.ResponseBody; |
| 39 | 40 | import retrofit2.Call; |
| 40 | 41 | import retrofit2.Callback; |
| 42 | +import retrofit2.Retrofit; | |
| 41 | 43 | import retrofit2.http.Body; |
| 42 | 44 | import retrofit2.http.GET; |
| 43 | 45 | import retrofit2.http.Header; |
| ... | ... | @@ -58,7 +60,8 @@ import retrofit2.http.Url; |
| 58 | 60 | */ |
| 59 | 61 | public class NetWorks extends RetrofitUtils { |
| 60 | 62 | //服务器路径 |
| 61 | - public static final NetService service_url = getMachineRetrofit(BuildConfig.SERVER_URL).create(NetService.class); | |
| 63 | + public static final Retrofit retrofit = getMachineRetrofit(BuildConfig.SERVER_URL); | |
| 64 | + public static final NetService service_url = retrofit.create(NetService.class); | |
| 62 | 65 | |
| 63 | 66 | //设缓存有效期为1天 |
| 64 | 67 | protected static final long CACHE_STALE_SEC = 60 * 60 * 24 * 1; |
| ... | ... | @@ -152,6 +155,12 @@ public class NetWorks extends RetrofitUtils { |
| 152 | 155 | @POST |
| 153 | 156 | Observable<JsonObject> removeWriting(@Url String url, @Body RequestBody body); |
| 154 | 157 | |
| 158 | + @POST("https://aip.baidubce.com/rest/2.0/ocr/v1/doc_crop_enhance") | |
| 159 | + Observable<JsonObject> baiduEnhance( | |
| 160 | + @Query("access_token") String token, | |
| 161 | + @Body Object any | |
| 162 | + ); | |
| 163 | + | |
| 155 | 164 | @POST |
| 156 | 165 | Observable<CutPicBean> cut(@Url String url, @Body RequestBody body); |
| 157 | 166 | |
| ... | ... | @@ -336,6 +345,14 @@ public class NetWorks extends RetrofitUtils { |
| 336 | 345 | setSubscribe(service_url.removeWriting(("https://aip.baidubce.com/rest/2.0/ocr/v1/remove_handwriting?access_token=" + getBaiduToken()), body), observer); |
| 337 | 346 | } |
| 338 | 347 | |
| 348 | + public static void baiduEnhance(String base64, Observer<JsonObject> observer) { | |
| 349 | + Map<String, Object> body = new LinkedHashMap<>(); | |
| 350 | + body.put("image", base64); | |
| 351 | + body.put("scan_type", 3); | |
| 352 | + body.put("enhance_type", 3); | |
| 353 | + setSubscribe(service_url.baiduEnhance(getBaiduToken(), body), observer); | |
| 354 | + } | |
| 355 | + | |
| 339 | 356 | public static void searchTeacher(String phone, Observer<ResponseResult<Teacher>> observer) { |
| 340 | 357 | setSubscribe(service_url.searchTeacher(getHeader(), phone), observer); |
| 341 | 358 | } | ... | ... |
libs/common/src/main/java/com/prws/common/net/SignInterceptor.java
| 1 | 1 | package com.prws.common.net; |
| 2 | 2 | |
| 3 | +import androidx.annotation.NonNull; | |
| 4 | + | |
| 5 | +import com.prws.common.BuildConfig; | |
| 3 | 6 | import com.prws.common.utils.SharedPreferencesUtil; |
| 4 | 7 | |
| 5 | 8 | import org.apache.commons.codec.binary.Hex; |
| ... | ... | @@ -14,23 +17,20 @@ import okhttp3.Request; |
| 14 | 17 | import okhttp3.Response; |
| 15 | 18 | |
| 16 | 19 | public class SignInterceptor implements Interceptor { |
| 20 | + @NonNull | |
| 17 | 21 | @Override |
| 18 | 22 | public Response intercept(Chain chain) throws IOException { |
| 19 | -// String appId = "1"; | |
| 20 | -// String APP_SECRET = "a839c40e04477d43705a64ba9db23b0dba969df360c3e428add85a6f5b8d0622"; | |
| 21 | -// String timestamp = String.valueOf(new Date().getTime()); | |
| 22 | -// String nonce = String.valueOf(new Random().nextInt(10000)); | |
| 23 | -// String sign = DigestUtils.md5Hex(appId + APP_SECRET + timestamp + nonce); // yidao板子报错 采用如下代码 | |
| 24 | -// byte[] signBytes = DigestUtils.md5(appId + APP_SECRET + timestamp + nonce); | |
| 25 | -// String sign = new String(Hex.encodeHex(signBytes)); | |
| 23 | + String host = BuildConfig.SERVER_URL; | |
| 24 | + if (!chain.request().url().toString().startsWith(host)) { | |
| 25 | + return chain.proceed(chain.request()); | |
| 26 | + } | |
| 27 | + | |
| 26 | 28 | String token = (String) SharedPreferencesUtil.getData("token",""); |
| 27 | 29 | Request.Builder requestBuilder = chain.request().newBuilder(); |
| 28 | 30 | if (token != null && token.length() > 0) { |
| 29 | 31 | requestBuilder = chain.request().newBuilder() |
| 30 | 32 | .header("Authorization", token); |
| 31 | 33 | } |
| 32 | -// .header("Content-Type","application/json"); | |
| 33 | - | |
| 34 | 34 | return chain.proceed(requestBuilder.build()); |
| 35 | 35 | } |
| 36 | 36 | } | ... | ... |
libs/common/src/main/java/com/prws/common/utils/BitmapUtils.java
| ... | ... | @@ -166,7 +166,12 @@ public class BitmapUtils { |
| 166 | 166 | /** |
| 167 | 167 | * 将图片转换成Base64编码的字符串 |
| 168 | 168 | */ |
| 169 | - public static String imageToBase64(String path) { | |
| 169 | + public static String imageToBase64WithUrlEncode(String path) { | |
| 170 | + return imageToBase64(path, true); | |
| 171 | + } | |
| 172 | + | |
| 173 | + public static String imageToBase64(String path, boolean urlEncode) { | |
| 174 | + | |
| 170 | 175 | if (TextUtils.isEmpty(path)) { |
| 171 | 176 | return null; |
| 172 | 177 | } |
| ... | ... | @@ -193,7 +198,8 @@ public class BitmapUtils { |
| 193 | 198 | } |
| 194 | 199 | |
| 195 | 200 | } |
| 196 | - return toURLEncoded(result); | |
| 201 | + if (urlEncode) return toURLEncoded(result); | |
| 202 | + else return result; | |
| 197 | 203 | } |
| 198 | 204 | |
| 199 | 205 | ... | ... |