1 package org.coolreader.crengine;
2 
3 import android.graphics.Bitmap;
4 import android.graphics.Bitmap.Config;
5 import android.graphics.Canvas;
6 import android.graphics.ColorFilter;
7 import android.graphics.Paint;
8 import android.graphics.PixelFormat;
9 import android.graphics.Rect;
10 import android.graphics.drawable.BitmapDrawable;
11 import android.graphics.drawable.Drawable;
12 import android.os.Bundle;
13 import android.text.Editable;
14 import android.text.TextWatcher;
15 import android.util.DisplayMetrics;
16 import android.view.KeyEvent;
17 import android.view.LayoutInflater;
18 import android.view.View;
19 import android.view.ViewGroup;
20 import android.view.WindowManager;
21 import android.widget.EditText;
22 import android.widget.ImageButton;
23 import android.widget.ImageView;
24 import android.widget.LinearLayout;
25 import android.widget.RadioGroup;
26 import android.widget.RatingBar;
27 import android.widget.ScrollView;
28 import android.widget.TextView;
29 
30 import org.coolreader.CoolReader;
31 import org.coolreader.R;
32 import org.coolreader.genrescollection.GenresCollection;
33 
34 import java.util.ArrayList;
35 
36 public class BookInfoEditDialog extends BaseDialog {
37 	private CoolReader mActivity;
38 	private BookInfo mBookInfo;
39 	private FileInfo mParentDir;
40 	private LayoutInflater mInflater;
41 	private int mWindowSize;
42 	private boolean mIsRecentBooksItem;
BookInfoEditDialog(CoolReader activity, FileInfo baseDir, BookInfo book, boolean isRecentBooksItem)43 	public BookInfoEditDialog(CoolReader activity, FileInfo baseDir, BookInfo book, boolean isRecentBooksItem)
44 	{
45 		super(activity, null, false, false);
46 		this.mParentDir = baseDir;
47 		DisplayMetrics outMetrics = new DisplayMetrics();
48 		activity.getWindowManager().getDefaultDisplay().getMetrics(outMetrics);
49 		this.mWindowSize = Math.min(outMetrics.widthPixels, outMetrics.heightPixels);
50 		this.mActivity = activity;
51 		this.mBookInfo = book;
52 		this.mIsRecentBooksItem = isRecentBooksItem;
53 		if(getWindow().getAttributes().softInputMode==WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
54 		    getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN);
55 		}
56 	}
57 
58 	@Override
onCreate()59 	protected void onCreate() {
60         setCancelable(true);
61         setCanceledOnTouchOutside(true);
62 
63         super.onCreate();
64 		L.v("BookInfoEditDialog is created");
65 	}
66 
67 	class AuthorItem {
68 		EditText editor;
69 		String value;
70 	}
71 	class AuthorList {
72 		String oldValue;
73 		ArrayList<AuthorItem> authorItems = new ArrayList<>();
74 		ViewGroup parent;
adjustEditors(AuthorItem item, boolean empty)75 		void adjustEditors(AuthorItem item, boolean empty) {
76 			int index = authorItems.indexOf(item);
77 			if (empty) {
78 				// remove extra empty lines
79 				for (int i=authorItems.size() - 1; i >= 0; i--) {
80 					if (index == i)
81 						continue;
82 					AuthorItem v = authorItems.get(i);
83 					if (v.value.length() == 0) {
84 						parent.removeView(v.editor);
85 						authorItems.remove(i);
86 					}
87 				}
88 			} else {
89 				// add one empty line, if none
90 				boolean found = false;
91 				for (int i=authorItems.size() - 1; i >= 0; i--) {
92 					if (index == i)
93 						continue;
94 					AuthorItem v = authorItems.get(i);
95 					if (v.value.length() == 0) {
96 						found = true;
97 					}
98 				}
99 				if (!found) {
100 					add("");
101 				}
102 			}
103 		}
add(String value)104 		void add(String value) {
105 			final AuthorItem item = new AuthorItem();
106 			item.editor = new EditText(getContext());
107 			item.value = value != null ? value : "";
108 			item.editor.setText(value != null ? value : "");
109 			//item.editor.setFocusableInTouchMode(false);
110 			authorItems.add(item);
111 			parent.addView(item.editor);
112 			item.editor.addTextChangedListener(new TextWatcher() {
113 				@Override
114 				public void onTextChanged(CharSequence s, int start, int before, int count) {
115 					boolean oldValueEmpty = item.value == null || item.value.trim().length() == 0;
116 					item.value = String.valueOf(s).trim();
117 					boolean newValueEmpty = item.value == null || item.value.trim().length() == 0;
118 					if (oldValueEmpty != newValueEmpty) {
119 						adjustEditors(item, newValueEmpty);
120 					}
121 				}
122 
123 				@Override
124 				public void beforeTextChanged(CharSequence s, int start, int count,
125 						int after) {
126 				}
127 
128 				@Override
129 				public void afterTextChanged(Editable s) {
130 				}
131 			});
132 		}
AuthorList(ViewGroup parent, String authors)133 		public AuthorList(ViewGroup parent, String authors) {
134 			this.oldValue = authors;
135 			this.parent = parent;
136 	        parent.removeAllViews();
137 	        if (authors != null) {
138 		        String[] list = authors.split("\\|");
139 		        for (String author : list)
140 		        	add(author);
141 	        }
142 			add("");
143 		}
getAuthorsList()144 		public String getAuthorsList() {
145 			StringBuilder buf = new StringBuilder();
146 			for (AuthorItem item : authorItems) {
147 				String author = item.value != null ? item.value.trim() : "";
148 				if (author.length() > 0) {
149 					if (buf.length() > 0)
150 						buf.append("|");
151 					buf.append(author);
152 				}
153 			}
154 			return buf.toString();
155 		}
156 	}
157 
158 	static class ProgressDrawable extends Drawable {
159 
160 		int progress;
161 		int dx;
162 		int dy;
ProgressDrawable(int dx, int dy, int progress)163 		public ProgressDrawable(int dx, int dy, int progress) {
164 			this.dx = dx;
165 			this.dy = dy;
166 			this.progress = progress;
167 		}
168 
169 		@Override
getIntrinsicHeight()170 		public int getIntrinsicHeight() {
171 			return dy;
172 		}
173 
174 		@Override
getIntrinsicWidth()175 		public int getIntrinsicWidth() {
176 			return dx;
177 		}
178 
drawRect(Canvas canvas, Rect rc, Paint paint)179 		private void drawRect(Canvas canvas, Rect rc, Paint paint) {
180 			canvas.drawRect(rc.left, rc.top, rc.right, rc.top + 1, paint);
181 			canvas.drawRect(rc.left, rc.bottom - 1, rc.right, rc.bottom, paint);
182 			canvas.drawRect(rc.left, rc.top + 1, rc.left + 1, rc.bottom - 1, paint);
183 			canvas.drawRect(rc.right - 1, rc.top + 1, rc.right, rc.bottom - 1, paint);
184 		}
185 
shrink(Rect rc, int by)186 		private void shrink(Rect rc, int by) {
187 			rc.top += by;
188 			rc.bottom -= by;
189 			rc.left += by;
190 			rc.right -= by;
191 		}
192 
193 		@Override
draw(Canvas canvas)194 		public void draw(Canvas canvas) {
195 			Rect bounds = getBounds();
196 			if (progress >= 0 && progress <= 10000) {
197 				Rect rc = new Rect(bounds);
198 				Paint light = new Paint();
199 				light.setColor(0xC0FFFFFF);
200 				Paint dark = new Paint();
201 				dark.setColor(0xC0404040);
202 				drawRect(canvas, rc, light);
203 				shrink(rc, 1);
204 				drawRect(canvas, rc, dark);
205 				shrink(rc, 2);
206 				int x = rc.width() * progress / 10000 + rc.left;
207 				Rect rc1 = new Rect(rc);
208 				rc1.right = x;
209 				canvas.drawRect(rc1, light);
210 				Rect rc2 = new Rect(rc);
211 				rc2.left = x;
212 				canvas.drawRect(rc2, dark);
213 			}
214 		}
215 
216 		@Override
getOpacity()217 		public int getOpacity() {
218 			return PixelFormat.TRANSPARENT;
219 		}
220 
221 		@Override
setAlpha(int alpha)222 		public void setAlpha(int alpha) {
223 		}
224 
225 		@Override
setColorFilter(ColorFilter cf)226 		public void setColorFilter(ColorFilter cf) {
227 		}
228 	}
229 
230     LinearLayout mainView;
231 	ScrollView scrollView;
232     EditText edTitle;
233     EditText edSeriesName;
234     EditText edSeriesNumber;
235     TextView lblGenres;
236 	EditText edGenres;
237     EditText edDescription;
238 	AuthorList authors;
239 	RatingBar rbBookRating;
240 	RadioGroup rgState;
241 	@Override
onCreate(Bundle savedInstanceState)242 	protected void onCreate(Bundle savedInstanceState) {
243 		super.onCreate(savedInstanceState);
244 
245         mInflater = LayoutInflater.from(getContext());
246         FileInfo file = mBookInfo.getFileInfo();
247         mainView = (LinearLayout) mInflater.inflate(R.layout.book_info_edit_dialog, null);
248 
249         ImageButton btnBack = mainView.findViewById(R.id.base_dlg_btn_back);
250         btnBack.setOnClickListener(v -> onNegativeButtonClick());
251         ImageButton btnOpenBook = mainView.findViewById(R.id.btn_open_book);
252         btnOpenBook.setOnClickListener(v -> onPositiveButtonClick());
253         ImageButton btnDeleteBook = mainView.findViewById(R.id.book_delete);
254         btnDeleteBook.setOnClickListener(v -> {
255 			mActivity.askDeleteBook(mBookInfo.getFileInfo());
256 			dismiss();
257 		});
258 
259         scrollView = mainView.findViewById(R.id.book_scrollview);
260         edTitle = mainView.findViewById(R.id.book_title);
261         edSeriesName = mainView.findViewById(R.id.book_series_name);
262         edSeriesNumber = mainView.findViewById(R.id.book_series_number);
263         lblGenres = mainView.findViewById(R.id.lbl_book_genres);
264         edGenres = mainView.findViewById(R.id.book_genres);
265         edDescription = mainView.findViewById(R.id.book_description);
266 
267         rbBookRating = mainView.findViewById(R.id.book_rating);
268         rgState = mainView.findViewById(R.id.book_state);
269         int state = file.getReadingState();
270         int[] stateButtons = new int[] {R.id.book_state_new, R.id.book_state_toread, R.id.book_state_reading, R.id.book_state_finished};
271         rgState.check(state >= 0 && state < stateButtons.length ? stateButtons[state] : R.id.book_state_new);
272 
273         final ImageView image = mainView.findViewById(R.id.book_cover);
274         image.setOnClickListener(v -> {
275 			// open book
276 			onPositiveButtonClick();
277 		});
278         int w = mWindowSize * 4 / 10;
279         int h = w * 4 / 3;
280         image.setMinimumHeight(h);
281         image.setMaxHeight(h);
282         image.setMinimumWidth(w);
283         image.setMaxWidth(w);
284         Bitmap bmp = Bitmap.createBitmap(w, h, Config.RGB_565);
285         Services.getCoverpageManager().drawCoverpageFor(mActivity.getDB(), file, bmp, (file1, bitmap) -> {
286 			BitmapDrawable drawable = new BitmapDrawable(bitmap);
287 			image.setImageDrawable(drawable);
288 		});
289 
290         final ImageView progress = mainView.findViewById(R.id.book_progress);
291         int percent = -1;
292         Bookmark bmk = mBookInfo.getLastPosition();
293         if (bmk != null)
294         	percent = bmk.getPercent();
295         if (percent >= 0 && percent <= 10000) {
296         	progress.setMinimumWidth(w);
297         	progress.setMaxWidth(w);
298         	progress.setMinimumHeight(8);
299         	progress.setMaxHeight(8);
300         } else {
301         	progress.setMinimumWidth(w);
302         	progress.setMaxWidth(w);
303         	progress.setMinimumHeight(0);
304         	progress.setMaxHeight(0);
305         }
306         progress.setImageDrawable(new ProgressDrawable(w, 8, percent));
307 
308         edTitle.setText(file.title);
309         //edAuthor.setText(file.authors);
310         edSeriesName.setText(file.series);
311         if (file.series != null && file.series.trim().length() > 0 && file.seriesNumber > 0)
312         	edSeriesNumber.setText(String.valueOf(file.seriesNumber));
313         else
314             edSeriesNumber.setText("");
315         if (DocumentFormat.FB2 == file.format) {
316             if (file.genres != null && file.genres.length() > 0) {
317                 // genre codes separated by "|", see MainDB.READ_FILEINFO_FIELDS:
318                 StringBuilder genres = new StringBuilder();
319                 String[] parts = file.genres.split("\\|");
320                 for (String code : parts) {
321                     code = code.trim();
322                     if (code.length() > 0) {
323                         if (genres.length() > 0)
324                             genres.append("\n");
325                         genres.append(Services.getGenresCollection().translate(code));
326                     }
327                 }
328                 edGenres.setText(genres.toString());
329                 lblGenres.setVisibility(View.VISIBLE);
330                 edGenres.setVisibility(View.VISIBLE);
331             }
332         }
333         if (file.description != null && file.description.length() > 0) {
334             edDescription.setText(file.description);
335             edDescription.setVisibility(View.VISIBLE);
336         }
337         LinearLayout llBookAuthorsList = mainView.findViewById(R.id.book_authors_list);
338         authors = new AuthorList(llBookAuthorsList, file.authors);
339         rbBookRating.setRating(file.getRate());
340 
341     	ImageButton btnRemoveRecent = mainView.findViewById(R.id.book_recent_delete);
342     	ImageButton btnOpenFolder = mainView.findViewById(R.id.book_folder_open);
343         if (mIsRecentBooksItem) {
344         	btnRemoveRecent.setOnClickListener(v -> {
345 				mActivity.askDeleteRecent(mBookInfo.getFileInfo());
346 				dismiss();
347 			});
348         	btnOpenFolder.setOnClickListener(v -> {
349 				mActivity.showDirectory(mBookInfo.getFileInfo());
350 				dismiss();
351 			});
352         } else {
353         	ViewGroup parent = ((ViewGroup)btnRemoveRecent.getParent());
354         	parent.removeView(btnRemoveRecent);
355         	parent.removeView(btnOpenFolder);
356         }
357 
358         setView(mainView);
359 	}
360 
save()361 	private void save() {
362 		L.d("BookInfoEditDialog.save()");
363 
364         FileInfo file = mBookInfo.getFileInfo();
365         boolean modified = false;
366         modified = file.setTitle(edTitle.getText().toString().trim()) || modified;
367         modified = file.setAuthors(authors.getAuthorsList()) || modified;
368         modified = file.setSeriesName(edSeriesName.getText().toString().trim()) || modified;
369         int number = 0;
370         if (file.series != null && file.series.length() > 0) {
371     	    String numberString = edSeriesNumber.getText().toString().trim();
372     	    try {
373     	    	number = Integer.parseInt(numberString);
374     	    } catch (NumberFormatException e) {
375     	    	// ignore
376     	    }
377         }
378         modified = file.setSeriesNumber(number) || modified;
379         int rate =(int)(rbBookRating.getRating() + 0.5f);
380         if (rate >=0 && rate <= 5)
381             modified = file.setRate(rate) || modified;
382         int state = FileInfo.STATE_NEW;
383 		int currentStateId = rgState.getCheckedRadioButtonId();
384 		if (currentStateId == R.id.book_state_new)
385 			state = FileInfo.STATE_NEW;
386 		else if (currentStateId == R.id.book_state_toread)
387 			state = FileInfo.STATE_TO_READ;
388 		else if (currentStateId == R.id.book_state_reading)
389 			state = FileInfo.STATE_READING;
390 		else if (currentStateId == R.id.book_state_finished)
391 			state = FileInfo.STATE_FINISHED;
392         modified = file.setReadingState(state) || modified;
393         if (modified) {
394         	mActivity.getDB().saveBookInfo(mBookInfo);
395         	mActivity.getDB().flush();
396         	BookInfo bi = Services.getHistory().getBookInfo(file);
397         	if (bi != null)
398         		bi.getFileInfo().setFileProperties(file);
399         	mParentDir.setFile(file);
400         	mActivity.directoryUpdated(mParentDir, file);
401         }
402 	}
403 
404 	@Override
onPositiveButtonClick()405 	protected void onPositiveButtonClick() {
406 		save();
407 		mActivity.loadDocument(mBookInfo.getFileInfo(), null, () -> {
408 			// error occured
409 			// ignoring
410 		}, true);
411 		super.onPositiveButtonClick();
412 	}
413 
414 	@Override
onNegativeButtonClick()415 	protected void onNegativeButtonClick() {
416 		save();
417 		super.onNegativeButtonClick();
418 	}
419 
420 	@Override
onKeyDown(int keyCode, KeyEvent event)421 	public boolean onKeyDown(int keyCode, KeyEvent event) {
422 		if (KeyEvent.KEYCODE_PAGE_DOWN == keyCode) {
423 			scrollView.pageScroll(View.FOCUS_DOWN);
424 			return true;
425 		} else if (KeyEvent.KEYCODE_PAGE_UP == keyCode) {
426 			scrollView.pageScroll(View.FOCUS_UP);
427 			return true;
428 		} else if (KeyEvent.KEYCODE_BACK == keyCode) {
429 			return true;
430 		}
431 		return super.onKeyDown(keyCode, event);
432 	}
433 
434 	@Override
onKeyUp(int keyCode, KeyEvent event)435 	public boolean onKeyUp(int keyCode, KeyEvent event) {
436 		if (KeyEvent.KEYCODE_BACK == keyCode) {
437 			dismiss();
438 			return true;
439 		}
440 		return super.onKeyDown(keyCode, event);
441 	}
442 
443 }
444