|
1 | 1 | package com.nkanaev.comics.fragment;
|
2 | 2 |
|
| 3 | +import android.Manifest; |
3 | 4 | import android.content.Context;
|
4 | 5 | import android.content.DialogInterface;
|
5 | 6 | import android.content.Intent;
|
6 | 7 | import android.content.SharedPreferences;
|
| 8 | +import android.content.pm.PackageManager; |
7 | 9 | import android.content.res.Configuration;
|
8 | 10 | import android.graphics.Bitmap;
|
9 | 11 | import android.graphics.Rect;
|
10 | 12 | import android.graphics.drawable.BitmapDrawable;
|
11 | 13 | import android.graphics.drawable.Drawable;
|
12 | 14 | import android.net.Uri;
|
13 | 15 | import android.os.Bundle;
|
| 16 | +import android.os.Environment; |
14 | 17 | import android.os.Handler;
|
15 | 18 | import android.util.Log;
|
16 | 19 | import android.util.SparseArray;
|
17 | 20 | import android.util.TypedValue;
|
18 | 21 | import android.view.*;
|
19 | 22 | import android.view.animation.Animation;
|
20 |
| -import android.widget.ImageButton; |
21 |
| -import android.widget.ImageView; |
22 |
| -import android.widget.SeekBar; |
23 |
| -import android.widget.TextView; |
| 23 | +import android.widget.*; |
| 24 | +import androidx.activity.result.ActivityResultLauncher; |
| 25 | +import androidx.activity.result.contract.ActivityResultContracts; |
24 | 26 | import androidx.annotation.NonNull;
|
25 | 27 | import androidx.annotation.Nullable;
|
26 | 28 | import androidx.appcompat.app.ActionBar;
|
27 | 29 | import androidx.appcompat.app.AlertDialog;
|
28 | 30 | import androidx.appcompat.app.AppCompatActivity;
|
| 31 | +import androidx.core.content.ContextCompat; |
29 | 32 | import androidx.fragment.app.Fragment;
|
30 | 33 | import androidx.interpolator.view.animation.FastOutSlowInInterpolator;
|
31 | 34 | import androidx.viewpager.widget.PagerAdapter;
|
|
49 | 52 | import com.squareup.picasso.Target;
|
50 | 53 |
|
51 | 54 | import java.io.File;
|
| 55 | +import java.io.FileOutputStream; |
52 | 56 | import java.io.IOException;
|
53 | 57 | import java.io.InputStream;
|
54 | 58 | import java.lang.ref.WeakReference;
|
| 59 | +import java.text.DecimalFormat; |
| 60 | +import java.text.DecimalFormatSymbols; |
| 61 | +import java.text.NumberFormat; |
55 | 62 | import java.util.*;
|
56 | 63 |
|
57 | 64 |
|
58 | 65 | public class ReaderFragment extends Fragment implements View.OnTouchListener {
|
59 | 66 | public static final String PARAM_HANDLER = "PARAM_HANDLER";
|
60 | 67 | public static final String PARAM_URI = "PARAM_URI";
|
61 | 68 | public static final String PARAM_MODE = "PARAM_MODE";
|
62 |
| - |
63 | 69 | public static final String STATE_FULLSCREEN = "STATE_FULLSCREEN";
|
64 | 70 | public static final String STATE_PAGEINFO = "STATE_PAGEINFO";
|
65 | 71 | public static final String STATE_NEW_COMIC = "STATE_NEW_COMIC";
|
@@ -112,6 +118,17 @@ public enum Mode {
|
112 | 118 | RESOURCE_VIEW_MODE.put(R.id.view_mode_fit_width, Constants.PageViewMode.FIT_WIDTH);
|
113 | 119 | }
|
114 | 120 |
|
| 121 | + /* |
| 122 | + private ActivityResultLauncher<String> requestPermissionLauncher = |
| 123 | + registerForActivityResult(new ActivityResultContracts.RequestPermission(), isGranted -> { |
| 124 | + if (isGranted) { |
| 125 | + Toast.makeText(getContext(), "Permission is accepted", Toast.LENGTH_SHORT).show(); |
| 126 | + } else { |
| 127 | + Toast.makeText(getContext(), "Permission is denied", Toast.LENGTH_SHORT).show(); |
| 128 | + } |
| 129 | + }); |
| 130 | + */ |
| 131 | + |
115 | 132 | public static ReaderFragment create(int comicId) {
|
116 | 133 | ReaderFragment fragment = new ReaderFragment();
|
117 | 134 | Bundle args = new Bundle();
|
@@ -241,22 +258,6 @@ public void run() {
|
241 | 258 | mPageViewMode = Constants.PageViewMode.values()[viewModeInt];
|
242 | 259 | mIsLeftToRight = mPreferences.getBoolean(Constants.SETTINGS_READING_LEFT_TO_RIGHT, true);
|
243 | 260 |
|
244 |
| - // workaround: extract rar achive |
245 |
| - /* |
246 |
| - if (mParser instanceof RarParser) { |
247 |
| - File cacheDir = new File(getActivity().getExternalCacheDir(), "c"); |
248 |
| - if (!cacheDir.exists()) { |
249 |
| - cacheDir.mkdir(); |
250 |
| - } |
251 |
| - else { |
252 |
| - for (File f : cacheDir.listFiles()) { |
253 |
| - f.delete(); |
254 |
| - } |
255 |
| - } |
256 |
| - ((RarParser)mParser).setCacheDirectory(cacheDir); |
257 |
| - } |
258 |
| - */ |
259 |
| - |
260 | 261 | setHasOptionsMenu(true);
|
261 | 262 | }
|
262 | 263 |
|
@@ -404,6 +405,14 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat
|
404 | 405 | super.onViewCreated(view, savedInstanceState);
|
405 | 406 | }
|
406 | 407 |
|
| 408 | + @Override |
| 409 | + public void onPrepareOptionsMenu(@NonNull Menu menu) { |
| 410 | + super.onPrepareOptionsMenu(menu); |
| 411 | + |
| 412 | + // TODO: fix up page export permission bs |
| 413 | + menu.findItem(R.id.menu_reader_export).setVisible(BuildConfig.DEBUG); |
| 414 | + } |
| 415 | + |
407 | 416 | @Override
|
408 | 417 | public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
409 | 418 | inflater.inflate(R.menu.reader, menu);
|
@@ -521,13 +530,33 @@ public boolean onOptionsItemSelected(MenuItem item) {
|
521 | 530 | degrees = 0;
|
522 | 531 | degrees += 90;
|
523 | 532 | mRotations.put(pos,degrees);
|
524 |
| - //apply rotation |
| 533 | + // apply rotation during (re)load |
525 | 534 | mViewPager.getAdapter().notifyDataSetChanged();
|
| 535 | + //updatePageViews(mViewPager,pos,true); |
| 536 | + // work in progress, |
| 537 | + // rotating imageview does not reset boundings unfortunately, dunno howto fix for now |
| 538 | + // also touch events are registered to the imageview and rotate with the image, not good |
| 539 | + //rotatePage(pos, degrees); |
| 540 | + break; |
| 541 | + case R.id.menu_reader_export: |
| 542 | + exportCurrentPage(); |
526 | 543 | break;
|
527 | 544 | }
|
528 | 545 | return super.onOptionsItemSelected(item);
|
529 | 546 | }
|
530 | 547 |
|
| 548 | + private void rotatePage(int pos, int degrees){ |
| 549 | + try { |
| 550 | + MyTarget t = mTargets.get(pos); |
| 551 | + View v = t.mLayout.get(); |
| 552 | + PageImageView piv = v.findViewById(R.id.pageImageView); |
| 553 | + piv.rotate(degrees); |
| 554 | + } catch (NullPointerException ne) { |
| 555 | + // huh, wasn't there, need to reload it |
| 556 | + mViewPager.getAdapter().notifyDataSetChanged(); |
| 557 | + } |
| 558 | + } |
| 559 | + |
531 | 560 | private void setCurrentPage(int page) {
|
532 | 561 | setCurrentPage(page, true);
|
533 | 562 | }
|
@@ -570,34 +599,48 @@ private void updatePageImageInfo() {
|
570 | 599 | new Thread(new Runnable() {
|
571 | 600 | @Override
|
572 | 601 | public void run() {
|
573 |
| - Map<String, String> metadata = null; |
| 602 | + Map<String, Object> metadata = null; |
574 | 603 | try {
|
575 | 604 | metadata = mParser.getPageMetaData(pageNum);
|
576 | 605 | } catch (IOException e) {
|
577 | 606 | Log.e("", "", e);
|
578 | 607 | }
|
579 | 608 | String metaText = "";
|
580 | 609 | if (metadata != null && !metadata.isEmpty()) {
|
581 |
| - String name = metadata.get(Parser.PAGEMETADATA_KEY_NAME); |
| 610 | + String name = (String) metadata.get(Parser.PAGEMETADATA_KEY_NAME); |
582 | 611 | if (name != null)
|
583 | 612 | metaText += name;
|
584 |
| - String t = metadata.get(Parser.PAGEMETADATA_KEY_MIME); |
585 |
| - String w = metadata.get(Parser.PAGEMETADATA_KEY_WIDTH); |
586 |
| - String h = metadata.get(Parser.PAGEMETADATA_KEY_HEIGHT); |
| 613 | + Object t = metadata.get(Parser.PAGEMETADATA_KEY_MIME); |
| 614 | + Object w = metadata.get(Parser.PAGEMETADATA_KEY_WIDTH); |
| 615 | + Object h = metadata.get(Parser.PAGEMETADATA_KEY_HEIGHT); |
587 | 616 | if (t != null)
|
588 | 617 | metaText += (metaText.isEmpty() ? "" : "\n")
|
589 | 618 | + String.valueOf(t) + ", "
|
590 | 619 | + String.valueOf(w) + "x" + String.valueOf(h) + "px";
|
| 620 | + // append Byte size |
| 621 | + Object size = metadata.get(Parser.PAGEMETADATA_KEY_SIZE); |
| 622 | + if (size != null) { |
| 623 | + DecimalFormat formatter = (DecimalFormat) NumberFormat.getInstance(Locale.US); |
| 624 | + DecimalFormatSymbols symbols = formatter.getDecimalFormatSymbols(); |
| 625 | + symbols.setGroupingSeparator('.'); |
| 626 | + formatter.setDecimalFormatSymbols(symbols); |
| 627 | + try { |
| 628 | + metaText += (t != null ? ", " : "") + formatter.format(Long.valueOf(size.toString())) + " Bytes"; |
| 629 | + } catch (Exception e) { |
| 630 | + // eat it |
| 631 | + } |
| 632 | + } |
591 | 633 | // append the rest, ignore the already added from above
|
592 |
| - List<String> ignoredKeys = |
| 634 | + List<String> keysToIgnore = |
593 | 635 | Arrays.asList(new String[]{
|
594 | 636 | Parser.PAGEMETADATA_KEY_NAME,
|
595 | 637 | Parser.PAGEMETADATA_KEY_MIME,
|
596 | 638 | Parser.PAGEMETADATA_KEY_WIDTH,
|
597 |
| - Parser.PAGEMETADATA_KEY_HEIGHT}); |
598 |
| - for (Map.Entry<String, String> entry : metadata.entrySet()) { |
| 639 | + Parser.PAGEMETADATA_KEY_HEIGHT, |
| 640 | + Parser.PAGEMETADATA_KEY_SIZE}); |
| 641 | + for (Map.Entry<String, Object> entry : metadata.entrySet()) { |
599 | 642 | String key = entry.getKey();
|
600 |
| - if (ignoredKeys.contains(key)) |
| 643 | + if (keysToIgnore.contains(key)) |
601 | 644 | continue;
|
602 | 645 | metaText += (metaText.isEmpty() ? "" : "\n") +
|
603 | 646 | key + ": " + String.valueOf(entry.getValue());
|
@@ -658,6 +701,7 @@ public Object instantiateItem(ViewGroup container, int position) {
|
658 | 701 | loadImage(t);
|
659 | 702 | mTargets.put(position, t);
|
660 | 703 |
|
| 704 | + layout.setTag(Integer.valueOf(position)); |
661 | 705 | return layout;
|
662 | 706 | }
|
663 | 707 |
|
@@ -1044,4 +1088,78 @@ private void updateSeekBar() {
|
1044 | 1088 | mPageSeekBar.getProgressDrawable().setBounds(bounds);
|
1045 | 1089 | }
|
1046 | 1090 |
|
| 1091 | + private void exportCurrentPage() { |
| 1092 | + int pageNum = getCurrentPage(); |
| 1093 | + int index = pageNum-1; |
| 1094 | + File folder = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS); |
| 1095 | + FileOutputStream fOut = null; |
| 1096 | + Bitmap bitmap = null; |
| 1097 | + try { |
| 1098 | + if (folder==null) |
| 1099 | + throw new Exception("Cannot determine Downloads folder."); |
| 1100 | + else if (!folder.isDirectory() && !folder.mkdirs()) |
| 1101 | + throw new Exception("Couldn't create Downloads folder."); |
| 1102 | + else if (mFile==null) |
| 1103 | + throw new Exception("Not a file"); |
| 1104 | + |
| 1105 | + String permission = Manifest.permission.WRITE_EXTERNAL_STORAGE; |
| 1106 | + |
| 1107 | + /* |
| 1108 | + new Handler().post(new Runnable() { |
| 1109 | + @Override |
| 1110 | + public void run() { |
| 1111 | + requestPermissionLauncher.launch(permission, ActivityOptionsCompat.makeBasic()); |
| 1112 | + } |
| 1113 | + }); |
| 1114 | + */ |
| 1115 | + |
| 1116 | + //if (true) return; |
| 1117 | + |
| 1118 | + if (ContextCompat.checkSelfPermission(getActivity(), permission) |
| 1119 | + != PackageManager.PERMISSION_GRANTED) { |
| 1120 | + requestPermissions(new String[]{permission}, |
| 1121 | + 1); |
| 1122 | + // still no permission? |
| 1123 | + if (ContextCompat.checkSelfPermission(getActivity(), permission) |
| 1124 | + != PackageManager.PERMISSION_GRANTED) |
| 1125 | + throw new Exception("No write permission."); |
| 1126 | + } |
| 1127 | + |
| 1128 | + Map metadata = mParser.getPageMetaData(index); |
| 1129 | + String mime = (String) metadata.get(Parser.PAGEMETADATA_KEY_MIME); |
| 1130 | + File file = new File(folder, |
| 1131 | + (mFile.isDirectory()?mFile.getName():Utils.removeExtensionIfAny(mFile.getName()))+ |
| 1132 | + ".page"+pageNum+".jpg"); |
| 1133 | + if (mime!=null && mime.endsWith("/jpeg")) { |
| 1134 | + InputStream is = mParser.getPage(index); |
| 1135 | + Utils.copyToFile(is, file); |
| 1136 | + } |
| 1137 | + else { |
| 1138 | + bitmap = null; //BitmapFactory.decodeStream(is); |
| 1139 | + if (bitmap == null){ |
| 1140 | + MyTarget t = mTargets.get(index); |
| 1141 | + View v = t.mLayout.get(); |
| 1142 | + PageImageView piv = v.findViewById(R.id.pageImageView); |
| 1143 | + bitmap = ((BitmapDrawable)piv.getDrawable()).getBitmap(); |
| 1144 | + } |
| 1145 | + fOut = new FileOutputStream(file); |
| 1146 | + bitmap.compress(Bitmap.CompressFormat.JPEG, 95, fOut); |
| 1147 | + } |
| 1148 | + } catch (Exception e) { |
| 1149 | + throw new RuntimeException(e); |
| 1150 | + } finally { |
| 1151 | + Utils.close(fOut); |
| 1152 | + //Utils.close(bitmap); |
| 1153 | + } |
| 1154 | + } |
| 1155 | + |
| 1156 | + @Override |
| 1157 | + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { |
| 1158 | + if (requestCode == 1) { |
| 1159 | + if (permissions[0].equals(Manifest.permission.WRITE_EXTERNAL_STORAGE) |
| 1160 | + && grantResults[0] == PackageManager.PERMISSION_GRANTED) { |
| 1161 | + Log.d("granted","profit+1"); |
| 1162 | + } |
| 1163 | + } |
| 1164 | + } |
1047 | 1165 | }
|
0 commit comments