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