1 package org.coolreader.crengine; 2 3 import android.annotation.SuppressLint; 4 import android.annotation.TargetApi; 5 import android.app.Activity; 6 import android.app.AlertDialog; 7 import android.content.Context; 8 import android.content.Intent; 9 import android.content.SharedPreferences; 10 import android.content.pm.ActivityInfo; 11 import android.content.pm.PackageInfo; 12 import android.content.pm.PackageManager; 13 import android.content.pm.PackageManager.NameNotFoundException; 14 import android.content.res.Configuration; 15 import android.content.res.Resources; 16 import android.content.res.TypedArray; 17 import android.os.Build; 18 import android.os.Bundle; 19 import android.os.PowerManager; 20 import android.text.ClipboardManager; 21 import android.util.DisplayMetrics; 22 import android.view.Display; 23 import android.view.Gravity; 24 import android.view.KeyEvent; 25 import android.view.MenuItem; 26 import android.view.View; 27 import android.view.ViewConfiguration; 28 import android.view.Window; 29 import android.view.WindowManager; 30 import android.view.WindowManager.LayoutParams; 31 import android.widget.Toast; 32 33 import org.coolreader.Dictionaries; 34 import org.coolreader.Dictionaries.DictInfo; 35 import org.coolreader.R; 36 import org.coolreader.db.CRDBService; 37 import org.coolreader.db.CRDBServiceAccessor; 38 import org.coolreader.genrescollection.GenresCollection; 39 40 import java.io.File; 41 import java.io.FileInputStream; 42 import java.io.FileOutputStream; 43 import java.lang.reflect.Field; 44 import java.lang.reflect.InvocationTargetException; 45 import java.lang.reflect.Method; 46 import java.util.ArrayList; 47 import java.util.Arrays; 48 import java.util.Collections; 49 import java.util.Iterator; 50 import java.util.List; 51 import java.util.Locale; 52 import java.util.Map.Entry; 53 import java.util.Set; 54 55 @SuppressLint("Registered") 56 public class BaseActivity extends Activity implements Settings { 57 58 private static final Logger log = L.create("ba"); 59 private View mDecorView; 60 61 private CRDBServiceAccessor mCRDBService; 62 protected Dictionaries mDictionaries; 63 unbindCRDBService()64 protected void unbindCRDBService() { 65 if (mCRDBService != null) { 66 mCRDBService.unbind(); 67 mCRDBService = null; 68 } 69 } 70 bindCRDBService()71 protected void bindCRDBService() { 72 if (mCRDBService == null) { 73 mCRDBService = new CRDBServiceAccessor(this, Engine.getInstance(this).getPathCorrector()); 74 } 75 mCRDBService.bind(null); 76 } 77 78 /** 79 * Wait until database is bound. 80 * 81 * @param readyCallback to be called after DB is ready 82 */ waitForCRDBService(Runnable readyCallback)83 public synchronized void waitForCRDBService(Runnable readyCallback) { 84 if (mCRDBService == null) { 85 mCRDBService = new CRDBServiceAccessor(this, Engine.getInstance(this).getPathCorrector()); 86 } 87 mCRDBService.bind(readyCallback); 88 } 89 getDBService()90 public CRDBServiceAccessor getDBService() { 91 return mCRDBService; 92 } 93 getDB()94 public CRDBService.LocalBinder getDB() { 95 return mCRDBService != null ? mCRDBService.get() : null; 96 } 97 settings()98 public Properties settings() { 99 return mSettingsManager.mSettings; 100 } 101 102 private SettingsManager mSettingsManager; 103 104 private EinkScreen mEinkScreen; 105 getEinkScreen()106 public EinkScreen getEinkScreen() { 107 return mEinkScreen; 108 } 109 startServices()110 protected void startServices() { 111 if (DeviceInfo.EINK_NOOK) 112 mEinkScreen = new EinkScreenNook(); 113 else if (DeviceInfo.EINK_TOLINO) 114 mEinkScreen = new EinkScreenTolino(); 115 else if (DeviceInfo.EINK_ONYX) 116 mEinkScreen = new EinkScreenOnyx(); 117 else 118 mEinkScreen = new EinkScreenDummy(); 119 120 DisplayMetrics dm = new DisplayMetrics(); 121 getWindowManager().getDefaultDisplay().getMetrics(dm); 122 123 try { 124 Field fld = dm.getClass().getField("densityDpi"); 125 if (fld != null) { 126 Object v = fld.get(dm); 127 if (v != null && v instanceof Integer) { 128 densityDpi = ((Integer) v).intValue(); 129 log.i("Screen density detected: " + densityDpi + "DPI"); 130 } 131 } 132 } catch (Exception e) { 133 log.e("Cannot find field densityDpi, using default value"); 134 } 135 float widthInches = (float)dm.widthPixels / (float)densityDpi; 136 float heightInches = (float)dm.heightPixels / (float)densityDpi; 137 diagonalInches = (float) Math.sqrt(widthInches * widthInches + heightInches * heightInches); 138 log.i("diagonal=" + diagonalInches + " isSmartphone=" + isSmartphone()); 139 140 // create settings 141 mSettingsManager = new SettingsManager(this); 142 // create rest of settings 143 Services.startServices(this); 144 } 145 146 @SuppressLint("NewApi") 147 @Override onWindowFocusChanged(boolean hasFocus)148 public void onWindowFocusChanged(boolean hasFocus) { 149 super.onWindowFocusChanged(hasFocus); 150 if (hasFocus) 151 setSystemUiVisibility(); 152 } 153 154 /** 155 * Called when the activity is first created. 156 */ 157 @Override onCreate(Bundle savedInstanceState)158 protected void onCreate(Bundle savedInstanceState) { 159 log.i("BaseActivity.onCreate() entered"); 160 requestWindowFeature(Window.FEATURE_NO_TITLE); 161 mDecorView = getWindow().getDecorView(); 162 163 super.onCreate(savedInstanceState); 164 165 mDictionaries = new Dictionaries(this); 166 167 try { 168 PackageInfo pi = getPackageManager().getPackageInfo(getPackageName(), 0); 169 mVersion = pi.versionName; 170 } catch (NameNotFoundException e) { 171 // ignore 172 } 173 log.i("CoolReader version : " + getVersion()); 174 //log.i("CoolReader.window=" + getWindow()); 175 if (!DeviceInfo.EINK_SCREEN) { 176 WindowManager.LayoutParams lp = new WindowManager.LayoutParams(); 177 lp.alpha = 1.0f; 178 lp.dimAmount = 0.0f; 179 lp.format = DeviceInfo.PIXEL_FORMAT; 180 lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.CENTER_VERTICAL; 181 lp.horizontalMargin = 0; 182 lp.verticalMargin = 0; 183 lp.windowAnimations = 0; 184 lp.layoutAnimationParameters = null; 185 lp.memoryType = WindowManager.LayoutParams.MEMORY_TYPE_NORMAL; 186 getWindow().setAttributes(lp); 187 } 188 189 // load settings 190 Properties props = settings(); 191 String theme = props.getProperty(ReaderView.PROP_APP_THEME, DeviceInfo.FORCE_HC_THEME ? "HICONTRAST1" : "LIGHT"); 192 String lang = props.getProperty(ReaderView.PROP_APP_LOCALE, Lang.DEFAULT.code); 193 setLanguage(lang); 194 setCurrentTheme(theme); 195 196 setScreenBacklightDuration(props.getInt(ReaderView.PROP_APP_SCREEN_BACKLIGHT_LOCK, 3)); 197 198 setFullscreen(props.getBool(ReaderView.PROP_APP_FULLSCREEN, DeviceInfo.EINK_SCREEN)); 199 int orientation = props.getInt(ReaderView.PROP_APP_SCREEN_ORIENTATION, 0); //(DeviceInfo.EINK_SCREEN?0:4) 200 if (orientation < 0 || orientation > 5) 201 orientation = 5; 202 setScreenOrientation(orientation); 203 int backlight = props.getInt(ReaderView.PROP_APP_SCREEN_BACKLIGHT, -1); 204 if (backlight < -1 || backlight > DeviceInfo.MAX_SCREEN_BRIGHTNESS_VALUE) 205 backlight = -1; 206 if (!DeviceInfo.EINK_SCREEN) 207 setScreenBacklightLevel(backlight); 208 209 bindCRDBService(); 210 } 211 212 protected BaseDialog currentDialog; onDialogCreated(BaseDialog dlg)213 public void onDialogCreated(BaseDialog dlg) { 214 currentDialog = dlg; 215 } onDialogClosed(BaseDialog dlg)216 public void onDialogClosed(BaseDialog dlg) { 217 if (currentDialog == dlg) { 218 currentDialog = null; 219 } 220 } getCurrentDialog()221 public BaseDialog getCurrentDialog() { 222 return currentDialog; 223 } isDialogActive()224 public boolean isDialogActive() { 225 return currentDialog != null; 226 } 227 228 @Override onDestroy()229 protected void onDestroy() { 230 super.onDestroy(); 231 unbindCRDBService(); 232 } 233 234 @Override onStart()235 protected void onStart() { 236 super.onStart(); 237 238 // Properties props = settings().get(); 239 // String theme = props.getProperty(ReaderView.PROP_APP_THEME, DeviceInfo.FORCE_HC_THEME ? "WHITE" : "LIGHT"); 240 // setCurrentTheme(theme); 241 242 mIsStarted = true; 243 mPaused = false; 244 onUserActivity(); 245 } 246 247 @Override onStop()248 protected void onStop() { 249 mIsStarted = false; 250 super.onStop(); 251 } 252 253 @Override onPause()254 protected void onPause() { 255 log.i("CoolReader.onPause() : saving reader state"); 256 mIsStarted = false; 257 mPaused = true; 258 releaseBacklightControl(); 259 super.onPause(); 260 } 261 262 protected static String PREF_FILE = "CR3LastBook"; 263 protected static String PREF_LAST_BOOK = "LastBook"; 264 protected static String PREF_LAST_LOCATION = "LastLocation"; 265 protected static String PREF_LAST_NOTIFICATION_MASK = "LastNoticeMask"; 266 protected static String PREF_EXT_DATADIR_CREATETIME = "ExtDataDirCreateTime"; 267 protected static String PREF_LAST_LOGCAT = "LastLogcat"; 268 269 @Override onResume()270 protected void onResume() { 271 log.i("CoolReader.onResume()"); 272 mPaused = false; 273 mIsStarted = true; 274 backlightControl.onUserActivity(); 275 super.onResume(); 276 } 277 278 private boolean mIsStarted = false; 279 private boolean mPaused = false; 280 isStarted()281 public boolean isStarted() { 282 return mIsStarted; 283 } 284 285 private String mVersion = "3.1"; 286 getVersion()287 public String getVersion() { 288 return mVersion; 289 } 290 rebaseSettings()291 public void rebaseSettings() { 292 // if rootFs changed (for example, when external storage permission firstly granted) open config from new root 293 Properties oldProps = mSettingsManager.mSettings; 294 mSettingsManager.rebaseSettings(); 295 onSettingsChanged(settings(), oldProps); 296 } 297 loadSettings(int profile)298 public Properties loadSettings(int profile) { 299 return mSettingsManager.loadSettings(profile); 300 } 301 saveSettings(int profile, Properties settings)302 public void saveSettings(int profile, Properties settings) { 303 mSettingsManager.saveSettings(profile, settings); 304 } 305 saveSettings(File f, Properties settings)306 public void saveSettings(File f, Properties settings) { 307 mSettingsManager.saveSettings(f, settings); 308 } 309 getPalmTipPixels()310 public int getPalmTipPixels() { 311 return densityDpi / 3; // 1/3" 312 } 313 getDensityDpi()314 public int getDensityDpi() { 315 return densityDpi; 316 } 317 getDensityFactor()318 public float getDensityFactor() { 319 return ((float) densityDpi) / 160f; 320 } 321 getDiagonalInches()322 public float getDiagonalInches() { 323 return diagonalInches; 324 } 325 getSettingsFile(int profile)326 public String getSettingsFile(int profile) { 327 File file = mSettingsManager.getSettingsFile(profile); 328 if (file.exists()) 329 return file.getAbsolutePath(); 330 return null; 331 } 332 isSmartphone()333 public boolean isSmartphone() { 334 return diagonalInches <= 6.8; //5.8; 335 } 336 337 private int densityDpi = 160; 338 private float diagonalInches = 5; 339 340 341 private InterfaceTheme currentTheme = null; 342 getCurrentTheme()343 public InterfaceTheme getCurrentTheme() { 344 return currentTheme; 345 } 346 setCurrentTheme(String themeCode)347 public void setCurrentTheme(String themeCode) { 348 InterfaceTheme theme = InterfaceTheme.findByCode(themeCode); 349 if (null == theme) 350 theme = DeviceInfo.FORCE_HC_THEME ? InterfaceTheme.HICONTRAST1 : InterfaceTheme.LIGHT; 351 if (currentTheme != theme) { 352 setCurrentTheme(theme); 353 } 354 } 355 356 private int preferredItemHeight = 36; 357 getPreferredItemHeight()358 public int getPreferredItemHeight() { 359 return preferredItemHeight; 360 } 361 362 private int minFontSize = 9; 363 getMinFontSize()364 public int getMinFontSize() { 365 return minFontSize; 366 } 367 368 private int maxFontSize = 90; 369 getMaxFontSize()370 public int getMaxFontSize() { 371 return maxFontSize; 372 } 373 updateBackground()374 public void updateBackground() { 375 TypedArray a = getTheme().obtainStyledAttributes(new int[]{android.R.attr.windowBackground, android.R.attr.background, android.R.attr.textColor, android.R.attr.colorBackground, android.R.attr.colorForeground, android.R.attr.listPreferredItemHeight}); 376 int bgRes = a.getResourceId(0, 0); 377 //int clText = a.getColor(1, 0); 378 int clBackground = a.getColor(2, 0); 379 //int clForeground = a.getColor(3, 0); 380 preferredItemHeight = densityDpi / 3; //a.getDimensionPixelSize(5, 36); 381 //View contentView = getContentView(); 382 if (contentView != null) { 383 if (bgRes != 0) { 384 //Drawable d = getResources().getDrawable(bgRes); 385 //log.v("Setting background resource " + d.getIntrinsicWidth() + "x" + d.getIntrinsicHeight()); 386 //contentView.setBackgroundResource(null); 387 contentView.setBackgroundResource(bgRes); 388 //getWindow().setBackgroundDrawableResource(bgRes);//Drawable(d); 389 //getWindow().setBackgroundDrawable(d); 390 } else if (clBackground != 0) { 391 contentView.setBackgroundColor(clBackground); 392 //getWindow().setBackgroundDrawable(Utils.solidColorDrawable(clBackground)); 393 } 394 } else { 395 // if (bgRes != 0) 396 // getWindow().setBackgroundDrawableResource(bgRes); 397 // else if (clBackground != 0) 398 // getWindow().setBackgroundDrawable(Utils.solidColorDrawable(clBackground)); 399 } 400 a.recycle(); 401 Display display = getWindowManager().getDefaultDisplay(); 402 int sz = display.getWidth(); 403 if (sz > display.getHeight()) 404 sz = display.getHeight(); 405 minFontSize = sz / 45; 406 maxFontSize = sz / 8; 407 if (maxFontSize > 340) 408 maxFontSize = 340; 409 if (minFontSize < 9) 410 minFontSize = 9; 411 } 412 413 @SuppressLint("ResourceType") updateActionsIcons()414 public void updateActionsIcons() { 415 int[] attrs = {R.attr.cr3_button_prev_drawable, R.attr.cr3_button_next_drawable, R.attr.cr3_viewer_toc_drawable, 416 R.attr.cr3_viewer_find_drawable, R.attr.cr3_viewer_settings_drawable, R.attr.cr3_button_bookmarks_drawable, 417 R.attr.cr3_browser_folder_root_drawable, R.attr.cr3_option_night_drawable, R.attr.cr3_option_touch_drawable, 418 R.attr.cr3_button_go_page_drawable, R.attr.cr3_button_go_percent_drawable, R.attr.cr3_browser_folder_drawable, 419 R.attr.cr3_button_tts_drawable, R.attr.cr3_browser_folder_recent_drawable, R.attr.cr3_button_scroll_go_drawable, 420 R.attr.cr3_btn_books_swap_drawable, R.attr.cr3_logo_button_drawable, R.attr.cr3_viewer_exit_drawable, 421 R.attr.cr3_button_book_open_drawable, R.attr.cr3_browser_folder_current_book_drawable, R.attr.cr3_browser_folder_opds_drawable, 422 R.attr.google_drive_drawable, R.attr.cr3_button_log_drawable, R.attr.cr3_button_light_drawable }; 423 TypedArray a = getTheme().obtainStyledAttributes(attrs); 424 int btnPrevDrawableRes = a.getResourceId(0, 0); 425 int btnNextDrawableRes = a.getResourceId(1, 0); 426 int viewerTocDrawableRes = a.getResourceId(2, 0); 427 int viewerFindDrawableRes = a.getResourceId(3, 0); 428 int viewerSettingDrawableRes = a.getResourceId(4, 0); 429 int btnBookmarksDrawableRes = a.getResourceId(5, 0); 430 int brFolderRootDrawableRes = a.getResourceId(6, 0); 431 int optionNightDrawableRes = a.getResourceId(7, 0); 432 int optionTouchDrawableRes = a.getResourceId(8, 0); 433 int btnGoPageDrawableRes = a.getResourceId(9, 0); 434 int btnGoPercentDrawableRes = a.getResourceId(10, 0); 435 int brFolderDrawableRes = a.getResourceId(11, 0); 436 int btnTtsDrawableRes = a.getResourceId(12, 0); 437 int brFolderRecentDrawableRes = a.getResourceId(13, 0); 438 int btnScrollGoDrawableRes = a.getResourceId(14, 0); 439 int btnBooksSwapDrawableRes = a.getResourceId(15, 0); 440 int logoBtnDrawableRes = a.getResourceId(16, 0); 441 int viewerExitDrawableRes = a.getResourceId(17, 0); 442 int btnBookOpenDrawableRes = a.getResourceId(18, 0); 443 int brFolderCurrBookDrawableRes = a.getResourceId(19, 0); 444 int brFolderOpdsDrawableRes = a.getResourceId(20, 0); 445 int googleDriveDrawableRes = a.getResourceId(21, 0); 446 int btnLogDrawableRes = a.getResourceId(22, 0); 447 int btnLightDrawableRes = a.getResourceId(23, 0); 448 a.recycle(); 449 if (btnPrevDrawableRes != 0) { 450 ReaderAction.GO_BACK.setIconId(btnPrevDrawableRes); 451 ReaderAction.FILE_BROWSER_UP.setIconId(btnPrevDrawableRes); 452 } 453 if (btnNextDrawableRes != 0) 454 ReaderAction.GO_FORWARD.setIconId(btnNextDrawableRes); 455 if (viewerTocDrawableRes != 0) 456 ReaderAction.TOC.setIconId(viewerTocDrawableRes); 457 if (viewerFindDrawableRes != 0) 458 ReaderAction.SEARCH.setIconId(viewerFindDrawableRes); 459 if (viewerSettingDrawableRes != 0) 460 ReaderAction.OPTIONS.setIconId(viewerSettingDrawableRes); 461 if (btnBookmarksDrawableRes != 0) 462 ReaderAction.BOOKMARKS.setIconId(btnBookmarksDrawableRes); 463 if (brFolderRootDrawableRes != 0) 464 ReaderAction.FILE_BROWSER_ROOT.setIconId(brFolderRootDrawableRes); 465 if (optionNightDrawableRes != 0) 466 ReaderAction.TOGGLE_DAY_NIGHT.setIconId(optionNightDrawableRes); 467 if (optionTouchDrawableRes != 0) 468 ReaderAction.TOGGLE_SELECTION_MODE.setIconId(optionTouchDrawableRes); 469 if (btnGoPageDrawableRes != 0) 470 ReaderAction.GO_PAGE.setIconId(btnGoPageDrawableRes); 471 if (btnGoPercentDrawableRes != 0) 472 ReaderAction.GO_PERCENT.setIconId(btnGoPercentDrawableRes); 473 if (brFolderDrawableRes != 0) 474 ReaderAction.FILE_BROWSER.setIconId(brFolderDrawableRes); 475 if (btnTtsDrawableRes != 0) 476 ReaderAction.TTS_PLAY.setIconId(btnTtsDrawableRes); 477 if (brFolderRecentDrawableRes != 0) 478 ReaderAction.RECENT_BOOKS.setIconId(brFolderRecentDrawableRes); 479 if (btnScrollGoDrawableRes != 0) 480 ReaderAction.TOGGLE_AUTOSCROLL.setIconId(btnScrollGoDrawableRes); 481 if (btnBooksSwapDrawableRes != 0) 482 ReaderAction.OPEN_PREVIOUS_BOOK.setIconId(btnBooksSwapDrawableRes); 483 if (logoBtnDrawableRes != 0) 484 ReaderAction.ABOUT.setIconId(logoBtnDrawableRes); 485 if (viewerExitDrawableRes != 0) 486 ReaderAction.EXIT.setIconId(viewerExitDrawableRes); 487 if (btnBookOpenDrawableRes != 0) 488 ReaderAction.CURRENT_BOOK.setIconId(btnBookOpenDrawableRes); 489 if (brFolderCurrBookDrawableRes != 0) 490 ReaderAction.CURRENT_BOOK_DIRECTORY.setIconId(brFolderCurrBookDrawableRes); 491 if (brFolderOpdsDrawableRes != 0) 492 ReaderAction.OPDS_CATALOGS.setIconId(brFolderOpdsDrawableRes); 493 if (googleDriveDrawableRes != 0) { 494 ReaderAction.GDRIVE_SYNCTO.setIconId(googleDriveDrawableRes); 495 ReaderAction.GDRIVE_SYNCFROM.setIconId(googleDriveDrawableRes); 496 } 497 if (btnLogDrawableRes != 0) 498 ReaderAction.SAVE_LOGCAT.setIconId(btnLogDrawableRes); 499 if (btnLightDrawableRes != 0) 500 ReaderAction.SHOW_SYSTEM_BACKLIGHT_DIALOG.setIconId(btnLightDrawableRes); 501 } 502 setCurrentTheme(InterfaceTheme theme)503 public void setCurrentTheme(InterfaceTheme theme) { 504 log.i("setCurrentTheme(" + theme + ")"); 505 currentTheme = theme; 506 getApplication().setTheme(theme.getThemeId()); 507 setTheme(theme.getThemeId()); 508 updateBackground(); 509 updateActionsIcons(); 510 } 511 512 int screenOrientation = ActivityInfo.SCREEN_ORIENTATION_USER; 513 applyScreenOrientation(Window wnd)514 public void applyScreenOrientation(Window wnd) { 515 if (wnd != null) { 516 WindowManager.LayoutParams attrs = wnd.getAttributes(); 517 attrs.screenOrientation = screenOrientation; 518 wnd.setAttributes(attrs); 519 if (DeviceInfo.EINK_SCREEN) { 520 //TODO: 521 //EinkScreen.ResetController(mReaderView); 522 } 523 } 524 } 525 getScreenOrientation()526 public int getScreenOrientation() { 527 switch (screenOrientation) { 528 case ActivityInfo.SCREEN_ORIENTATION_PORTRAIT: 529 return 0; 530 case ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE: 531 return 1; 532 case ActivityInfo_SCREEN_ORIENTATION_REVERSE_PORTRAIT: 533 return 2; 534 case ActivityInfo_SCREEN_ORIENTATION_REVERSE_LANDSCAPE: 535 return 3; 536 case ActivityInfo.SCREEN_ORIENTATION_USER: 537 return 5; 538 default: 539 return orientationFromSensor; 540 } 541 } 542 543 @TargetApi(Build.VERSION_CODES.GINGERBREAD) isReverseLandscape()544 private boolean isReverseLandscape() { 545 return screenOrientation == ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE; 546 } 547 isLandscape()548 public boolean isLandscape() { 549 if (screenOrientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) 550 return true; 551 if (DeviceInfo.getSDKLevel() >= 9 && isReverseLandscape()) 552 return true; 553 return false; 554 } 555 556 // support pre API LEVEL 9 557 final static public int ActivityInfo_SCREEN_ORIENTATION_SENSOR_PORTRAIT = 7; 558 final static public int ActivityInfo_SCREEN_ORIENTATION_SENSOR_LANDSCAPE = 6; 559 final static public int ActivityInfo_SCREEN_ORIENTATION_REVERSE_PORTRAIT = 9; 560 final static public int ActivityInfo_SCREEN_ORIENTATION_REVERSE_LANDSCAPE = 8; 561 final static public int ActivityInfo_SCREEN_ORIENTATION_FULL_SENSOR = 10; 562 setScreenOrientation(int angle)563 public void setScreenOrientation(int angle) { 564 int newOrientation = screenOrientation; 565 boolean level9 = DeviceInfo.getSDKLevel() >= 9; 566 switch (angle) { 567 case 0: 568 newOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; // level9 ? ActivityInfo_SCREEN_ORIENTATION_SENSOR_PORTRAIT : ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; 569 break; 570 case 1: 571 newOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; // level9 ? ActivityInfo_SCREEN_ORIENTATION_SENSOR_LANDSCAPE : ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; 572 break; 573 case 2: 574 newOrientation = level9 ? ActivityInfo_SCREEN_ORIENTATION_REVERSE_PORTRAIT : ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; 575 break; 576 case 3: 577 newOrientation = level9 ? ActivityInfo_SCREEN_ORIENTATION_REVERSE_LANDSCAPE : ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; 578 break; 579 case 4: 580 newOrientation = level9 ? ActivityInfo_SCREEN_ORIENTATION_FULL_SENSOR : ActivityInfo.SCREEN_ORIENTATION_SENSOR; 581 break; 582 case 5: 583 newOrientation = ActivityInfo.SCREEN_ORIENTATION_USER; 584 break; 585 } 586 if (newOrientation != screenOrientation) { 587 log.d("setScreenOrientation(" + angle + ")"); 588 screenOrientation = newOrientation; 589 setRequestedOrientation(screenOrientation); 590 applyScreenOrientation(getWindow()); 591 } 592 } 593 594 595 private int orientationFromSensor = 0; 596 getOrientationFromSensor()597 public int getOrientationFromSensor() { 598 return orientationFromSensor; 599 } 600 601 @Override onConfigurationChanged(Configuration newConfig)602 public void onConfigurationChanged(Configuration newConfig) { 603 // pass 604 orientationFromSensor = newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE ? 1 : 0; 605 //final int orientation = newConfig.orientation==Configuration.ORIENTATION_LANDSCAPE ? ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE : ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; 606 // if ( orientation!=screenOrientation ) { 607 // log.d("Screen orientation has been changed: ask for change"); 608 // AlertDialog.Builder dlg = new AlertDialog.Builder(this); 609 // dlg.setTitle(R.string.win_title_screen_orientation_change_apply);//R.string.win_title_options_apply); 610 // dlg.setPositiveButton(R.string.dlg_button_ok, new OnClickListener() { 611 // public void onClick(DialogInterface arg0, int arg1) { 612 // //onPositiveButtonClick(); 613 // Properties oldSettings = mReaderView.getSettings(); 614 // Properties newSettings = new Properties(oldSettings); 615 // newSettings.setInt(ReaderView.PROP_APP_SCREEN_ORIENTATION, orientation==ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE ? 1 : 0); 616 // mReaderView.setSettings(newSettings, oldSettings); 617 // } 618 // }); 619 // dlg.setNegativeButton(R.string.dlg_button_cancel, new OnClickListener() { 620 // public void onClick(DialogInterface arg0, int arg1) { 621 // //onNegativeButtonClick(); 622 // } 623 // }); 624 // } 625 super.onConfigurationChanged(newConfig); 626 } 627 628 private boolean mFullscreen = false; 629 isFullscreen()630 public boolean isFullscreen() { 631 return mFullscreen; 632 } 633 applyFullscreen(Window wnd)634 public void applyFullscreen(Window wnd) { 635 if (mFullscreen) { 636 //mActivity.getWindow().requestFeature(Window.) 637 wnd.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, 638 WindowManager.LayoutParams.FLAG_FULLSCREEN); 639 } else { 640 wnd.setFlags(0, WindowManager.LayoutParams.FLAG_FULLSCREEN); 641 } 642 // enforce new window ui visibility flags 643 lastSystemUiVisibility = -1; 644 setSystemUiVisibility(); 645 } 646 setFullscreen(boolean fullscreen)647 public void setFullscreen(boolean fullscreen) { 648 if (mFullscreen != fullscreen) { 649 mFullscreen = fullscreen; 650 applyFullscreen(getWindow()); 651 } 652 } 653 654 655 // public void simulateTouch() { 656 // // Obtain MotionEvent object 657 // long downTime = SystemClock.uptimeMillis(); 658 // long eventTime = SystemClock.uptimeMillis() + 10; 659 // float x = 0.0f; 660 // float y = 0.0f; 661 // // List of meta states found here: developer.android.com/reference/android/view/KeyEvent.html#getMetaState() 662 // int metaState = 0; 663 // MotionEvent motionEvent = MotionEvent.obtain( 664 // downTime, 665 // downTime, 666 // MotionEvent.ACTION_DOWN, 667 // x, 668 // y, 669 // metaState 670 // ); 671 // MotionEvent motionEvent2 = MotionEvent.obtain( 672 // downTime, 673 // eventTime, 674 // MotionEvent.ACTION_UP, 675 // x, 676 // y, 677 // metaState 678 // ); 679 // //motionEvent.setEdgeFlags(flags) 680 // // Dispatch touch event to view 681 // //new Handler().dispatchMessage(motionEvent); 682 // if (getContentView() != null) { 683 // getContentView().dispatchTouchEvent(motionEvent); 684 // getContentView().dispatchTouchEvent(motionEvent2); 685 // } 686 // 687 // } 688 wantHideNavbarInFullscreen()689 protected boolean wantHideNavbarInFullscreen() { 690 return false; 691 } 692 693 @TargetApi(Build.VERSION_CODES.HONEYCOMB) 694 @SuppressLint("NewApi") setSystemUiVisibility()695 public boolean setSystemUiVisibility() { 696 if (DeviceInfo.getSDKLevel() >= DeviceInfo.HONEYCOMB) { 697 int flags = 0; 698 if (getKeyBacklight() == 0) { 699 if (DeviceInfo.getSDKLevel() < 19) 700 // backlight of hardware buttons enabled/disabled 701 // in updateButtonsBrightness(), turnOffKeyBacklight(), turnOnKeyBacklight() 702 // entry point onUserActivity(). 703 // This flag just shade software navigation bar and system UI 704 flags |= View.SYSTEM_UI_FLAG_LOW_PROFILE; 705 } 706 if (isFullscreen() /*&& wantHideNavbarInFullscreen() && isSmartphone()*/) { 707 if (DeviceInfo.getSDKLevel() >= 19) 708 // Flag View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY added in API 19 709 // without this flag SYSTEM_UI_FLAG_HIDE_NAVIGATION will be force cleared by the system on any user interaction, 710 // and SYSTEM_UI_FLAG_FULLSCREEN will be force-cleared by the system if the user swipes from the top of the screen. 711 // So use this flags only on API >= 19 712 flags |= View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | 713 View.SYSTEM_UI_FLAG_LAYOUT_STABLE | 714 View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | 715 View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | 716 View.SYSTEM_UI_FLAG_FULLSCREEN | 717 View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY; 718 else 719 flags |= View.SYSTEM_UI_FLAG_LOW_PROFILE; 720 } 721 setSystemUiVisibility(flags); 722 // if (isFullscreen() && DeviceInfo.getSDKLevel() >= DeviceInfo.ICE_CREAM_SANDWICH) 723 // simulateTouch(); 724 return true; 725 } 726 return false; 727 } 728 729 730 private int lastSystemUiVisibility = -1; 731 private boolean systemUiVisibilityListenerIsSet = false; 732 733 @TargetApi(Build.VERSION_CODES.HONEYCOMB) 734 @SuppressLint("NewApi") setSystemUiVisibility(int value)735 private boolean setSystemUiVisibility(int value) { 736 if (DeviceInfo.getSDKLevel() >= DeviceInfo.HONEYCOMB) { 737 if (!systemUiVisibilityListenerIsSet && null != mDecorView) { 738 mDecorView.setOnSystemUiVisibilityChangeListener(visibility -> lastSystemUiVisibility = visibility); 739 systemUiVisibilityListenerIsSet = true; 740 } 741 boolean a4 = DeviceInfo.getSDKLevel() >= DeviceInfo.ICE_CREAM_SANDWICH; 742 if (!a4) 743 value &= View.SYSTEM_UI_FLAG_LOW_PROFILE; 744 if (value == lastSystemUiVisibility)// && a4) 745 return false; 746 //lastSystemUiVisibility = value; 747 748 if (null == mDecorView) 749 return false; 750 try { 751 Method m = mDecorView.getClass().getMethod("setSystemUiVisibility", int.class); 752 m.invoke(mDecorView, value); 753 return true; 754 } catch (SecurityException e) { 755 // ignore 756 } catch (NoSuchMethodException e) { 757 // ignore 758 } catch (IllegalArgumentException e) { 759 // ignore 760 } catch (IllegalAccessException e) { 761 // ignore 762 } catch (InvocationTargetException e) { 763 // ignore 764 } 765 } 766 return false; 767 } 768 769 private int currentKeyBacklightLevel = 1; 770 getKeyBacklight()771 public int getKeyBacklight() { 772 return currentKeyBacklightLevel; 773 } 774 setKeyBacklight(int value)775 public boolean setKeyBacklight(int value) { 776 currentKeyBacklightLevel = value; 777 // Try ICS way 778 if (DeviceInfo.getSDKLevel() >= DeviceInfo.HONEYCOMB) { 779 setSystemUiVisibility(); 780 } 781 // thread safe 782 return Engine.getInstance(this).setKeyBacklight(value); 783 } 784 785 786 private boolean keyBacklightOff = true; 787 isKeyBacklightDisabled()788 public boolean isKeyBacklightDisabled() { 789 return keyBacklightOff; 790 } 791 setKeyBacklightDisabled(boolean disabled)792 public void setKeyBacklightDisabled(boolean disabled) { 793 keyBacklightOff = disabled; 794 onUserActivity(); 795 } 796 getScreenBacklightLevel()797 public int getScreenBacklightLevel() { 798 if (!DeviceInfo.EINK_SCREEN) 799 return screenBacklightBrightness; 800 else if (DeviceInfo.EINK_HAVE_FRONTLIGHT) { 801 // on E-INK devices fetch the system backlight level 802 return mEinkScreen.getFrontLightValue(this); 803 } 804 return 0; 805 } 806 getWarmBacklightLevel()807 public int getWarmBacklightLevel() { 808 if (!DeviceInfo.EINK_SCREEN) 809 return screenWarmBacklightBrightness; 810 else if (DeviceInfo.EINK_HAVE_NATURAL_BACKLIGHT) 811 return mEinkScreen.getWarmLightValue(this); 812 return 0; 813 } 814 setScreenBacklightLevel(int value)815 public void setScreenBacklightLevel(int value) { 816 if (value < -1) 817 value = -1; 818 else if (value > DeviceInfo.MAX_SCREEN_BRIGHTNESS_VALUE) 819 value = -1; 820 screenBacklightBrightness = value; 821 if (!DeviceInfo.EINK_SCREEN) 822 onUserActivity(); 823 else if (null != mEinkScreen) 824 mEinkScreen.setFrontLightValue(this, value); 825 } 826 setScreenWarmBacklightLevel(int value)827 public void setScreenWarmBacklightLevel(int value) { 828 if (value < -1) 829 value = -1; 830 else if (value > DeviceInfo.MAX_SCREEN_BRIGHTNESS_WARM_VALUE) 831 value = -1; 832 if (null != mEinkScreen) { 833 if (mEinkScreen.setWarmLightValue(this, value)) 834 screenWarmBacklightBrightness = value; 835 } 836 } 837 838 private int screenBacklightBrightness = -1; // use default 839 private int screenWarmBacklightBrightness = -1; // use default 840 //private boolean brightnessHackError = false; 841 private boolean brightnessHackError = DeviceInfo.SAMSUNG_BUTTONS_HIGHLIGHT_PATCH; 842 turnOffKeyBacklight()843 private void turnOffKeyBacklight() { 844 if (!isStarted()) 845 return; 846 if (DeviceInfo.getSDKLevel() >= DeviceInfo.HONEYCOMB) { 847 setKeyBacklight(0); 848 } 849 // repeat again in short interval 850 if (!Engine.getInstance(this).setKeyBacklight(0)) { 851 //log.w("Cannot control key backlight directly"); 852 return; 853 } 854 // repeat again in short interval 855 Runnable task = () -> { 856 if (!isStarted()) 857 return; 858 if (!Engine.getInstance(BaseActivity.this).setKeyBacklight(0)) { 859 //log.w("Cannot control key backlight directly (delayed)"); 860 } 861 }; 862 BackgroundThread.instance().postGUI(task, 1); 863 //BackgroundThread.instance().postGUI(task, 10); 864 } 865 turnOnKeyBacklight()866 private void turnOnKeyBacklight() { 867 if (!isStarted()) 868 return; 869 setKeyBacklight(1); 870 // repeat again in short interval 871 if (!Engine.getInstance(this).setKeyBacklight(1)) { 872 //log.w("Cannot control key backlight directly"); 873 return; 874 } 875 // repeat again in short interval 876 Runnable task = () -> { 877 if (!isStarted()) 878 return; 879 if (!Engine.getInstance(BaseActivity.this).setKeyBacklight(1)) { 880 //log.w("Cannot control key backlight directly (delayed)"); 881 } 882 }; 883 BackgroundThread.instance().postGUI(task, 1); 884 //BackgroundThread.instance().postGUI(task, 10); 885 } 886 updateBacklightBrightness(float b)887 private void updateBacklightBrightness(float b) { 888 Window wnd = getWindow(); 889 if (wnd != null) { 890 LayoutParams attrs = wnd.getAttributes(); 891 boolean changed = false; 892 if (b < 0 && b > -0.99999f) { 893 //log.d("dimming screen by " + (int)((1 + b)*100) + "%"); 894 b = -b * attrs.screenBrightness; 895 if (b < 0.15) 896 return; 897 } 898 float delta = attrs.screenBrightness - b; 899 if (delta < 0) 900 delta = -delta; 901 if (delta > 0.01) { 902 attrs.screenBrightness = b; 903 changed = true; 904 } 905 if (changed) { 906 log.d("Window attribute changed: " + attrs); 907 wnd.setAttributes(attrs); 908 } 909 } 910 } 911 updateButtonsBrightness(float buttonBrightness)912 private void updateButtonsBrightness(float buttonBrightness) { 913 Window wnd = getWindow(); 914 if (wnd != null) { 915 LayoutParams attrs = wnd.getAttributes(); 916 boolean changed = false; 917 // hack to set buttonBrightness field 918 //float buttonBrightness = keyBacklightOff ? 0.0f : -1.0f; 919 if (!brightnessHackError) 920 try { 921 Field bb = attrs.getClass().getField("buttonBrightness"); 922 if (bb != null) { 923 Float oldValue = (Float) bb.get(attrs); 924 if (oldValue == null || oldValue.floatValue() != buttonBrightness) { 925 bb.set(attrs, buttonBrightness); 926 changed = true; 927 } 928 } 929 } catch (Exception e) { 930 log.e("WindowManager.LayoutParams.buttonBrightness field is not found, cannot turn buttons backlight off"); 931 brightnessHackError = true; 932 } 933 //attrs.buttonBrightness = 0; 934 if (changed) { 935 log.d("Window attribute changed: " + attrs); 936 wnd.setAttributes(attrs); 937 } 938 if (keyBacklightOff) 939 turnOffKeyBacklight(); 940 else 941 turnOnKeyBacklight(); 942 } 943 } 944 945 private final static int MIN_BACKLIGHT_LEVEL_PERCENT = DeviceInfo.MIN_SCREEN_BRIGHTNESS_VALUE; 946 setDimmingAlpha(int alpha)947 protected void setDimmingAlpha(int alpha) { 948 // override it 949 } 950 allowLowBrightness()951 protected boolean allowLowBrightness() { 952 // override to force higher brightness in non-reading mode (to avoid black screen on some devices when brightness level set to small value) 953 return true; 954 } 955 956 private final static int MIN_BRIGHTNESS_IN_BROWSER = 12; 957 onUserActivity()958 public void onUserActivity() { 959 if (backlightControl != null) 960 backlightControl.onUserActivity(); 961 // Hack 962 //if ( backlightControl.isHeld() ) 963 BackgroundThread.instance().executeGUI(() -> { 964 try { 965 float b; 966 int dimmingAlpha = 255; 967 // screenBacklightBrightness is 0..100 968 if (screenBacklightBrightness >= 0) { 969 int percent = screenBacklightBrightness; 970 if (!allowLowBrightness() && percent < MIN_BRIGHTNESS_IN_BROWSER) 971 percent = MIN_BRIGHTNESS_IN_BROWSER; 972 float minb = MIN_BACKLIGHT_LEVEL_PERCENT / 100.0f; 973 if (percent >= 10) { 974 // real brightness control, no colors dimming 975 b = (percent - 10) / (100.0f - 10.0f); // 0..1 976 b = minb + b * (1 - minb); // minb..1 977 if (b < minb) // BRIGHTNESS_OVERRIDE_OFF 978 b = minb; 979 else if (b > 1.0f) 980 b = 1.0f; //BRIGHTNESS_OVERRIDE_FULL 981 } else { 982 // minimal brightness with colors dimming 983 b = minb; 984 dimmingAlpha = 255 - (11 - percent) * 180 / 10; 985 } 986 } else { 987 // system 988 b = -1.0f; //BRIGHTNESS_OVERRIDE_NONE 989 } 990 setDimmingAlpha(dimmingAlpha); 991 //log.v("Brightness: " + b + ", dim: " + dimmingAlpha); 992 updateBacklightBrightness(b); 993 updateButtonsBrightness(keyBacklightOff ? 0.0f : -1.0f); 994 } catch (Exception e) { 995 // ignore 996 } 997 }); 998 } 999 1000 isWakeLockEnabled()1001 public boolean isWakeLockEnabled() { 1002 return screenBacklightDuration > 0; 1003 } 1004 1005 /** 1006 * @param backlightDurationMinutes 0 = system default, 1 == 3 minutes, 2..5 == 2..5 minutes 1007 */ setScreenBacklightDuration(int backlightDurationMinutes)1008 public void setScreenBacklightDuration(int backlightDurationMinutes) { 1009 if (backlightDurationMinutes == 1) 1010 backlightDurationMinutes = 3; 1011 if (screenBacklightDuration != backlightDurationMinutes * 60 * 1000) { 1012 screenBacklightDuration = backlightDurationMinutes * 60 * 1000; 1013 if (screenBacklightDuration == 0) 1014 backlightControl.release(); 1015 else 1016 backlightControl.onUserActivity(); 1017 } 1018 } 1019 1020 private Runnable backlightTimerTask = null; 1021 private static long lastUserActivityTime; 1022 public static final int DEF_SCREEN_BACKLIGHT_TIMER_INTERVAL = 3 * 60 * 1000; 1023 private int screenBacklightDuration = DEF_SCREEN_BACKLIGHT_TIMER_INTERVAL; 1024 1025 private class ScreenBacklightControl { 1026 PowerManager.WakeLock wl = null; 1027 ScreenBacklightControl()1028 public ScreenBacklightControl() { 1029 } 1030 1031 long lastUpdateTimeStamp; 1032 onUserActivity()1033 public void onUserActivity() { 1034 lastUserActivityTime = Utils.timeStamp(); 1035 if (Utils.timeInterval(lastUpdateTimeStamp) < 5000) 1036 return; 1037 lastUpdateTimeStamp = android.os.SystemClock.uptimeMillis(); 1038 if (!isWakeLockEnabled()) 1039 return; 1040 if (wl == null) { 1041 PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); 1042 wl = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK 1043 /* | PowerManager.ON_AFTER_RELEASE */, "cr3:wakelock"); 1044 log.d("ScreenBacklightControl: WakeLock created"); 1045 } 1046 if (!isStarted()) { 1047 log.d("ScreenBacklightControl: user activity while not started"); 1048 release(); 1049 return; 1050 } 1051 1052 if (!isHeld()) { 1053 log.d("ScreenBacklightControl: acquiring WakeLock"); 1054 wl.acquire(); 1055 } 1056 1057 if (backlightTimerTask == null) { 1058 log.v("ScreenBacklightControl: timer task started"); 1059 backlightTimerTask = new BacklightTimerTask(); 1060 BackgroundThread.instance().postGUI(backlightTimerTask, 1061 screenBacklightDuration / 10); 1062 } 1063 } 1064 isHeld()1065 public boolean isHeld() { 1066 return wl != null && wl.isHeld(); 1067 } 1068 release()1069 public void release() { 1070 if (wl != null && wl.isHeld()) { 1071 log.d("ScreenBacklightControl: wl.release()"); 1072 wl.release(); 1073 } 1074 backlightTimerTask = null; 1075 lastUpdateTimeStamp = 0; 1076 } 1077 1078 private class BacklightTimerTask implements Runnable { 1079 1080 @Override run()1081 public void run() { 1082 if (backlightTimerTask == null) 1083 return; 1084 long interval = Utils.timeInterval(lastUserActivityTime); 1085 // log.v("ScreenBacklightControl: timer task, lastActivityMillis = " 1086 // + interval); 1087 int nextTimerInterval = screenBacklightDuration / 20; 1088 boolean dim = false; 1089 if (interval > screenBacklightDuration * 8 / 10) { 1090 nextTimerInterval = nextTimerInterval / 8; 1091 dim = true; 1092 } 1093 if (interval > screenBacklightDuration) { 1094 log.v("ScreenBacklightControl: interval is expired"); 1095 release(); 1096 } else { 1097 BackgroundThread.instance().postGUI(backlightTimerTask, nextTimerInterval); 1098 if (dim) { 1099 updateBacklightBrightness(-0.9f); // reduce by 9% 1100 } 1101 } 1102 } 1103 1104 } 1105 1106 ; 1107 1108 } 1109 releaseBacklightControl()1110 public void releaseBacklightControl() { 1111 backlightControl.release(); 1112 } 1113 1114 ScreenBacklightControl backlightControl = new ScreenBacklightControl(); 1115 1116 1117 private EinkScreen.EinkUpdateMode mScreenUpdateMode = EinkScreen.EinkUpdateMode.Clear; 1118 getScreenUpdateMode()1119 public EinkScreen.EinkUpdateMode getScreenUpdateMode() { 1120 return mScreenUpdateMode; 1121 } 1122 setScreenUpdateMode(EinkScreen.EinkUpdateMode screenUpdateMode, View view)1123 public void setScreenUpdateMode(EinkScreen.EinkUpdateMode screenUpdateMode, View view) { 1124 //if (mReaderView != null) { 1125 if (null != mEinkScreen) { 1126 mScreenUpdateMode = screenUpdateMode; 1127 if (mEinkScreen.getUpdateMode() != screenUpdateMode || mEinkScreen.getUpdateMode() == EinkScreen.EinkUpdateMode.Active) { 1128 mEinkScreen.setupController(mScreenUpdateMode, mScreenUpdateInterval, view); 1129 } 1130 } 1131 //} 1132 } 1133 1134 private int mScreenUpdateInterval = 0; 1135 getScreenUpdateInterval()1136 public int getScreenUpdateInterval() { 1137 return mScreenUpdateInterval; 1138 } 1139 setScreenUpdateInterval(int screenUpdateInterval, View view)1140 public void setScreenUpdateInterval(int screenUpdateInterval, View view) { 1141 if (null != mEinkScreen) { 1142 mScreenUpdateInterval = screenUpdateInterval; 1143 if (mEinkScreen.getUpdateInterval() != screenUpdateInterval) { 1144 mEinkScreen.setupController(mScreenUpdateMode, screenUpdateInterval, view); 1145 } 1146 } 1147 } 1148 1149 showToast(int stringResourceId)1150 public void showToast(int stringResourceId) { 1151 showToast(stringResourceId, Toast.LENGTH_LONG); 1152 } 1153 showToast(int stringResourceId, Object... formatArgs)1154 public void showToast(int stringResourceId, Object... formatArgs) { 1155 String s = getString(stringResourceId, formatArgs); 1156 if (s != null) 1157 showToast(s, Toast.LENGTH_LONG); 1158 } 1159 showToast(int stringResourceId, int duration)1160 public void showToast(int stringResourceId, int duration) { 1161 String s = getString(stringResourceId); 1162 if (s != null) 1163 showToast(s, duration); 1164 } 1165 showToast(String msg)1166 public void showToast(String msg) { 1167 showToast(msg, Toast.LENGTH_LONG); 1168 } 1169 showToast(String msg, int duration)1170 public void showToast(String msg, int duration) { 1171 log.v("showing toast: " + msg); 1172 if (DeviceInfo.USE_CUSTOM_TOAST) { 1173 ToastView.showToast(getContentView(), msg, Toast.LENGTH_LONG, settings().getInt(ReaderView.PROP_FONT_SIZE, 20)); 1174 } else { 1175 // classic Toast 1176 Toast toast = Toast.makeText(this, msg, duration); 1177 toast.show(); 1178 } 1179 } 1180 1181 1182 protected View contentView; 1183 getContentView()1184 public View getContentView() { 1185 return contentView; 1186 } 1187 setContentView(View view)1188 public void setContentView(View view) { 1189 this.contentView = view; 1190 super.setContentView(view); 1191 //systemUiVisibilityListenerIsSet = false; 1192 //updateBackground(); 1193 setCurrentTheme(currentTheme); 1194 } 1195 1196 1197 private boolean mNightMode = false; 1198 isNightMode()1199 public boolean isNightMode() { 1200 return mNightMode; 1201 } 1202 setNightMode(boolean nightMode)1203 public void setNightMode(boolean nightMode) { 1204 mNightMode = nightMode; 1205 } 1206 1207 getClipboardmanager()1208 public ClipboardManager getClipboardmanager() { 1209 return (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); 1210 } 1211 1212 showHomeScreen()1213 public void showHomeScreen() { 1214 Intent intent = new Intent(Intent.ACTION_MAIN); 1215 intent.addCategory(Intent.CATEGORY_HOME); 1216 startActivity(intent); 1217 } 1218 1219 1220 private static String PREF_HELP_FILE = "HelpFile"; 1221 getLastGeneratedHelpFileSignature()1222 public String getLastGeneratedHelpFileSignature() { 1223 SharedPreferences pref = getSharedPreferences(PREF_FILE, 0); 1224 String res = pref.getString(PREF_HELP_FILE, null); 1225 return res; 1226 } 1227 setLastGeneratedHelpFileSignature(String v)1228 public void setLastGeneratedHelpFileSignature(String v) { 1229 SharedPreferences pref = getSharedPreferences(PREF_FILE, 0); 1230 pref.edit().putString(PREF_HELP_FILE, v).commit(); 1231 } 1232 1233 1234 private String currentLanguage; 1235 getCurrentLanguage()1236 public String getCurrentLanguage() { 1237 return currentLanguage; 1238 } 1239 setLanguage(String lang)1240 public void setLanguage(String lang) { 1241 setLanguage(Lang.byCode(lang)); 1242 // reload Genres Collection 1243 GenresCollection.reloadGenresFromResource(this); 1244 } 1245 setLanguage(Lang lang)1246 public void setLanguage(Lang lang) { 1247 try { 1248 Resources res = getResources(); 1249 // Change locale settings in the app. 1250 DisplayMetrics dm = res.getDisplayMetrics(); 1251 android.content.res.Configuration conf = res.getConfiguration(); 1252 conf.locale = (lang == Lang.DEFAULT) ? defaultLocale : lang.getLocale(); 1253 currentLanguage = (lang == Lang.DEFAULT) ? Lang.getCode(defaultLocale) : lang.code; 1254 res.updateConfiguration(conf, dm); 1255 } catch (Exception e) { 1256 log.e("error while setting locale " + lang, e); 1257 } 1258 } 1259 1260 // Store system locale here, on class creation 1261 private static final Locale defaultLocale = Locale.getDefault(); 1262 1263 applyAppSetting(String key, String value)1264 public void applyAppSetting(String key, String value) { 1265 boolean flg = "1".equals(value); 1266 if (key.equals(PROP_APP_FULLSCREEN)) { 1267 setFullscreen("1".equals(value)); 1268 } else if (key.equals(PROP_APP_LOCALE)) { 1269 setLanguage(value); 1270 } else if (key.equals(PROP_APP_KEY_BACKLIGHT_OFF)) { 1271 setKeyBacklightDisabled(flg); 1272 } else if (key.equals(PROP_APP_SCREEN_BACKLIGHT_LOCK)) { 1273 setScreenBacklightDuration(Utils.parseInt(value, 0)); 1274 } else if (key.equals(PROP_NIGHT_MODE)) { 1275 setNightMode(flg); 1276 } else if (key.equals(PROP_APP_SCREEN_UPDATE_MODE)) { 1277 setScreenUpdateMode(EinkScreen.EinkUpdateMode.byCode(Utils.parseInt(value, 0)), getContentView()); 1278 } else if (key.equals(PROP_APP_SCREEN_UPDATE_INTERVAL)) { 1279 setScreenUpdateInterval(Utils.parseInt(value, 10), getContentView()); 1280 } else if (key.equals(PROP_APP_THEME)) { 1281 setCurrentTheme(value); 1282 } else if (key.equals(PROP_APP_SCREEN_ORIENTATION)) { 1283 setScreenOrientation(Utils.parseInt(value, 0)); 1284 } else if (PROP_APP_SCREEN_BACKLIGHT.equals(key) && !DeviceInfo.EINK_SCREEN) { 1285 final int n = Utils.parseInt(value, -1, -1, 100); 1286 // delay before setting brightness 1287 BackgroundThread.instance().postGUI(() -> BackgroundThread.instance() 1288 .postBackground(() -> BackgroundThread.instance() 1289 .postGUI(() -> setScreenBacklightLevel(n))), 100); 1290 } else if (key.equals(PROP_APP_FILE_BROWSER_HIDE_EMPTY_FOLDERS)) { 1291 Services.getScanner().setHideEmptyDirs(flg); 1292 } 1293 // Don't apply screen brightness on e-ink devices on program startup and at any other events 1294 // On e-ink in ReaderView gesture handlers setScreenBacklightLevel() & setScreenWarmBacklightLevel() called directly 1295 } 1296 1297 showNotice(int questionResourceId, final Runnable action, final Runnable cancelAction)1298 public void showNotice(int questionResourceId, final Runnable action, final Runnable cancelAction) { 1299 NoticeDialog dlg = new NoticeDialog(this, action, cancelAction); 1300 dlg.setMessage(questionResourceId); 1301 dlg.show(); 1302 } 1303 showNotice(int questionResourceId, final Runnable action)1304 public void showNotice(int questionResourceId, final Runnable action) { 1305 NoticeDialog dlg = new NoticeDialog(this, action, null); 1306 dlg.setMessage(questionResourceId); 1307 dlg.show(); 1308 } 1309 showMessage(String title, String message)1310 public void showMessage(String title, String message) { 1311 AlertDialog.Builder dlg = new AlertDialog.Builder(this); 1312 if (null != title) 1313 dlg.setTitle(title); 1314 dlg.setMessage(message); 1315 dlg.setPositiveButton(R.string.dlg_button_ok, (arg0, arg1) -> {}); 1316 dlg.show(); 1317 } 1318 askConfirmation(int questionResourceId, final Runnable action)1319 public void askConfirmation(int questionResourceId, final Runnable action) { 1320 askConfirmation(questionResourceId, action, null); 1321 } 1322 askConfirmation(int questionResourceId, final Runnable action, final Runnable cancelAction)1323 public void askConfirmation(int questionResourceId, final Runnable action, final Runnable cancelAction) { 1324 String question = getString(questionResourceId); 1325 askConfirmation(null, question, action, cancelAction); 1326 } 1327 askConfirmation(int titleResourceId, int questionResourceId, final Runnable action, final Runnable cancelAction)1328 public void askConfirmation(int titleResourceId, int questionResourceId, final Runnable action, final Runnable cancelAction) { 1329 String title = getString(titleResourceId); 1330 String question = getString(questionResourceId); 1331 askConfirmation(title, question, action, cancelAction); 1332 } 1333 askQuestion(int titleResourceId, int questionResourceId, final Runnable yesAction, final Runnable noAction)1334 public void askQuestion(int titleResourceId, int questionResourceId, final Runnable yesAction, final Runnable noAction) { 1335 String title = getString(titleResourceId); 1336 String question = getString(questionResourceId); 1337 askQuestion(title, question, yesAction, noAction); 1338 } 1339 askConfirmation(String title, String question, final Runnable action, final Runnable cancelAction)1340 public void askConfirmation(String title, String question, final Runnable action, final Runnable cancelAction) { 1341 AlertDialog.Builder dlg = new AlertDialog.Builder(this); 1342 if (null != title) 1343 dlg.setTitle(title); 1344 dlg.setMessage(question); 1345 dlg.setPositiveButton(R.string.dlg_button_ok, (arg0, arg1) -> action.run()); 1346 dlg.setNegativeButton(R.string.dlg_button_cancel, (arg0, arg1) -> { 1347 if (cancelAction != null) 1348 cancelAction.run(); 1349 }); 1350 dlg.show(); 1351 } 1352 askQuestion(String title, String question, final Runnable yesAction, final Runnable noAction)1353 public void askQuestion(String title, String question, final Runnable yesAction, final Runnable noAction) { 1354 AlertDialog.Builder dlg = new AlertDialog.Builder(this); 1355 if (null != title) 1356 dlg.setTitle(title); 1357 dlg.setMessage(question); 1358 dlg.setPositiveButton(R.string.dlg_button_yes, (arg0, arg1) -> yesAction.run()); 1359 dlg.setNegativeButton(R.string.dlg_button_no, (arg0, arg1) -> { 1360 if (noAction != null) 1361 noAction.run(); 1362 }); 1363 dlg.show(); 1364 } 1365 askConfirmation(String question, final Runnable action)1366 public void askConfirmation(String question, final Runnable action) { 1367 AlertDialog.Builder dlg = new AlertDialog.Builder(this); 1368 dlg.setTitle(question); 1369 dlg.setPositiveButton(R.string.dlg_button_ok, (arg0, arg1) -> action.run()); 1370 dlg.setNegativeButton(R.string.dlg_button_cancel, (arg0, arg1) -> { 1371 // do nothing 1372 }); 1373 dlg.show(); 1374 } 1375 directoryUpdated(FileInfo dir)1376 public void directoryUpdated(FileInfo dir) { 1377 // override it to use 1378 } 1379 onSettingsChanged(Properties props, Properties oldProps)1380 public void onSettingsChanged(Properties props, Properties oldProps) { 1381 // override for specific actions 1382 } 1383 showActionsPopupMenu(final ReaderAction[] actions, final CRToolBar.OnActionHandler onActionHandler)1384 public void showActionsPopupMenu(final ReaderAction[] actions, final CRToolBar.OnActionHandler onActionHandler) { 1385 ArrayList<ReaderAction> list = new ArrayList<>(actions.length); 1386 Collections.addAll(list, actions); 1387 showActionsPopupMenu(list, onActionHandler); 1388 } 1389 showActionsPopupMenu(final ArrayList<ReaderAction> actions, final CRToolBar.OnActionHandler onActionHandler)1390 public void showActionsPopupMenu(final ArrayList<ReaderAction> actions, final CRToolBar.OnActionHandler onActionHandler) { 1391 registerForContextMenu(contentView); 1392 contentView.setOnCreateContextMenuListener((menu, v, menuInfo) -> { 1393 //populate only it is not populated by children 1394 if (menu.size() == 0) { 1395 int order = 0; 1396 for (final ReaderAction action : actions) { 1397 MenuItem item = menu.add(0, action.menuItemId, order++, action.nameId); 1398 item.setOnMenuItemClickListener(item1 -> onActionHandler.onActionSelected(action)); 1399 } 1400 } 1401 }); 1402 contentView.showContextMenu(); 1403 } 1404 showBrowserOptionsDialog()1405 public void showBrowserOptionsDialog() { 1406 OptionsDialog dlg = new OptionsDialog(BaseActivity.this, OptionsDialog.Mode.BROWSER, null, null, null); 1407 dlg.show(); 1408 } 1409 1410 private int currentProfile = 0; 1411 getCurrentProfile()1412 public int getCurrentProfile() { 1413 if (currentProfile == 0) { 1414 currentProfile = mSettingsManager.getInt(PROP_PROFILE_NUMBER, 1); 1415 if (currentProfile < 1 || currentProfile > MAX_PROFILES) 1416 currentProfile = 1; 1417 } 1418 return currentProfile; 1419 } 1420 setCurrentProfile(int profile)1421 public void setCurrentProfile(int profile) { 1422 if (profile == 0 || profile == getCurrentProfile()) 1423 return; 1424 log.i("Switching from profile " + currentProfile + " to " + profile); 1425 mSettingsManager.saveSettings(currentProfile, null); 1426 final Properties loadedSettings = mSettingsManager.loadSettings(profile); 1427 mSettingsManager.setSettings(loadedSettings, 0, true); 1428 currentProfile = profile; 1429 } 1430 setSetting(String name, String value, boolean notify)1431 public void setSetting(String name, String value, boolean notify) { 1432 mSettingsManager.setSetting(name, value, notify); 1433 } 1434 setSettings(Properties settings, int delayMillis, boolean notify)1435 public void setSettings(Properties settings, int delayMillis, boolean notify) { 1436 mSettingsManager.setSettings(settings, delayMillis, notify); 1437 } 1438 mergeSettings(Properties settings, boolean notify)1439 public void mergeSettings(Properties settings, boolean notify) { 1440 mSettingsManager.mergeSettings(settings, notify); 1441 } 1442 notifySettingsChanged()1443 public void notifySettingsChanged() { 1444 setSettings(mSettingsManager.get(), -1, true); 1445 } 1446 1447 private static class SettingsManager { 1448 1449 public static final Logger log = L.create("cr"); 1450 1451 private BaseActivity mActivity; 1452 private Properties mSettings; 1453 1454 private final DisplayMetrics displayMetrics = new DisplayMetrics(); 1455 private final File defaultSettingsDir; 1456 SettingsManager(BaseActivity activity)1457 public SettingsManager(BaseActivity activity) { 1458 this.mActivity = activity; 1459 activity.getWindowManager().getDefaultDisplay().getMetrics(displayMetrics); 1460 defaultSettingsDir = activity.getDir("settings", Context.MODE_PRIVATE); 1461 1462 mSettings = loadSettings(); 1463 } 1464 rebaseSettings()1465 public void rebaseSettings() { 1466 mSettings = loadSettings(); 1467 } 1468 1469 //int lastSaveId = 0; setSettings(Properties settings, int delayMillis, boolean notify)1470 public void setSettings(Properties settings, int delayMillis, boolean notify) { 1471 Properties oldSettings = mSettings; 1472 mSettings = new Properties(settings); 1473 saveSettings(mSettings); 1474 // if (delayMillis >= 0) { 1475 // saveSettingsTask.postDelayed(new Runnable() { 1476 // public void run() { 1477 // BackgroundThread.instance().postGUI(new Runnable() { 1478 // @Override 1479 // public void run() { 1480 // saveSettings(mSettings); 1481 // } 1482 // }); 1483 // } 1484 // }, delayMillis); 1485 // } 1486 if (notify) 1487 mActivity.onSettingsChanged(mSettings, oldSettings); 1488 } 1489 mergeSettings(Properties settings, boolean notify)1490 public void mergeSettings(Properties settings, boolean notify) { 1491 Properties oldSettings = mSettings; 1492 mSettings = new Properties(oldSettings); 1493 Set<Entry<Object, Object>> entries = settings.entrySet(); 1494 for (Entry<Object, Object> entry : entries) { 1495 mSettings.put(entry.getKey(), entry.getValue()); 1496 } 1497 saveSettings(mSettings); 1498 if (notify) 1499 mActivity.onSettingsChanged(mSettings, oldSettings); 1500 } 1501 setSetting(String name, String value, boolean notify)1502 public void setSetting(String name, String value, boolean notify) { 1503 Properties props = new Properties(mSettings); 1504 if (value.equals(mSettings.getProperty(name))) 1505 return; 1506 props.setProperty(name, value); 1507 setSettings(props, 1000, notify); 1508 } 1509 1510 private static class DefKeyAction { 1511 public int keyCode; 1512 public int type; 1513 public ReaderAction action; 1514 DefKeyAction(int keyCode, int type, ReaderAction action)1515 public DefKeyAction(int keyCode, int type, ReaderAction action) { 1516 this.keyCode = keyCode; 1517 this.type = type; 1518 this.action = action; 1519 } 1520 getProp()1521 public String getProp() { 1522 return ReaderView.PROP_APP_KEY_ACTIONS_PRESS + ReaderAction.getTypeString(type) + keyCode; 1523 } 1524 } 1525 1526 1527 private static class DefTapAction { 1528 public int zone; 1529 public boolean longPress; 1530 public ReaderAction action; 1531 DefTapAction(int zone, boolean longPress, ReaderAction action)1532 public DefTapAction(int zone, boolean longPress, ReaderAction action) { 1533 this.zone = zone; 1534 this.longPress = longPress; 1535 this.action = action; 1536 } 1537 } 1538 1539 private static DefKeyAction[] DEF_KEY_ACTIONS = { 1540 new DefKeyAction(KeyEvent.KEYCODE_BACK, ReaderAction.NORMAL, ReaderAction.GO_BACK), 1541 new DefKeyAction(KeyEvent.KEYCODE_BACK, ReaderAction.LONG, ReaderAction.EXIT), 1542 new DefKeyAction(KeyEvent.KEYCODE_BACK, ReaderAction.DOUBLE, ReaderAction.EXIT), 1543 new DefKeyAction(KeyEvent.KEYCODE_DPAD_CENTER, ReaderAction.NORMAL, ReaderAction.RECENT_BOOKS), 1544 new DefKeyAction(KeyEvent.KEYCODE_DPAD_CENTER, ReaderAction.LONG, ReaderAction.BOOKMARKS), 1545 new DefKeyAction(KeyEvent.KEYCODE_DPAD_UP, ReaderAction.NORMAL, ReaderAction.PAGE_UP), 1546 new DefKeyAction(KeyEvent.KEYCODE_DPAD_DOWN, ReaderAction.NORMAL, ReaderAction.PAGE_DOWN), 1547 new DefKeyAction(KeyEvent.KEYCODE_DPAD_UP, ReaderAction.LONG, (DeviceInfo.EINK_SONY ? ReaderAction.PAGE_UP_10 : ReaderAction.REPEAT)), 1548 new DefKeyAction(KeyEvent.KEYCODE_DPAD_DOWN, ReaderAction.LONG, (DeviceInfo.EINK_SONY ? ReaderAction.PAGE_DOWN_10 : ReaderAction.REPEAT)), 1549 new DefKeyAction(KeyEvent.KEYCODE_DPAD_LEFT, ReaderAction.NORMAL, (DeviceInfo.NAVIGATE_LEFTRIGHT ? ReaderAction.PAGE_UP : ReaderAction.PAGE_UP_10)), 1550 new DefKeyAction(KeyEvent.KEYCODE_DPAD_RIGHT, ReaderAction.NORMAL, (DeviceInfo.NAVIGATE_LEFTRIGHT ? ReaderAction.PAGE_DOWN : ReaderAction.PAGE_DOWN_10)), 1551 new DefKeyAction(KeyEvent.KEYCODE_DPAD_LEFT, ReaderAction.LONG, ReaderAction.REPEAT), 1552 new DefKeyAction(KeyEvent.KEYCODE_DPAD_RIGHT, ReaderAction.LONG, ReaderAction.REPEAT), 1553 new DefKeyAction(KeyEvent.KEYCODE_VOLUME_UP, ReaderAction.NORMAL, ReaderAction.PAGE_UP), 1554 new DefKeyAction(KeyEvent.KEYCODE_VOLUME_DOWN, ReaderAction.NORMAL, ReaderAction.PAGE_DOWN), 1555 new DefKeyAction(KeyEvent.KEYCODE_VOLUME_UP, ReaderAction.LONG, ReaderAction.REPEAT), 1556 new DefKeyAction(KeyEvent.KEYCODE_VOLUME_DOWN, ReaderAction.LONG, ReaderAction.REPEAT), 1557 new DefKeyAction(KeyEvent.KEYCODE_MENU, ReaderAction.NORMAL, ReaderAction.READER_MENU), 1558 new DefKeyAction(KeyEvent.KEYCODE_MENU, ReaderAction.LONG, ReaderAction.OPTIONS), 1559 new DefKeyAction(KeyEvent.KEYCODE_CAMERA, ReaderAction.NORMAL, ReaderAction.NONE), 1560 new DefKeyAction(KeyEvent.KEYCODE_CAMERA, ReaderAction.LONG, ReaderAction.NONE), 1561 new DefKeyAction(KeyEvent.KEYCODE_SEARCH, ReaderAction.NORMAL, ReaderAction.SEARCH), 1562 new DefKeyAction(KeyEvent.KEYCODE_SEARCH, ReaderAction.LONG, ReaderAction.TOGGLE_SELECTION_MODE), 1563 1564 new DefKeyAction(KeyEvent.KEYCODE_PAGE_UP, ReaderAction.NORMAL, ReaderAction.PAGE_UP), 1565 new DefKeyAction(KeyEvent.KEYCODE_PAGE_UP, ReaderAction.LONG, ReaderAction.NONE), 1566 new DefKeyAction(KeyEvent.KEYCODE_PAGE_UP, ReaderAction.DOUBLE, ReaderAction.NONE), 1567 new DefKeyAction(KeyEvent.KEYCODE_PAGE_DOWN, ReaderAction.NORMAL, ReaderAction.PAGE_DOWN), 1568 new DefKeyAction(KeyEvent.KEYCODE_PAGE_DOWN, ReaderAction.LONG, ReaderAction.NONE), 1569 new DefKeyAction(KeyEvent.KEYCODE_PAGE_DOWN, ReaderAction.DOUBLE, ReaderAction.NONE), 1570 1571 new DefKeyAction(ReaderView.SONY_DPAD_DOWN_SCANCODE, ReaderAction.NORMAL, ReaderAction.PAGE_DOWN), 1572 new DefKeyAction(ReaderView.SONY_DPAD_UP_SCANCODE, ReaderAction.NORMAL, ReaderAction.PAGE_UP), 1573 new DefKeyAction(ReaderView.SONY_DPAD_DOWN_SCANCODE, ReaderAction.LONG, ReaderAction.PAGE_DOWN_10), 1574 new DefKeyAction(ReaderView.SONY_DPAD_UP_SCANCODE, ReaderAction.LONG, ReaderAction.PAGE_UP_10), 1575 1576 new DefKeyAction(KeyEvent.KEYCODE_8, ReaderAction.NORMAL, ReaderAction.PAGE_DOWN), 1577 new DefKeyAction(KeyEvent.KEYCODE_2, ReaderAction.NORMAL, ReaderAction.PAGE_UP), 1578 new DefKeyAction(KeyEvent.KEYCODE_8, ReaderAction.LONG, ReaderAction.PAGE_DOWN_10), 1579 new DefKeyAction(KeyEvent.KEYCODE_2, ReaderAction.LONG, ReaderAction.PAGE_UP_10), 1580 1581 new DefKeyAction(ReaderView.KEYCODE_ESCAPE, ReaderAction.NORMAL, ReaderAction.PAGE_DOWN), 1582 new DefKeyAction(ReaderView.KEYCODE_ESCAPE, ReaderAction.LONG, ReaderAction.REPEAT), 1583 1584 // public static final int KEYCODE_PAGE_BOTTOMLEFT = 0x5d; // fwd 1585 // public static final int KEYCODE_PAGE_BOTTOMRIGHT = 0x5f; // fwd 1586 // public static final int KEYCODE_PAGE_TOPLEFT = 0x5c; // back 1587 // public static final int KEYCODE_PAGE_TOPRIGHT = 0x5e; // back 1588 1589 }; 1590 // Some key codes on Nook devices conflicted with standard keyboard, for example, KEYCODE_PAGE_BOTTOMLEFT with PAGE_DOWN 1591 private static DefKeyAction[] DEF_NOOK_KEY_ACTIONS = { 1592 new DefKeyAction(ReaderView.NOOK_KEY_NEXT_RIGHT, ReaderAction.NORMAL, ReaderAction.PAGE_DOWN), 1593 new DefKeyAction(ReaderView.NOOK_KEY_SHIFT_DOWN, ReaderAction.NORMAL, ReaderAction.PAGE_DOWN), 1594 new DefKeyAction(ReaderView.NOOK_KEY_PREV_LEFT, ReaderAction.NORMAL, ReaderAction.PAGE_UP), 1595 new DefKeyAction(ReaderView.NOOK_KEY_PREV_RIGHT, ReaderAction.NORMAL, ReaderAction.PAGE_UP), 1596 new DefKeyAction(ReaderView.NOOK_KEY_SHIFT_UP, ReaderAction.NORMAL, ReaderAction.PAGE_UP), 1597 1598 new DefKeyAction(ReaderView.NOOK_12_KEY_NEXT_LEFT, ReaderAction.NORMAL, (DeviceInfo.EINK_NOOK ? ReaderAction.PAGE_UP : ReaderAction.PAGE_DOWN)), 1599 new DefKeyAction(ReaderView.NOOK_12_KEY_NEXT_LEFT, ReaderAction.LONG, (DeviceInfo.EINK_NOOK ? ReaderAction.PAGE_UP_10 : ReaderAction.PAGE_DOWN_10)), 1600 1601 new DefKeyAction(ReaderView.KEYCODE_PAGE_BOTTOMLEFT, ReaderAction.NORMAL, ReaderAction.PAGE_UP), 1602 // new DefKeyAction(ReaderView.KEYCODE_PAGE_BOTTOMRIGHT, ReaderAction.NORMAL, ReaderAction.PAGE_UP), 1603 new DefKeyAction(ReaderView.KEYCODE_PAGE_TOPLEFT, ReaderAction.NORMAL, ReaderAction.PAGE_DOWN), 1604 new DefKeyAction(ReaderView.KEYCODE_PAGE_TOPRIGHT, ReaderAction.NORMAL, ReaderAction.PAGE_DOWN), 1605 new DefKeyAction(ReaderView.KEYCODE_PAGE_BOTTOMLEFT, ReaderAction.LONG, ReaderAction.PAGE_UP_10), 1606 // new DefKeyAction(ReaderView.KEYCODE_PAGE_BOTTOMRIGHT, ReaderAction.LONG, ReaderAction.PAGE_UP_10), 1607 new DefKeyAction(ReaderView.KEYCODE_PAGE_TOPLEFT, ReaderAction.LONG, ReaderAction.PAGE_DOWN_10), 1608 new DefKeyAction(ReaderView.KEYCODE_PAGE_TOPRIGHT, ReaderAction.LONG, ReaderAction.PAGE_DOWN_10), 1609 }; 1610 private static DefTapAction[] DEF_TAP_ACTIONS = { 1611 new DefTapAction(1, false, ReaderAction.PAGE_UP), 1612 new DefTapAction(2, false, ReaderAction.PAGE_UP), 1613 new DefTapAction(4, false, ReaderAction.PAGE_UP), 1614 new DefTapAction(1, true, ReaderAction.GO_BACK), // back by link 1615 new DefTapAction(2, true, ReaderAction.TOGGLE_DAY_NIGHT), 1616 new DefTapAction(4, true, ReaderAction.PAGE_UP_10), 1617 new DefTapAction(3, false, ReaderAction.PAGE_DOWN), 1618 new DefTapAction(6, false, ReaderAction.PAGE_DOWN), 1619 new DefTapAction(7, false, ReaderAction.PAGE_DOWN), 1620 new DefTapAction(8, false, ReaderAction.PAGE_DOWN), 1621 new DefTapAction(9, false, ReaderAction.PAGE_DOWN), 1622 new DefTapAction(3, true, ReaderAction.TOGGLE_AUTOSCROLL), 1623 new DefTapAction(6, true, ReaderAction.PAGE_DOWN_10), 1624 new DefTapAction(7, true, ReaderAction.PAGE_DOWN_10), 1625 new DefTapAction(8, true, ReaderAction.PAGE_DOWN_10), 1626 new DefTapAction(9, true, ReaderAction.PAGE_DOWN_10), 1627 new DefTapAction(5, false, ReaderAction.READER_MENU), 1628 new DefTapAction(5, true, ReaderAction.OPTIONS), 1629 }; 1630 1631 isValidFontFace(String face)1632 private boolean isValidFontFace(String face) { 1633 String[] fontFaces = Engine.getFontFaceList(); 1634 if (fontFaces == null) 1635 return true; 1636 for (String item : fontFaces) { 1637 if (item.equals(face)) 1638 return true; 1639 } 1640 return false; 1641 } 1642 applyDefaultFont(Properties props, String propName, String defFontFace)1643 private boolean applyDefaultFont(Properties props, String propName, String defFontFace) { 1644 String currentValue = props.getProperty(propName); 1645 boolean changed = false; 1646 if (currentValue == null) { 1647 currentValue = defFontFace; 1648 changed = true; 1649 } 1650 if (!isValidFontFace(currentValue)) { 1651 if (isValidFontFace("Droid Sans")) 1652 currentValue = "Droid Sans"; 1653 else if (isValidFontFace("Roboto")) 1654 currentValue = "Roboto"; 1655 else if (isValidFontFace("Droid Serif")) 1656 currentValue = "Droid Serif"; 1657 else if (isValidFontFace("Arial")) 1658 currentValue = "Arial"; 1659 else if (isValidFontFace("Times New Roman")) 1660 currentValue = "Times New Roman"; 1661 else if (isValidFontFace("Droid Sans Fallback")) 1662 currentValue = "Droid Sans Fallback"; 1663 else { 1664 String[] fontFaces = Engine.getFontFaceList(); 1665 if (fontFaces != null) 1666 currentValue = fontFaces[0]; 1667 } 1668 changed = true; 1669 } 1670 if (changed) 1671 props.setProperty(propName, currentValue); 1672 return changed; 1673 } 1674 applyDefaultFallbackFontList(Properties props, String propName, String defFontList)1675 private boolean applyDefaultFallbackFontList(Properties props, String propName, String defFontList) { 1676 String currentValue = props.getProperty(propName); 1677 boolean changed = false; 1678 if (currentValue == null) { 1679 currentValue = defFontList; 1680 changed = true; 1681 } 1682 List<String> faces = Arrays.asList(currentValue.split(";")); 1683 StringBuilder allowedFaces = new StringBuilder(); 1684 Iterator<String> it = faces.iterator(); 1685 while (it.hasNext()) { 1686 String face = it.next().trim(); 1687 if (isValidFontFace(face)) { 1688 allowedFaces.append(face); 1689 if (it.hasNext()) 1690 allowedFaces.append("; "); 1691 } 1692 } 1693 if (!changed) 1694 changed = !allowedFaces.toString().equals(currentValue); 1695 if (changed) { 1696 currentValue = allowedFaces.toString(); 1697 props.setProperty(propName, currentValue); 1698 } 1699 return changed; 1700 } 1701 fixFontSettings(Properties props)1702 public boolean fixFontSettings(Properties props) { 1703 boolean res = false; 1704 res = applyDefaultFont(props, ReaderView.PROP_FONT_FACE, DeviceInfo.DEF_FONT_FACE) || res; 1705 res = applyDefaultFont(props, ReaderView.PROP_STATUS_FONT_FACE, DeviceInfo.DEF_FONT_FACE) || res; 1706 res = applyDefaultFallbackFontList(props, ReaderView.PROP_FALLBACK_FONT_FACES, "Droid Sans Fallback; Noto Sans CJK SC; Noto Sans Arabic UI; Noto Sans Devanagari UI; Roboto; FreeSans; FreeSerif; Noto Serif; Noto Sans; Arial Unicode MS") || res; 1707 return res; 1708 } 1709 upgradeSettings(Properties props)1710 private void upgradeSettings(Properties props) { 1711 String oldHyphenCode = props.getProperty("crengine.hyphenation.dictionary.code"); 1712 if (null != oldHyphenCode && oldHyphenCode.length() > 1) { 1713 String newHyphenValue = props.getProperty(ReaderView.PROP_HYPHENATION_DICT); 1714 if (null == newHyphenValue || newHyphenValue.length() == 0) { 1715 if ("RUSSIAN".equals(oldHyphenCode)) { 1716 newHyphenValue = "Russian_EnUS"; 1717 } else if ("ENGLISH".equals(oldHyphenCode)) { 1718 newHyphenValue = "English_US"; 1719 } else { 1720 newHyphenValue = oldHyphenCode.substring(0, 1); 1721 newHyphenValue = newHyphenValue + oldHyphenCode.substring(1).toLowerCase(); 1722 } 1723 props.applyDefault(ReaderView.PROP_HYPHENATION_DICT, newHyphenValue); 1724 props.remove("crengine.hyphenation.dictionary.code"); 1725 } 1726 } 1727 } 1728 loadSettings(BaseActivity activity, File file)1729 public Properties loadSettings(BaseActivity activity, File file) { 1730 Properties props = new Properties(); 1731 1732 if (file.exists() && !DEBUG_RESET_OPTIONS) { 1733 try { 1734 FileInputStream is = new FileInputStream(file); 1735 props.load(is); 1736 log.v("" + props.size() + " settings items loaded from file " + propsFile.getAbsolutePath()); 1737 } catch (Exception e) { 1738 log.e("error while reading settings"); 1739 } 1740 } 1741 1742 // default key actions 1743 boolean menuKeyActionFound = false; 1744 for (DefKeyAction ka : DEF_KEY_ACTIONS) { 1745 props.applyDefault(ka.getProp(), ka.action.id); 1746 if (ReaderAction.READER_MENU.id.equals(ka.action.id)) 1747 menuKeyActionFound = true; 1748 } 1749 if (DeviceInfo.NOOK_NAVIGATION_KEYS) { 1750 // Add default key mappings for Nook devices & also override defaults for some keys (PAGE_UP, PAGE_DOWN) 1751 for (DefKeyAction ka : DEF_NOOK_KEY_ACTIONS) { 1752 props.applyDefault(ka.getProp(), ka.action.id); 1753 } 1754 } 1755 1756 boolean menuTapActionFound = false; 1757 for (DefTapAction ka : DEF_TAP_ACTIONS) { 1758 String paramName = ka.longPress ? ReaderView.PROP_APP_TAP_ZONE_ACTIONS_TAP + ".long." + ka.zone : ReaderView.PROP_APP_TAP_ZONE_ACTIONS_TAP + "." + ka.zone; 1759 String value = props.getProperty(paramName); 1760 if (ReaderAction.READER_MENU.id.equals(value)) 1761 menuTapActionFound = true; 1762 } 1763 1764 // default tap zone actions 1765 for (DefTapAction ka : DEF_TAP_ACTIONS) { 1766 String paramName = ka.longPress ? ReaderView.PROP_APP_TAP_ZONE_ACTIONS_TAP + ".long." + ka.zone : ReaderView.PROP_APP_TAP_ZONE_ACTIONS_TAP + "." + ka.zone; 1767 1768 if (ka.zone == 5 && !activity.hasHardwareMenuKey() && !menuTapActionFound && !menuKeyActionFound) { 1769 // force assignment of central tap zone 1770 props.setProperty(paramName, ka.action.id); 1771 } else { 1772 props.applyDefault(paramName, ka.action.id); 1773 } 1774 } 1775 1776 if (DeviceInfo.EINK_NOOK) { 1777 props.applyDefault(ReaderView.PROP_PAGE_ANIMATION, ReaderView.PAGE_ANIMATION_NONE); 1778 } else { 1779 props.applyDefault(ReaderView.PROP_PAGE_ANIMATION, ReaderView.PAGE_ANIMATION_SLIDE2); 1780 } 1781 1782 props.applyDefault(ReaderView.PROP_APP_LOCALE, Lang.DEFAULT.code); 1783 1784 props.applyDefault(ReaderView.PROP_APP_THEME, DeviceInfo.FORCE_HC_THEME ? "HICONTRAST1" : "LIGHT"); 1785 props.applyDefault(ReaderView.PROP_APP_THEME_DAY, DeviceInfo.FORCE_HC_THEME ? "HICONTRAST1" : "LIGHT"); 1786 props.applyDefault(ReaderView.PROP_APP_THEME_NIGHT, DeviceInfo.FORCE_HC_THEME ? "HICONTRAST2" : "DARK"); 1787 props.applyDefault(ReaderView.PROP_APP_SELECTION_PERSIST, "0"); 1788 props.applyDefault(ReaderView.PROP_APP_SCREEN_BACKLIGHT_LOCK, "3"); 1789 if ("1".equals(props.getProperty(ReaderView.PROP_APP_SCREEN_BACKLIGHT_LOCK))) 1790 props.setProperty(ReaderView.PROP_APP_SCREEN_BACKLIGHT_LOCK, "3"); 1791 props.applyDefault(ReaderView.PROP_APP_MOTION_TIMEOUT, "0"); 1792 props.applyDefault(ReaderView.PROP_APP_BOOK_PROPERTY_SCAN_ENABLED, "1"); 1793 props.applyDefault(ReaderView.PROP_APP_KEY_BACKLIGHT_OFF, DeviceInfo.SAMSUNG_BUTTONS_HIGHLIGHT_PATCH ? "0" : "1"); 1794 props.applyDefault(ReaderView.PROP_LANDSCAPE_PAGES, DeviceInfo.ONE_COLUMN_IN_LANDSCAPE ? "0" : "1"); 1795 //props.applyDefault(ReaderView.PROP_TOOLBAR_APPEARANCE, "0"); 1796 // autodetect best initial font size based on display resolution 1797 int screenHeight = displayMetrics.heightPixels; 1798 int screenWidth = displayMetrics.widthPixels;//getWindowManager().getDefaultDisplay().getWidth(); 1799 if (screenWidth > screenHeight) 1800 screenWidth = screenHeight; 1801 int fontSize = 20; 1802 int statusFontSize = 16; 1803 String hmargin = "4"; 1804 String vmargin = "2"; 1805 if (screenWidth <= 320) { 1806 fontSize = 20; 1807 statusFontSize = 16; 1808 hmargin = "4"; 1809 vmargin = "2"; 1810 } else if (screenWidth <= 400) { 1811 fontSize = 24; 1812 statusFontSize = 20; 1813 hmargin = "10"; 1814 vmargin = "4"; 1815 } else if (screenWidth <= 600) { 1816 fontSize = 28; 1817 statusFontSize = 24; 1818 hmargin = "20"; 1819 vmargin = "8"; 1820 } else if (screenWidth <= 800) { 1821 fontSize = 32; 1822 statusFontSize = 28; 1823 hmargin = "25"; 1824 vmargin = "15"; 1825 } else { 1826 fontSize = 48; 1827 statusFontSize = 32; 1828 hmargin = "30"; 1829 vmargin = "20"; 1830 } 1831 if (DeviceInfo.DEF_FONT_SIZE != null) 1832 fontSize = DeviceInfo.DEF_FONT_SIZE; 1833 1834 int statusLocation = props.getInt(PROP_STATUS_LOCATION, VIEWER_STATUS_PAGE_HEADER); 1835 if (statusLocation == VIEWER_STATUS_BOTTOM || statusLocation == VIEWER_STATUS_TOP) 1836 statusLocation = VIEWER_STATUS_PAGE_HEADER; 1837 props.setInt(PROP_STATUS_LOCATION, statusLocation); 1838 1839 1840 fixFontSettings(props); 1841 upgradeSettings(props); 1842 props.applyDefault(ReaderView.PROP_FONT_SIZE, String.valueOf(fontSize)); 1843 props.applyDefault(ReaderView.PROP_FONT_HINTING, "2"); 1844 props.applyDefault(ReaderView.PROP_STATUS_FONT_SIZE, DeviceInfo.EINK_NOOK ? "15" : String.valueOf(statusFontSize)); 1845 props.applyDefault(ReaderView.PROP_FONT_COLOR, "#000000"); 1846 props.applyDefault(ReaderView.PROP_FONT_COLOR_DAY, "#000000"); 1847 props.applyDefault(ReaderView.PROP_FONT_COLOR_NIGHT, !DeviceInfo.EINK_SCREEN ? "#D0B070" : "#FFFFFF"); 1848 props.applyDefault(ReaderView.PROP_BACKGROUND_COLOR, "#FFFFFF"); 1849 props.applyDefault(ReaderView.PROP_BACKGROUND_COLOR_DAY, "#FFFFFF"); 1850 props.applyDefault(ReaderView.PROP_BACKGROUND_COLOR_NIGHT, !DeviceInfo.EINK_SCREEN ? "#101010" : "#000000"); 1851 props.applyDefault(ReaderView.PROP_STATUS_FONT_COLOR, "#FF000000"); // don't use separate color 1852 props.applyDefault(ReaderView.PROP_STATUS_FONT_COLOR_DAY, "#FF000000"); // don't use separate color 1853 props.applyDefault(ReaderView.PROP_STATUS_FONT_COLOR_NIGHT, "#80000000"); // don't use separate color 1854 props.setProperty(ReaderView.PROP_ROTATE_ANGLE, "0"); // crengine's rotation will not be user anymore 1855 props.setProperty(ReaderView.PROP_DISPLAY_INVERSE, "0"); 1856 props.applyDefault(ReaderView.PROP_APP_FULLSCREEN, "0"); 1857 props.applyDefault(ReaderView.PROP_APP_VIEW_AUTOSCROLL_SPEED, "1500"); 1858 props.applyDefault(ReaderView.PROP_APP_SCREEN_BACKLIGHT, "-1"); 1859 props.applyDefault(ReaderView.PROP_APP_SCREEN_WARM_BACKLIGHT, "-1"); 1860 props.applyDefault(ReaderView.PROP_SHOW_BATTERY, "1"); 1861 props.applyDefault(ReaderView.PROP_SHOW_POS_PERCENT, "0"); 1862 props.applyDefault(ReaderView.PROP_SHOW_PAGE_COUNT, "1"); 1863 props.applyDefault(ReaderView.PROP_FONT_KERNING_ENABLED, "0"); // by default disabled 1864 props.applyDefault(ReaderView.PROP_FONT_SHAPING, "1"); // by default 'Light (HarfBuzz without ligatures)' 1865 props.applyDefault(ReaderView.PROP_SHOW_TIME, "1"); 1866 props.applyDefault(ReaderView.PROP_FONT_ANTIALIASING, "2"); 1867 props.applyDefault(ReaderView.PROP_APP_GESTURE_PAGE_FLIPPING, "1"); 1868 props.applyDefault(ReaderView.PROP_APP_SHOW_COVERPAGES, "1"); 1869 props.applyDefault(ReaderView.PROP_APP_COVERPAGE_SIZE, "1"); 1870 props.applyDefault(ReaderView.PROP_APP_SCREEN_ORIENTATION, "0"); // "0" 1871 props.applyDefault(ReaderView.PROP_CONTROLS_ENABLE_VOLUME_KEYS, "1"); 1872 props.applyDefault(ReaderView.PROP_APP_TAP_ZONE_HILIGHT, "0"); 1873 props.applyDefault(ReaderView.PROP_APP_BOOK_SORT_ORDER, FileInfo.DEF_SORT_ORDER.name()); 1874 DictInfo dict = Dictionaries.defaultDictionary(); 1875 props.applyDefault(ReaderView.PROP_APP_DICTIONARY, (dict != null) ? dict.id : ""); 1876 props.applyDefault(ReaderView.PROP_APP_FILE_BROWSER_HIDE_EMPTY_FOLDERS, "0"); 1877 props.applyDefault(ReaderView.PROP_APP_FILE_BROWSER_HIDE_EMPTY_GENRES, "0"); 1878 props.applyDefault(ReaderView.PROP_APP_SELECTION_ACTION, "0"); 1879 props.applyDefault(ReaderView.PROP_APP_MULTI_SELECTION_ACTION, "0"); 1880 1881 props.setProperty(ReaderView.PROP_RENDER_DPI, Integer.valueOf((int)(96*mActivity.getDensityFactor())).toString()); 1882 1883 props.applyDefault(ReaderView.PROP_IMG_SCALING_ZOOMOUT_BLOCK_MODE, "1"); 1884 props.applyDefault(ReaderView.PROP_IMG_SCALING_ZOOMIN_BLOCK_MODE, "1"); 1885 props.applyDefault(ReaderView.PROP_IMG_SCALING_ZOOMOUT_INLINE_MODE, "1"); 1886 props.applyDefault(ReaderView.PROP_IMG_SCALING_ZOOMIN_INLINE_MODE, "1"); 1887 props.applyDefault(ReaderView.PROP_IMG_SCALING_ZOOMOUT_BLOCK_SCALE, "0"); 1888 props.applyDefault(ReaderView.PROP_IMG_SCALING_ZOOMIN_BLOCK_SCALE, "0"); 1889 props.applyDefault(ReaderView.PROP_IMG_SCALING_ZOOMOUT_INLINE_SCALE, "0"); 1890 props.applyDefault(ReaderView.PROP_IMG_SCALING_ZOOMIN_INLINE_SCALE, "0"); 1891 1892 props.applyDefault(ReaderView.PROP_PAGE_MARGIN_LEFT, hmargin); 1893 props.applyDefault(ReaderView.PROP_PAGE_MARGIN_RIGHT, hmargin); 1894 props.applyDefault(ReaderView.PROP_PAGE_MARGIN_TOP, vmargin); 1895 props.applyDefault(ReaderView.PROP_PAGE_MARGIN_BOTTOM, vmargin); 1896 props.applyDefault(ReaderView.PROP_ROUNDED_CORNERS_MARGIN, "0"); 1897 1898 if (DeviceInfo.EINK_SCREEN_REGAL) 1899 props.applyDefault(ReaderView.PROP_APP_SCREEN_UPDATE_MODE, String.valueOf(EinkScreen.EinkUpdateMode.Regal.code)); 1900 else 1901 props.applyDefault(ReaderView.PROP_APP_SCREEN_UPDATE_MODE, String.valueOf(EinkScreen.EinkUpdateMode.Clear.code)); 1902 props.applyDefault(ReaderView.PROP_APP_SCREEN_UPDATE_INTERVAL, "10"); 1903 1904 props.applyDefault(ReaderView.PROP_NIGHT_MODE, "0"); 1905 if (DeviceInfo.FORCE_HC_THEME) { 1906 props.applyDefault(ReaderView.PROP_PAGE_BACKGROUND_IMAGE, Engine.NO_TEXTURE.id); 1907 } else { 1908 if (props.getBool(ReaderView.PROP_NIGHT_MODE, false)) 1909 props.applyDefault(ReaderView.PROP_PAGE_BACKGROUND_IMAGE, Engine.DEF_NIGHT_BACKGROUND_TEXTURE); 1910 else 1911 props.applyDefault(ReaderView.PROP_PAGE_BACKGROUND_IMAGE, Engine.DEF_DAY_BACKGROUND_TEXTURE); 1912 } 1913 props.applyDefault(ReaderView.PROP_PAGE_BACKGROUND_IMAGE_DAY, !DeviceInfo.EINK_SCREEN ? Engine.DEF_DAY_BACKGROUND_TEXTURE : Engine.NO_TEXTURE.id); 1914 props.applyDefault(ReaderView.PROP_PAGE_BACKGROUND_IMAGE_NIGHT, !DeviceInfo.EINK_SCREEN ? Engine.DEF_NIGHT_BACKGROUND_TEXTURE : Engine.NO_TEXTURE.id); 1915 1916 props.applyDefault(ReaderView.PROP_FONT_GAMMA, DeviceInfo.EINK_SCREEN ? "1.5" : "1.0"); 1917 1918 props.setProperty(ReaderView.PROP_MIN_FILE_SIZE_TO_CACHE, "100000"); 1919 props.setProperty(ReaderView.PROP_FORCED_MIN_FILE_SIZE_TO_CACHE, "32768"); 1920 props.applyDefault(ReaderView.PROP_HYPHENATION_DICT, Engine.HyphDict.RUSSIAN.toString()); 1921 props.applyDefault(ReaderView.PROP_APP_FILE_BROWSER_SIMPLE_MODE, "0"); 1922 1923 props.applyDefault(ReaderView.PROP_TEXTLANG_EMBEDDED_LANGS_ENABLED, "0"); 1924 props.applyDefault(ReaderView.PROP_TEXTLANG_HYPHENATION_ENABLED, "1"); 1925 props.applyDefault(ReaderView.PROP_TEXTLANG_HYPH_SOFT_HYPHENS_ONLY, "0"); 1926 props.applyDefault(ReaderView.PROP_TEXTLANG_HYPH_FORCE_ALGORITHMIC, "0"); 1927 1928 props.applyDefault(ReaderView.PROP_STATUS_LOCATION, !DeviceInfo.EINK_SCREEN ? Settings.VIEWER_STATUS_PAGE_HEADER : Settings.VIEWER_STATUS_PAGE_FOOTER); 1929 //props.applyDefault(ReaderView.PROP_TOOLBAR_LOCATION, DeviceInfo.getSDKLevel() < DeviceInfo.HONEYCOMB ? Settings.VIEWER_TOOLBAR_NONE : Settings.VIEWER_TOOLBAR_SHORT_SIDE); 1930 props.applyDefault(ReaderView.PROP_TOOLBAR_LOCATION, Settings.VIEWER_TOOLBAR_NONE); 1931 props.applyDefault(ReaderView.PROP_TOOLBAR_HIDE_IN_FULLSCREEN, "0"); 1932 1933 props.applyDefault(ReaderView.PROP_APP_CLOUDSYNC_GOOGLEDRIVE_ENABLED, "0"); 1934 props.applyDefault(ReaderView.PROP_APP_CLOUDSYNC_GOOGLEDRIVE_SETTINGS, "0"); 1935 props.applyDefault(ReaderView.PROP_APP_CLOUDSYNC_GOOGLEDRIVE_BOOKMARKS, "0"); 1936 props.applyDefault(ReaderView.PROP_APP_CLOUDSYNC_GOOGLEDRIVE_CURRENTBOOK_INFO, "0"); 1937 props.applyDefault(ReaderView.PROP_APP_CLOUDSYNC_GOOGLEDRIVE_CURRENTBOOK_BODY, "0"); 1938 props.applyDefault(ReaderView.PROP_APP_CLOUDSYNC_GOOGLEDRIVE_AUTOSAVEPERIOD, "5"); // 5 min. 1939 props.applyDefault(ReaderView.PROP_APP_CLOUDSYNC_CONFIRMATIONS, "1"); 1940 props.applyDefault(ReaderView.PROP_APP_CLOUDSYNC_DATA_KEEPALIVE, "14"); // 2 weeks 1941 1942 if (!DeviceInfo.EINK_SCREEN) { 1943 props.applyDefault(ReaderView.PROP_APP_HIGHLIGHT_BOOKMARKS, "1"); 1944 props.applyDefault(ReaderView.PROP_HIGHLIGHT_SELECTION_COLOR, "#AAAAAA"); 1945 props.applyDefault(ReaderView.PROP_HIGHLIGHT_BOOKMARK_COLOR_COMMENT, "#AAAA55"); 1946 props.applyDefault(ReaderView.PROP_HIGHLIGHT_BOOKMARK_COLOR_CORRECTION, "#C07070"); 1947 props.applyDefault(ReaderView.PROP_HIGHLIGHT_SELECTION_COLOR_DAY, "#AAAAAA"); 1948 props.applyDefault(ReaderView.PROP_HIGHLIGHT_BOOKMARK_COLOR_COMMENT_DAY, "#AAAA55"); 1949 props.applyDefault(ReaderView.PROP_HIGHLIGHT_BOOKMARK_COLOR_CORRECTION_DAY, "#C07070"); 1950 props.applyDefault(ReaderView.PROP_HIGHLIGHT_SELECTION_COLOR_NIGHT, "#808080"); 1951 props.applyDefault(ReaderView.PROP_HIGHLIGHT_BOOKMARK_COLOR_COMMENT_NIGHT, "#A09060"); 1952 props.applyDefault(ReaderView.PROP_HIGHLIGHT_BOOKMARK_COLOR_CORRECTION_NIGHT, "#906060"); 1953 } else { 1954 props.applyDefault(ReaderView.PROP_APP_HIGHLIGHT_BOOKMARKS, "2"); 1955 props.applyDefault(ReaderView.PROP_HIGHLIGHT_SELECTION_COLOR, "#808080"); 1956 props.applyDefault(ReaderView.PROP_HIGHLIGHT_BOOKMARK_COLOR_COMMENT, "#000000"); 1957 props.applyDefault(ReaderView.PROP_HIGHLIGHT_BOOKMARK_COLOR_CORRECTION, "#000000"); 1958 } 1959 1960 return props; 1961 } 1962 getSettingsFile(int profile)1963 public File getSettingsFile(int profile) { 1964 if (profile == 0) 1965 return propsFile; 1966 return new File(propsFile.getAbsolutePath() + ".profile" + profile); 1967 } 1968 1969 File propsFile; 1970 private static final String SETTINGS_FILE_NAME = "cr3.ini"; 1971 private static boolean DEBUG_RESET_OPTIONS = false; 1972 loadSettings()1973 private Properties loadSettings() { 1974 File[] dataDirs = Engine.getDataDirectories(null, false, true); 1975 File existingFile = null; 1976 for (File dir : dataDirs) { 1977 File f = new File(dir, SETTINGS_FILE_NAME); 1978 if (f.exists() && f.isFile()) { 1979 existingFile = f; 1980 break; 1981 } 1982 } 1983 if (existingFile != null) { 1984 propsFile = existingFile; 1985 } else { 1986 File propsDir = defaultSettingsDir; 1987 propsFile = new File(propsDir, SETTINGS_FILE_NAME); 1988 File dataDir = Engine.getExternalSettingsDir(); 1989 if (dataDir != null) { 1990 log.d("external settings dir: " + dataDir); 1991 propsFile = Engine.checkOrMoveFile(dataDir, propsDir, SETTINGS_FILE_NAME); 1992 } else { 1993 propsDir.mkdirs(); 1994 } 1995 } 1996 1997 Properties props = loadSettings(mActivity, propsFile); 1998 1999 return props; 2000 } 2001 loadSettings(int profile)2002 public Properties loadSettings(int profile) { 2003 File f = getSettingsFile(profile); 2004 if (!f.exists() && profile != 0) 2005 f = getSettingsFile(0); 2006 Properties res = loadSettings(mActivity, f); 2007 if (profile != 0) { 2008 res = filterProfileSettings(res); 2009 res.setInt(Settings.PROP_PROFILE_NUMBER, profile); 2010 } 2011 return res; 2012 } 2013 filterProfileSettings(Properties settings)2014 public static Properties filterProfileSettings(Properties settings) { 2015 Properties res = new Properties(); 2016 res.entrySet(); 2017 for (Object k : settings.keySet()) { 2018 String key = (String) k; 2019 String value = settings.getProperty(key); 2020 boolean found = false; 2021 for (String pattern : Settings.PROFILE_SETTINGS) { 2022 if (pattern.endsWith("*")) { 2023 if (key.startsWith(pattern.substring(0, pattern.length() - 1))) { 2024 found = true; 2025 break; 2026 } 2027 } else if (pattern.equalsIgnoreCase(key)) { 2028 found = true; 2029 break; 2030 } else if (key.startsWith("styles.")) { 2031 found = true; 2032 break; 2033 } 2034 } 2035 if (found) { 2036 res.setProperty(key, value); 2037 } 2038 } 2039 return res; 2040 } 2041 saveSettings(int profile, Properties settings)2042 public void saveSettings(int profile, Properties settings) { 2043 if (settings == null) 2044 settings = mSettings; 2045 File f = getSettingsFile(profile); 2046 if (profile != 0) { 2047 settings = filterProfileSettings(settings); 2048 settings.setInt(Settings.PROP_PROFILE_NUMBER, profile); 2049 } 2050 saveSettings(f, settings); 2051 } 2052 2053 DelayedExecutor saveSettingsTask = DelayedExecutor.createBackground("saveSettings"); 2054 saveSettings(File f, Properties settings)2055 public void saveSettings(File f, Properties settings) { 2056 try { 2057 log.v("saveSettings()"); 2058 FileOutputStream os = new FileOutputStream(f); 2059 settings.store(os, "Cool Reader 3 settings"); 2060 log.i("Settings successfully saved to file " + f.getAbsolutePath()); 2061 } catch (Exception e) { 2062 log.e("exception while saving settings", e); 2063 } 2064 } 2065 saveSettings(Properties settings)2066 public void saveSettings(Properties settings) { 2067 saveSettings(propsFile, settings); 2068 } 2069 2070 getSetting(String name)2071 public String getSetting(String name) { 2072 return mSettings.getProperty(name); 2073 } 2074 getSetting(String name, String defaultValue)2075 public String getSetting(String name, String defaultValue) { 2076 return mSettings.getProperty(name, defaultValue); 2077 } 2078 getBool(String name, boolean defaultValue)2079 public boolean getBool(String name, boolean defaultValue) { 2080 return mSettings.getBool(name, defaultValue); 2081 } 2082 getInt(String name, int defaultValue)2083 public int getInt(String name, int defaultValue) { 2084 return mSettings.getInt(name, defaultValue); 2085 } 2086 get()2087 public Properties get() { 2088 return new Properties(mSettings); 2089 } 2090 2091 } 2092 2093 getDictList()2094 public static DictInfo[] getDictList() { 2095 return Dictionaries.getDictList(); 2096 } 2097 isPackageInstalled(String packageName)2098 public boolean isPackageInstalled(String packageName) { 2099 PackageManager pm = getPackageManager(); 2100 try { 2101 pm.getPackageInfo(packageName, 0); //PackageManager.GET_ACTIVITIES); 2102 return true; 2103 } catch (PackageManager.NameNotFoundException e) { 2104 return false; 2105 } 2106 } 2107 2108 private Boolean hasHardwareMenuKey = null; 2109 hasHardwareMenuKey()2110 public boolean hasHardwareMenuKey() { 2111 if (hasHardwareMenuKey == null) { 2112 ViewConfiguration vc = ViewConfiguration.get(this); 2113 if (DeviceInfo.getSDKLevel() >= 14) { 2114 //boolean vc.hasPermanentMenuKey(); 2115 try { 2116 Method m = vc.getClass().getMethod("hasPermanentMenuKey", new Class<?>[]{}); 2117 try { 2118 hasHardwareMenuKey = (Boolean) m.invoke(vc, new Object[]{}); 2119 } catch (IllegalArgumentException e) { 2120 hasHardwareMenuKey = false; 2121 } catch (IllegalAccessException e) { 2122 hasHardwareMenuKey = false; 2123 } catch (InvocationTargetException e) { 2124 hasHardwareMenuKey = false; 2125 } 2126 } catch (NoSuchMethodException e) { 2127 hasHardwareMenuKey = false; 2128 } 2129 } 2130 if (hasHardwareMenuKey == null) { 2131 if (DeviceInfo.EINK_SCREEN) 2132 hasHardwareMenuKey = false; 2133 else if (DeviceInfo.getSDKLevel() < DeviceInfo.ICE_CREAM_SANDWICH) 2134 hasHardwareMenuKey = true; 2135 else 2136 hasHardwareMenuKey = false; 2137 } 2138 } 2139 return hasHardwareMenuKey; 2140 } 2141 } 2142